2015-10-28 69 views
14

Próbuję użyć Fluent NHibernate w celu przeprowadzenia migracji bazy danych wymagającej "masowania" bazy danych. Źródłowa baza danych to baza danych MS Access, a aktualna tabela, na której utknąłem, to jedna z polem OLE Object. Docelowa baza danych to baza danych MS SQL Server Express.Wywołanie wyjątku NHibernate OutOfMemoryException dla dużego bajtu []

w jednostce po prostu musiałem to pole zdefiniowane jako byte[] jednak podczas ładowania jednak nawet wtedy, gdy po prostu ładuje że pojedyncze pole dla pojedynczego rekordu byłem uderzenie System.OutOfMemoryException

byte[] test = aSession.Query<Entities.Access.Revision>().Where(x => x.Id == 5590).Select(x => x.FileData).SingleOrDefault<byte[]>(); 

Następnie próbowałem wdrażającego blob type listed here ale teraz, gdy uruchomiony że otrzymam błędu:

„Nie można rzutować obiektu typu«System.Byte []»wpisz «TestProg.DatabaseConverter.Entities.Blob».”}

Nie mogę sobie wyobrazić, że obiekt Ole ma rozmiar większy niż 100 MB, ale nie był w stanie go sprawdzić. Czy jest jakiś dobry sposób korzystania z Fluent NHibernate w celu skopiowania tego z jednej bazy danych i zapisania jej w innym, czy będę musiał przyjrzeć się innym opcjom?

Moja normalna pętla przetwarzania nich to:

IList<Entities.Access.Revision> result; 
IList<int> recordIds = aSession.Query<Entities.Access.Revision>().Select(x => x.Id).ToList<int>(); 

foreach (int recordId in recordIds) 
{ 
    result = aSession.Query<Entities.Access.Revision>().Where(x => x.Id == recordId).ToList<Entities.Access.Revision>(); 
    Save(sqlDb, result); 
} 

Zapisz funkcji wystarczy kopiuje właściwości z jednego do drugiego i dla niektórych jednostek służy do manipulowania danymi lub przekazywać informacje zwrotne do problemów związanych z danymi użytkownika. Używam sesji bezstanowych dla obu baz danych.

-

z dalszego testowania obiektów wydaje się być wiszące na to około 60-70mb. Obecnie testuję pobieranie danych za pomocą OleDbDataReader przy użyciu GetBytes.

-

Update (24 listopada): Mam jeszcze znaleźć sposób, aby uzyskać to do pracy z NHibernate. Zrobiłem to działając z normalnymi obiektami poleceń db. Umieściłem kod funkcji, którą zrobiłem poniżej, dla każdego, kto jest ciekawy, kto to znajdzie. Jest to kod z mojego konwertera bazy danych, więc obiekty z prefiksem "a" są obiektami bazy danych dostępu, a 's' są obiektami sql.

public void MigrateBinaryField(int id, string tableName, string fieldName) 
{ 
    var aCmd = new OleDbCommand(String.Format(@"SELECT ID, {0} FROM {1} WHERE ID = {2}", fieldName, tableName, id), aConn); 

    using (var reader = aCmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess)) 
    { 
     while (reader.Read()) 
     { 
      if (reader[fieldName] == DBNull.Value) 
       return; 

      long read = 0; 
      long offset = 0; 

      // Can't .WRITE a NULL column so need to set an initial value 
      var sCmd = new SqlCommand(string.Format(@"UPDATE {0} SET {1} = @data WHERE OldId = @OldId", tableName, fieldName), sConn); 
      sCmd.Parameters.AddWithValue("@data", new byte[0]); 
      sCmd.Parameters.AddWithValue("@OldId", id); 
      sCmd.ExecuteNonQuery(); 

      // Incrementally store binary field to avoid OutOfMemoryException from having entire field loaded in memory 
      sCmd = new SqlCommand(string.Format(@"UPDATE {0} SET {1}.WRITE(@data, @offset, @len) WHERE OldId = @OldId", tableName, fieldName), sConn); 
      while ((read = reader.GetBytes(reader.GetOrdinal(fieldName), offset, buffer, 0, buffer.Length)) > 0) 
      { 
       sCmd.Parameters.Clear(); 
       sCmd.Parameters.AddWithValue("@data", buffer); 
       sCmd.Parameters.AddWithValue("@offset", offset); 
       sCmd.Parameters.AddWithValue("@len", read); 
       sCmd.Parameters.AddWithValue("@OldId", id); 

       sCmd.ExecuteNonQuery(); 

       offset += read; 
      }      
     } 
    } 
} 
+1

Czy próbowałeś tego? https://github.com/bittercoder/Lob – Najera

+0

@Najera Dałem tego strzału i nie działał. Nie pamiętam szczegółów, ale wydaje mi się, że wciąż pamiętam wyjątki od pamięci. Minęło trochę czasu, więc nie w 100%. –

+0

Czy możesz podać nam mapowanie, które wykonałeś? – TedOnTheNet

Odpowiedz

0

To brzmi jak wynikach widziałem z wykorzystaniem .NET na wierzchu innych ram, jak również.

Natywny sterownik bazy danych pod ADO.NET pod NHibernate (dwa "poniżej" są tutaj celowe) będzie wymagał przypiętego bloku pamięci docelowej, który nie może zostać przeniesiony do pamięci, podczas gdy sterownik je wypełnia. Ponieważ odśmiecacz .NET może losowo przenosić bloki pamięci na osobny wątek w celu kompaktowania stert, podstawowa warstwa bazy danych .NET bazy NHibernate musi utworzyć niezarządzany blok pamięci, aby odebrać dane, co skutecznie podwaja ilość pamięci wymagane do załadowania rekordu.

Ponadto, nie zweryfikowałem tego następnego punktu, ale NHibernate powinien próbować buforować bloki rekordów, ponieważ pomija niektóre operacje kwerend relacyjnych baz danych. Dzięki temu NHibernate może tworzyć mniej żądań baz danych, co jest optymalne dla mniejszych rozmiarów rekordów, ale wymaga wielu rekordów (w tym wielu obiektów typu blob), aby zmieścić się w pamięci na raz.

Pierwszym krokiem do rozwiązania problemu jest upewnienie się, że proces rzeczywiście uruchamia pamięć urządzenia (lub 32-bitowy, upewnij się, że osiąga limit 2 GB). Jeśli tak, spróbuj określić linię podstawową - jeśli przetwarza rekordy o różnych rozmiarach obiektów typu blob, jaka jest minimalna i maksymalna używana pamięć? Z tego można oszacować, ile pamięci będzie wymagało tego dużego rekordu (lub bloku pamięci podręcznej zawierającego ten rekord!). t już działa 64-bitowe, a jeśli większy sprzęt to nawet opcja.

Innym możliwym rozwiązaniem jest sprawdzenie, czy NHibernate ma konfigurowalne ustawienia lub właściwości dla buforowania danych. Na przykład sprawdź, czy możesz ustawić właściwość, która ogranicza liczbę załadowanych rekordów na raz, lub nakazać ograniczenie jej pamięci podręcznej do określonego rozmiaru w bajtach.

Bardziej wydajnym rozwiązaniem jest użycie kodu ADO.NET dla obiektów typu blob; To może być najlepsze rozwiązanie, zwłaszcza jeśli spodziewasz się większych plam niż ten konkretny blob 60-70MB. MS Access normalnie zezwala na wiele połączeń tylko do odczytu, więc powinno to działać tak długo, jak usługa NHibernate nie ustawi bazy danych w celu blokowania innych połączeń.

Powiązane problemy