在webapi中使用OData来获取仅在运行时知道的属性

假设我有一个非常简单的类型,我想在OData feed上公开它作为使用.NET C#webapi控制器的集合的一部分:

public class Image { ///  /// Get the name of the image. ///  public string Name { get; set; } public int Id { get; set; } internal System.IO.Stream GetProperty(string p) { throw new System.NotImplementedException(); } private Dictionary propBag = new Dictionary(); internal string GetIt(string p) { return propBag[p]; } } 

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

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

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

  ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(); var imagesES = modelBuilder.EntitySet("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) { } ///  /// If we are looking at the proper type, try to do a prop bag lookup first. ///  ///  ///  ///  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("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为null。 实际上,它是一个折旧的属性 – 您将使用EdmObject。 这确实有对实际对象实例的引用。 但是,在当前的夜间构建中(您必须具有任何此类工作),EdmObject的访问权限是内部的 – 因此无法使用它。

更新2:在asp CodePlex站点上有一些关于此的最小文档。

非常接近!

不是解决问题的方法,但希望这会有所帮助。

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

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

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

关于如何在Web API Odata中完成序列化的扩展点

这是一个例子。

从asp.net web api定制odata输出

虽然问题不同,但我想你可以使用相同的方法完成你想做的事情(即重写条目的序列化方式。)

特别是,在重写的CreateEntry中,您可以更改entry.Properties

(请注意,此版本尚未发布AFAIK,但可以作为预发布版本下载。)