无法在 Dapper 中使用多重映射

发布于 2024-11-07 21:12:09 字数 1673 浏览 1 评论 0原文

与 Dapper 一起玩,我对到目前为止的结果非常满意 - 有趣!

但现在,我的下一个场景是从两个表读取数据 - 一个 Student 和一个 Address 表。

Student 表的主键为 StudentID (INT IDENTITY)Address 具有 AddressID (INT IDENTITY)Student 还有一个名为 AddressID 的 FK,链接到 Address 表中。

我的想法是创建两个类,每个表一个,并具有我感兴趣的属性。此外,我将 Address 类型的 PrimaryAddress 属性放入我的 Student 类。

然后,我尝试在单个查询中检索学生和地址数据 - 我模仿 Github page

var data = connection.Query<Post, User>(sql, (post, user) => { post.Owner = user; });
var post = data.First();

这里检索一个 Post 和一个 User,并将帖子的所有者设置为用户 - 返回的类型是一个帖子 - 正确的?

因此,在我的代码中,我为通用 Query 扩展方法定义了两个参数 - Student 作为应返回的第一个参数,以及 Address作为第二个,它将存储到学生实例中:

var student = _conn.Query<Student, Address>
                  ("SELECT s.*, a.* FROM dbo.Student s 
                        INNER JOIN dbo.Address a ON s.AddressID = a.AddressID 
                        WHERE s.StudentenID = @Id", 
                    (stu, adr) => { stu.PrimaryAddress = adr; },  
                    new { Id = 4711 });

问题是 - 我在 Visual Studio 中收到错误:

使用通用方法 'Dapper.SqlMapper.Query(System.Data.IDbConnection, 细绳, 系统.Func, 动态的,System.Data.IDbTransaction, 布尔值、字符串、整数?, System.Data.CommandType?)' 需要 6 类型参数

我真的不明白为什么 Dapper 坚持使用带有 6 个类型参数的重载...

Playing around with Dapper, I'm quite pleased with the results so far - intriguing!

But now, my next scenario would be to read data from two tables - a Student and an Address table.

Student table has a primary key of StudentID (INT IDENTITY), Address has an AddressID (INT IDENTITY). Student also has an FK called AddressID linking into the Address table.

My idea was to create two classes, one for each table, with the properties I'm interested in. Additionally, I put an PrimaryAddress property of type Address onto my Student class in C#.

I then tried to retrieve both student and address data in a single query - I mimick the sample that's given on the Github page:

var data = connection.Query<Post, User>(sql, (post, user) => { post.Owner = user; });
var post = data.First();

Here, a Post and a User are retrieved, and the owner of the post is set to the user - the type returned is a Post - correct?

So in my code, I define two parameters to the generic Query extension method - a Student as the first which should be returned, and an Address as the second, which will be stored onto the student instance:

var student = _conn.Query<Student, Address>
                  ("SELECT s.*, a.* FROM dbo.Student s 
                        INNER JOIN dbo.Address a ON s.AddressID = a.AddressID 
                        WHERE s.StudentenID = @Id", 
                    (stu, adr) => { stu.PrimaryAddress = adr; },  
                    new { Id = 4711 });

Trouble is - I get an error in Visual Studio:

Using the generic method
'Dapper.SqlMapper.Query(System.Data.IDbConnection,
string,
System.Func,
dynamic, System.Data.IDbTransaction,
bool, string, int?,
System.Data.CommandType?)' requires 6
type arguments

I don't really understand why Dapper insists on using this overload with 6 type arguments...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

滿滿的愛 2024-11-14 21:12:09

那是因为我更改了 API 并忘记更新文档,我纠正了错误。

请务必查看 Tests.cs< /a> 获取完整的最新规范。

特别是,旧的 API 过去常常采用 Action 来执行映射,问题是它感觉既随意又不灵活。您无法完全控制返回类型。新的 API 采用 Func。因此,您可以控制从映射器返回的类型,并且它不需要是映射类型。

我只是在多重映射方面增加了一些额外的灵活性,这个测试应该清楚地表明:

class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
}

class Address
{
    public int AddressId { get; set; }
    public string Name { get; set; }
    public int PersonId { get; set; }
}

