Не могу заставить работать мультикартирование в Dapper
Играя с 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 ответ
Это было бы, потому что я изменил 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();