Odata Webapi-如何在7.0内核中注入ODataResourceDeserializer?

时间:2018-08-13 15:27:17

标签: odata asp.net-core-webapi

文档非常稀少,我尝试的所有结果都注入了反序列化器,但普通的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路由的情况下完成此工作。

有人知道如何在不破坏基本功能的情况下将其用于当前版本吗?

1 个答案:

答案 0 :(得分:0)

如果要维护基本功能,则分三个步骤:

  1. 您的 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);
        }
    }
    
  2. provider 一样,您的自定义_Deserializer应该调用基本实现以实现不需要自定义的所有内容。在以下实现中,我们仅尝试强制反序列化的属性的顺序以及调用DataContract OnDeserializingOnDeserialized方法,其他反序列化不受影响:< / 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);
        }
    }
    
  3. 最后,您需要用自己的注册表替换默认的 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)
                        )
                    );
        }