2016-04-06 4 views
2

NPoco (ein .NET-Mikro-ORM, abgeleitet von PetaPoco) verfügt über eine Methode zum Masseneinfügen von Datensätzen in eine Datenbank mit einer Liste eines generischen Typs . Die Methodensignatur ist:Aufruf von InsertBulk <T> (IEnumerable <T> pocos) aber nur zur Laufzeit bekannt

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

Intern nimmt es den Namen des Typs T und verwendet es die DB-Tabelle, um zu bestimmen einzufügen in (in ähnlicher Weise die Eigenschaftsnamen des Typs sind mit den Spaltennamen abgebildet). Daher ist es von entscheidender Bedeutung, dass eine Variable vom richtigen Typ an die Methode übergeben wird.

Meine Herausforderung ist dies:

  • ich gegeben wird eine Liste der Objekte in die DB einzufügen, wie List<IDataItem> wo IDataItem ist eine Schnittstelle, die alle einsetzbaren Objekte Klassen
  • Die Liste implementieren muss, kann enthalten Objekte jeglicher Art, die IDataItem, und es kann eine Mischung von Typen in der Liste
  • um das Problem zu unterstreichen implementiert - ich weiß nicht, zum Zeitpunkt der Kompilierung des tatsächlichen konkreten Typen, ich muß passieren zu InsertBulk

Ich habe den folgenden Ansatz versucht, aber das Ergebnis von Convert.ChangeType ist Objekt, also übergebe ich eine Liste von Objekten an InsertBulk, die ungültig ist.

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

Gibt es eine Möglichkeit, dies zu erreichen?

+0

haben Sie versucht, mit 'dynamic' Stichwort? –

+0

Wenn ich dtosOfType als dynamisch deklariere, bekomme ich das gleiche Ergebnis - die an BulkInsert übergebene Variable ist eine Liste von Object. – Laurence

+0

Haben Sie versucht, Reflection zu verwenden? Verwenden Sie 'Type.GetMethod()', um die Methodendefinition zu erhalten, und verwenden Sie 'MethodInfo.Invoke()' mit den entsprechenden 'System.Type's? – Adwaenyth

Antwort

3

Sie müssen eine neue Liste des Typs List<T> erstellen und alle Ihre Artikel dorthin kopieren, dann rufen Sie InsertBulk über Reflexion.

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

} 

Sehen Sie es, hier zu arbeiten: https://dotnetfiddle.net/BS2FLy

+0

Ex in dieser Zeile abrufen: Objekt des Typs 'System.Collections.Generic.List'1 [TestConsole.Program + IDataItem]' kann nicht in den Typ 'System.Collections.Generic.IEnumerable'1 [Programm + Test1Dto]' konvertiert werden. – Laurence

+0

@Laurence behoben, funktioniert jetzt definitiv. – dcastro

+0

Ja, es funktioniert! Vielen Dank - ich dachte ich hätte ein unmögliches Problem! – Laurence

1

Dieser Code könnte Ihnen helfen zu tun, was Sie (wenn auch ein wenig hacky) wollen.

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

Wenn Ihr Original-Code verwenden, wird es so etwas wie:

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

Dies ist im Grunde die gleiche Antwort als dcastro. –

+0

Als ich es schrieb - seine Antwort wurde gelöscht. Außerdem wird seine Antwort nicht wirklich funktionieren - er muss das Array umwandeln, um den Typ zu korrigieren. – Evk

+0

Das funktioniert auch ... sehr ähnlich wie bei dcastro, aber er hat seine ein paar Minuten vorher gepostet. Danke trotzdem. – Laurence

1

Sie so etwas wie dies versuchen könnte:

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

Ich erhalte diese Ausnahme, wenn Invoke aufgerufen: Objekt des Typs 'System.Collections.Generic.List'1 [TestConsole.Program + IDataItem]' kann nicht in Typ 'System.Collections.Generic' konvertiert werden. IEnumerable'1 [TestConsole.Program + Test1Dto] '. – Laurence

+0

Test1Dto ist einer meiner Typen, die IDataItem implementiert – Laurence

+0

@Laurence meine Antwort zu sehen, um diesen Fehler zu vermeiden – Evk

0

Ich werde auf eine Vermutung nehmen dieses, weil ich nicht den Code habe, um es auf meiner Seite zu führen.

Wie wäre:

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

Die gleiche Art von Fehler wie zuvor ("Zugriff auf die Zieltabelle 'IDataItem'.") .... weil BulkInsert mit einer Variablen vom Typ List of IDataItem aufgerufen wird. – Laurence

Verwandte Themen