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

查看:655
本文介绍了添加自定义查询,支持导航属性来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屋!

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