C# 无法将验证程序添加到EF生成的元数据中
我有一个实体框架中的数据库,它有一组从中创建的DTO,然后由客户端的Breeze使用 我们在服务器上使用DataAnnotation来验证来自Breeze的数据,我希望能够在客户端上复制这些验证器。由于Breeze已经实现了这些验证器,并且显然支持将验证器添加到元数据中,所以我想我应该尝试扩展Breeze服务器项目 我已经知道EDMXWriter只支持一小部分数据注释 基本上,我的项目所做的就是将生成后所需的验证器添加到Breeze发送的json中 这里是“表”的一部分,该表在Title属性上有StringLength(Breeze确实支持)的DataAnnotation。C# 无法将验证程序添加到EF生成的元数据中,c#,entity-framework,breeze,C#,Entity Framework,Breeze,我有一个实体框架中的数据库,它有一组从中创建的DTO,然后由客户端的Breeze使用 我们在服务器上使用DataAnnotation来验证来自Breeze的数据,我希望能够在客户端上复制这些验证器。由于Breeze已经实现了这些验证器,并且显然支持将验证器添加到元数据中,所以我想我应该尝试扩展Breeze服务器项目 我已经知道EDMXWriter只支持一小部分数据注释 基本上,我的项目所做的就是将生成后所需的验证器添加到Breeze发送的json中 这里是“表”的一部分,该表在Title属性上有
{
"name":"Table",
"customannotation:ClrType":"...",
"key":{
"propertyRef":{
"name":"Id"
}
},
"property":[
{
"name":"Title",
"type":"Edm.String",
"fixedLength":"false",
"unicode":"true",
"validators":[
{
"validatorName":"stringLength",
"maxLength":"Max",
"minLength":1
}
]
}
]
}
我已对输出生成进行了格式化,以符合breeze网站上方案设置的要求:
但是Breeze并没有解释我添加到元数据中的这些验证器
我注意到Breeze Server为EF提供的模式与上面web链接上设置的模式有不同的设计。BreezeJS不解释EF提供的元数据的验证器吗?如果是这样的话,有没有一个简单的方法来启用它,或者我也必须将它写入客户端
我知道Breeze团队确实说过他们计划实施更好的EF DataAnnotation支持,但是我没有看到任何结果。也许这已经实现了,我错过了什么我们只能希望事情会这么简单。
{
"name":"Table",
"customannotation:ClrType":"...",
"key":{
"propertyRef":{
"name":"Id"
}
},
"property":[
{
"name":"Title",
"type":"Edm.String",
"fixedLength":"false",
"unicode":"true",
"validators":[
{
"validatorName":"stringLength",
"maxLength":"Max",
"minLength":1
}
]
}
]
}
问候,,
Oliver Baker似乎EFContextProvider的验证注释支持非常有限,基本上只是:
- 必需-如果!可为空
- maxLength-如果指定了maxLength
因此,如果您使用其他元数据扩展EFContextProvider,则必须手动处理此问题,并将其添加到元数据存储区属性信息中的validators对象中。breeze了解两种元数据格式。第一个是基于EDM(实体框架)的模型的默认版本,是EDMX CSDL的json序列化版本。这是一种MS格式,不容易扩展,只支持上面列出的有限数量的数据注释 另一种选择是breeze的原生元数据格式。此格式通常由任何基于非实体框架的breeze服务器使用。这也是应用MetadataStore.exportMetadata和MetadataStore.importMetadata方法调用时使用的格式。如果您的服务器以这种格式提供元数据,那么您可以包括您想要的任何验证。研究这种格式的最好方法是简单地导出当前应用程序的元数据并查看一下。结果就是字符串化的本机元数据json 几个breeze开发人员采用的一种方法是使用预构建过程,该过程将CSDL格式的元数据从EF服务器通过breeze客户端进行往返,以将其转换为本机格式,然后简单地将该结果存储在服务器上(在您的例子中,添加了一些验证程序)在元数据调用期间,只需将预存储的元数据返回到生产中的客户机 此外,还可以扩展breeze元数据格式:请参见: 我们有许多开发人员将这种扩展元数据用于各种目的,包括添加验证元数据 几个breeze开发人员采用的一种方法是使用预构建过程,该过程将CSDL格式的元数据从EF服务器通过breeze客户端进行往返,以将其转换为本机格式,然后简单地将该结果存储在服务器上 下面是我使用jint的解决方案。显然,计算代价很高,因此调用它的方法有一个[Conditional[“DEBUG”]]属性
public static class MedSimDtoMetadata
{
const string breezeJsPath = @"C:\Users\OEM\Documents\Visual Studio 2015\Projects\SimManager\SM.Web\Scripts\breeze.min.js";
public static string GetBreezeMetadata(bool pretty = false)
{
var engine = new Engine().Execute("var setInterval;var setTimeout = setInterval = function(){}"); //if using an engine like V8.NET, would not be required - not part of DOM spec
engine.Execute(File.ReadAllText(breezeJsPath));
engine.Execute("breeze.NamingConvention.camelCase.setAsDefault();" + //mirror here what you are doing in the client side code
"var edmxMetadataStore = new breeze.MetadataStore();" +
"edmxMetadataStore.importMetadata(" + MedSimDtoRepository.GetEdmxMetadata() + ");" +
"edmxMetadataStore.exportMetadata();");
var exportedMeta = JObject.Parse(engine.GetCompletionValue().AsString());
AddValidators(exportedMeta);
return exportedMeta.ToString(pretty ? Formatting.Indented : Formatting.None);
}
//http://stackoverflow.com/questions/26570638/how-to-add-extend-breeze-entity-types-with-metadata-pulled-from-property-attribu
static void AddValidators(JObject metadata)
{
Assembly thisAssembly = typeof(ParticipantDto).Assembly; //any type in the assembly containing the Breeze entities.
var attrValDict = GetValDictionary();
var unaccountedVals = new HashSet<string>();
foreach (var breezeEntityType in metadata["structuralTypes"])
{
string shortEntityName = breezeEntityType["shortName"].ToString();
string typeName = breezeEntityType["namespace"].ToString() + '.' + shortEntityName;
Type entityType = thisAssembly.GetType(typeName, true);
Type metaTypeFromAttr = ((MetadataTypeAttribute)entityType.GetCustomAttributes(typeof(MetadataTypeAttribute), false).Single()).MetadataClassType;
foreach (var breezePropertyInfo in breezeEntityType["dataProperties"])
{
string propName = breezePropertyInfo["name"].ToString();
propName = char.ToUpper(propName[0]) + propName.Substring(1); //IF client using breeze.NamingConvention.camelCase & server using PascalCase
var propInfo = metaTypeFromAttr.GetProperty(propName);
if (propInfo == null)
{
Debug.WriteLine("No metadata property attributes available for " + breezePropertyInfo["dataType"] + " "+ shortEntityName +'.' + propName);
continue;
}
var validators = breezePropertyInfo["validators"].Select(bp => bp.ToObject<Dictionary<string, object>>()).ToDictionary(key => (string)key["name"]);
//usingMetaProps purely on property name - could also use the DTO object itself
//if metadataType not found, or in reality search the entity framework entity
//for properties with the same name (that is certainly how I am mapping)
foreach (Attribute attr in propInfo.GetCustomAttributes())
{
Type t = attr.GetType();
if (t.Namespace == "System.ComponentModel.DataAnnotations.Schema") {
continue;
}
Func<Attribute, Dictionary<string,object>> getVal;
if (attrValDict.TryGetValue(t, out getVal))
{
var validatorsFromAttr = getVal(attr);
if (validatorsFromAttr != null)
{
string jsValidatorName = (string)validatorsFromAttr["name"];
if (jsValidatorName == "stringLength")
{
validators.Remove("maxLength");
}
Dictionary<string, object> existingVals;
if (validators.TryGetValue(jsValidatorName, out existingVals))
{
existingVals.AddOrOverwrite(validatorsFromAttr);
}
else
{
validators.Add(jsValidatorName, validatorsFromAttr);
}
}
}
else
{
unaccountedVals.Add(t.FullName);
}
}
breezePropertyInfo["validators"] = JToken.FromObject(validators.Values);
}
}
foreach (var u in unaccountedVals)
{
Debug.WriteLine("unaccounted attribute:" + u);
}
}
static Dictionary<Type, Func<Attribute, Dictionary<string, object>>> GetValDictionary()
{
var ignore = new Func<Attribute, Dictionary<string, object>>(x => null);
return new Dictionary<Type, Func<Attribute, Dictionary<string, object>>>
{
[typeof(RequiredAttribute)] = x => new Dictionary<string, object>
{
["name"] = "required",
["allowEmptyStrings"] = ((RequiredAttribute)x).AllowEmptyStrings
//["message"] = ((RequiredAttribute)x).ErrorMessage
},
[typeof(EmailAddressAttribute)] = x => new Dictionary<string, object>
{
["name"] = "emailAddress",
},
[typeof(PhoneAttribute)] = x => new Dictionary<string, object>
{
["name"] = "phone",
},
[typeof(RegularExpressionAttribute)] = x => new Dictionary<string, object>
{
["name"] = "regularExpression",
["expression"] = ((RegularExpressionAttribute)x).Pattern
},
[typeof(StringLengthAttribute)] = x => {
var sl = (StringLengthAttribute)x;
return GetStrLenDictionary(sl.MaximumLength, sl.MinimumLength);
},
[typeof(MaxLengthAttribute)] = x => GetStrLenDictionary(((MaxLengthAttribute)x).Length),
[typeof(UrlAttribute)] = x => new Dictionary<string, object>
{
["name"] = "url",
},
[typeof(CreditCardAttribute)] = x=> new Dictionary<string, object>
{
["name"] = "creditCard",
},
[typeof(FixedLengthAttribute)] = x => //note this is one of my attributes to force fixed length
{
var len = ((FixedLengthAttribute)x).Length;
return GetStrLenDictionary(len, len);
},
[typeof(RangeAttribute)] = x => {
var ra = (RangeAttribute)x;
return new Dictionary<string, object>
{
["name"] = "range",
["min"] = ra.Minimum,
["max"] = ra.Maximum
};
},
[typeof(KeyAttribute)] = ignore
};
}
static Dictionary<string,object> GetStrLenDictionary(int maxLength, int minLength = 0)
{
if (minLength == 0)
{
return new Dictionary<string, object>
{
["name"] = "maxLength",
["maxLength"] = maxLength
};
}
return new Dictionary<string, object>
{
["name"] = "stringLength",
["minLength"] = minLength,
["maxLength"] = maxLength
};
}
static void AddOrOverwrite<K,V>(this Dictionary<K,V> oldValues, Dictionary<K,V> newValues)
{
foreach (KeyValuePair<K,V> kv in newValues)
{
if (oldValues.ContainsKey(kv.Key))
{
oldValues[kv.Key] = kv.Value;
}
else
{
oldValues.Add(kv.Key, kv.Value);
}
}
}
}
公共静态类MedSimDtoMetadata
{
const string breezeJsPath=@“C:\Users\OEM\Documents\Visual Studio 2015\Projects\SimManager\SM.Web\Scripts\breeze.min.js”;
公共静态字符串GetBreezeMetadata(bool pretty=false)
{
var engine=new engine().Execute(“var setInterval;var setTimeout=setInterval=function(){}”);//如果使用像V8.NET这样的引擎,则不需要它,这不是DOM规范的一部分
Execute(File.ReadAllText(breezeJsPath));
engine.Execute(“breeze.NamingConvention.camelCase.setAsDefault();”+//在这里镜像您在客户端代码中的操作
“var edmxMetadataStore=new breeze.MetadataStore();”+
“edmxMetadataStore.importMetadata(“+MedSimDtoRepository.GetEdmxMetadata()+”)+
“edmxMetadataStore.exportMetadata();”;
var exportedMeta=JObject.Parse(engine.GetCompletionValue().AsString());
AddValidators(exportedMeta);
返回exportedMeta.ToString(pretty?Formatting.Indented:Formatting.None);
}
//http://stackoverflow.com/questions/26570638/how-to-add-extend-breeze-entity-types-with-metadata-pulled-from-property-attribu
静态void AddValidators(JObject元数据)
{
Assembly thisAssembly=typeof(ParticipantDto).Assembly;//程序集中包含Breeze实体的任何类型。
var attrValDict=GetValDictionary();
var uncountedvals=new HashSet();
foreach(元数据[“StructureTypes”]中的var breezeEntityType)
{