我有一个基于现有模型动态构建sql语句的函数,然后添加每个表中存在的一些公共字段。
问题在于,当我尝试添加公共字段时,我遇到了一些奇怪的行为。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.SqlClient;
using Dapper;
public class Program
{
private static readonly dynamic[] Data = new dynamic[] { new {Foo = 1, Bar = 2}, new {Foo = 3, Bar = 4} };
public static void Main()
{
Console.WriteLine("Hello World");
}
public static void Broken()
{
var parameters = ((IEnumerable<dynamic>)Data).Select(x => new DynamicParameters(x));
parameters.ToList().ForEach(x => x.AddDynamicParams(new {Bax = 7}));
using (var connection = new SqlConnection(""))
{
// This fails with a "Must declare scalar variable @Bax".
connection.Execute("INSERT INTO DataTable(Foo, Bar, Bax) VALUES(@Foo, @Bar, @Bax)", parameters);
}
}
public static void Working()
{
var parameters = ((IEnumerable<dynamic>)Data).Select(x => new DynamicParameters(x)).ToList();
parameters.ForEach(x => x.AddDynamicParams(new {Bax = 7}));
using (var connection = new SqlConnection(""))
{
// This works fine.
connection.Execute("INSERT INTO DataTable(Foo, Bar, Bax) VALUES(@Foo, @Bar, @Bax)", parameters);
}
}
}
如果您注意到,唯一的区别就是我对.ToList()
的通话位置。在Broken
函数中,我在.ForEach
之前将其称为链的一部分,在Working
函数中,我将其称为与赋值相同的行。
我已尝试将调用替换为AddDynamicParams
,并为每个参数添加Add
次调用,但这似乎并不重要。我也试过没有运气for(var parameter in parameters) { parameter.AddDynamicParams(new {Bax = 7}) }
。
不幸的是,没有好的方法可以在不设置断点的情况下显示templates
并在Visual Studio中检查对象的.templates
属性。如果有人可以提供更好的方式来证明模板消失,我会全力以赴。
答案 0 :(得分:2)
在Broken
方法中,您有效地枚举了两次收藏。这是由于LINQ Deferred Execution。
这一行:
parameters.ToList().ForEach(x => x.AddDynamicParams(new {Bax = 7}));
将使用IEnumerable<dynamic>
创建List<dynamic>
来枚举您的ForEach
,然后根本不使用此列表,然后再次传递parameters
}对象connection.Execute
方法:
connection.Execute("**your query**", parameters);
每当Dapper枚举parameter
对象时,它将再次投射每个对象(不在上面的列表中找到的相同对象),从而创建一个未使用AddDynamicParams
修改的新实例。
更清楚一点,你破碎的方法与以下内容相同:
public static void Broken()
{
var parameters = ((IEnumerable<dynamic>)Data).Select(x => new DynamicParameters(x));
var parametersList = parameters.ToList(); // you create a list
parametersList.ForEach(x => x.AddDynamicParams(new {Bax = 7})); // then you modify that list
// parametersList != parameters
using (var connection = new SqlConnection(""))
{
// This fails with a "Must declare scalar variable @Bax".
connection.Execute("INSERT INTO DataTable(Foo, Bar, Bax) VALUES(@Foo, @Bar, @Bax)", parameters); // but here you are passing the old parameters collection
}
}
要解决您的问题,请使用您的Working
版本,或再次使用LINQ创建可运行的可枚举版本:
var parameters = ((IEnumerable<dynamic>)Data).Select(x =>
{
var p = new DynamicParameters(x);
p.AddDynamicParams(new {Bax = 7});
return p;
});