c# - 在 webapi 中使用 OData 获取仅在运行时已知的属性

标签 c# asp.net-web-api odata

假设我有一个非常简单的类型,我想使用 .NET C# webapi Controller 将其作为集合的一部分公开在 OData 提要上:

public class Image
{
    /// <summary>
    /// Get the name of the image.
    /// </summary>
    public string Name { get; set; }

    public int Id { get; set; }

    internal System.IO.Stream GetProperty(string p)
    {
        throw new System.NotImplementedException();
    }

    private Dictionary<string, string> propBag = new Dictionary<string, string>();
    internal string GetIt(string p)
    {
        return propBag[p];
    }
}

在我的 WebApiConfig.cs 中,我做了标准的配置:

        ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
        var imagesES = modelBuilder.EntitySet<Image>("Images");

根据 Excel,这是一个很棒的 Feed。但在我的收藏中,propBag 包含其他数据的有限列表(例如“a”、“b”和“c”或类似数据)。我希望它们作为我的 OData 源中的额外属性。当配置发生时,我的第一个想法是尝试这样的事情:

        ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
        var imagesES = modelBuilder.EntitySet<Image>("Images");
        images.EntityType.Property(c => c.GetIt("a"))

这完全失败了,因为传入的实际上是一个表达式树,而不是 lambda 函数,并且此方法尝试解析它。并期望取消引用属性。

我应该往哪个方向走?对于某些上下文:我正在尝试使用单个简单的平面对象创建一个 odata 只读源。按照网上找到的教程,让简单版本工作起来很容易。

更新:

下面的 cellik 给我指明了一个方向。我只是尽可能地跟着它,而且我已经非常接近了。

首先,我创建了一个属性信息类来表示动态属性:

public class LookupInfoProperty : PropertyInfo
{
    private Image _image;
    private string _propName;
    public LookupInfoProperty(string pname)
    {
        _propName = pname;
    }

    public override PropertyAttributes Attributes
    {
        get { throw new NotImplementedException(); }
    }

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override MethodInfo[] GetAccessors(bool nonPublic)
    {
        throw new NotImplementedException();
    }

    public override MethodInfo GetGetMethod(bool nonPublic)
    {
        throw new NotImplementedException();
    }

    public override ParameterInfo[] GetIndexParameters()
    {
        throw new NotImplementedException();
    }

    public override MethodInfo GetSetMethod(bool nonPublic)
    {
        throw new NotImplementedException();
    }

    public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public override Type PropertyType
    {
        get { return typeof(string); }
    }

    public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public override Type DeclaringType
    {
        get { throw new NotImplementedException(); }
    }

    public override object[] GetCustomAttributes(Type attributeType, bool inherit)
    {
        throw new NotImplementedException();
    }

    public override object[] GetCustomAttributes(bool inherit)
    {
        return new object[0];
    }

    public override bool IsDefined(Type attributeType, bool inherit)
    {
        throw new NotImplementedException();
    }

    public override string Name
    {
        get { return _propName; }
    }

    public override Type ReflectedType
    {
        get { return typeof(Image); }
    }
}

如您所见,需要实现的方法很少。然后我创建了一个自定义序列化程序:

public class CustomSerializerProvider : DefaultODataSerializerProvider
{
    public override ODataEdmTypeSerializer CreateEdmTypeSerializer(IEdmTypeReference edmType)
    {
        if (edmType.IsEntity())
        {
            // entity type serializer
            return new CustomEntityTypeSerializer(edmType.AsEntity(), this);
        }
        return base.CreateEdmTypeSerializer(edmType);
    }
}

public class CustomEntityTypeSerializer : ODataEntityTypeSerializer
{
    public CustomEntityTypeSerializer(IEdmEntityTypeReference edmType, ODataSerializerProvider serializerProvider)
        : base(edmType, serializerProvider)
    {
    }

    /// <summary>
    /// If we are looking at the proper type, try to do a prop bag lookup first.
    /// </summary>
    /// <param name="structuralProperty"></param>
    /// <param name="entityInstanceContext"></param>
    /// <returns></returns>
    public override ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, EntityInstanceContext entityInstanceContext)
    {
        if ((structuralProperty.DeclaringType as IEdmEntityType).Name == "Image")
        {
            var r = (entityInstanceContext.EntityInstance as Image).GetIt(structuralProperty.Name);
            if (r != null)
                return new ODataProperty() { Name = structuralProperty.Name, Value = r };
        }
        return base.CreateStructuralProperty(structuralProperty, entityInstanceContext);
    }
}

哪些在我的 WebApiConfig Register 方法中配置:

config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create(new CustomSerializerProvider(), new DefaultODataDeserializerProvider()));

最后,我创建了 Image 类,并向其添加了“a”属性:

        ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
        var imagesES = modelBuilder.EntitySet<Image>("Images");
        var iST = modelBuilder.StructuralTypes.Where(t => t.Name == "Image").FirstOrDefault();
        iST.AddProperty(new LookupInfoProperty("a"));
        Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
        config.Routes.MapODataRoute("ODataRoute", "odata", model);

只有一个问题 - 在大多数来自 Excel 等客户端的测试查询中,EntityInstance 为空。实际上,它是一个贬值的属性——您应该改用 EdmObject。并且确实有对实际对象实例的引用。但是,在当前的每晚构建中(您必须拥有它才能使其中的任何一个工作)EdmObject 的访问权限是内部的 - 因此不能使用它。

更新 2:asp CodePlex site 上有一些关于此的最少文档.

非常接近!

最佳答案

不是您问题的真正解决方案,但希望这对您有所帮助。

这是我们积压工作中的主要功能之一。在引用它时,我们倾向于在团队内部将其称为“无类型支持”。

Web API 的问题在于,它需要为服务公开的每个 EDM 类型提供一个强大的 CLR 类型。此外,CLR 类型和 EDM 类型之间的映射是一对一的且不可配置的。这也是大多数 IQueryable 实现的工作方式。

无类型支持的想法是打破这一要求,并在没有支持强 CLR 类型的情况下提供对 EDM 类型的支持。例如,您的所有 EDM 实体都可以由键值字典支持。

关于c# - 在 webapi 中使用 OData 获取仅在运行时已知的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17140614/

相关文章:

c# - .NET 默认值属性

c# - 在单元测试中出现错误 "Provider com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl not found"但在主程序中却没有

c# - .NET Core 授权 - JWT Bearer 上的常量 403

c# - 具有多个筛选器的 Azure 搜索服务

OData 服务未返回完整响应

c# - 如何在 C# 中连接到数据库并遍历记录集?

c# - 以编程方式获取sql server的默认备份路径

c# - 从 ASP.NET Web API 层使用的方法中抛出或不抛出异常

powershell - ADFS PowerShell : Scripting Web API (Add-AdfsWebApiApplication) with RuleTemplate in IssuanceTransformRules

PHP curl 与 Microsoft Dynamics CRM 2011 对话