#c# #dapper
#c# #dapper
Вопрос:
У меня есть две такие модели
public class Teacher
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Class> Classes { get; set; } = new();
}
public class Class
{
public int Id { get; set;}
public int NumOfStudents { get; set;}
}
У меня также есть хранимая процедура для выбора всех записей.
Моя проблема в том, как можно использовать dapper с дженериками. В общем случае я использую этот общий метод
public List<T> LoadData<T, U>(string storedProcedure, U parameters, string connectionStringName)
{
var connectionString = GetConnectionString(connectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
var list = connection.Query<T>(storedProcedure, parameters,
commandType: CommandType.StoredProcedure).ToList();
return list;
}
}
Теперь из-за отношения «один ко многим» я пытаюсь использовать этот общий метод, но безуспешно
public List<T> LoadMultipleData<T, L, U>(string storedProcedure, T parentModel, L childModel, U parameters, string connectionStringName)
{
var connectionString = GetConnectionString(connectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
var list= connection.Query<T, U, T>(storedProcedure,
(parent, child) =>
{
parent.parentModel = child;
return parent;
},
parameters,
commandType: CommandType.StoredProcedure).ToList();
return list;
}
}
Как правильно это сделать?
Комментарии:
1. Посмотрите на это: dapper-tutorial.net/result-multi-mapping
2. Обратите внимание, что Dapper использует ‘splitOn’ в запросах с несколькими отображениями, поэтому он может определить, где заканчивается объект и начинается другой, не уверен, правильно ли вы делаете это в своем SP, но явно не делаете этого на стороне Dapper
Ответ №1:
После долгого времени я придумал решение для этого:
public async Task<List<TEntity>> LoadMultipleDataAsync<TEntity, TManyEntity, TParameters>(string query, TParameters parameters, string connectionStringName)
{
var connectionString = GetConnectionString(connectionStringName);
using IDbConnection connection = new SqlConnection(connectionString);
var dictionary = new Dictionary<int, TEntity>();
var result = await connection.QueryAsync<TEntity, TManyEntity, TEntity>(query,
(one, many) =>
{
var onePropertyInfo = typeof(TEntity).GetProperty("Id");
var oneId = (int)onePropertyInfo?.GetValue(one);
if (!dictionary.TryGetValue(oneId, out var currentOne))
{
currentOne = one;
dictionary.Add(oneId, currentOne);
}
var manyPropertyType = typeof(TManyEntity).Name;
var manyPropertyName = typeof(TEntity)?.GetProperty($"{manyPropertyType}es")?.Name;
var list = (IList<TManyEntity>)currentOne?.GetType()?.GetProperty(manyPropertyName)?.GetValue(currentOne);
list.GetType().GetMethod("Add").Invoke(list, new object[] { many });
return currentOne;
}, parameters);
return result.Distinct().ToList();
}
Это решение предполагает, что Id
всегда int
.
В этой строке кода:
var manyPropertyName = typeof(TEntity)?.GetProperty($"{manyPropertyType}es")?.Name;
Мы ссылаемся на множественное число Class
(Классы) через отражение, но это всего лишь пример, и его можно улучшить.
Ответ №2:
Как насчет этого:
public List<T> LoadMultipleData<T, L, U>(string storedProcedure, T parentModel, L childModel, U parameters, string connectionStringName)
{
var connectionString = GetConnectionString(connectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
var parents = new Dictionary<int, T>();
var list= connection.Query<T, U, T>(storedProcedure,
(parent, child) =>
{
T foundParent;
if (!parents.TryGetValue(parent.id, out foundParent))
{
foundParent = parent;
parents.Add(foundParent.id, foundParent);
}
foundParent.parentModel = child;
return foundParent;
},
parameters,
commandType: CommandType.StoredProcedure).ToList();
return list.Distinct(); // or return parents if you like.
}
}
splitOn
Параметр не нужен, если ваши поля имеют имя «Id». Вы должны помнить, что любое соединение возвращает 2D «таблицу», а родительские элементы (левая часть соединения) будут умножены столько раз, сколько у них есть дочерних элементов. Вот почему вам нужен родительский словарь, чтобы отслеживать, является ли он «новым» родительским или существующим.
Комментарии:
1. Это только я или этот код даже не компилируется?
T doesn't contain a definition for id
.