需要调用InsertBulk <t>(IEnumerable <t> pocos),但T仅在运行时

时间:2016-04-06 14:48:16

标签: c# generics petapoco dynamictype npoco

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

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

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

我的挑战是:

  • 我获得了一个要插入数据库的对象列表,如List<IDataItem>,其中IDataItem是所有可插入对象的类必须实现的接口
  • 列表可能包含实现IDataItem的任何类型的对象,列表中可能有多种类型
  • 强调问题 - 我不知道在编译时我必须传递给InsertBulk的实际具体类型

我尝试了以下方法,但Convert.ChangeType的结果是Object,所以我将一个对象列表传递给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;
            }
        }
    }

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

4 个答案:

答案 0 :(得分:3)

您必须创建一个类型为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

答案 1 :(得分:1)

这段代码可能会帮助你做你想做的事情(虽然有点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;
            }
        }
    }

答案 2 :(得分:1)

您可以尝试这样的事情:

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

答案 3 :(得分:0)

我会对这个问题采取有根据的猜测,因为我没有代码来运行它。

怎么样:

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