实体框架中联合数据“视图”上的 OData

Posted

技术标签:

【中文标题】实体框架中联合数据“视图”上的 OData【英文标题】:OData over a "View" of unioned data in Entity Framework 【发布时间】:2014-03-18 15:45:47 【问题描述】:

我正在开发一个 OData 服务,该服务公开了一个使用 Entity Framework 6 创建的模型。这些实体是只读的,并且加载起来非常大。 (几千兆字节)

为了实现这一点,我正在使用 Microsoft.OData.EntityFrameworkProvider,(我目前没有使用 WebAPI)这大部分工作正常,但是我有一个新要求需要公开一个实际上是多个联合的实体为我们的最终用户/客户提供方便的其他实体。

EF 上下文片段

    ...
    public DbSet<Foo> FooRecs  get; set; 
    public DbSet<Bar> BarRecs  get; set; 
    public IQueryable<FooBarRec> FooBarRecs
    
        get
        
            return FooRecs.Select(f => new FooBarRec()  Id = f.Id, Description =  f.Description )
                .Union(
                BarRecs.Select(b => new FooBarRec()  Id = b.Id, Description = b.Description )
                );
        
    
    ...

我无法通过 odata 公开此 IQueryable 属性,因为 EntityFrameworkProvider 似乎只公开 DbSets 而不是任何 IQueryable 对象,这是有道理的。

我的问题是,使用 OData 完成此任务的最佳方法是什么?

我想避免将重复数据加载到第三个中间表中,因为数据可能很大,而且加载该数据的时间是每晚完成的。看来 QueryInterceptor 只允许您对已经查询过的数据进行子过滤,因此这似乎不起作用。

我尝试做一些疯狂的事情,比如扩展 DbSet 并制作我自己的 DbViewSet,它在它的构造函数中接受两个集合,但找不到关闭循环的方法。

使用 OData 和 EF6 完成类似于视图的最佳方法是什么?

谢谢!

【问题讨论】:

好吧,如果两个集合返回相同的 POCO ... 为了清楚起见,从技术上讲,FooBarRec 与 Foo 具有相同的结构,但与 Bar 不同。无论如何,您不能将查询投射到映射实体上:***.com/questions/5325797/… 在这种情况下,我决定创建一个未映射的 POCO 来表示联合集,以便我可以对实体集进行子过滤。我的问题是如何在 OData 中表示这一点 我知道这没什么用,但是您需要 TSQL 中的联合服务器端和映射的 POCO 来让 OData 为您工作。 如果联合由视图执行,EF 仍然可以在 ODataController 指定时应用额外条件。 您不能在同一服务中混合使用 Entity Frawmework 数据提供者和其他提供者。阅读WCF, OData, DbContext, and Joins 【参考方案1】:

我使用 Web API 和 OData 创建了一个示例。看起来很简单,可以满足你的要求:

首先,我定义了以下 CLR 类来映射您的类型并且不需要创建任何视图:

public class FooBarRec

    public int Id  get; set; 
    public string Name  get; set; 
    public Foo FooRec  get; set; 
    public Bar BarRec  get; set; 


public class Foo

    public int FooId  get; set; 
    public string FooName  get; set; 


public class Bar

    public int BarId  get; set; 
    public string BarName  get; set; 

接下来,我根据上述 CLR 类型创建一个 OData EdmModel:

private static IEdmModel GetEdmModel()

    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<FooBarRec>("FooBarRecs");
    builder.EntitySet<Foo>("Foos");
    builder.EntitySet<Bar>("Bars");
    return builder.GetEdmModel();

接下来,我创建 OData 控制器来处理 OData URI 查询:

 // Controller
 public class FoosController : ODataController
 
     public const int Num = 10;
     public static IList<Foo> foos = Enumerable.Range(0, Num).Select(i =>
            new Foo
            
                FooId = 100 + i,
                FooName = "Foo #" + (100 + i)
            ).ToList();

     [EnableQuery]
     public IHttpActionResult Get()
     
         return Ok(foos);
     
 

 public class BarsController : ODataController
 
     public const int Num = 10;
     public static IList<Bar> bars = Enumerable.Range(0, Num).Select(i =>
            new Bar
            
                BarId = 1000 + i,
                BarName = "Bar #" + (1000 + i)
            ).ToList();

    [EnableQuery]
    public IHttpActionResult Get()
    
        return Ok(bars);
    
 

 public class FooBarRecsController : ODataController
 
     public const int Num = 10;
     public static IList<FooBarRec> fooBarRecs = Enumerable.Range(0, Num).Select(i =>
             new FooBarRec
             
                   Id = i,
                   Name = "ForBarRec #" + i
              ).ToList();

     static FooBarRecsController()
     
        for(int i = 0; i < Num; i++)
        
            fooBarRecs[i].FooRec = FoosController.foos[i];
            fooBarRecs[i].BarRec = BarsController.bars[i];
        
     

     [EnableQuery]
     public IHttpActionResult Get()
     
         return Ok(fooBarRecs);
     
 

注意:在 FooBarRec 中,我创建了两个属性(FooRec 和 BarRec)并使用它们来建立 FooBarRec、Foo 和 Bar 之间的关系。

现在,我们可以提供最终用户想要的任何数据。

例如,EndUser 可以使用以下两个 URI 来查询单个数据:

    ~/odata/Foos

    ~/odata/酒吧

    或者,他可以使用以下URI来查询Union数据:

    ~/odata/FooBarRecs?$expand=FooRec,BarRec

就是这样。

【讨论】:

【参考方案2】:

正如 Jodrell 在 cmets 中所说。

完成这项工作的唯一方法是创建一个 DbSet 来表示一个 SQL 视图。

我可以让 Entity Framework 正确处理模型绑定的唯一方法是在创建表后删除表,然后创建然后创建视图。

因为它是一个联合视图,我必须在模型完成初始化后处理这个问题,否则它会尝试在视图上强制使用聚集索引,如果视图包含联合则这是不可能的。

【讨论】:

以上是关于实体框架中联合数据“视图”上的 OData的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 OData 提要 C# 的存储过程的自定义分页,没有实体框架

实体框架 + ODATA + 动态列映射

在没有实体框架的情况下创建 Odata Web API 应用程序

OData 带更新的实例,并能取得元数据格式类型

您如何将乐观并发与 WebAPI OData 控制器一起使用

OData 和 WebAPI:模型上不存在导航属性