gpt4 book ai didi

c# - "No supported translation to SQL"反序列化 IQueryable 表达式后

转载 作者:太空狗 更新时间:2023-10-29 21:55:10 30 4
gpt4 key购买 nike

我正在为 JSON.NET 创建一个 JsonConverter,它能够序列化和反序列化表达式 (System.Linq.Expressions)。我只完成了最后 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 中非常简单的分组查询。 TestQuerySourceSystem.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正在访问属性以到达 BundleItemProductID属性(property)。

我想不通的是区别是什么——为什么在原始表达式中访问该属性可以正常工作,但在反序列化表达式中却不行。

我猜这个问题与序列化过程中丢失的匿名类型的某种信息有关,但我不确定去哪里找它,甚至不知道要找什么。


其他示例:

值得注意的是,像这样更简单的表达式效果很好:

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()}

最佳答案

我认为不同之处在于,在工作示例中,匿名类型是使用属性构造的,而在损坏的情况下,它是使用构造函数实例化的。

L2S 假设在查询翻译期间,如果您为属性分配特定值,该属性将只返回该值。

L2S 不假定名称为 abc 的构造函数参数将初始化名为 Abc 的属性。这里的想法是,一个 ctor 可以做任何事情,而一个属性只会存储一个值。

请记住,匿名类型与自定义 DTO 类没有什么不同(字面意思!L2S 无法区分它们)。

在您的示例中,您要么 a) 不使用匿名类型(有效) b) 仅在最终投影中使用 ctor(有效 - 一切都作为最终投影工作,甚至是任意方法调用。L2S 很棒。)或c) 在查询的 sql 部分使用构造函数(损坏)。这证实了我的理论。

试试这个:

var query1 = someTable.Select(x => new CustomDTO(x.SomeString)).Where(x => x.SomeString != null).ToList();
var query2 = someTable.Select(x => new CustomDTO() { SomeString = x.SomeString }).Where(x => x.SomeString != null).ToList();

第二个可以,第一个不行。


(大牛更新)

重建反序列化表达式时,确保使用正确的重载 Expression.New如果需要通过构造函数设置属性。正确使用的重载是 Expression.New(ConstructorInfo, IEnumerable<Expression>, IEnumerable<MemberInfo>) Expression.New(ConstructorInfo, IEnumerable<Expression>, MemberInfo[]) .如果使用其他重载之一,参数将只传递到构造函数,而不是分配给属性。

关于c# - "No supported translation to SQL"反序列化 IQueryable 表达式后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10035633/

30 4 0