Как поддерживать вложенный открытый сложный тип в драйвере OData C #? - PullRequest
0 голосов
/ 04 июня 2018

Я использую следующие пакеты C # OData в проекте .NET Web Api:

Install-Package Microsoft.AspNet.OData
Install-Package Microsoft.AspNet.WebApi.OData

При следовании примеру Microsoft Использовать открытые типы в OData v4 все кажется работающим должным образом, если открытый тип не содержит дополнительных вложенных открытых сложных типов.

Это означает, что это будет работать нормально:

public class WplController : ODataController
{
    private List<AbstractMongoDocument> _documents = new List<AbstractMongoDocument>
    {
        new AbstractMongoDocument
        {
            Id = "2",
            Meta = new MongoMeta(),
            Data = new MongoData
            {
                Document = new Dictionary<string, object>()
                {
                    {"root_open_type", "This works!" },
                }
            }
        }
    };

    [EnableQuery]
    public IQueryable<AbstractMongoDocument> Get()
    {    return _documents.AsQueryable();}
}

Хотя это вызывает исключение

public class WplController : ODataController
{
    private List<AbstractMongoDocument> _documents = new List<AbstractMongoDocument>
    {
        new AbstractMongoDocument
        {
            Id = "1",
            Meta = new MongoMeta(),
            Data = new MongoData
            {
                Document = new Dictionary<string, object>()
                {
                    {"root_open_type", "This works!" },
                    {"nested_open_type",  new Dictionary<string, object>() //Nested dictionary throws exception!
                        {
                            {"field1", "value2" }, 
                            {"field2", "value2" }
                        }
                    }
                }
            }
        }
    };

    [EnableQuery]
    public IQueryable<AbstractMongoDocument> Get()
    {    return _documents.AsQueryable();}
}

Исключение выглядит следующим образом:

Произошло исключение System.InvalidOperationException

Сообщение: типу ObjectContent`1 не удалось сериализовать тело ответа для типа контента application / json;odata.metadata = минимальный.

Сообщение: сгенерировано исключение: 'System.InvalidOperationException' в System.Web.OData.dll

Дополнительная информация: Данная модель не содержит тип 'System.Collections.Generic.Dictionary`2 [System.String, System.Object]».


Это можно исправить, добавив следующую строку в ODataConventionModelBuilder в WebApiConfig.cs:

builder.ComplexType<Dictionary<string, object>>();

Однако это приводит к следующему ответу OData JSON:

 {
      "@odata.context": "http://localhost:50477/odata/$metadata#wpl",
      "value": 
      [
           {
                "Id": "1",
                "Meta": {},
                "Data": 
                {
                     "root_open_type": "This works!",
                     "nested_open_type": 
                     {
                          "@odata.type": "#System.Collections.Generic.Dictionary_2OfString_Object",
                          "Keys": 
                          [
                               "field1",
                               "field2"
                          ]
                     }
                }
           }
      ]
 }

Как я могу убедиться, что ODate также правильно сериализует вложенные открытые поля?Т.е. мне бы хотелось получить следующий OData JSON:

 {
      "@odata.context": "http://localhost:50477/odata/$metadata#wpl",
      "value": 
      [
           {
                "Id": "1",
                "Meta": {},
                "Data": 
                {
                     "root_open_type": "This works!",
                     "nested_open_type": 
                     {
                          "field1": "value1",
                          "field2": "value2"
                     }
                }
           }
      ]
 }

Заранее благодарен за любую потенциальную помощь!

Ответы [ 2 ]

0 голосов
/ 25 августа 2019

Я в той же лодке, что и ты.Мне нужно представить некоторые данные в виде чистого JSON.Это рабочее решение с использованием класса ODataUntypedValue.Он сериализуется с тем, что вы ожидаете.Я протестировал его с вашими моделями и с моими.

Реализация MongoDataSerializer класса:

public class MongoDataSerializer: ODataResourceSerializer
{
    public MongoDataSerializer(ODataSerializerProvider serializerProvider)
        : base(serializerProvider)
    {
    }

    /// <summary>
    /// Serializes the open complex type as an <see cref="ODataUntypedValue"/>.
    /// </summary>
    /// <param name="graph"></param>
    /// <param name="expectedType"></param>
    /// <param name="writer"></param>
    /// <param name="writeContext"></param>
    public override void WriteObjectInline(
        object graph,
        IEdmTypeReference expectedType,
        ODataWriter writer,
        ODataSerializerContext writeContext)
    {
        // This cast is safe because the type is checked before using this serializer.
        var mongoData = (MongoData)graph;
        var properties = new List<ODataProperty>();

        foreach (var item in mongoData.Document)
        {
            properties.Add(new ODataProperty
            {
                Name = item.Key,
                Value = new ODataUntypedValue
                {
                    RawValue = JsonConvert.SerializeObject(item.Value),
                },
            });
        }

        writer.WriteStart(new ODataResource
        {
            TypeName = expectedType.FullName(),
            Properties = properties,
        });

        writer.WriteEnd();
    }
}

Реализация CustomODataSerializerProvider класса:

public class CustomODataSerializerProvider : DefaultODataSerializerProvider
{
    private readonly MongoDataSerializer mongoDataSerializer;

    public CustomODataSerializerProvider(
        IServiceProvider odataServiceProvider)
        : base(odataServiceProvider)
    {
        this.mongoDataSerializer = new MongoDataSerializer(this);
    }

    public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
    {
        if (edmType.FullName() == typeof(MongoData).FullName)
        {
            return this.mongoDataSerializer;
        }

        return base.GetEdmTypeSerializer(edmType);
    }
}

Регистрация CustomODataSerializerProvider в вашем Startup.cs:

        app.UseMvc(options =>
        {
            var model = builder.GetEdmModel();
            options
                .MapODataServiceRoute(
                    "odata",
                    "odata",
                    b => b
                            .AddService(Microsoft.OData.ServiceLifetime.Scoped, s => model)
                            .AddService<IEnumerable<IODataRoutingConvention>>(
                                Microsoft.OData.ServiceLifetime.Scoped,
                                s => ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", options))
                            .AddService<ODataSerializerProvider, CustomODataSerializerProvider>(Microsoft.OData.ServiceLifetime.Singleton));
        }

Это вывод, использующий ваши модели (обратите внимание, что имена свойств начинаются со строчной буквы, потому что я включил ODataConventionModelBuilder.EnableLowerCamelCase()): Working output

0 голосов
/ 14 сентября 2018

Добавление Dictionary<string, object> в качестве открытого сложного типа может сбить с толку разработчика.Открытые типы должны определять свойство, которое содержит динамические свойства.В случае Dictionary<string, object> кажется, что коллекция Keys является контейнером динамических свойств для открытого типа.Попробуйте создать тип, который будет определяться как сложный тип, например:

public class OpenComplexType
{
    public IDictionary<string, object> DynamicProperties { get; set; }
}

Затем зарегистрируйте его как сложный тип:

builder.ComplexType<OpenComplexType>();

Наконец, определите ваше свойство Documentиспользуя OpenComplexType в качестве типа:

public class MongoData
{
    public OpenComplexType Document { get; set; }
}

Я ни в коем случае не эксперт по библиотекам OData WebAPI, и могут быть другие способы обойти это, используя Dictionary<string, object>, но это должно бытьместо для начала.

...