【问题标题】:How to add an expand object at runtime with OData and EF如何在运行时使用 OData 和 EF 添加展开对象
【发布时间】:2017-11-12 07:17:29
【问题描述】:

所以,情况如下:我有一个来自数据库的复杂对象。我还有第二个对象(实际上是一个对象列表),我想向用户展示它可以扩展为的东西,但实际上只是我在应用程序中拥有的运行时对象..

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

所以在上图中,正常的 EF/OData 设置在扩展到“RelatedPosts”b/c 时返回左侧,数据库中没有,但我想基本上在序列化时插入数据。

可能吗?

对于某些历史,在覆盖 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);
        }
    }
}

【问题讨论】:

  • 可以,提供你试过的代码
  • 好的,在上面添加...
  • 而不是 ODataProperty 的值为 "This Thing" 尝试构建一个(集合)ODataComplexValue 并添加它
  • rockyourteeth,你找到方法了吗?我想实现显示词汇但没有 OData 属性显示属性。 my problem@user326608 谢谢,有例子吗?
  • @flieks 在stackoverflow.com/a/36976343 底部的 utils 类中查看 'CreateComplexValue' 辅助方法

标签: c# entity-framework serialization odata


【解决方案1】:

感谢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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 2021-08-27
    • 2017-09-26
    • 1970-01-01
    相关资源
    最近更新 更多