我正在为 JSON.NET 创建一个能够序列化和反序列化表达式 (System.Linq.Expressions) 的 JsonConverter。我已经完成了最后 5% 左右的工作,并且在运行从反序列化表达式生成的 LINQ-to-SQL 查询时遇到了问题。
这是表达式:
Expression<Func<TestQuerySource, Bundle>> expression = db => (
from b in db.Bundles
join bi in db.BundleItems on b.ID equals bi.BundleID
join p in db.Products on bi.ProductID equals p.ID
group p by b).First().Key;
这是 LINQ-to-SQL 中非常简单的分组查询。TestQuerySource
是一个实现System.Data.Linq.DataContext
。Bundle
, BundleItem
, Product
, 都是用TableAttribute
其他映射属性修饰的 LINQ-to-SQL 实体。它们对应的 datacontext 属性都是Table<T>
正常的属性。换句话说,这里没有什么特别值得注意的。
但是,当我在反序列化表达式后尝试运行查询时,出现以下错误:
System.Reflection.TargetInvocationException:
Exception has been thrown by the target of an invocation. --->
System.NotSupportedException: The member '<>f__AnonymousType0`2[Bundle,BundleItem].bi' has no supported translation to SQL.
我了解这意味着表达式正在执行的操作无法通过 LINQ-to-SQL 查询提供程序转换为 SQL。似乎它与创建匿名类型作为查询的一部分有关,就像作为连接语句的一部分一样。通过比较原始表达式和反序列化表达式的字符串表示来支持这个假设:
原始(工作):
{db => db.Bundles
.Join(db.BundleItems,
b => b.ID,
bi => bi.BundleID,
(b, bi) => new <>f__AnonymousType0`2(b = b, bi = bi))
.Join(db.Products,
<>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.bi.ProductID,
p => p.ID,
(<>h__TransparentIdentifier0, p) =>
new <>f__AnonymousType1`2(<>h__TransparentIdentifier0 = <>h__TransparentIdentifier0, p = p))
.GroupBy(<>h__TransparentIdentifier1 =>
<>h__TransparentIdentifier1.<>h__TransparentIdentifier0.b,
<>h__TransparentIdentifier1 => <>h__TransparentIdentifier1.p)
.First().Key}
反序列化(损坏):
{db => db.Bundles
.Join(db.BundleItems,
b => b.ID,
bi => bi.BundleID,
(b, bi) => new <>f__AnonymousType0`2(b, bi))
.Join(db.Products,
<>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.bi.ProductID,
p => p.ID,
(<>h__TransparentIdentifier0, p) => new <>f__AnonymousType1`2(<>h__TransparentIdentifier0, p))
.GroupBy(<>h__TransparentIdentifier1 =>
<>h__TransparentIdentifier1.<>h__TransparentIdentifier0.b,
<>h__TransparentIdentifier1 => <>h__TransparentIdentifier1.p)
.First().Key}
当需要访问匿名类型的非原始类型属性时,似乎会出现此问题。在这种情况下,bi
正在访问该属性以获取 BundleItem
' 的ProductID
属性。
我无法弄清楚区别是什么 - 为什么访问原始表达式中的属性可以正常工作,但在反序列化表达式中却不行。
我猜这个问题与一些关于匿名类型在序列化过程中丢失的信息有关,但我不确定在哪里可以找到它,甚至不知道要寻找什么。
其他示例:
值得注意的是,像这样更简单的表达式可以正常工作:
Expression<Func<TestQuerySource, Category>> expression = db => db.Categories.First();
即使进行分组(不加入)也可以:
Expression<Func<TestQuerySource, Int32>> expression = db => db.Categories.GroupBy(c => c.ID).First().Key;
简单的连接工作:
Expression<Func<TestQuerySource, Product>> expression = db => (
from bi in db.BundleItems
join p in db.Products on bi.ProductID equals p.ID
select p).First();
选择匿名类型有效:
Expression<Func<TestQuerySource, dynamic>> expression = db => (
from bi in db.BundleItems
join p in db.Products on bi.ProductID equals p.ID
select new { a = bi, b = p }).First();
这是最后一个示例的字符串表示形式:
原来的:
{db => db.BundleItems
.Join(db.Products,
bi => bi.ProductID,
p => p.ID,
(bi, p) => new <>f__AnonymousType0`2(a = bi, b = p))
.First()}
反序列化:
{db => db.BundleItems
.Join(db.Products,
bi => bi.ProductID,
p => p.ID,
(bi, p) => new <>f__AnonymousType0`2(bi, p))
.First()}