试图序列化[NotMapped]实体框架属性以供Breeze使用
本文关键字:属性 Breeze 使用 框架 实体 序列化 NotMapped | 更新日期: 2023-09-27 18:09:00
这是一个有点复杂的问题,所以请耐心听我说。我们目前在服务器端使用的是实体框架6.1.1,在客户端使用的是OData 5.6,而在客户端使用的是Breeze JS 1.5.4。简而言之,我们在将模型上的[NotMapped]属性序列化成json并传递给客户端时遇到了问题。
这是我们的模型:public class Request
{
...
public int UserId { get; set; }
[NotMapped]
public string UserName {get; set; }
}
因为我们使用的是OData,而不是通过默认的JsonMediaTypeFormatter
序列化,它通过OdataMediaTypeFormatter
,完全忽略任何具有[NotMapped]
属性的内容。我们可以通过手动向modelBuilder
添加属性来解决这个问题。然而,当尝试与Breeze集成时,这就成了一个问题,因为他们有自己的自定义EdmBuilder,必须使用它来保存可导航的属性,而我们不能使用标准的ODataConventionModelBuilder
。这个定制构建器似乎不允许对模型进行任何级别的控制。是否有可能强制OData正确序列化这些属性,并保留与Breeze有关的元数据?以前有人尝试过类似的东西吗?
注:我们试图避免在数据库中存储或只是为这些数据创建虚拟列,因为我们需要5个这些属性,但如果我们在这方面投入太多时间,这可能会成为我们的行动方针。
Thanks in Advance
在序列化方面,伤害您的是由breeze提供的中间EdmBuilder。参见:https://github.com/Breeze/breeze.server.labs/blob/master/EdmBuilder.cs
由于EdmBuilder.cs
注释中定义的限制<>之前我们需要EDM来定义Web API OData路由,并将其作为Breeze客户端的元数据源。Web API OData文献推荐使用system .Web. http .OData. builder . odatacontiononmodelbuilder。该组件对于路由定义来说是足够的,但是作为Breeze的元数据来源是失败的,因为(在撰写本文时)它忽略了包括维护客户端JavaScript实体的导航属性所需的外键定义。这个EDM Builder要求EF DbContext提供同时满足路由定义和Breeze的元数据。您只获得了EntityFramework选择公开的元数据。这可以防止OData格式化器/序列化器包含该属性——它没有映射到模型元数据中。之前您可以尝试使用自定义序列化器的解决方案,类似于本文中所表示的。在webapi中使用OData来获取只在运行时才知道的属性
一个自定义序列化器看起来大概像这样(注意:这不起作用…
public class CustomEntitySerializer : ODataEntityTypeSerializer
{
public CustomEntitySerializer(ODataSerializerProvider serializerProvider) : base(serializerProvider) { }
public override ODataEntry CreateEntry(SelectExpandNode selectExpandNode, EntityInstanceContext entityInstanceContext)
{
ODataEntry entry = base.CreateEntry(selectExpandNode, entityInstanceContext);
Request item = entityInstanceContext.EntityInstance as Request;
if (entry != null && item != null)
{
// add your "NotMapped" property here.
entry.Properties = new List<ODataProperty>(entry.Properties) { new ODataProperty { Name = "UserName", Value = item.UserName} };
}
return entry;
}
}
这样做的问题在于底层的ODataJsonLightPropertySerializer在试图写入时检查模型是否存在属性。它调用Microsoft.Data.OData.WriterValidationUtils类中的ValidatePropertyDefined方法。
internal static IEdmProperty ValidatePropertyDefined(string propertyName, IEdmStructuredType owningStructuredType)
这会导致运行时异常:
<>之前属性'UserName'在类型' yournamespaces . models . request '上不存在. 确保只使用由类型定义的属性名。","type":"Microsoft.Data.OData.ODataException","stacktrace":" at Microsoft.Data.OData.WriterValidationUtils. "ValidatePropertyDefined (String propertyName'r'n在Microsoft.Data.OData.JsonLight.ODataJsonLightPropertySerializer.WriteProperty(ODataProperty属性,IEdmStructuredType owningType, Boolean isTopLevel, Boolean allowStreamProperty、DuplicatePropertyNamesChecker、projectedpropertiesannotationprojectedproperties之前底线是属性需要在模型中定义,以便序列化它。可以想象,您可以重写序列化层的大部分内容,但是OData框架中有许多内部/静态/私有/非虚拟的位,这使您不愉快。
解决方案最终以Breeze强迫您生成模型的方式呈现。假设是代码优先的实现,您可以将额外的模型元数据直接注入到EntityFramework生成的XmlDocument中。以Breeze EdmBuilder中的方法为例,稍加修改:
static IEdmModel GetCodeFirstEdm<T>(this T dbContext) where T : DbContext
{
// create the XmlDoc from the EF metadata
XmlDocument metadataDocument = new XmlDocument();
using (var stream = new MemoryStream())
using (var writer = XmlWriter.Create(stream))
{
System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(dbContext, writer);
stream.Position = 0;
metadataDocument.Load(stream);
}
// to support proper xpath queries
var nsm = new XmlNamespaceManager(metadataDocument.NameTable);
nsm.AddNamespace("ssdl", "http://schemas.microsoft.com/ado/2009/02/edm/ssdl");
nsm.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2009/11/edmx");
nsm.AddNamespace("edm", "http://schemas.microsoft.com/ado/2009/11/edm");
// find the node we want to work with & add the 1..N property metadata
var typeElement = metadataDocument.SelectSingleNode("//edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/edm:Schema/edm:EntityType[@Name='"Request'"]", nsm);
// effectively, we want to insert this.
// <Property Name="UserName" Type="String" MaxLength="1000" FixedLength="false" Unicode="true" Nullable="true" />
var propElement = metadataDocument.CreateElement(null, "Property", "http://schemas.microsoft.com/ado/2009/11/edm");
propElement.SetAttribute("Name", "UserName");
propElement.SetAttribute("Type", "String");
propElement.SetAttribute("FixedLength", "false");
propElement.SetAttribute("Unicode", "true");
propElement.SetAttribute("Nullable", "true");
// append the node to the type element
typeElement.AppendChild(propElement);
// now we're going to save the updated xml doc and parse it.
using (var stream = new MemoryStream())
{
metadataDocument.Save(stream);
stream.Position = 0;
using (var reader = XmlReader.Create(stream))
{
return EdmxReader.Parse(reader);
}
}
}
这将把属性放置到OData层要使用的元数据中,使任何促进序列化的额外步骤变得不必要。然而,您需要注意如何塑造模型元数据,因为任何需求/规范都将反映在Breeze的客户端验证中。
我已经在Breeze提供的ODataBreezejs示例中验证了这种方法的CRUD操作。https://github.com/Breeze/breeze.js.samples/tree/master/net/ODataBreezejsSample