在使用表继承的LINQ to Entities查询中将类型转换为派生类型

24

我有一个基于Table Per Hierarchy继承的LINQ to Entities模型。我在基类型上进行了查询,想要执行特定类型相关的逻辑操作。例如:

IQueryable<BaseType> base = ...

// this works fine
var result = base.Select(b => b is DerivedType1 ? 1 : 2).ToList();

// this doesn't compile to SQL
var result2 = base.Select(b => b is DerivedType1 ? ((DerivedType1)b).DerivedProperty : null).ToList();

有没有办法在不分别处理每个派生类型的IQueryable的情况下完成类似于这样的操作:

// I'd rather not do this:
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty);
var resultB = base.OfType<DerivedType2>().Select(d => default(int?));
var result = resultA.Concat(resultB).ToList();

1
连接和拼接似乎是唯一的选项。 - Danny Varod
1
问题可能是你的DerivedProperty不可为空吗?那么查询是否会混淆列是否允许为空? - Kirsten
5个回答

31

在 LINQ-to-Entities 中,不支持将一个实体类型直接转换成另一个实体类型(例如 (DerivedType1)b),但是可以使用 as 运算符(b as DerivedType1)。因此你可以尝试以下方法:

var result2 = base
    .Select(b => b is DerivedType1
        ? (b as DerivedType1).DerivedProperty
        : null)
    .ToList();

这似乎是有效的,尽管我记得在发布之前尝试过这个,所以可能只适用于EF5+? - ChaseMedallion
3
@ChaseMedallion:可能是的,我认为在 EF 5 之前我从未使用过它,但我不确定。顺便说一下:同时我发现如果 DerivedProperty 是可空的,那么三元运算符就不需要了,你可以直接使用:Select(b => (b as DerivedType1).DerivedProperty)。如果 b 不是 DerivedType1 类型,EF 不会抛出异常,而是自动返回 null - Slauma
好的。EF会执行null合并操作,除非您将一个null作为非可空类型加载到内存中。 - ChaseMedallion
1
@Slauma 在你所写的基础上,我想补充一点:对于非可空类型,似乎可以直接转换为可空类型。例如,假设 DerivedProperty 是一个 int 类型:Select(b => (int?) (b as DerivedType1).DerivedProperty) - Zero3

1
OfType<DerivedType1>() 

如果可能的话,将返回一个IEnumerable。尝试将基础类型更改为IEnumerable而不是IQueryable,这样使用IQueryable时可能会遇到一些SQL限制。

当然,前提是您实际上没有查询数据库?


1

您可以使用EntityFramework.Extended来改善查询性能,而不是进行2次数据库往返。

var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty).Future();
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)).Future();
var result = resultA.Concat(resultB).ToList();

在这种情况下,只执行一次到 bd 的往返。 这个框架对 EF 中的许多其他事情非常有用。

0

试试这个,我从来没做过需要这样做的事情,但这应该可以。如果使用base,首先不要使用它,因为它是关键字,但如果你必须使用,请使用@base,名称前面的@表示它不被用作关键字。

var resultA = base.Select(aVar =>
                            (aVar is DerivedType1) ?
                                (DerivedType)(((DerivedType1)aVar).DerivedProperty)
                                :
                                (DerivedType)(default(int?))
                        ).ToList();

你是不是想回答另一个问题?这不是我问题的答案... - ChaseMedallion

0
你可以在基础类型上有一个方法,然后在派生类型中重写它来提供相关的属性值。
public class MyBaseClass
{
    public virtual int GetSomething()
    {
        throw new NotImplementedException();
    }
}

public class MyDerivedClass1 : MyBaseClass
{
    public int SomeProperty { get; set; }

    public override int GetSomething()
    {
        return this.SomeProperty;
    }
}

public class MyDerivedClass2 : MyBaseClass
{
    public int SomeOtherProperty { get; set; }

    public override int GetSomething()
    {
        return this.SomeOtherProperty;
    }
}

然后你可以:

var result = base.Select(b => b.GetSomething()).ToList();

1
这在LINQ to Entities中不起作用 - 它需要先使用.ToList()将实体带入内存,这是非常低效的,因为查询只需要每个实体的一个属性。 - Danny Varod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接