【问题标题】:Entity Framework Linq set property of an object without additional projection by SELECT实体框架 Linq 设置对象的属性,无需通过 SELECT 进行额外投影
【发布时间】:2016-06-13 17:38:18
【问题描述】:

我正在使用 Entity Framework 6 实现继承方案。继承仅存在于 DTO 级别,即我有两个类 FooBar : Foo, 我有第一个方法选择IQueryable<Foo>,然后有几个方法为特定继承类(如 Bar)选择附加属性。

通常情况下,我会有类似

的代码
from foo in SelectFoo()
join barAdditionalProps in .....    
select new Bar{
    Id = foo.Id,
    Description = foo.Description,
    Baz = barAdditionalProps.Baz}

这将给出一个很好的单个 SQL 查询结果。

不幸的是,这意味着必须在第二次投影期间复制来自 foo 的所有属性(第一个在 SelectFoo 内)。在现实生活中的代码中,这意味着使用 SelectFoo 在每个方法中复制了 20 多个属性。

我想做这样的事情(代码在 LINQPad 中准备,假设 this == EFContext):

void Main()
{      
    (from barBase in SelectT<Bar>()
     join field in this.Fields on barBase.Id equals field.ProductId
     let _1 = barBase.Baz = field.Baz // this part fails with exception
                                      // An expression tree may not contain an assigment operator         
     select barBase)
    .First()
    .Dump();      
}

public IQueryable<T> SelectT<T>() where T : Foo, new() 
{
    return this
           .Products
           .Select(x => new T
           {
                 Id = x.Id,
                 Description = x.Description
           });
}

public class Foo
{
    public string Description {get;set;}
    public int Id {get;set;}
}

public class Bar : Foo
{
    public int Baz {get;set;}
}

收到上述异常后,我正在寻找一种方法来完成这项工作或任何其他解决方案,让我在第二次投影期间不复制所有基类属性。

【问题讨论】:

  • 您应该加入原始查询并将结果投影到 DTO。
  • 只有一个 Foo 会更容易吗,它包含到其他部分的导航属性,以及必要时的 .Include
  • @Gert Arnold,但这意味着我必须在每种方法中复制原始 Foo 的投影,即 3x 相同的 20 行代码? Robert McKree,不确定你的意思。生成此 DTO 的查询使用 7-20 个表,总共超过 50 列,其中仅使用了大约 30 列。 DTO 是 POCO,而不是实体。
  • 这取决于SelectFoo() 中发生的事情,真的。如果它返回仍然连接到 EF 查询提供程序的 IQueryable,您应该仍然能够在其他 LINQ 语句和投影中使用,它们只会选择请求的属性。
  • @Gert Arnold,这就是重点,我对原始 Foo 的 20 个属性感兴趣,我不会在每个使用 SelectFoo() 的方法中重复复制它们的代码。

标签: c# entity-framework linq inheritance select


【解决方案1】:

由于没有现有的工具可以胜任这项工作,我编写了自己的库,该库使用表达式树修改来自动将基类 dto 投影到子类 dto 中。

现在代替这个

     IQueryable<BaseDto> baseQuery = GetBaseQuery();

  IQueryable<SubclassDto> query = from baseDto in baseQuery
                                  let moreData = DataContext.vMoreData.FirstOrDefault(x => x.Id == baseDto.Id)
                                  select new SubclassDto()
                                  {
                                    NewProp1 = moreData.Foo,
                                    NewProp2 = moreData.Baz,
                                    OldProp1 = moreData.SomeOverridingData,

                                    OldProp2 = baseDto.OldProp2,
                                    OldProp3 = baseDto.OldProp3,
                                    OldProp4 = baseDto.OldProp4,
                                    //... 20 more projections from BaseDto to SubclassDto
                                  };

我们有这个

 IQueryable<BaseDto> baseQuery = GetBaseQuery();

 IQueryable<SubclassDto> query = from baseDto in baseQuery                                  
                                 let moreData = DataContext.vMoreData.FirstOrDefault(x => x.Id == baseDto.Id) 
                                 select baseDto.AutoProjectInto(() => new SubclassDto()
                                 {
                                  NewProp1 = moreData.Foo,
                                  NewProp2 = moreData.Baz,
                                  OldProp1 = moreData.SomeOverridingData
                                 });

 IQueryable<SubclassDto> activateQuery = query.ActivateAutoProjects(); 

并且所有未受 SubclassDto 初始化绑定的属性都会自动从 baseDto 投影。

库可通过 Github https://github.com/IKoshelev/Linq.AutoProject 和 NuGet https://www.nuget.org/packages/Linq.AutoProject 获得

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    相关资源
    最近更新 更多