将自定义查询支持的导航属性添加到 ODataConventionModelBuilder [英] Adding a custom query backed Navigation Property to ODataConventionModelBuilder

查看:36
本文介绍了将自定义查询支持的导航属性添加到 ODataConventionModelBuilder的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了以下模型类

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

和匹配的 DbContext

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

并使用脚手架模板Web API 2 OData Controller with Actions, using Entity Framework"创建了一个 WebApplication 以通过 odata 提供此功能

And created a WebApplication to make this available via odata, using the scaffolding template "Web API 2 OData Controller with Actions, using Entity Framework"

我还创建了以下 webapi 配置:

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

并使用此 Url 检索数据:

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?!

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

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.

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

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

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

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)
   at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)
   at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()"
      }
    }
  }
}

推荐答案

您必须在 EntitySet 上调用AddNavigationTarget".假设您的命名空间是MyNamespace",然后将以下代码添加到您的 WebApiConfig.cs.这样,使用Get: odata/Cars(1)/Parts"检索数据就可以了.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