【问题标题】:Dapper parameters not workingDapper 参数不起作用
【发布时间】:2019-12-03 17:11:06
【问题描述】:

我正在尝试通过以下简单查询使用 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
    });            

但是,当我使用已知存在的 accountNumber 执行此操作时,dapper 什么也不返回。因此,我尝试删除引号以验证该参数实际上是否已替换为帐号,但是从服务器返回的错误表明“@ZYX”周围存在语法错误。这意味着 dapper 不会用给定的值替换参数。任何想法为什么会发生这种情况?从那里有限的文档来看,这应该“正常工作”。


编辑1

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

【问题讨论】:

    标签: c# dapper


    【解决方案1】:

    这里有两个问题;首先(尽管您在问题中注意到这一点)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 使用,所以可以是...任何你喜欢的。在更复杂的场景中,例如:

    .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 我将更新示例以包含 ODBC - 同时部署 NuGet 包
    • @BitFiddler 我应该注意:这些限制都与 dapper 无关;这就是ODBC 的工作原理
    • 我希望我的评论没有被解释为责备小巧玲珑。我只是想就文档提出一些有用的指导。即使在带有上述解释链接的普通示例旁边加上一个小星号(直到下一个版本可用)就足够了。我猜你不会因为小巧玲珑而得到太多报酬,所以我不想给人一种太需要的印象。我会自己添加链接,但您可能需要更多的工作来授予我更改文档的权限,然后确保它们正确,然后自己添加链接;-) 干杯感谢您的时间。
    • @MarkGravell 刚刚从 git 下载并尝试了 OleDb 解决方案,它完美地完成了工作!感谢您的时间和精力。 Dapper 是一个非常方便的工具。当这种变化会成为 nuget 的时候,你有什么想法吗?我现在正在使用我自己的构建,但我想长期使用 nuget 包,所以如果你有一个猜测,我会在那个时候回来查看。
    【解决方案2】:

    我从重复的问题来到这里:Dapper must declare the scalar variable

    错误:必须声明标量变量“@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; }
    }
    

    它为我解决了问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多