Я не знаю ни одного редактора графического интерфейса, но я использую шаблон T4, чтобы сгенерировать стартер для каждого класса метаданных.Я получил идею из сообщения от Дана Уолина .Вот шаблон, который я использую:
<#
// Created by Dan Wahlin - http://www.thewahlingroup.com
// Title: T4 Metadata and Data Annotations template
// Usage: Generates the initial "buddy" classes to handle data validation across multiple frameworks
// Description: I got tired of writing my initial "buddy" classes by hand once my EF model was created. This template
// handles generating all the classes and generates the primitive and navigation properties. It also
// decorates the properties with the [Required] and [StringLength] attributes as appropriate. Once the
// template generates the code you can copy it to a new file and make all the tweaks you want to support
// custom data annotations.
// License: UIFAYW - Use it for anything you want (free or commercial)
// Made enhancements? Please let me know at dwahlin at xmlforasp dot net
// To use:
// 1. Add the file into your project (it needs a .tt extension)
// 2. Change the Source CsdlPath to point to your Entity Framework 4 .edmx file
// 3. Whatever you name the .tt file will also be the name given to the code file it generates
// 4. Don't make changes directly to the modified code. You'll need to copy it to a file
// you control or else the template may overwrite changes you make.
#>
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".cs"#><#
UserSettings userSettings =
new UserSettings
{
SourceCsdlPath = @"YourModel.edmx",
ReferenceCsdlPaths = new string[] {},
FullyQualifySystemTypes = true,
CreateContextAddToMethods = true,
CamelCaseFields = false,
};
ApplyUserSettings(userSettings);
if(Errors.HasErrors)
{
return String.Empty;
}
MetadataLoader loader = new MetadataLoader(this);
MetadataTools ef = new MetadataTools(this);
CodeRegion region = new CodeRegion(this);
CodeGenerationTools code = new CodeGenerationTools(this){FullyQualifySystemTypes = userSettings.FullyQualifySystemTypes, CamelCaseFields = userSettings.CamelCaseFields};
ItemCollection = loader.CreateEdmItemCollection(SourceCsdlPath, ReferenceCsdlPaths.ToArray());
ModelNamespace = loader.GetModelNamespace(SourceCsdlPath);
string namespaceName = code.VsNamespaceSuggestion();
UpdateObjectNamespaceMap(namespaceName);
#>
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data.Objects.DataClasses;
/*
namespace <#=namespaceName#>
{
<#
foreach (EntityType entity in GetSourceSchemaTypes<EntityType>().OrderBy(e => e.Name))
{
#>
[MetadataType(typeof(<#=code.Escape(entity)#>Metadata))]
<#=Accessibility.ForType(entity)#> <#=code.SpaceAfter(code.AbstractOption(entity))#>partial class <#=code.Escape(entity)#>
{
internal sealed class <#=code.Escape(entity)#>Metadata
{
<#
bool first = true;
foreach (EdmProperty property in entity.Properties.Where(p => p.DeclaringType == entity && p.TypeUsage.EdmType is PrimitiveType))
{
if (first)
{
WriteLine(string.Empty);
first = false;
}
WritePrimitiveTypeProperty(property, code);
}
foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(n => n.DeclaringType == entity))
{
WriteNavigationTypeProperty(navProperty, code);
}
#>
}
}
<#
}
WriteLine(string.Empty);
#>
}
*/
<#+
////////
//////// Write PrimitiveType Properties.
////////
private void WritePrimitiveTypeProperty(EdmProperty property, CodeGenerationTools code)
{
MetadataTools ef = new MetadataTools(this);
var dataTypeAtt = GetDataTypeAttribute(property, ef);
if (!property.Nullable)
{
#>
[Required(ErrorMessage="<#=FixName(code.Escape(property))#> is required")]
<#+
}
foreach (Facet facet in property.TypeUsage.Facets)
{
if (facet.Name == "MaxLength" && facet.Value != null && facet.IsUnbounded == false)
{
#>
[StringLength(<#=facet.Value#>)]
<#+
}
}
if (dataTypeAtt != String.Empty)
{
#>
<#=dataTypeAtt#>
<#+
}
#>
<#=code.SpaceAfter(NewModifier(property))#><#=Accessibility.ForProperty(property)#> <#=ef.ClrType(property.TypeUsage).Name#> <#=code.Escape(property)#> { get; set; }
<#+
}
////////
//////// Write PrimitiveType Properties.
////////
private void WriteNavigationTypeProperty(NavigationProperty navProperty, CodeGenerationTools code)
{
#>
<#=code.SpaceAfter(NewModifier(navProperty))#><#=Accessibility.ForProperty(navProperty)#> EntityCollection<<#=MultiSchemaEscape(navProperty.ToEndMember.GetEntityType(), code)#>> <#=code.Escape(navProperty)#> { get; set; }
<#+
}
public string FixName(string propName)
{
if (propName.ToLower() != "id" && propName.ToLower().EndsWith("id"))
{
propName = propName.Replace("Id","").Replace("ID","");
}
return Regex.Replace(propName,"([A-Z])"," $1",RegexOptions.Compiled).Trim();
}
public string GetDataTypeAttribute(EdmProperty property, MetadataTools ef)
{
var propLower = property.Name.ToLower();
if (ef.ClrType(property.TypeUsage) == typeof(System.DateTime))
{
return "[DataType(DataType.DateTime)]";
}
if (propLower.Contains("phone"))
{
return "[DataType(DataType.PhoneNumber)]";
}
if (propLower.Contains("html"))
{
return "[DataType(DataType.Html)]";
}
if (propLower.Contains("email"))
{
return "[DataType(DataType.EmailAddress)]";
}
if (propLower.Contains("url"))
{
return "[DataType(DataType.Url)]";
}
return String.Empty;
}
////////
//////// Declare Template Public Properties.
////////
public string SourceCsdlPath{ get; set; }
public string ModelNamespace{ get; set; }
public EdmItemCollection ItemCollection{ get; set; }
public IEnumerable<string> ReferenceCsdlPaths{ get; set; }
public Nullable<bool> CreateContextAddToMethods{ get; set; }
public Dictionary<string, string> EdmToObjectNamespaceMap
{
get { return _edmToObjectNamespaceMap; }
set { _edmToObjectNamespaceMap = value; }
}
public Dictionary<string, string> _edmToObjectNamespaceMap = new Dictionary<string, string>();
public Double SourceEdmVersion
{
get
{
if (ItemCollection != null)
{
return ItemCollection.EdmVersion;
}
return 0.0;
}
}
////////
//////// Declare Template Private Properties.
////////
static System.Resources.ResourceManager ResourceManager
{
get
{
if (_resourceManager == null)
{
System.Resources.ResourceManager resourceManager = new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(System.Data.Entity.Design.MetadataItemCollectionFactory).Assembly);
System.Threading.Interlocked.CompareExchange(ref _resourceManager, resourceManager, null);
}
return _resourceManager;
}
}
static System.Resources.ResourceManager _resourceManager;
#>
<#+
private static string GetResourceString(string resourceName)
{
return ResourceManager.GetString(resourceName,
null); // Take default culture.
}
private void VerifyTypeUniqueness()
{
HashSet<string> hash = new HashSet<string>();
IEnumerable<GlobalItem> allTypes = GetSourceSchemaTypes<GlobalItem>().Where(i => i is StructuralType || i is EntityContainer);
foreach (GlobalItem type in allTypes)
{
if (!hash.Add(GetGlobalItemName(type)))
{
// 6034 is the error number used by System.Data.Entity.Design EntityClassGenerator.
Errors.Add(new System.CodeDom.Compiler.CompilerError(SourceCsdlPath, -1, -1, "6034",
String.Format(CultureInfo.CurrentCulture,
GetResourceString("Template_DuplicateTopLevelType"),
GetGlobalItemName(type))));
}
}
}
protected string GetGlobalItemName(GlobalItem item)
{
if (item is EdmType)
{
// EntityType or ComplexType.
return ((EdmType)item).Name;
}
else
{
// Must be an EntityContainer.
return ((EntityContainer)item).Name;
}
}
void ApplyUserSettings(UserSettings userSettings)
{
// Setup template UserSettings.
if (SourceCsdlPath == null)
{
#if !PREPROCESSED_TEMPLATE
if(userSettings.SourceCsdlPath == "$" + "edmxInputFile" + "$")
{
Errors.Add(new System.CodeDom.Compiler.CompilerError(Host.TemplateFile, 0, 0, "",
GetResourceString("Template_ReplaceVsItemTemplateToken")));
return;
}
SourceCsdlPath = Host.ResolvePath(userSettings.SourceCsdlPath);
#else
SourceCsdlPath = userSettings.SourceCsdlPath;
#endif
}
// normalize the path, remove ..\ from it
SourceCsdlPath = Path.GetFullPath(SourceCsdlPath);
if (ReferenceCsdlPaths == null)
{
ReferenceCsdlPaths = userSettings.ReferenceCsdlPaths;
}
if (!CreateContextAddToMethods.HasValue)
{
CreateContextAddToMethods = userSettings.CreateContextAddToMethods;
}
DefaultSummaryComment = GetResourceString("Template_CommentNoDocumentation");
}
class UserSettings
{
public string SourceCsdlPath{ get; set; }
public string[] ReferenceCsdlPaths{ get; set; }
public bool FullyQualifySystemTypes{ get; set; }
public bool CreateContextAddToMethods{ get; set; }
public bool CamelCaseFields{ get; set; }
}
string MultiSchemaEscape(TypeUsage usage, CodeGenerationTools code)
{
StructuralType structural = usage.EdmType as StructuralType;
if (structural != null)
{
return MultiSchemaEscape(structural, code);
}
return code.Escape(usage);
}
string MultiSchemaEscape(StructuralType type, CodeGenerationTools code)
{
if (type.NamespaceName != ModelNamespace)
{
return code.CreateFullName(code.EscapeNamespace(GetObjectNamespace(type.NamespaceName)), code.Escape(type));
}
return code.Escape(type);
}
string NewModifier(NavigationProperty navigationProperty)
{
Type baseType = typeof(EntityObject);
return NewModifier(baseType, navigationProperty.Name);
}
string NewModifier(EdmFunction edmFunction)
{
Type baseType = typeof(ObjectContext);
return NewModifier(baseType, edmFunction.Name);
}
string NewModifier(EntitySet set)
{
Type baseType = typeof(ObjectContext);
return NewModifier(baseType, set.Name);
}
string NewModifier(EdmProperty property)
{
Type baseType;
if (property.DeclaringType.BuiltInTypeKind == BuiltInTypeKind.EntityType)
{
baseType = typeof(EntityObject);
}
else
{
baseType = typeof(ComplexObject);
}
return NewModifier(baseType, property.Name);
}
string NewModifier(Type type, string memberName)
{
if (HasBaseMemberWithMatchingName(type, memberName))
{
return "new";
}
return string.Empty;
}
static bool HasBaseMemberWithMatchingName(Type type, string memberName)
{
BindingFlags bindingFlags = BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public
| BindingFlags.Instance | BindingFlags.Static;
return type.GetMembers(bindingFlags).Where(m => IsVisibleMember(m)).Any(m => m.Name == memberName);
}
string ChangingMethodName(EdmMember member)
{
return string.Format(CultureInfo.InvariantCulture, "On{0}Changing", member.Name);
}
string ChangedMethodName(EdmMember member)
{
return string.Format(CultureInfo.InvariantCulture, "On{0}Changed", member.Name);
}
string InitializedTrackingField(EdmProperty property, CodeGenerationTools code)
{
string namePart = property.Name + "Initialized";
if (code.CamelCaseFields)
{
namePart = code.CamelCase(namePart);
}
return "_" + namePart;
}
string OptionalNullableParameterForSetValidValue(EdmMember member, CodeGenerationTools code)
{
MetadataTools ef = new MetadataTools(this);
string list = string.Empty;
if (((PrimitiveType)member.TypeUsage.EdmType).ClrEquivalentType.IsClass)
{
MetadataProperty storeGeneratedPatternProperty = null;
bool isNullable = ef.IsNullable(member.TypeUsage) ||
(member.MetadataProperties.TryGetValue(MetadataConstants.EDM_ANNOTATION_09_02 + ":StoreGeneratedPattern", false, out storeGeneratedPatternProperty) &&
Object.Equals(storeGeneratedPatternProperty.Value, "Computed"));
list += ", " + code.CreateLiteral(isNullable);
}
return list;
}
static bool IsVisibleMember(MemberInfo memberInfo)
{
if (memberInfo is EventInfo)
{
EventInfo ei = (EventInfo)memberInfo;
MethodInfo add = ei.GetAddMethod();
MethodInfo remove = ei.GetRemoveMethod();
return IsVisibleMethod(add) || IsVisibleMethod(remove);
}
else if (memberInfo is FieldInfo)
{
FieldInfo fi = (FieldInfo)memberInfo;
return !fi.IsPrivate && !fi.IsAssembly;
}
else if (memberInfo is MethodBase)
{
MethodBase mb = (MethodBase)memberInfo;
if (mb.IsSpecialName)
return false;
return IsVisibleMethod(mb);
}
else if (memberInfo is PropertyInfo)
{
PropertyInfo pi = (PropertyInfo)memberInfo;
MethodInfo get = pi.GetGetMethod();
MethodInfo set = pi.GetSetMethod();
return IsVisibleMethod(get) || IsVisibleMethod(set);
}
return false;
}
static bool IsVisibleMethod(MethodBase methodBase)
{
if (methodBase == null)
return false;
return !methodBase.IsPrivate && !methodBase.IsAssembly;
}
IEnumerable<T> GetSourceSchemaTypes<T>() where T : GlobalItem
{
if (Path.GetExtension(SourceCsdlPath) != ".edmx")
{
return ItemCollection.GetItems<T>().Where(e => e.MetadataProperties.Any(mp => mp.Name == "SchemaSource" && (string)mp.Value == SourceCsdlPath));
}
else
{
return ItemCollection.GetItems<T>();
}
}
string EndName(AssociationType association, int index)
{
return association.AssociationEndMembers[index].Name;
}
string EndMultiplicity(AssociationType association, int index, CodeGenerationTools code)
{
return code.CreateLiteral(association.AssociationEndMembers[index].RelationshipMultiplicity);
}
string EscapeEndTypeName(AssociationType association, int index, CodeGenerationTools code)
{
EntityType entity = association.AssociationEndMembers[index].GetEntityType();
return code.CreateFullName(code.EscapeNamespace(GetObjectNamespace(entity.NamespaceName)), code.Escape(entity));
}
string GetObjectNamespace(string csdlNamespaceName)
{
string objectNamespace;
if (EdmToObjectNamespaceMap.TryGetValue(csdlNamespaceName, out objectNamespace))
{
return objectNamespace;
}
return csdlNamespaceName;
}
void UpdateObjectNamespaceMap(string objectNamespace)
{
if(objectNamespace != ModelNamespace && !EdmToObjectNamespaceMap.ContainsKey(ModelNamespace))
{
EdmToObjectNamespaceMap.Add(ModelNamespace, objectNamespace);
}
}
static string FixParameterName(string name, CodeGenerationTools code)
{
// Change any property that is 'id' (case insensitive) to 'ID'
// since 'iD' is a violation of FxCop rules.
if (StringComparer.OrdinalIgnoreCase.Equals(name, "id"))
{
// Return all lower case since it is an abbreviation, not an acronym.
return "id";
}
return code.CamelCase(name);
}
string BaseTypeName(EntityType entity, CodeGenerationTools code)
{
return entity.BaseType == null ? "EntityObject" : MultiSchemaEscape((StructuralType)entity.BaseType, code);
}
bool IncludePropertyInFactoryMethod(StructuralType factoryType, EdmProperty edmProperty)
{
if (edmProperty.Nullable)
{
return false;
}
if (edmProperty.DefaultValue != null)
{
return false;
}
if ((Accessibility.ForReadOnlyProperty(edmProperty) != "public" && Accessibility.ForWriteOnlyProperty(edmProperty) != "public") ||
(factoryType != edmProperty.DeclaringType && Accessibility.ForWriteOnlyProperty(edmProperty) == "private")
)
{
// There is no public part to the property.
return false;
}
return true;
}
class FactoryMethodParameter
{
public EdmProperty Source;
public string RawParameterName;
public string ParameterName;
public string ParameterType;
public string ParameterComment;
public bool IsComplexType;
public static IEnumerable<FactoryMethodParameter> CreateParameters(IEnumerable<EdmProperty> properties, UniqueIdentifierService unique, Func<TypeUsage, CodeGenerationTools, string> multiSchemaEscape, CodeGenerationTools code)
{
List<FactoryMethodParameter> parameters = new List<FactoryMethodParameter>();
foreach (EdmProperty property in properties)
{
FactoryMethodParameter parameter = new FactoryMethodParameter();
parameter.Source = property;
parameter.IsComplexType = property.TypeUsage.EdmType is ComplexType;
parameter.RawParameterName = unique.AdjustIdentifier(FixParameterName(property.Name, code));
parameter.ParameterName = code.Escape(parameter.RawParameterName);
parameter.ParameterType = multiSchemaEscape(property.TypeUsage, code);
parameter.ParameterComment = String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CommentFactoryMethodParam"), property.Name);
parameters.Add(parameter);
}
return parameters;
}
}
string DefaultSummaryComment{ get; set; }
string SummaryComment(MetadataItem item)
{
if (item.Documentation != null && item.Documentation.Summary != null)
{
return PrefixLinesOfMultilineComment(XMLCOMMENT_START + " ", XmlEntityize(item.Documentation.Summary));
}
if (DefaultSummaryComment != null)
{
return DefaultSummaryComment;
}
return string.Empty;
}
string LongDescriptionCommentElement(MetadataItem item, int indentLevel)
{
if (item.Documentation != null && !String.IsNullOrEmpty(item.Documentation.LongDescription))
{
string comment = Environment.NewLine;
string lineStart = CodeRegion.GetIndent(indentLevel) + XMLCOMMENT_START + " ";
comment += lineStart + "<LongDescription>" + Environment.NewLine;
comment += lineStart + PrefixLinesOfMultilineComment(lineStart, XmlEntityize(item.Documentation.LongDescription)) + Environment.NewLine;
comment += lineStart + "</LongDescription>";
return comment;
}
return string.Empty;
}
string PrefixLinesOfMultilineComment(string prefix, string comment)
{
return comment.Replace(Environment.NewLine, Environment.NewLine + prefix);
}
string ParameterComments(IEnumerable<Tuple<string, string>> parameters, int indentLevel)
{
System.Text.StringBuilder builder = new System.Text.StringBuilder();
foreach (Tuple<string, string> parameter in parameters)
{
builder.AppendLine();
builder.Append(CodeRegion.GetIndent(indentLevel));
builder.Append(XMLCOMMENT_START);
builder.Append(String.Format(CultureInfo.InvariantCulture, " <param name=\"{0}\">{1}</param>", parameter.Item1, parameter.Item2));
}
return builder.ToString();
}
string XmlEntityize(string text)
{
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}
text = text.Replace("&","&");
text = text.Replace("<","<").Replace(">",">");
string id = Guid.NewGuid().ToString();
text = text.Replace(Environment.NewLine, id);
text = text.Replace("\r", "
").Replace("\n","
");
text = text.Replace(id, Environment.NewLine);
return text.Replace("\'","'").Replace("\"",""");
}
const string XMLCOMMENT_START = "///";
IEnumerable<EdmProperty> GetProperties(StructuralType type)
{
if (type.BuiltInTypeKind == BuiltInTypeKind.EntityType)
{
return ((EntityType)type).Properties;
}
else
{
return ((ComplexType)type).Properties;
}
}
protected void VerifyGetterAndSetterAccessibilityCompatability(EdmMember member)
{
string rawGetterAccessibility = Accessibility.ForReadOnlyProperty(member);
string rawSetterAccessibility = Accessibility.ForWriteOnlyProperty(member);
if ((rawGetterAccessibility == "internal" && rawSetterAccessibility == "protected") ||
(rawGetterAccessibility == "protected" && rawSetterAccessibility == "internal"))
{
Errors.Add(new System.CodeDom.Compiler.CompilerError(SourceCsdlPath, -1, -1, "6033", String.Format(CultureInfo.CurrentCulture,
GetResourceString("GeneratedPropertyAccessibilityConflict"),
member.Name, rawGetterAccessibility, rawSetterAccessibility)));
}
}
private void VerifyEntityTypeAndSetAccessibilityCompatability(EntitySet set)
{
string typeAccess = Accessibility.ForType(set.ElementType);
string setAccess = Accessibility.ForReadOnlyProperty(set);
if(typeAccess == "internal" && (setAccess == "public" || setAccess == "protected"))
{
Errors.Add(new System.CodeDom.Compiler.CompilerError(SourceCsdlPath, -1, -1, "6036", String.Format(CultureInfo.CurrentCulture,
GetResourceString("EntityTypeAndSetAccessibilityConflict"),
set.ElementType.Name, typeAccess, set.Name, setAccess)));
}
}
////////
//////// UniqueIdentifierService
////////
sealed class UniqueIdentifierService
{
private readonly HashSet<string> _knownIdentifiers;
public UniqueIdentifierService()
{
_knownIdentifiers = new HashSet<string>(StringComparer.Ordinal);
}
/// <summary>
/// Makes the supplied identifier unique within the scope by adding
/// a suffix (1, 2, 3, ...), and returns the unique identifier.
/// </summary>
public string AdjustIdentifier(string identifier)
{
// find a unique name by adding suffix as necessary
var numberOfConflicts = 0;
var adjustedIdentifier = identifier;
while (!_knownIdentifiers.Add(adjustedIdentifier))
{
++numberOfConflicts;
adjustedIdentifier = identifier + numberOfConflicts.ToString(CultureInfo.InvariantCulture);
}
return adjustedIdentifier;
}
}
#>
Это очень немного изменено по сравнению с оригиналом Дэна, так как я скомментировал сгенерированный код, чтобы он не компилировался в проект.Когда вы будете готовы к запуску класса метаданных, просто скопируйте соответствующий код из одного сгенерированного файла и вставьте его в файл реального кода, который вы планируете использовать для определения метаданных.Вы можете добавить аннотации данных, чтобы они не перезаписывались при каждой сборке шаблона.