C# 简洁的一对多关系与泛型方法

问题描述 投票:0回答:3

我有两个这样的模型

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;
     }
 }

正确的做法是什么?

c# dapper
3个回答
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
(类),但这只是一个示例,可以改进(例如使用
Attributes
)。


0
投票
public interface IParentEntity<TChildEntity>
{
 public List<TChildEntity> Child { get; }
}

公共异步任务> ExecuteQueryAsync(字符串commandText,对象参数=默认!,CommandType commandType = CommandType.StoredProcedure,Action操作=默认!) 其中 TParent : IParentEntity { 尝试 { 等待使用 var con = _sqlConnectionFactory.GetSqlConnection(); { 打开连接(con); Log.Verbose("正在执行命令文本{commandText}", commandText); var isDynamicParams = 参数是 DynamicParameters; var 动态参数 = isDynamicParams ?参数:新的动态参数(参数); var 字典 = 新字典(); var result =等待con.QueryAsync(commandText,(父级,子级) => { 父.子.添加(子); 返回父级; }, 动态参数, 默认, true, "Id", 100, commandType); if (isDynamicParams) { 动作?.Invoke((DynamicParameters)dynamicParameters); } con.Close(); 返回 QResults.From(结果)!; } } catch(异常前) { 返回前任!; } }


-1
投票

这个怎么样:

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“表”,并且父级(联接的左侧)将乘以与子级相同的次数。这就是为什么您需要父母字典来记录它是“新”父母还是现有父母。

© www.soinside.com 2019 - 2024. All rights reserved.