Odata Webapi-如何在7.0核心中注入ODataResourceDeserializer? [英] Odata Webapi - how to inject a ODataResourceDeserializer in 7.0 core?

查看:66
本文介绍了Odata Webapi-如何在7.0核心中注入ODataResourceDeserializer?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

文档非常稀少,我尝试的所有结果都注入了反序列化器,但普通的odata url不再起作用.

documentation is very sparce and all i tried results in the deserializer injected but normal odata url's not working anymore.

https://github.com/OData/WebApi/issues/158 具有针对5.6的解决方案.

https://github.com/OData/WebApi/issues/158 has solutions buut for 5.6.

最后相关的评论是:

@dbenzhuser-在该提交中,查看ODataFormatterTests.cs以了解如何注入自定义解串器/解串器提供程序.您仍然可以使用一个自定义的DeserializerProvider,但它是通过DI而不是通过DI注入的通过ODataMediaTypeFormatters注入它.

@dbenzhuser - In that commit, look at ODataFormatterTests.cs for how inject a custom deserializer/deserializer provider. You can still use a custom DeserializerProvider but it's injected through DI instead of injecting it through ODataMediaTypeFormatters.

这是毫无意义的.我在那儿尝试了代码,但正如我所说的那样,它破坏了URL.

which is quite meaningless. I tried the code there, but it breaks, as I said, the URL's.

现在我的Odata设置很简单:

Right now my Odata setup is simple:

    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddOData();

\ UnitTest \ Microsoft.AspNet.OData.Test.Shared \ Formatter \ ODataFormatterTests.cs

\UnitTest\Microsoft.AspNet.OData.Test.Shared\Formatter\ODataFormatterTests.cs

有示例将其注入(如379-383行)

has examples to inject them (like in lines 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路由的情况下完成此工作.

but I seem unable to get this working without removing the core odata routing.

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

Anyone an idea how to use that for the current version without breaking the base functionality?

推荐答案

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

There are three steps if you want to maintain the base functionality:

  1. 您的 DeserializerProvider 实现应默认为自定义 Deserializer 无法管理的所有方案的基本实现.在下面的示例中,自定义解串器仅在资源"而不是集合"上运行:

  1. Your DeserializerProvider implementation should default to the base implementation for all scenarios that your custom Deserializer can't manage. In the following example the custom deserializer only operates on Resources and not Sets:

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

  • As with the provider your custom _Deserializer should call through to the base implementation for everything that you do not need to customize. In the following implementation we are only trying to enforce the Order of the properties that are deserialized as well as calling the DataContract OnDeserializing and OnDeserialized methods, the rest of the deserialization is unaffected:

    /// <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的示例

  • Finally, You need to replace the default DeserializerProvider registration with your own, the following is an example of an overload to MapODataServiceRoute that registers the DeserializerProvider from the previous 2 examples.
    I have commented out an example of registering a specific 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)
                        )
                    );
        }
    

  • 这篇关于Odata Webapi-如何在7.0核心中注入ODataResourceDeserializer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