【问题标题】:LINQ inheritance "Down-Cast" with a new Property具有新属性的 LINQ 继承“向下转换”
【发布时间】:2011-11-22 15:03:28
【问题描述】:

我有以下两个类:

public class Base
{
    public int Id { get; set; }
    public string PropertyA { get; set; }
}

[NotMapped]
public class Derived : Base
{
    public string PropertyB { get; set; }
}

我有以下查询:

var baseQ = from b in db.Bases
            let propertyB = SomeCalculation()
            select new { Base = b, PropertyB = propertyB };

这是按原样工作的。 我想要的是这样的(伪代码):

List<Derived> list =  (from b in db.Bases
                      let propertyB = SomeCalculation()
                      select new { Base = b, PropertyB = propertyB }).ToList();

是否可以将选择“向下转换”到派生类,或者我必须为派生类编写一个构造函数,看起来像这样:

public Derived(Base b, string b) { ... }

我的最终解决方案:我将派生类更改为以下内容(因为您甚至不能在对象初始化程序中使用 string.Format):

public class Derived
{
    public Base Base { get; set; }
    public string PropertyB { get; set; }

    public string CalculatedProperty { get { ... } }//For string.Format and other stuff
}

我正在做如下分配:

List<Derived> list =  (from b in db.Bases
                      let propertyB = SomeCalculation()
                      select new Derived { Base = b, PropertyB = propertyB }).ToList();

【问题讨论】:

    标签: c# .net linq linq-to-entities


    【解决方案1】:

    在这种情况下您不能强制转换,因为您的查询返回的是匿名类型。相反,您可以使用对象初始化器将属性设置为 Derived 类:

    List<Derived> list = (from b in db.Bases
                         let propertyB = SomeCalculation()
                         select new Derived
                         {
                             Id = b.Id,
                             PropertyA = b.PropertyA,
                             PropertyB = propertyB
                         }).ToList();
    

    如果您有大量属性,您可以采用您建议的方法,或者添加一个包含您的属性的数据传输对象 (DTO),然后使用 AutoMapper 之类的库为您执行映射。

    【讨论】:

    • 假设db 是一个数据上下文,这将抛出旧的'Cannot constructor a Derived,因为它是一个非标量'类型错误。在执行选择之前,您需要将 Bases 检索到集合中。
    • 在我的例子中,DB 是一个 DbContext。所以,这是有效的。我将为我的班级选择一个构造函数。对于这个简单的场景,DTO 似乎太多了。 :)
    • @Kirk:你是对的!这行不通。我以某种方式过度阅读了您的部分答案。那么执行此操作的方法是对象初始化程序吗? :)
    【解决方案2】:
    List<Derived> list = db.Bases.ToList()
    .Select(p => new Derived()
                     {
                         Id = b.Id,
                         PropertyA = b.PropertyA,
                         PropertyB =  SomeCalculation(),
                     }).ToList();;
    

    【讨论】:

    • 这将返回一个空列表,因为Bases 都不是DerivedTypes - 我们可以看到DerivedType 实际上是具有额外计算属性的Base (let propertyB = SomeCalculation() )
    • 虽然这行得通,但这不是我需要的。计算使用 Query 内部的东西(它实际上不是一种方法)。另一件事是:这将选择我的数据库中的每个基础。效率不会很高;)。
    【解决方案3】:

    接受的答案不起作用,因为 EF 不允许您在数据库中构造具体类型。解决方法 - 它确实允许您选择匿名类型。

    以 Ahmad 的回答为基础:

    var aList = (from b in db.Bases
                 let propertyB = SomeCalculation()
                 select new 
                 {
                     Id = b.Id,
                     PropertyA = b.PropertyA,
                     PropertyB = propertyB
                 }).ToList(); 
    
    List<Derived> list = (from a in aList
                          select new Derived
                          {
                              Id = a.Id,
                              PropertyA = a.PropertyA,
                              PropertyB = a.PropertyB
                          }).ToList(); 
    

    在您的查询上调用ToList 会强制执行查询并将数据检索到您的应用程序中(即它将不再在数据库中执行)。 aList 现在是内存中的一个列表,因此您可以将项目选择到新的 Derived 对象中。

    简单来说,问题是直到您调用ToList 之前的所有内容都被编译成SQL 查询(以提高效率)。这通常是一件好事,但问题是您的数据库没有Derived 的概念,查询select new Derived 无法转换为SQL。

    【讨论】:

    • 我同意你的看法。不幸的是,接受的答案也有效。它编译,运行,并获得所需的结果。我将检查该解决方案与您的新解决方案之间是否存在任何性能差异。谢谢。
    • @Shion 如果可行,请继续。我不认为它会。
    猜你喜欢
    • 1970-01-01
    • 2019-09-25
    • 2023-02-02
    • 2018-02-28
    • 2016-11-11
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多