OData WebApi V4 .net - 自定义序列化

7
我需要创建一个序列化器来支持以下所有任务:
  1. 删除空属性
  2. 删除空列表
我注意到 ODataMediaTypeFormatter 的语法已经改变。
我在尝试将我的序列化提供程序添加到管道时遇到了问题。
这是我尝试过的内容:
在 WebApiConfig.cs 中:
var odataFormatters = ODataMediaTypeFormatters.Create();
odataFormatters.Add(new MyDataMediaTypeFormatter());
config.Formatters.InsertRange(0, odataFormatters);

另外,我创建了以下的Odatameditatypeformatter

public class MyODataMediaTypeFormatter : ODataMediaTypeFormatter
{
    static IEnumerable<ODataPayloadKind> payloadKinds = new List<ODataPayloadKind>
    {

        ODataPayloadKind.Asynchronous,
        ODataPayloadKind.Batch,
        ODataPayloadKind.BinaryValue,
        ODataPayloadKind.Collection,
        ODataPayloadKind.EntityReferenceLink,
        ODataPayloadKind.EntityReferenceLinks,
        ODataPayloadKind.Error,
        ODataPayloadKind.Delta,
        ODataPayloadKind.IndividualProperty,
        ODataPayloadKind.MetadataDocument,
        ODataPayloadKind.Parameter,
        ODataPayloadKind.Resource,
        ODataPayloadKind.ServiceDocument,
        ODataPayloadKind.Unsupported,
        ODataPayloadKind.Value
    };

    public MyODataMediaTypeFormatter() : base(payloadKinds)
    {
    }
}

我检查了所有基本方法,但在向我的OData控制器创建Get/Post请求时似乎没有触发断点。

有人成功在新版本的Microsoft.Aspnet.OData 7.0.1中实现了吗?


你在研究中是否遇到过这个答案 https://dev59.com/1GIj5IYBdhLWcg3wEhUA? - Nkosi
我相信提供的链接是6.3版的。我在7版中遇到了同样的问题。有任何更新吗? - Michael C. Gates
嘿,我想我找到了答案,请看一下它是否有帮助... - adi ben
2个回答

5
我发现了解决方案。 在新版本中,所有序列化和反序列化的自定义都只能通过依赖注入来启用。
首先,我们需要覆盖序列化提供程序:
/// <summary>
/// Provider that selects the IgnoreNullEntityPropertiesSerializer that omits null properties on resources from the response
/// </summary>
public class MySerializerProvider : DefaultODataSerializerProvider
{
    private readonly IgnoreNullsSerializer _propertiesSerializer;
    private readonly IgnoreEmptyListsResourceSetSerializer _ignoreEmptyListsSerializer;
    private readonly IgnoreEmptyListsCollectionSerializer _ignoreEmptyListsCollectionSerializer;

    /// <summary>
    /// constructor
    /// </summary>
    /// <param name="rootContainer"></param>
    public MySerializerProvider(IServiceProvider rootContainer)
        : base(rootContainer)
    {
        _ignoreEmptyListsSerializer = new IgnoreEmptyListsResourceSetSerializer(this);
        _propertiesSerializer = new IgnoreNullsSerializer(this);
        _ignoreEmptyListsCollectionSerializer = new IgnoreEmptyListsCollectionSerializer(this);
    }

    /// <summary>
    /// Mark edmtype to apply the serialization on
    /// </summary>
    /// <param name="edmType"></param>
    /// <returns></returns>
    public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
    {
        // Support for Entity types AND Complex types
        if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
        {
            return _propertiesSerializer;
        }
        if (edmType.Definition.TypeKind == EdmTypeKind.Collection)
        {
            if(edmType.Definition.AsElementType().IsDecimal() || edmType.Definition.AsElementType().IsString())
                return _ignoreEmptyListsCollectionSerializer;

            return _ignoreEmptyListsSerializer;
        }            

        var result = base.GetEdmTypeSerializer(edmType);
        return result;
    }
}

根据要覆盖的EdmType,您可能需要覆盖不同的序列化器以改变其行为。

我将添加一个示例序列化器,它可以根据请求中的“HideEmptyLists”标头忽略实体中的空列表...

