Dapper参数无效

7

我正在尝试使用Dapper orm执行以下简单查询:

var sqlString = new StringBuilder();
sqlString.Append("select a.acct AccountNumber,");
sqlString.Append("       b.first_name FirstName,");
sqlString.Append("       b.last_name LastName,");
sqlString.Append("       a.rr RrNumber,");
sqlString.Append("       c.addr1 AddressLine1,");
sqlString.Append("       c.addr2 AddressLine2,");
sqlString.Append("       c.addr3 AddressLine3,");
sqlString.Append("       c.addr4 AddressLine4,");
sqlString.Append("       c.addr5 AddressLine5,");
sqlString.Append("       c.addr6 AddressLine6,");
sqlString.Append("       c.addr7 AddressLine7,");
sqlString.Append("       c.addr8 AddressLine8 ");
sqlString.Append("from (pub.mfclac as a left join pub.mfcl as b on a.client=b.client) ");
sqlString.Append("left join pub.mfclad as c on a.client=c.client ");
sqlString.Append("where a.acct = '@ZYX'");

var connection = new OdbcConnection(_connectionString);

var result = connection.Query(sqlString.ToString(),
    new
    {
        ZYX = accountNumber
    });            

然而,当我使用已知存在的账号号码执行此操作时,dapper没有返回任何内容。因此,我尝试删除引号以验证参数实际上是否被账号号码替换,但是从服务器返回的错误表明"@ZYX"周围存在语法错误。这意味着dapper没有用给定值替换参数。你有什么想法,为什么会发生这种情况?根据有限的文档,这应该是“可以工作”的。

编辑1

无法使其工作。使用string.format作为解决方法插入参数。

2个回答

19

这里有两个问题; 首先(尽管您在问题中已经注意到了这一点) where a.acct = '@ZYX', 根据SQL规则,不使用任何参数 - 它看起来是为了匹配包含@符号的字面字符串。对于SQL-Server(见下面的注释),正确的用法应该是where a.acct = @ZYX

可是!由于您使用的是OdbcConnection,命名参数不适用。如果您实际上连接到像SQL-Server这样的数据库,我强烈建议您使用纯粹的ADO.NET客户端,其具有比ODBC更好的功能和性能。但是,如果ODBC是您唯一的选择:它不使用命名参数。直到几天前,这将代表一个重大问题,但是根据Passing query parameters in Dapper using OleDb,代码(但还没有NuGet软件包)现在支持ODBC。如果您从源代码构建(或等待下一个版本),您应该能够使用:

...
where a.acct = ?

在您的命令中,并且:

var result = connection.Query(sqlString.ToString(),
new {
    anythingYouLike = accountNumber
});

请注意,名称(anythingYouLike)不被ODBC使用,因此可以是任何你喜欢的名字。在更复杂的场景中,例如:

Note that the name (anythingYouLike) is not used by ODBC, so can be... anything you like. In a more complex scenario, for example:


.Execute(sql, new { id = 123, name = "abc", when = DateTime.Now });

dapper 使用了一些匿名类型的实现知识来理解值的原始顺序,以便将它们按正确的顺序添加到命令中(idnamewhen)。

最后观察:

这意味着dapper不会用给定的值替换参数。

Dapper 绝对不会用给定的值替换参数。这并不是正确的 SQL 参数化方式:通常情况下,参数是单独发送的,从而确保:

  • 没有 SQL 注入风险
  • 最大限度的查询计划重用
  • 没有格式问题

请注意,某些 ADO.NET / ODBC 提供程序 理论上可能会 选择通过替换来在内部实现这些功能,但这与 dapper 无关。


感谢Marc的澄清。只是提醒一下: 是的,我必须使用Odbc(我正在连接到Progress DB,这是我唯一可用的连接类型)。 我不确定您是否参与了该项目,但是顺便说一句,如果文档明确提到了ODBC参数限制,那对于那些不知道ODBC工作原理细节的人来说会很好。文档反复使用IDbconnection或var作为连接类型,这让我觉得任何连接都可以使用参数。感谢您的解释。我学到了关于dapper和ODBC的新知识! - Bitfiddler
@BitFiddler 我会在部署NuGet包的同时更新示例,包括ODBC。 - Marc Gravell
@BitFiddler 我应该指出:这些限制与dapper无关;这只是ODBC的工作方式 - Marc Gravell
希望我的评论没有被解释为责备Dapper。我只是想提供一些有用的指导意见,以便完善文档。即使在常规示例旁边加上一个小星号,并附上您的解释链接(直到下一个版本发布),这也足够了。我猜您可能没有得到多少报酬,所以我不想显得太需要帮助。我可以自己添加链接,但是给我更改文档的权限并确保它们正确可能比您自己添加链接更费力;-) 谢谢您的时间。 - Bitfiddler
@MarkGravell 刚刚从git下载并尝试了OleDb解决方案,并且它完美地完成了任务!感谢您的时间和努力。Dapper是一个非常实用的工具。您大约有什么时候会把这个更改更新到nuget呢?我现在正在使用自己的构建,但长期来看我想要使用nuget软件包,如果您能猜测一下更新的时间,那我会在那个时候再来查看。 - Bitfiddler
显示剩余3条评论

0

我从重复的问题Dapper必须声明标量变量跳转到这里。

错误:必须声明标量变量"@Name"。

我使用以下代码动态创建查询:

public static bool Insert<T>(T entity)
        {
            var tableName = entity.GetType().CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == nameof(TableAttribute))?.ConstructorArguments?.FirstOrDefault().Value as string;
            if (string.IsNullOrEmpty(tableName))
                throw new Exception($"Cannot save {entity.GetType().Name}. Database models should have [Table(\"tablename\")] attribute.");
            DBSchema.TryGetValue(tableName.ToLower(), out var fields);
            using (var con = new SqlConnection(ConnectionString))
            {
                con.Open();

                var sql = $"INSERT INTO [{tableName}] (";
                foreach (var field in fields.Where(x => x != "id")) 
                {
                    sql += $"[{field}]"+",";
                }
                sql = sql.TrimEnd(',');
                sql += ")";
                sql += " VALUES (";
                foreach (var field in fields.Where(x => x != "id"))
                {
                    sql += "@"+field + ",";
                }
                sql = sql.TrimEnd(',');
                sql += ")";

                var affectedRows = con.Execute(sql, entity);
                return affectedRows > 0;
            }
        }

当我的模型像这样时,我遇到了相同的错误:

[Table("Users")]
public class User
{
   public string Name;
   public string Age;
}

我把它们改成了这样:
[Table("Users")]
public class User
{
    public string Name { get; set; }
    public string Age { get; set; }
}

这解决了我的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接