传入模型时如何使用 Dapper 构建动态 Where 子句

Posted

技术标签:

【中文标题】传入模型时如何使用 Dapper 构建动态 Where 子句【英文标题】:How do I build a dynamic Where clause with Dapper when passing in a model 【发布时间】:2017-02-08 09:22:49 【问题描述】:

我有一个看起来像这样的示例模型:

public class PersonModel

     public int Id get; set;
     public string FirstName get; set;
     public string Lastname get; set;
     public string City get; set;

在我的存储库中,我想创建一个搜索方法,在其中传递我的模型 - 但并非所有字段都将始终被填充。我想根据模型中的字段是否已填充来创建 WHERE 和 AND。如果该字段未填充,那么我不想为其创建 WHERE 子句。

例如 - 如果我传入 FirstName = "Bob" 和 City = "Boston",那么我希望我的搜索看起来像这样:

SELECT * FROM PersonTable WHERE FirstName = @firstName AND City = @city

由于我没有传入 Id 或 LastName,我不希望将它们添加到查询中。如果我只是传入 City = "Boston" 那么我希望它看起来像这样:

SELECT * FROM PersonTable WHERE City = @city

我的 repo 方法看起来像这样

using Dapper;
public List<PersonModel> Search(PersonModel model)

//db = DbConnection connection
    var selectSql = "SELECT * FROM PersonTable "; //build out where clause somehow
    return db.Query<PersonModel>(selectSql).ToList();

我的问题是如何在我的 repo 方法中正确构建它?

【问题讨论】:

您可以有条件地附加 where 子句。仪式? 我假设是的,是的。但是,我正在寻找一个很好的例子来说明如何做到这一点。 【参考方案1】:

你也可以使用 Dapper 的SqlBuilder。

请注意,您必须安装 Dapper.SqlBuilder NuGet 包,因为 Dapper 的主要发行版不附带它。

这是一个例子:

    [Test]
    public void Test()
    
        var model = new PersonModel FirstName = "Bar", City = "New York";

        var builder = new SqlBuilder();

        //note the 'where' in-line comment is required, it is a replacement token
        var selector = builder.AddTemplate("select * from table /**where**/");

        if (model.Id > 0)
            builder.Where("Id = @Id", new  model.Id );

        if (!string.IsNullOrEmpty(model.FirstName))
            builder.Where("FirstName = @FirstName", new  model.FirstName );

        if (!string.IsNullOrEmpty(model.Lastname))
            builder.Where("Lastname = @Lastname", new  model.Lastname );

        if (!string.IsNullOrEmpty(model.City))
            builder.Where("City = @City", new  model.City );

        Assert.That(selector.RawSql, Is.EqualTo("select * from table WHERE FirstName = @FirstName AND City = @City\n"));

        //var rows = sqlConnection.Query(selector.RawSql, selector.Parameters);
    

你可以找到一些例子here。

【讨论】:

也适用于List&lt;int&gt;List&lt;string&gt; 参数 ? SqlBuilder 对于这句话:SELECT * FROM TABLE1 WHERE id IN (SELECT t2.id FROM TABLE2 as t2 , TABLE1 as t1 WHERE t1.id=t2.id AND /**where**/ ) ? @tCode,只需将 'select *' 更改为 'update' 并将 .Query() 更改为 .Execute()【参考方案2】:

这应该可以为您解决问题,简洁明了:

var selectSql = "SELECT * FROM PersonTable WHERE (@FirstName IS NULL OR FirstName =  @FirstName) AND (@LastName IS NULL OR LastName =  @LastName) AND (@City IS NULL OR City =  @City) AND (@Id IS NULL OR Id =  @Id) OPTION(RECOMPILE)";

return conn.Query<PersonModel>(selectSql, new

     model.FirstName,
     model.Lastname,
     model.City,
     Id = model.Id == 0? (int?)null: (int?)model.Id        
).ToList();

【讨论】:

这比使用 C# 条件构建 SQL 更简洁。考虑使用 OPTION(RECOMPILE),以便查询优化可以考虑提供的实际参数。 如果我没有选择使用 Dapper.SqlBuilder,我会选择这个作为接受的答案。 对于List&lt;int&gt;List&lt;string&gt; 参数 ? 重申 bbsimonbbs 的观点,如果您不添加 OPTION (RECOMPILE),您的查询在某些情况下可能会非常慢。 SQL 会针对一个执行路径进行优化,只有该路径才能合理运行。 关于 OPTION(RECOMPILE) 声明,非常感谢 bbsimonbb 和 Marie 的陈述,我现在已将其添加到帖子中【参考方案3】:

如果您想尝试其他替代方案,DapperQueryBuilder 可能更易于使用:

var query = cn.QueryBuilder($@"
    SELECT * 
    FROM PersonTable
   /**where**/
");

if (model.Id > 0)
    query.Where($"Id = model.Id");

if (!string.IsNullOrEmpty(model.FirstName))
    query.Where($"FirstName = model.FirstName");

if (!string.IsNullOrEmpty(model.Lastname))
    query.Where($"Lastname = model.Lastname");

if (!string.IsNullOrEmpty(model.City))
    query.Where($"City = model.City");


var results = query.Query<Person>(); 

Query&lt;Person&gt;() 将调用 Dapper 传递底层 SQL 和参数。

底层查询是完全参数化的 SQLWHERE FirstName = @p0 AND LastName = @p1

免责声明:我是这个库的作者之一

【讨论】:

【参考方案4】:
bool isFirstWhereSet = false;
bool isCityWhereSet = false;
string sqlQuery = "SELECT * FROM PersonTable "  ;
if (! String.IsNullOrEmpty(model.FirstName ))

sqlQuery  =sqlQuery  + "WHERE FirstName =@FirstName" ;
isFirstWhereSet = true;


if (! String.IsNullOrEmpty(model.City))

isCityWhereSet  = true ;
if (! isFirstWhereSet )
sqlQuery  = sqlQuery  + " WHERE City = @city";
else
sqlQuery  = sqlQuery  + " AND City = @city";




if (isFirstWhereSet == true && isCityWhereSet == true )
 return db.Query<PersonModel>(sqlQuery , new  FirstName = model.FirstName  , City = mode.City).ToList();
else if (isFirstWhereSet == true && isCityWhereSet  == false)
 return db.Query<PersonModel>(sqlQuery , new  FirstName = model.FirstName ).ToList();
else if (isFirstWhereSet == false && isCityWhereSet  == true)
 return db.Query<PersonModel>(sqlQuery , new  City= model.City).ToList();
else

 return db.Query<PersonModel>(sqlQuery).ToList();

【讨论】:

【参考方案5】:

您可以使用ExpressionExtensionSQL 库。该库将 lambda 表达式转换为 where 子句,并且可以与 dapper 和 ADO 一起使用。

【讨论】:

以上是关于传入模型时如何使用 Dapper 构建动态 Where 子句的主要内容,如果未能解决你的问题,请参考以下文章

具有动态模型的 Dapper ORM - 如何不返回字段而不是“字段”= NULL?

使用Dapper时,如何将MySqlParameters[] 变成Dapper.DynamicParameters动态对象

如何让 Dapper 动态不区分大小写?

dapper利用DynamicParameters构建动态参数查询

如何使用休眠条件运行函数?

使用dapper时动态拼接查询sql有啥好的方法吗