如何使用OData和EF

时间:2017-11-12 07:17:30

标签: c# entity-framework serialization odata

所以,情况就是这样:我有一个来自数据库的复杂对象。我有第二个对象(实际上是一个对象列表),我想向用户提供他们可以扩展的东西,但实际上只是我在应用程序中的运行时对象..

因此,我要做的是创建一个自定义ODataResourceSerializer,将模型附加到正在序列化的数据上。我猜我需要覆盖AppendDynamicProperties,CreateStructuralProperty,CreateResource或CreateSelectExpandNode,但我只是不知道哪一个以及如何。

enter image description here

因此在上图中,正常的EF / OData设置在扩展到“RelatedPosts”时返回左侧b / c数据库中没有任何内容,但我希望基本上在序列化时插入数据。 / p>

可能的?

对于某些历史记录,在重写AppendDynamicProperties时,我已经能够添加一个全新属性,您可以在上面的屏幕截图中看到“MyCustomProp”。但我还没弄明白如何添加整个对象。这是我的类扩展ODataResourceSerializer

的代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.OData.Formatter.Serialization;
using Microsoft.OData;
using System.Web.OData;
using Microsoft.OData.Edm;

namespace Boomerang.OData
{
    public class CustomODataResourceSerializer : ODataResourceSerializer
    {
        public CustomODataResourceSerializer(ODataSerializerProvider serializerProvider) : base(serializerProvider)
        {

        }

        public override Microsoft.OData.ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
        {
            Microsoft.OData.ODataProperty property = base.CreateStructuralProperty(structuralProperty, resourceContext);
            return property;
        }

        public override void AppendDynamicProperties(ODataResource resource, SelectExpandNode selectExpandNode, ResourceContext resourceContext)
        {
            // add property
            var list = resource.Properties.ToList();
            list.Add(new ODataProperty() { Name = "MyCustomProp", Value = "This Thing" });
            resource.Properties = list.AsEnumerable();

            base.AppendDynamicProperties(resource, selectExpandNode, resourceContext);
        }
        public override ODataResource CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
        {
            return base.CreateResource(selectExpandNode, resourceContext);
        }
        public override SelectExpandNode CreateSelectExpandNode(ResourceContext resourceContext)
        {
            return base.CreateSelectExpandNode(resourceContext);
        }
        public override void WriteObject(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
        {
            base.WriteObject(graph, type, messageWriter, writeContext);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

感谢 user326608 为我们指出有关 ODataMediaTypeFormatter 的 SO 答案:https://stackoverflow.com/a/36976343

我的问题与 OP 有点不同,但是它显示了一个扩展点,您可以在其中有效地构建自己的输出,而不管 OData 在元数据中知道什么结构。

以下解决方案完全忽略了复杂类型序列化的 SelectExpandNode,您可能可以构造 SelectExpandNode 以包含必要的元数据,但只需将值强制输入输出即可。

在您的 ODataResourceSerializer 中只需覆盖 CreateResource

public override ODataResource CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
{
    var resource = base.CreateResource(selectExpandNode, resourceContext);
    // Check for conditions where you want to Augment the output
    // for me (CS) I don't want to allow partial complex types.
    if (resourceContext.StructuredType.TypeKind == EdmTypeKind.Complex)
    {
        // I want ALL the properties from the complex type, if there are NONE, then generate them all!
        // This shows how to add properties to the existing output, 
        // The data I want is already available to me in the data query (ResourceInstance) that we are serializing, but you could go get it now if you need to.
        // the only caveate for manipulating existing resources is that you need to include any existing properties if you want them
        if (!resource.Properties.Any())
        {
            var type = resourceContext.ResourceInstance.GetType();
            PropertyInfo[] complex_properties = type.GetProperties();

            List<ODataProperty> child_properties = new List<ODataProperty>();
            foreach (PropertyInfo property in complex_properties)
            {
                ODataProperty child_property = CreateProperty(property.Name, property.GetValue(resourceContext.ResourceInstance));
                child_properties.Add(child_property);
            }
            resource.Properties = child_properties;
        }
    }
    return resource;
}

public static ODataResource CreateComplexValue(Type type, object value)
{
    ODataResource complex_value = new ODataResource();
    // Not sure this name will work, we probably need to lookup the name of this type from the OData metadata,
    // but I'm not sure that we can have complex value properties on a complex value anyway.
    complex_value.TypeName = type.ToString();
    PropertyInfo[] complex_properties = type.GetProperties();
    List<ODataProperty> child_properties = new List<ODataProperty>();
    foreach (PropertyInfo property in complex_properties)
    {
        ODataProperty child_property = CreateProperty(property.Name, property.GetValue(value));
        if(child_property != null)
            child_properties.Add(child_property);
    }
    complex_value.Properties = child_properties;
    return complex_value;
}

public static bool IsPrimitiveType(Type t)
{
    if (!t.IsPrimitive && t != typeof(Decimal) && t != typeof(String) && t != typeof(Guid) && t != typeof(DateTime)) // todo
    {
        return false;
    }
    return true;
}

public static ODataProperty CreateProperty(string name, object value)
{
    object property_value = value;
    if (value != null)
    {
        Type t = value.GetType();
        if (!IsPrimitiveType(t))
        {
            property_value = CreateComplexValue(t, value);
        }
        else if (t == typeof(DateTime) || t == typeof(DateTime?))
        {
            DateTime dt = (DateTime)value;
            dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
            DateTimeOffset dto = dt;
            property_value = dto;
        }
    }
    ODataProperty new_property = new ODataProperty()
    {
        Name = name,
        Value = property_value
    };
    return new_property;
}