class Extra
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public void TestFlexibleMultiMapping()
{
    var sql = 
@"select 
1 as PersonId, 'bob' as Name, 
2 as AddressId, 'abc street' as Name, 1 as PersonId,
3 as Id, 'fred' as Name
";
    var personWithAddress = connection.Query<Person, Address, Extra, Tuple<Person, Address,Extra>>
        (sql, (p,a,e) => Tuple.Create(p, a, e), splitOn: "AddressId,Id").First();

    personWithAddress.Item1.PersonId.IsEqualTo(1);
    personWithAddress.Item1.Name.IsEqualTo("bob");
    personWithAddress.Item2.AddressId.IsEqualTo(2);
    personWithAddress.Item2.Name.IsEqualTo("abc street");
    personWithAddress.Item2.PersonId.IsEqualTo(1);
    personWithAddress.Item3.Id.IsEqualTo(3);
    personWithAddress.Item3.Name.IsEqualTo("fred");

}

Dapper 通过单一方法管道所有多重映射 API,因此如果出现故障,它将最终出现在 6 参数中。另一个难题是我不允许一些超级灵活的分割,这是我刚刚添加的。

请注意,splitOn 参数默认为 Id,这意味着它将采用名为 idId 的列作为第一个对象边界。但是,如果您需要多个具有不同名称的主键的边界(例如“3 路”多重映射),您现在可以传入逗号分隔的列表。

因此,如果我们要解决上述问题,下面的方法可能会起作用:

 var student = _conn.Query<Student,Address,Student>
              ("SELECT s.*, a.* FROM dbo.Student s 
                    INNER JOIN dbo.Address a ON s.AddressID = a.AddressID 
                    WHERE s.StudentenID = @Id", 
                (stu, adr) => { stu.PrimaryAddress = adr; return stu;},  
                new { Id = 4711 }, splitOn: "AddressID").FirstOrDefault();

That would be cause I changed APIs and forgot to update the documentation, I corrected the error.

Be sure to have a look at Tests.cs for a full up-to-date spec.

In particular, the old API used to take in an Action<T,U> to perform the mapping, the trouble was that it felt both arbitrary and inflexible. You could not fully control the return type. The new APIs take in a Func<T,U,V>. So you can control the type you get back from the mapper and it does not need to be a mapped type.

I just tied up some additional flexibility around multi mapping, this test should make it clear:

class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
}

class Address
{
    public int AddressId { get; set; }
    public string Name { get; set; }
    public int PersonId { get; set; }
}

class Extra
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public void TestFlexibleMultiMapping()
{
    var sql = 
@"select 
1 as PersonId, 'bob' as Name, 
2 as AddressId, 'abc street' as Name, 1 as PersonId,
3 as Id, 'fred' as Name
";
    var personWithAddress = connection.Query<Person, Address, Extra, Tuple<Person, Address,Extra>>
        (sql, (p,a,e) => Tuple.Create(p, a, e), splitOn: "AddressId,Id").First();

    personWithAddress.Item1.PersonId.IsEqualTo(1);
    personWithAddress.Item1.Name.IsEqualTo("bob");
    personWithAddress.Item2.AddressId.IsEqualTo(2);
    personWithAddress.Item2.Name.IsEqualTo("abc street");
    personWithAddress.Item2.PersonId.IsEqualTo(1);
    personWithAddress.Item3.Id.IsEqualTo(3);
    personWithAddress.Item3.Name.IsEqualTo("fred");

}

Dapper pipes all the multi mapping APIs through a single method, so if something fails it will end up in the 6 param one. The other piece of the puzzle was that I did not allow for some super flexible splits, which I just added.

Note, the splitOn param will default to Id, meaning it will take a column called id or Id as the first object boundary. However if you need boundaries on multiple primary keys that have different names for say a "3 way" multi mapping, you can now pass in a comma separated list.

So if we were to fix the above, probably the following would work:

 var student = _conn.Query<Student,Address,Student>
              ("SELECT s.*, a.* FROM dbo.Student s 
                    INNER JOIN dbo.Address a ON s.AddressID = a.AddressID 
                    WHERE s.StudentenID = @Id", 
                (stu, adr) => { stu.PrimaryAddress = adr; return stu;},  
                new { Id = 4711 }, splitOn: "AddressID").FirstOrDefault();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文