2

NPoco (a .NET micro ORM, derived from PetaPoco) has a method for bulk-inserting records into a database, given a list of a generic type. The method signature is:

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

Internally it takes the name of the type T and uses it to determine the DB table to insert into (similarly the type's property names are mapped to the column names). Therefore it is critically important that a variable of the correct type is passed to the method.

My challenge is this:

  • I am given a list of objects to insert into the DB, as List<IDataItem> where IDataItem is an interface that all insertable objects' classes must implement
  • The list may contain objects of any type that implements IDataItem, and there may be a mixture of types in the list
  • To underline the problem - I do not know at compile time the actual concrete type that I have to pass to InsertBulk

I have tried the following approach, but the result of Convert.ChangeType is Object, so I am passing a list of Objects to InsertBulk, which is invalid.

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

Is there any way I can accomplish this?

4

4 回答 4

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

于 2016-04-06T15:00:21.447 回答
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;
            }
        }
    }
于 2016-04-06T15:07:55.323 回答
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;
            }
        }
    }
于 2016-04-06T15:11:10.497 回答
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;
        }
    }
}
于 2016-04-06T15:13:50.197 回答