添加自定义查询,支持导航属性来ODataConventionModelBuilder [英] Adding a custom query backed Navigation Property to ODataConventionModelBuilder
问题描述
状况
我创建了下面的Model类
公共类车
{
公众诠释标识{获取;设置;}
公共字符串名称{;设置;} 公共虚拟的ICollection< PartState> PartStates {获取;设置; }
}公共类PartState
{
公众诠释标识{获取;设置;}
公共字符串状态{获取;设置;} 公众诠释CarId {获取;设置;}
公共虚拟小汽车{获取;设置;} 公众诠释PARTID {获取;设置;}
公共虚拟部件部件{获取;设置;}
}公共类部分
{
公众诠释标识{获取;设置;}
公共字符串名称{;设置;}
}
和匹配的DbContext
公共类CarContext:的DbContext
{
公共DbSet<汽车>汽车{获取;设置;}
公共DbSet< PartState> PartStates {获取;设置;}
公共DbSet<部分>部分{获取;设置;}
}
和创建web应用,使通过ODATA这个可用,用脚手架模板的Web API的OData 2控制器操作,使用实体框架
还创建以下的WebAPI配置:
公共静态类WebApiConfig
{
公共静态无效的注册(HttpConfiguration配置)
{
VAR建设者=新ODataConventionModelBuilder();
builder.EntitySet<汽车>(汽车总动员);
builder.EntitySet&所述; PartState&GT(PartStates);
builder.EntitySet<部分>(部件);
变种edmModel = builder.GetEdmModel();
config.Routes.MapODataRoute(ODATA,ODATA,edmModel);
}
}
我现在想下面的方法添加到我的汽车控制器
// GET:ODATA /汽车(5)/零件
[可查询]
公众的IQueryable<部分> GETPARTS([FromODataUri] INT键)
{
VAR部分= db.PartStates.Where(S = GT; s.CarId ==键)。选择(S = GT; s.Part).Distinct();
返回部分;
}
而与此网址检索数据:
的http://本地主机/ ODATA /汽车(1)/零件
但它不工作,相反,我得到以下错误:
{
odata.error:{
code:,消息:{
郎:EN-US,值:没有HTTP资源发现,请求URI相匹配的http://本地主机/ ODATA /汽车(1)/零件'。
},innererror:{
消息:没有约定的路由发现选择与模板'〜/ EntitySet的/键/未解决的路径OData的一个动作。,类型:,堆栈跟踪:
}
}
}
问题
所以我的问题是,是否可能?!
我试图手动创建一个导航属性,并把它添加到EDM模型,而这也解决调用新方法的问题上,还引入了新的错误。
编辑:
什么ID并试图以这种方式手动添加:
VAR edmModel =(EdmModel)builder.GetEdmModel();
VAR carType =(EdmEntityType)edmModel.FindDeclaredType(汽车);
VAR partType =(EdmEntityType)edmModel.FindDeclaredType(部件);VAR partsProperty =新EdmNavigationPropertyInfo();
partsProperty.TargetMultiplicity = EdmMultiplicity.Many;
partsProperty.Target = partType;
partsProperty.ContainsTarget = FALSE;
partsProperty.OnDelete = EdmOnDeleteAction.None;
partsProperty.Name =部件;VAR carsProperty =新EdmNavigationPropertyInfo();
carsProperty.TargetMultiplicity = EdmMultiplicity.Many;
carsProperty.Target = carType;
carsProperty.ContainsTarget = FALSE;
carsProperty.OnDelete = EdmOnDeleteAction.None;
carsProperty.Name =汽车总动员;VAR NAV = EdmNavigationProperty.CreateNavigationPropertyWithPartner(partsProperty,carsProperty);carType.AddProperty(NAV);config.Routes.MapODataRoute(ODATA,ODATA,edmModel);
虽然这让我援引上述speciefied方法线槽也高于指定的URL,它给了我以下错误:
{
odata.error:{
code:,消息:{
郎:EN-US,值:发生了错误。
},innererror:{
消息:在'ObjectContent`1类型没有序列化响应正文内容类型应用/ JSON; ODATA = fullmetadata;字符集= UTF-8。,类型:System.InvalidOperationException,堆栈跟踪:,internalexception:{
消息:的相关的实体组不能从OData的路径中发现的相关的实体集需要序列有效载荷。,类型:System.Runtime.Serialization.SerializationException,堆栈跟踪:在System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(对象图,类型类型,ODataMessageWriter messageWriter,ODataSerializerContext writeContext)\\ r \\ n在System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(类型类型,对象的值,流writeStream,HttpContent内容,HttpContentHeaders contentHeaders)\\ r \\ n在System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(类型类型,对象的值,流writeStream,HttpContent内容,TransportContext transportContext,的CancellationToken的CancellationToken) \\ r \\ n ---从previous位置堆栈跟踪,其中引发异常的结尾--- \\ r \\ n在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)\\ r \\ n在System.Runtime .CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\\ r \\ n在System.Runtime.CompilerServices.TaskAwaiter.GetResult(个)\\ r \\ n在System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()\"
}
}
}
}
您必须调用AddNavigationTarget的EntitySet的。
假设你的命名空间是myNameSpace对象,然后添加以下code你WebApiConfig.cs。通过这种方式,与检索数据获取:ODATA /汽车(1)/零件。将工作
VAR汽车=(EdmEntitySet)edmModel.EntityContainers()单()FindEntitySet(汽车总动员)。;
VAR部分=(EdmEntitySet)edmModel.EntityContainers()单()FindEntitySet(部件)。;
变种carType =(EdmEntityType)edmModel.FindDeclaredType(MyNamespace.Car);
变种partType =(EdmEntityType)edmModel.FindDeclaredType(MyNamespace.Part); VAR partsProperty =新EdmNavigationPropertyInfo();
partsProperty.TargetMultiplicity = EdmMultiplicity.Many;
partsProperty.Target = partType;
partsProperty.ContainsTarget = FALSE;
partsProperty.OnDelete = EdmOnDeleteAction.None;
partsProperty.Name =部件; cars.AddNavigationTarget(carType.AddUnidirectionalNavigation(partsProperty),零部件);
Situation
I created the following Model classes
public class Car
{
public int Id {get;set;}
public string Name {get;set;}
public virtual ICollection<PartState> PartStates {get;set; }
}
public class PartState
{
public int Id {get;set;}
public string State {get;set;}
public int CarId {get;set;}
public virtual Car Car {get;set;}
public int PartId {get;set;}
public virtual Part Part {get;set;}
}
public class Part
{
public int Id {get;set;}
public string Name {get;set;}
}
And a matching DbContext
public class CarContext : DbContext
{
public DbSet<Car> Cars {get;set;}
public DbSet<PartState> PartStates {get;set;}
public DbSet<Part> Parts {get;set;}
}
And created a WebApplication to make this available via odata, using the scaffolding template "Web API 2 OData Controller with Actions, using Entity Framework"
also i create following webapi config:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Car>("Cars");
builder.EntitySet<PartState>("PartStates");
builder.EntitySet<Part>("Parts");
var edmModel = builder.GetEdmModel();
config.Routes.MapODataRoute("odata", "odata", edmModel);
}
}
I now want to add the following Method to my Cars Controller
// GET: odata/Cars(5)/Parts
[Queryable]
public IQueryable<Part> GetParts([FromODataUri] int key)
{
var parts = db.PartStates.Where(s => s.CarId == key).Select(s => s.Part).Distinct();
return parts;
}
And retrieve the data with this Url:
http://localhost/odata/Cars(1)/Parts
But it does not work, instead i get the following error:
{
"odata.error":{
"code":"","message":{
"lang":"en-US","value":"No HTTP resource was found that matches the request URI 'http://localhost/odata/Cars(1)/Parts'."
},"innererror":{
"message":"No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'.","type":"","stacktrace":""
}
}
}
Question
So my question is, is that even possible?!
I tried to create a Navigation property manually, and added it to the edm model, while this does resolve the issue to invoke the new method, it also introduces new Errors.
EDIT:
What id did try to add it manually in this way:
var edmModel = (EdmModel)builder.GetEdmModel();
var carType = (EdmEntityType)edmModel.FindDeclaredType("Car");
var partType = (EdmEntityType)edmModel.FindDeclaredType("Part");
var partsProperty = new EdmNavigationPropertyInfo();
partsProperty.TargetMultiplicity = EdmMultiplicity.Many;
partsProperty.Target = partType;
partsProperty.ContainsTarget = false;
partsProperty.OnDelete = EdmOnDeleteAction.None;
partsProperty.Name = "Parts";
var carsProperty = new EdmNavigationPropertyInfo();
carsProperty.TargetMultiplicity = EdmMultiplicity.Many;
carsProperty.Target = carType;
carsProperty.ContainsTarget = false;
carsProperty.OnDelete = EdmOnDeleteAction.None;
carsProperty.Name = "Cars";
var nav = EdmNavigationProperty.CreateNavigationPropertyWithPartner(partsProperty, carsProperty);
carType.AddProperty(nav);
config.Routes.MapODataRoute("odata", "odata", edmModel);
while this allowed me to invoke the above speciefied method trough the also above specified URL, it gave me the following error:
{
"odata.error":{
"code":"","message":{
"lang":"en-US","value":"An error has occurred."
},"innererror":{
"message":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; odata=fullmetadata; charset=utf-8'.","type":"System.InvalidOperationException","stacktrace":"","internalexception":{
"message":"The related entity set could not be found from the OData path. The related entity set is required to serialize the payload.","type":"System.Runtime.Serialization.SerializationException","stacktrace":" at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)\r\n at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)\r\n at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()"
}
}
}
}
You have to call "AddNavigationTarget" on the EntitySet. Assume that your namespace is "MyNamespace", then add the following code to your WebApiConfig.cs. In this way, retrieving the data with "Get: odata/Cars(1)/Parts" will work.
var cars = (EdmEntitySet)edmModel.EntityContainers().Single().FindEntitySet("Cars");
var parts = (EdmEntitySet)edmModel.EntityContainers().Single().FindEntitySet("Parts");
var carType = (EdmEntityType)edmModel.FindDeclaredType("MyNamespace.Car");
var partType = (EdmEntityType)edmModel.FindDeclaredType("MyNamespace.Part");
var partsProperty = new EdmNavigationPropertyInfo();
partsProperty.TargetMultiplicity = EdmMultiplicity.Many;
partsProperty.Target = partType;
partsProperty.ContainsTarget = false;
partsProperty.OnDelete = EdmOnDeleteAction.None;
partsProperty.Name = "Parts";
cars.AddNavigationTarget(carType.AddUnidirectionalNavigation(partsProperty), parts);
这篇关于添加自定义查询,支持导航属性来ODataConventionModelBuilder的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!