Не могу заставить работать мультимаппирование в Dapper - PullRequest
17 голосов
/ 14 мая 2011

Играя с Dapper, я до сих пор доволен результатами - интригующе!

Но теперь мой следующий сценарий будет читать данные из двух таблиц - таблицы Student и Address.

Student таблица имеет первичный ключ StudentID (INT IDENTITY), Address имеет AddressID (INT IDENTITY). Student также имеет FK с именем AddressID, связывающий таблицу Address.

Моя идея состояла в том, чтобы создать два класса, по одному для каждой таблицы, со интересующими меня свойствами. Кроме того, я добавил свойство PrimaryAddress типа Address в свой класс Student в C #.

Затем я попытался извлечь данные об учениках и адресах в одном запросе - я имитирую образец, приведенный на странице Github :

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

Здесь извлекаются Post и User, а владельцу сообщения присваивается пользователь - возвращаемый тип - 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, динамический, System.Data.IDbTransaction, bool, string, int ?, System.Data.CommandType?) 'Требуется 6 введите аргументы

Я не очень понимаю, почему Даппер настаивает на использовании этой перегрузки с 6 аргументами типа ...

1 Ответ

22 голосов
/ 14 мая 2011

Это может быть связано с тем, что я изменил 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, то есть в качестве границы первого объекта будет принят столбец с именем id или Id. Однако если вам нужны границы для нескольких первичных ключей, которые имеют разные имена, например, для «трехстороннего» многократного отображения, теперь вы можете передать список через запятую.

Так что, если бы мы исправили вышесказанное, вероятно, сработало бы следующее:

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