(问题末尾的C#代码)
我有以下继承链:
abcdef Merged branch 'feature/1234' into branch 'master'
PreRecord <- Record <- (multiple entity types)
声明属性Record
。
ID As Integer
和PreRecord
不是EDM类型,并且与数据库中的表不对应。
我有一个方法,它将泛型参数约束为Record
,并使用泛型参数作为元素类型构建EF查询。在运行时,如果PreRecord
不仅继承自T
而是继承自PreRecord
,我希望在Record
上添加OrderBy
运算符:
ID
如果参数约束是'Sample 1
Function GetQuery(Of T As PreRecord)(row As T) As IQueryable(Of T)
Dim dcx = New MyDbContext
Dim qry = dcx.Set(Of T).AsQueryable
If TypeOf row Is RecordBase Then
'modify/rewrite the query here
End If
Return qry
End Function
,那么应用使用Record
属性的查询运算符会没有问题。如何在中间方法中使用不同的(缩小的)通用约束并仍然返回ID
/ IQueryable(Of T)
,其中IQueryable<T>
仍然被约束为T
?
我试过了:
PreRecord
不起作用:
LINQ to Entities仅支持转换EDM原语或枚举类型。
C#等价物:
'Sample 2
qry = dcx.Set(Of T).Cast(Of Record).OrderBy(Function(x) x.ID).Cast(Of PreRecord)()
这不起作用:
//Sample 1
public IQueryable<T> GetQuery<T>(T row) where T : PreRecord {
var dcx = new MyDbContext();
var qry = dcx.Set<T>.AsQueryable();
if (row is RecordBase) {
//modify/rewrite the query here
}
return qry;
}
答案 0 :(得分:2)
这里的问题是编译器在编译时检查了查询,而PreRecord类没有ID属性。我们不能简单地使用Cast,因为当它用于定义查询时,解析器会尝试将其转换为sql - 但是sql中不存在这样的东西。 Sql仅支持将一种列类型转换为另一种列类型 - 因此在.NET端,它仅支持原始类型和枚举类型。为了克服编译器查询检查,我们可以使用Expression类来构建动态查询:
ParameterExpression e = Expression.Parameter(typeof(Record));
Expression body = Expression.Property(e, "ID");
Expression<Func<PreRecord, int>> orderByExpression = Expression.Lambda<Func<PreRecord, int>>(body, e);
在查询中使用您的表达式:
qry = dcx.Set<T>.OrderBy(orderByExpression);
这样,您的linq查询将不会在编译期间验证,而是在执行时验证。这里我假设ID是int类型,如果类型不同,则相应地改变它。