我不理解dapper的映射、multimapping和QueryMultiple
本文关键字:multimapping QueryMultiple 映射 不理解 dapper | 更新日期: 2023-09-27 18:02:09
标题说明了一切,我试图使用它,但我不明白它。这个问题可能是由于我是一个业余爱好者而缺乏知识,但是我已经读了很多关于这个东西的问题,并且在谷歌上搜索了三天,我仍然不理解它。
我有这么多的问题,我不确定我应该写在一个问题,甚至有人会读它所有。如果有人有其他的解决方案,或者认为我应该把它分成不同的问题……好吧,我愿意听取建议。
我打算写一个例子,但是我又读了好几天的例子,没有帮助我。
我只是不能让我的头脑去理解像github的例子是如何工作的:
var sql =
@"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});
那么,Post
有一个类型为User
的属性这个属性叫做Owner
,对吧?比如:
public class Post
{
...
public User Owner { get; set;}
}
因此Query<Post, User, Post>
将返回具有所有属性的Post
实例,并且将创建User
实例并将其分配给Post.Owner
属性?如何将简单的参数添加到该查询中,例如,如果有人想将id作为int
参数传递给...WHERE Id = @Id", new {Id = id}
,那么给定参数现在是(post, user) => { post.Owner = user; return post;}
,该参数应该添加到哪里?参数总是引用给定的类型,只能使用简单的典型参数进行动态查询,两者可以同时使用吗?如何?
另外,如何区分哪个DB字段属于哪个对象?它让类名=DB表名?如果类没有与DB表相同的名称,我想使用[Table]
属性,它会起作用还是属性仅适用于Dapper.Contrib.Extensions
方法?它是否适用于共享同一DB表的对象?
关于同一表不同对象的问题,假设我有一个Person
对象它有一个BankAccount
对象:
public class Person
{
...
public BankAccount Account {get; set;}
...
}
public class BankAccount
{
private string _Account;
public string Account
{
get { return _Account; }
set
{
if(!CheckIfIBANIsCorrect(value))
throw new Exception();
_Account = value;
}
}
private bool CheckIfIBANIsCorrect(string IBAN)
{
//...
//Check it
}
}
我可以将字符串account存储在与Person
相同的表中,因为每个人都有一个由该人的Id引用的单个帐户。我该如何映射这样的东西呢?是否有一种方法,我应该简单地加载结果在一个动态对象,然后创建所有的对象,Query
将创建Person
对象的其余部分,我应该自己创建嵌套的对象?
顺便说一下,splitOn
应该如何在所有这些中使用?我知道它应该把结果分成各种"组",这样你就可以按id拆分结果,并采取你需要的东西,但我不明白我应该如何从不同的"组"中检索信息,以及它如何返回不同的"组",列表,枚举,什么?
QueryMultiple
是另一件远远超出我理解的事情,不管我读了多少问题和答案。你知道的….Read
的东西是如何工作的?我在这里读到的或在谷歌上搜索到的所有内容都假设Read
是某种自动的东西,可以奇迹般地区分物体。它是否按类名划分结果这样我就能确保每个对象都有正确的表名?在这种情况下,[Table]
属性会发生什么?
我想我的问题是我找不到(我想它不存在)一个web页面,描述这一切在GitHub非常稀缺(例子),以及在具体的案件中我仍然只有找到答案,没有回答什么我试着去了解但只有,具体的情况下,这是令人困惑的我当我读它们的时候越来越多,因为似乎每个人都使用不同的方法没有解释为什么或怎样。
我认为您的主要问题与连接表查询的Dapper查询是认为列表中的第二个参数始终是"参数"参数。考虑下面的代码:
var productsWithoutCategories = conn.Query<Product>(
"SELECT * FROM Products WHERE ProductName LIKE @nameStartsWith + '%'",
new { nameStartsWith = "a" }
);
这里,有两个参数"sql"answers"param" -如果我们使用命名参数,那么代码将是这样的:
var productsWithoutCategories = conn.Query<Product>(
sql: "SELECT * FROM Products WHERE ProductName LIKE @nameStartsWith + '%'",
param: new { nameStartsWith = "a" }
);
在你的例子中,你有
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});
第二个参数实际上是一个名为"map"的参数,它告诉Dapper如何在SQL查询中连接两个表的情况下组合实体。如果我们使用命名参数,那么它看起来像这样:
var data = connection.Query<Post, User, Post>(
sql: sql,
map: (post, user) => { post.Owner = user; return post;}
);
我将在一个完整的示例中使用northnd数据库类。假设我们有类
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public bool Discontinued { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
并且我们想要构建一个product列表,并填充嵌套的Category类型,我们将执行以下操作:
using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
{
var productsWithCategories = conn.Query<Product, Category, Product>(
"SELECT * FROM Products INNER JOIN Categories ON Categories.CategoryID = Products.CategoryID,
map: (product, category) =>
{
product.Category = category;
return product;
},
splitOn: "CategoryID"
);
}
这将遍历JOIN后的产品和类别数据的所有行,并生成一个唯一的产品列表,但无法确定如何将类别数据与之组合,因此它需要一个"map"函数,该函数接受一个产品实例和一个类别实例,并且必须返回一个将类别数据与之组合的产品实例。在本例中,这很简单——我们只需要将Product实例上的Category属性设置为Category实例。
注意,我必须指定一个"splitOn"值。Dapper假定表的关键列将被简单地称为"Id",如果是,那么它可以自动处理这些列上的连接。然而,在本例中,我们连接了一个名为"CategoryID"的列,因此我们必须告诉Dapper根据该列名将数据拆分(分为Products和Categories)。
如果我们还想指定"param"对象来过滤结果,那么我们可以做如下操作:
var productsWithCategories = conn.Query<Product, Category, Product>(
"SELECT * FROM Products INNER JOIN Categories ON Categories.CategoryID = Products.CategoryID WHERE ProductName LIKE @nameStartsWith + '%'",
map: (product, category) =>
{
product.Category = category;
return product;
},
param: new { nameStartsWith = "a" },
splitOn: "CategoryID"
);
为了回答你的最后一个问题,QueryMultiple简单地一次执行多个查询,然后允许你分别读取它们。例如,与其这样做(使用两个单独的查询):
using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
{
var categories = conn.Query("SELECT * FROM Categories");
var products = conn.Query("SELECT * FROM Products");
}
您可以指定一个SQL语句,在一个批处理中包含两个查询,但是您需要从QueryMultiple:
返回的组合结果集中分别读取它们:using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
{
var combinedResults = conn.QueryMultiple("SELECT * FROM Categories; SELECT * FROM Products");
var categories = combinedResults.Read<Category>();
var products = combinedResults.Read<Product>();
}
我认为我看到的QueryMultiple的其他例子有点令人困惑,因为它们通常从每个查询返回单个值,而不是完整的行集(这是在简单的查询调用中更常见的)。希望以上内容能帮你解决这个问题。
注意:我还没有涵盖你关于[Table]属性的问题-如果你在尝试了这个之后仍然有问题,那么我建议为它创建一个新的问题。Dapper使用"splitOn"值来决定一个实体的列何时结束,何时开始(在上面的JOIN示例中,有Product字段,然后是Category字段)。如果您将Category类重命名为其他东西,那么查询仍然可以工作,Dapper在这种情况下不依赖于表名-因此希望您根本不需要[table]。