I need to create a Serializer to support all of the following tasks:
I noticed the Syntax of the ODataMediaTypeFormatter
has been changed.
And I'm having trouble adding my Serialzation provider to the pipe.
Here what I've tried:
On WebApiConfig.cs:
var odataFormatters = ODataMediaTypeFormatters.Create();
odataFormatters.Add(new MyDataMediaTypeFormatter());
config.Formatters.InsertRange(0, odataFormatters);
Plus I've Created the following 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)
{
}
}
Currently I checked all the base methods and none of them seems to hit the breakpoint while creating a Get/Post request to my OData controllers.
Any one managed to do it on the new version of Microsoft.Aspnet.OData 7.0.1?
I found the solution. On the new versions all the serialization and deserialization customization is enabled only through dependency injection.
First we need to override the serialization provider:
/// <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;
}
}
You might need to override different serializers based on the EdmType you want to override it's behavior.
I'm adding an example of a serializer that ignore empty lists from entities based on "HideEmptyLists" header on the request...
/// <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);
}
}
And another one to ignore empty list for collections...
/// <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;
}
}
And to finalize I'll show how to inject the Serialization Provider into our OData pipeline:
config.MapODataServiceRoute(odata, odata, builder => builder
.AddService<ODataSerializerProvider>(ServiceLifetime.Scoped, sp => new MySerializerProvider(sp)));
That should wrap it up. cheers.
Thanks, but the solution for Endpoint routing is a little bit different, I post it here for those interested:
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));
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With