传入模型时如何使用 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<int>
或List<string>
参数 ?
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<int>
或List<string>
参数 ?
重申 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<Person>()
将调用 Dapper 传递底层 SQL 和参数。
底层查询是完全参数化的 SQL:WHERE 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动态对象