假设我有一个非常简单的类型,我希望在OData feed上使用.NET C#webapi控制器作为集合的一部分公开:
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,这是一个很好的饲料。但在我的集合中,propBag包含其他数据的有限列表(例如“a”,“b”和“c”或类似)。我希望它们是我的OData Feed中的额外属性。我的第一个想法是在配置发生时尝试这样的事情:
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为null。实际上,它是一个折旧的属性 - 你要使用EdmObject。这确实有对实际对象实例的引用。但是,在当前的夜间构建中(您必须具备这些构建中的任何一个),EdmObject的访问权限是内部的 - 因此无法使用它。
更新2:asp CodePlex site上有一些关于此的文档。
非常接近!
答案 0 :(得分:3)
不是解决问题的方法,但希望这会有所帮助。
这是我们积压的主要功能之一。我们倾向于在引用它时在我们的团队内部将其称为“无类型支持”。
Web API的问题在于它需要为服务所公开的每种EDM类型提供强大的CLR类型。此外,CLR类型和EDM类型之间的映射是一对一的,不可配置。这也是大多数IQueryable实现的工作方式。
无类型支持的想法是打破这一要求,并为没有支持强CLR类型的EDM类型提供支持。例如,所有EDM实体都可以由键值字典支持。
答案 1 :(得分:1)
关于如何在Web API Odata中完成序列化的扩展点
这是一个例子。
customizing odata output from asp.net web api
虽然问题不同,但我想您可以使用相同的方法完成所需的操作(即覆盖条目的序列化方式。)
特别是,在重写的CreateEntry中,您可以更改entry.Properties
(请注意,此版本尚未发布AFAIK,但可以作为预发布版本下载。)