我有两个这样的模型
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;
}
}
正确的做法是什么?
经过很长一段时间我想出了一个解决方案:
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
(类),但这只是一个示例,可以改进(例如使用Attributes
)。
public interface IParentEntity<TChildEntity>
{
public List<TChildEntity> Child { get; }
}
公共异步任务
这个怎么样:
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.
}
}
如果您的字段名为“Id”,则不需要
splitOn
参数。您应该记住,任何联接都会返回一个 2D“表”,并且父级(联接的左侧)将乘以与子级相同的次数。这就是为什么您需要父母字典来记录它是“新”父母还是现有父母。