如何在LINQ表达式树中访问TPH类型的属性?
我有两个SQL Server表:Order和OrderDetail。基于这些表定义了三个C#类:Order映射至Order表,OrderDetail(抽象基类)和SpecialOrderDetail(继承自前者)采用TPH(表每层次继承)方式映射至OrderDetail表。
我需要获取所有满足以下条件的Order:
Type为0,或者- 关联的
Detail是SpecialOrderDetail且其SpecialType为0
尝试了以下代码:
var orders = database.Order.Where(od => od.Type == 0 || (od.Detail is SpecialOrderDetail sp && sp.SpecialType == 0)).ToList();
但Visual Studio报错:CS8122: An expression tree may not contain an 'is' pattern-matching operator。
因为无法将SpecialOrderDetail的SpecialType属性移至OrderDetail类,目前采用的替代方案是:
var orders = database.Order.Where(od => od.Type == 0 || od.Detail is SpecialOrderDetail).ToList(); orders.RemoveAll(od => od.Type != 0 && od.Detail is SpecialOrderDetail sp && sp.SpecialType != 0);
但这个方案需要重复检查Type,还会从数据库拉取不必要的记录后再过滤。请问在不使用原生SQL语句的情况下,能否将SpecialType检查直接放入数据库查询中?我使用的是.NET Framework 4.8和Entity Framework 6.1.3。
EF6.1.3的表达式树不支持is模式匹配操作,但可以通过以下两种方式将过滤条件推送到数据库执行,避免内存中二次过滤:
方法1:显式类型转换+空值检查
利用TPH的映射特性,EF6会自动将类型转换和属性检查翻译成对应的SQL(包含判别列校验和属性值过滤):
var orders = database.Order.Where(od => od.Type == 0 || ((od.Detail as SpecialOrderDetail) != null && (od.Detail as SpecialOrderDetail).SpecialType == 0)).ToList();
方法2:使用OfType<T>关联查询
如果Order和OrderDetail是一对多关系(导航属性为Order.Details集合),可以结合Any和OfType实现过滤:
var orders = database.Order.Where(od => od.Type == 0 || od.Details.OfType<SpecialOrderDetail>().Any(sp => sp.SpecialType == 0)).ToList();
如果是一对一关系(Order.Detail为单个实体),可以通过关联主键的方式查询:
var orders = database.Order.Where(od => od.Type == 0 || database.OrderDetail.OfType<SpecialOrderDetail>().Any(sp => sp.OrderId == od.Id && sp.SpecialType == 0)).ToList();
以上两种方法都会让数据库直接执行完整的过滤逻辑,不会拉取冗余数据。
内容的提问来源于stack exchange,提问作者formicini

