文档非常稀少,我尝试的所有结果都注入了反序列化器,但普通的odata url不再起作用。
https://github.com/OData/WebApi/issues/158的解决方案适用于5.6。
最终相关评论为:
@dbenzhuser-在该提交中,查看ODataFormatterTests.cs以了解如何 注入自定义解串器/解串器提供程序。您仍然可以使用 一个自定义的DeserializerProvider,但它是通过DI而不是注入的 通过ODataMediaTypeFormatters注入它。
这是毫无意义的。我在那里尝试了代码,但正如我所说的那样,它破坏了URL。
现在我的Odata设置很简单:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddOData();
\ UnitTest \ Microsoft.AspNet.OData.Test.Shared \ Formatter \ ODataFormatterTests.cs
有示例将其注入(例如379-383行)
config.MapODataServiceRoute("IgnoredRouteName", null, builder =>
builder.AddService(Microsoft.OData.ServiceLifetime.Singleton, sp => ODataTestUtil.GetEdmModel())
.AddService<ODataSerializerProvider>(ServiceLifetime.Singleton, sp => new CustomSerializerProvider())
.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
ODataRoutingConventions.CreateDefaultWithAttributeRouting("IgnoredRouteName", config)));
但是我似乎无法在不删除核心odata路由的情况下完成此工作。
有人知道如何在不破坏基本功能的情况下将其用于当前版本吗?
答案 0 :(得分:0)
如果要维护基本功能,则分三个步骤:
您的 DeserializerProvider 实现应默认为自定义 Deserializer 无法管理的所有方案的基本实现。在下面的示例中,自定义解串器仅在“资源”而不是“集合”上运行:
public class EntityTypeDeserializerProvider : DefaultODataDeserializerProvider
{
private readonly DataContractDeserializer _dataContractDeserializer;
public EntityTypeDeserializerProvider(IServiceProvider rootContainer)
: base(rootContainer)
{
_dataContractDeserializer = new DataContractDeserializer(this);
}
public override ODataEdmTypeDeserializer GetEdmTypeDeserializer(IEdmTypeReference edmType)
{
if(edmType.Definition.TypeKind == EdmTypeKind.Complex || edmType.Definition.TypeKind == EdmTypeKind.Entity)
return _dataContractDeserializer;
else
return base.GetEdmTypeDeserializer(edmType);
}
}
与 provider 一样,您的自定义_Deserializer应该调用基本实现以实现不需要自定义的所有内容。在以下实现中,我们仅尝试强制反序列化的属性的顺序以及调用DataContract
OnDeserializing
和OnDeserialized
方法,其他反序列化不受影响:< / p>
/// <summary>
/// OData serializer that oberys the DataMember Order Attribute and OnDeserializing and OnDeserialized attributes on the resource definition
/// </summary>
public class DataContractDeserializer : ODataResourceDeserializer
{
public DataContractDeserializer(ODataDeserializerProvider provider)
: base(provider) { }
public override object CreateResourceInstance(IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)
{
var resource = base.CreateResourceInstance(structuredType, readContext);
var type = resource.GetType();
if(type.GetCustomAttributesData().Any(x => x.AttributeType == typeof(DataContractAttribute)))
{
// manually call OnDeserializing
var init = type.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).FirstOrDefault(x => x.GetCustomAttributesData().Any(a => a.AttributeType == typeof(OnDeserializingAttribute)));
if(init != null)
{
init.Invoke(resource, new object[] { new StreamingContext(StreamingContextStates.Remoting, readContext ) });
}
}
return resource;
}
public override object ReadResource(ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)
{
var resource = base.ReadResource(resourceWrapper, structuredType, readContext);
var type = resource.GetType();
if (type.GetCustomAttributesData().Any(x => x.AttributeType == typeof(DataContractAttribute)))
{
// manually call OnDeserialized
var final = type.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).FirstOrDefault(x => x.GetCustomAttributesData().Any(a => a.AttributeType == typeof(OnDeserializedAttribute)));
if (final != null)
{
final.Invoke(resource, new object[] { new StreamingContext(StreamingContextStates.Remoting, readContext) });
}
}
return resource;
}
public override void ApplyStructuralProperties(object resource, ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)
{
var type = resource.GetType();
var memberDescriptors = type.GetProperties().Where(x => x.HasAttribute<DataMemberAttribute>());
if (memberDescriptors.Any())
{
var orderedProperties = from p in resourceWrapper.Resource.Properties
let clsProperty = memberDescriptors.FirstOrDefault(m => m.Name == p.Name)
let memberAtt = (DataMemberAttribute)(clsProperty?.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(DataMemberAttribute)))
orderby (memberAtt?.Order).GetValueOrDefault()
select p;
foreach (var property in orderedProperties)
{
ApplyStructuralProperty(resource, property, structuredType, readContext);
}
}
else
base.ApplyStructuralProperties(resource, resourceWrapper, structuredType, readContext);
}
}
最后,您需要用自己的注册表替换默认的 DeserializerProvider 注册,以下是MapODataServiceRoute
的重载示例,该重载用于注册 DeserializerProvider 来自前面两个示例。
我已注释掉注册特定SerializerProvider的示例
private static ODataRoute MapODataServiceRoute(this HttpConfiguration configuration, string routeName,
string routePrefix, IEdmModel model, ODataBatchHandler batchHandler = null, ODataUriResolver uriResolver = null, IList<IODataRoutingConvention> routingConventions = null)
{
return configuration.MapODataServiceRoute(routeName, routePrefix, builder =>
builder
.AddService(ServiceLifetime.Singleton, sp => model)
//.AddService<ODataSerializerProvider>(ServiceLifetime.Singleton, sp => new DefaultODataSerializerProvider(sp))
.AddService<ODataDeserializerProvider>(ServiceLifetime.Singleton, sp => new EntityTypeDeserializerProvider(sp))
.AddService(ServiceLifetime.Singleton, sp => batchHandler ?? new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer))
.AddService(ServiceLifetime.Singleton, sp => uriResolver ?? new ODataUriResolver())
.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
routingConventions ??
ODataRoutingConventions.CreateDefaultWithAttributeRouting(routeName, configuration)
)
);
}