Невозможно получить мульти-картографию для работы в Dapper

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

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

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

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

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

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

Здесь извлекаются Post и a User, а владелец сообщения устанавливается пользователем - возвращаемый тип является Post - правильным?

Итак, в моем коде я определяю два параметра для общего метода расширения Query - a 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 аргументы типа

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

Ответ 1

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