将自定义查询支持的导航属性添加到ODataConventionModelBuilder

情况

我创建了以下Model类

public class Car { public int Id {get;set;} public string Name {get;set;} public virtual ICollection 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;} } 

和一个匹配的DbContext

 public class CarContext : DbContext { public DbSet Cars {get;set;} public DbSet PartStates {get;set;} public DbSet Parts {get;set;} } 

并创建了一个WebApplication,通过odata使用脚手架模板“Web API 2 OData Controller with Actions,using Entity Framework”

我还创建了以下webapi配置:

 public static class WebApiConfig { public static void Register(HttpConfiguration config) { var builder = new ODataConventionModelBuilder(); builder.EntitySet("Cars"); builder.EntitySet("PartStates"); builder.EntitySet("Parts"); var edmModel = builder.GetEdmModel(); config.Routes.MapODataRoute("odata", "odata", edmModel); } } 

我现在想要将以下方法添加到我的汽车控制器中

 // GET: odata/Cars(5)/Parts [Queryable] public IQueryable GetParts([FromODataUri] int key) { var parts = db.PartStates.Where(s => s.CarId == key).Select(s => s.Part).Distinct(); return parts; } 

并使用此Url检索数据:

 http://localhost/odata/Cars(1)/Parts 

但它不起作用,而是我得到以下错误:

 { "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":"" } } } 

所以我的问题是,甚至可能吗?!

我尝试手动创建一个Navigation属性,并将其添加到edm模型,虽然这确实解决了调用新方法的问题,但它也引入了新的错误。

编辑:

什么id尝试以这种方式手动添加它:

 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); 

虽然这允许我通过上面指定的URL调用上面的speciefied方法,它给了我以下错误:

 { "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.d__1b.MoveNext()" } } } } 

您必须在EntitySet上调用“AddNavigationTarget”。 假设您的命名空间是“MyNamespace”,然后将以下代码添加到WebApiConfig.cs。 通过这种方式,使用“Get:odata / Cars(1)/ Parts”检索数据将起作用。

  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); 

进一步考虑@ FengZhao的答案,为了使url odata / Cars正常工作,您还需要将导航属性链接构建器注册到实体集链接构建器。

 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"; var navigationProperty = carType.AddUnidirectionalNavigation(partsProperty); cars.AddNavigationTarget(navigationProperty, parts); var linkBuilder = edmModel.GetEntitySetLinkBuilder(cars); linkBuilder.AddNavigationPropertyLinkBuilder(navigationProperty, new NavigationLinkBuilder((context, property) => context.GenerateNavigationPropertyLink(property, false), true)); 

Web Api无法针对任何已注册的URI模板解析您的URL。

使用Route Debugger来解决这个问题。 http://blogs.msdn.com/b/webdev/archive/2013/04/04/debugging-asp-net-web-api-with-route-debugger.aspx

我相信我们的问题是您通过url传递的id参数尝试使其更明确

 config.Routes.MapHttpRoute ( "DefaultInternalApi", "api/{controller}/{objectType}/{Id}/{relation}", defaults: new { Id = System.Web.Http.RouteParameter.Optional, } );