需要调用 InsertBulk<T>(IEnumerable<T> pocos) 但 T 只在运行时知道

Need to call InsertBulk<T>(IEnumerable<T> pocos) but T only known at runtime

NPoco(一个 .NET 微型 ORM,派生自 PetaPoco)有一种方法可以在给定通用类型列表的情况下将记录批量插入数据库。方法签名是:

void InsertBulk<T>(IEnumerable<T> pocos);

在内部它采用类型 T 的名称并使用它来确定要插入的数据库 table(类似地,类型的 属性 名称映射到列名称)。 因此,将正确类型的变量传递给方法至关重要

我的挑战是:

我已经尝试了下面的方法,但是Convert.ChangeType的结果是对象,所以我将一个对象列表传递给InsertBulk,这是无效的。

   private static Exception SaveDataItemsToDatabase(List<IDataItem> dtos)
   {
        using (var db = new DbConnection())
        {
            try
            {
                var dtosByType = dtos.GroupBy(x => x.GetType());

                db.Data.BeginTransaction();

                foreach (var dataType in dtosByType)
                {
                    var type = dataType.Key;
                    var dtosOfType = dataType.Select(x => Convert.ChangeType(x, type));

                    db.Data.InsertBulk(dtosOfType);
                }

                db.Data.CommitTransaction();

                return null;
            }
            catch (Exception ex)
            {
                db.Data.RollbackTransaction();

                return ex;
            }
        }
    }

有什么方法可以做到这一点吗?

您必须创建一个类型为 List<T> 的新列表并将所有项目复制到其中,然后通过反射调用 InsertBulk

foreach(var g in groups)
{

    var dataItemType = g.Key;
    var listType = typeof(List<>).MakeGenericType(new [] { dataItemType });
    var list = (IList) Activator.CreateInstance(listType);

    foreach(var data in g)
        list.Add(data);

    db.Data.GetType()
           .GetMethod("InsertBulk")
           .MakeGenericMethod(dataItemType)
           .Invoke(db.Data, new object[] { list });

}

看到它在这里工作:https://dotnetfiddle.net/BS2FLy

这段代码可能会帮助你做你想做的事(虽然有点 hacky)。

class Program {
    static void Main() {
        var items = new IDataItem[] {
            new TestItem(),
            new TestItem(),
            new TestItem2(),
            new TestItem2(),
        };

        foreach (var kv in items.GroupBy(c => c.GetType())) {
            // group by actual type
            var type = kv.Key;
            var batch = kv.ToArray();
            // grab BulkInsert<Type> method
            var insert = typeof(Test).GetMethod("BulkInsert").MakeGenericMethod(type);
            // create array of Type[]
            var casted = Array.CreateInstance(type, batch.Length);
            Array.Copy(batch, casted, batch.Length);
            // invoke
            insert.Invoke(new Test(), new object[] { casted});
        }            

        Console.ReadKey();
    }        
}

public interface IDataItem {

}

public class TestItem : IDataItem {

}

public class TestItem2 : IDataItem
{

}

public class Test {
    public void BulkInsert<T>(IEnumerable<T> items) {
        Console.WriteLine(typeof(T).Name);
    }
}

如果使用您的原始代码,它将类似于:

    private static Exception SaveDataItemsToDatabase(List<IDataItem> dtos)
    {
        using (var db = new DbConnection())
        {
            try
            {                    
                db.Data.BeginTransaction();
                foreach (var dataType in dtos.GroupBy(x => x.GetType())) {
                    var type = dataType.Key;
                    var items = dataType.ToArray();
                    var insert = db.Data.GetType().GetMethod("BulkInsert").MakeGenericMethod(type);
                    // create array of Type[]
                    var casted = Array.CreateInstance(type, items.Length);
                    Array.Copy(items, casted, items.Length);
                    // invoke
                    insert.Invoke(db.Data, new object[] {casted});
                }

                db.Data.CommitTransaction();

                return null;
            }
            catch (Exception ex)
            {
                db.Data.RollbackTransaction();

                return ex;
            }
        }
    }

您可以尝试这样的操作:

   private static Exception SaveDataItemsToDatabase(List<IDataItem> dtos)
   {
        using (var db = new DbConnection())
        {
            try
            {
                var dtosByType = dtos.GroupBy(x => x.GetType());

                db.Data.BeginTransaction();
                var method = db.Data.GetType().GetMethod("InsertBulk");
                foreach (var dataType in dtosByType)
                {
                    var genericMethod = method.MakeGenericMethod(dataType.Key);
                    genericMethod.Invoke(db.Data, new object[] { dataType.Value };                   
                }

                db.Data.CommitTransaction();

                return null;
            }
            catch (Exception ex)
            {
                db.Data.RollbackTransaction();

                return ex;
            }
        }
    }

我将对此进行有根据的猜测,因为我手头没有 运行 它的代码。

怎么样:

private static Exception SaveDataItemsToDatabase(List<IDataItem> dtos)
{
    using (var db = new DbConnection())
    {
        try
        {

            db.Data.BeginTransaction();

            dtos
                .GroupBy(dto => dto.GetType())
                .ForEach(grp => {
                    db.Data.BulkInsert(dtos.Where(n => n.GetType().Equals(grp.Key).ToList());
                });

            db.Data.CommitTransaction();

            return null;
        }
        catch (Exception ex)
        {
            db.Data.RollbackTransaction();

            return ex;
        }
    }
}