【问题标题】:Odata Webapi - how to inject a ODataResourceDeserializer in 7.0 core?Odata Webapi - 如何在 7.0 核心中注入 ODataResourceDeserializer?
【发布时间】:2019-01-20 09:16:49
【问题描述】:

文档非常少,我尝试过的所有结果都注入了反序列化程序,但正常的 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 路由,我似乎无法正常工作。

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

【问题讨论】:

  • 我知道这是旧的,但如果您发布 您的 实现或您尝试过的内容,而不是您无法解释的资源中的实现,它会有所帮助。

标签: odata asp.net-core-webapi


【解决方案1】:

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

  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 方法,其余的反序列化不受影响:

    /// <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 2 个示例。
    我已经注释掉了一个注册特定 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)
                        )
                    );
        }
    

【讨论】:

    猜你喜欢
    • 2020-09-23
    • 2021-11-17
    • 1970-01-01
    • 2019-06-01
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多