基于“视图”的数据实体框架中的联合数据

本文关键字:数据 框架 实体 基于 视图 | 更新日期: 2023-09-27 17:50:45

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

为了完成这个,我使用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完成类似于视图的事情的最好方法是什么?

谢谢!

基于“视图”的数据实体框架中的联合数据

我创建了一个带有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来查询单个数据:

  1. ~/odata/foo
  2. ~/odata/酒吧

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

    • ~/odata/FooBarRecs吗?扩大美元= FooRec BarRec

正如Jodrell在评论中所说的。

唯一的方法是使用DbSet来表示SQL视图。

我能得到实体框架适当地处理模型绑定的唯一方法是在创建表之后删除表,然后创建视图。

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