/// <inheritdoc />
/// <summary>
/// OData Entity Serializer that omits empty listss properties from the response
/// </summary>
public class IgnoreEmptyListsResourceSetSerializer : ODataResourceSetSerializer
{
    /// <summary>
    /// constructor
    /// </summary>
    /// <param name="provider"></param>
    public IgnoreEmptyListsResourceSetSerializer(ODataSerializerProvider provider) : base(provider) { }


    /// <inheritdoc />
    public override void WriteObjectInline(object graph, IEdmTypeReference expectedType, ODataWriter writer,
        ODataSerializerContext writeContext)
    {
        var shouldHideEmptyLists = writeContext.Request.GetHeader("HideEmptyLists");
        if (shouldHideEmptyLists != null)
        {     
            IEnumerable enumerable = graph as IEnumerable; // Data to serialize

            if (enumerable.IsNullOrEmpty())
            {
                return;
                //ignore
            }
        }

        base.WriteObjectInline(graph, expectedType, writer, writeContext);
    }

}

另外一个忽略集合中空列表的方法...

/// <inheritdoc />
/// <summary>
/// OData Entity Serilizer that omits null properties from the response
/// </summary>
public class IgnoreEmptyListsCollectionSerializer : ODataCollectionSerializer
{
    /// <summary>
    /// constructor
    /// </summary>
    /// <param name="provider"></param>
    public IgnoreEmptyListsCollectionSerializer(ODataSerializerProvider provider)
        : base(provider) { }


    /// <summary>
    /// Creates an <see cref="ODataCollectionValue"/> for the enumerable represented by <paramref name="enumerable"/>.
    /// </summary>
    /// <param name="enumerable">The value of the collection to be created.</param>
    /// <param name="elementType">The element EDM type of the collection.</param>
    /// <param name="writeContext">The serializer context to be used while creating the collection.</param>
    /// <returns>The created <see cref="ODataCollectionValue"/>.</returns>
    public override ODataCollectionValue CreateODataCollectionValue(IEnumerable enumerable, IEdmTypeReference elementType,
        ODataSerializerContext writeContext)
    {

        var shouldHideEmptyLists = writeContext.Request.GetHeader("HideEmptyLists");
        if (shouldHideEmptyLists != null)
        {
            if (enumerable.IsNullOrEmpty())
            {
                return null;
                //ignore
            }
        }

        var result = base.CreateODataCollectionValue(enumerable, elementType, writeContext);            
        return result;
    }
}

最后,我将展示如何将序列化提供程序注入到我们的OData管道中:

        config.MapODataServiceRoute(odata, odata, builder => builder
            .AddService<ODataSerializerProvider>(ServiceLifetime.Scoped, sp => new MySerializerProvider(sp)));

那就这样吧,祝好。 干杯。

1
有没有想法如何在终结点路由中添加自定义序列化程序?该方法是MapODataRoute,但似乎没有重载以添加服务。 - fededim
@fededim 只需复制 .AddService 行,并将该行用于您当前添加其他服务的服务管道中。我在此帖子中提供了有关服务的更详细实现示例 https://dev59.com/Fa3la4cB1Zd3GeqPPZ3m#65297839 - Chris Schaller
我将Endpoint路由的解决方案作为新答案发布,因为它太长了,无法作为评论(不幸的是,它显示在此帖子上面,我不知道为什么)。 - fededim

3

谢谢,但是关于终端点路由的解决方案略有不同。我在这里发布给那些感兴趣的人:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.EnableDependencyInjection();
    endpoints.MaxTop(5000).SkipToken().Select().Filter().OrderBy().Expand().Count();
    //endpoints.MapODataRoute("odata", "odata", GetEdmModel(context));
    endpoints.MapODataRoute("odata", "odata",
        builder =>
        {
            builder.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), serviceProvider => GetEdmModel(context));
            builder.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEnumerable<IODataRoutingConvention>), serviceProvider => ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata",endpoints.ServiceProvider));
            builder.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataUriResolver), serviceProvider => new StringAsEnumResolver());
            builder.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), serviceProvider => new MySerializerProvider(serviceProvider));
        });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接