无法在Dapper中使用多映射

时间:2011-05-14 11:23:29

标签: c# dapper multi-mapping

和Dapper一起玩,我对目前的结果非常满意 - 很有趣!

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

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

我的想法是创建两个类,每个表一个,具有我感兴趣的属性。另外,我将PrimaryAddress类型的Address属性放到我的Student上C#中的课程。

然后我尝试在一个查询中检索学生和地址数据 - 我模仿Github page上给出的样本:

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

在此处,检索PostUser,并将帖子的所有者设置为用户 - 返回的类型为Post - 是否正确?

因此,在我的代码中,我为通用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,   串,   System.Func,   dynamic,System.Data.IDbTransaction,   bool,string,int?,   System.Data.CommandType?)'需要6   类型参数

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

1 个答案:

答案 0 :(得分:22)

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

请务必查看Tests.cs以获取完整的最新规范。

特别是旧的API用于接受Action<T,U>来执行映射,问题在于它感觉既武断又不灵活。您无法完全控制返回类型。新API采用Func<T,U,V>。因此,您可以控制从映射器返回的类型,它不需要是映射类型。

我只是围绕多映射增加了一些额外的灵活性,这个测试应该说清楚:

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();