data annotations - Visual Editor for DataAnnotations ASP.NET MVC? -
i writing dataannotations models in mvc program. after doing pretty same thing third model, can't wonder there easier way this?
[metadatatype(typeof(articlemd))] public partial class article { public class articlemd { [displayname("created by:")] public string createdby { get; set; } [displayname("additional information:")] [datatype(datatype.multilinetext)] public string additionalinformation { get; set; } [displayname("title:")] [required] public string title { get; set; } //...
does know of addons or plugins visual studio allow select model or viewmodel , use gui define of stuff (in separate metadata class)?
i don't know of gui editors, use t4 template generate starter each metadata class. got idea post dan wahlin. here's template use:
<# // created dan wahlin - http://www.thewahlingroup.com // title: t4 metadata , data annotations template // usage: generates initial "buddy" classes handle data validation across multiple frameworks // description: got tired of writing initial "buddy" classes hand once ef model created. template // handles generating classes , generates primitive , navigation properties. // decorates properties [required] , [stringlength] attributes appropriate. once // template generates code can copy new file , make tweaks want support // custom data annotations. // license: uifayw - use want (free or commercial) // made enhancements? please let me know @ dwahlin @ xmlforasp dot net // use: // 1. add file project (it needs .tt extension) // 2. change source csdlpath point entity framework 4 .edmx file // 3. whatever name .tt file name given code file generates // 4. don't make changes directly modified code. you'll need copy file // control or else template may overwrite changes 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 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))#> 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 { { return _edmtoobjectnamespacemap; } set { _edmtoobjectnamespacemap = value; } } public dictionary<string, string> _edmtoobjectnamespacemap = new dictionary<string, string>(); public double sourceedmversion { { if (itemcollection != null) { return itemcollection.edmversion; } return 0.0; } } //////// //////// declare template private properties. //////// static system.resources.resourcemanager resourcemanager { { 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 => structuraltype || entitycontainer); foreach (globalitem type in alltypes) { if (!hash.add(getglobalitemname(type))) { // 6034 error number used 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 edmtype) { // entitytype or complextype. return ((edmtype)item).name; } else { // must 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 path, remove ..\ 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 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 eventinfo) { eventinfo ei = (eventinfo)memberinfo; methodinfo add = ei.getaddmethod(); methodinfo remove = ei.getremovemethod(); return isvisiblemethod(add) || isvisiblemethod(remove); } else if (memberinfo fieldinfo) { fieldinfo fi = (fieldinfo)memberinfo; return !fi.isprivate && !fi.isassembly; } else if (memberinfo methodbase) { methodbase mb = (methodbase)memberinfo; if (mb.isspecialname) return false; return isvisiblemethod(mb); } else if (memberinfo propertyinfo) { propertyinfo pi = (propertyinfo)memberinfo; methodinfo = 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>() 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 property 'id' (case insensitive) 'id' // since 'id' violation of fxcop rules. if (stringcomparer.ordinalignorecase.equals(name, "id")) { // return lower case since abbreviation, not 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 no public part 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 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 supplied identifier unique within scope adding /// suffix (1, 2, 3, ...), , returns unique identifier. /// </summary> public string adjustidentifier(string identifier) { // find unique name adding suffix necessary var numberofconflicts = 0; var adjustedidentifier = identifier; while (!_knownidentifiers.add(adjustedidentifier)) { ++numberofconflicts; adjustedidentifier = identifier + numberofconflicts.tostring(cultureinfo.invariantculture); } return adjustedidentifier; } } #>
this modified dan's original in have generated code commented out doesn't compile project. when you're ready metadata class starter, copy appropriate code out of single generated file , paste real code file plan use metadata definition. can add data annotations there doesn't overwritten on each template build.
Comments
Post a Comment