2013-02-25 19 views
7

Mam aplikację, która używa DataSet.WriteXML do eksportu danych i DataSet.ReadXML do importowania danych. Podczas procesu importowania muszę zmienić niektóre klucze podstawowe w ramach logiki aplikacji.Dane Kaskadowe i zużycie pamięci

Gdy istnieje ponad 500 000 rekordów, zapisuje się do pliku XML i odczytuje z XML. Gdy raz zmienię klucz podstawowy, odczeka on jakiś czas i rzuci wyjątek OutOfMemory. Powodem moim zdaniem jest to, że musi wykonać wiele kaskadowych aktualizacji. Próbowałem BeginEdit i EndEdit podczas zmiany klucza głównego, ale nadal nie udało się w EndEdit w tym przypadku.

Jak już zrozumiałem, DataSets przechowuje niektóre z poprzednich danych również w pamięci. Czy istnieje sposób na zoptymalizowanie operacji aktualizacji DataSet w sposób zużywający minimum memeory?

+0

Byłoby to więcej pracy, ale czy rozważałeś zapisanie wszystkich danych w tymczasowym tablicie bazy danych? es i robi ponowne numerowanie na poziomie temp db? 500k to dużo wierszy do przechowywania w pamięci. Ile różnych tabel znajduje się w zestawie danych? – tgolisch

+0

@tgolisch: Mam około 70 różnych stołów. Czy można utworzyć tymczasową bazę danych? Nie wiem o tymczasowej bazie danych. Czy możesz wskazać mi przykład? Dzięki. – ABCD

+0

Myślę, że masz problemy z projektowaniem aplikacji. Mam na myśli "rekordy 500K, ...")). Może zamieścisz więcej informacji - kod - aby opisać swój problem? – MikroDel

Odpowiedz

1

Jeśli potrzebujesz większej kontroli, musisz usunąć niektóre funkcje, które daje zestaw danych. Jednym ze sposobów zmniejszenia pamięci spowodowanej kaskadami jest proste Do not Cascade. Zaktualizuj identyfikatory tabel ręcznie, korzystając ze schematu tabel.

Pomysł polega na tym, że możesz kontrolować, które wiersze są aktualizowane, AcceptChanges w dowolnym momencie, wymuszać aktualizację GC w środku lub cokolwiek innego, co chcesz kontrolować.

Został utworzony prosty scenariusz testowy, który pokazuje, co to znaczy:

enter image description here

Schema:

<?xml version="1.0"?> 
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> 
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> 
    <xs:complexType> 
     <xs:choice minOccurs="0" maxOccurs="unbounded"> 
     <xs:element name="Planet"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="Continent"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="PlanetID" type="xs:int" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="Country"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="ContinentID" type="xs:int" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="County"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="CountryID" type="xs:int" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="City"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="CountyID" type="xs:int" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="Street"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="CityID" type="xs:int" minOccurs="0" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="People"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="StreetID" type="xs:int" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="Job"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="PeopleID" type="xs:int" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     <xs:element name="Pets"> 
      <xs:complexType> 
      <xs:sequence> 
       <xs:element name="ID" type="xs:int" /> 
       <xs:element name="PeopleID" type="xs:int" minOccurs="0" /> 
       <xs:element name="Name" type="xs:string" minOccurs="0" /> 
      </xs:sequence> 
      </xs:complexType> 
     </xs:element> 
     </xs:choice> 
    </xs:complexType> 
    <xs:unique name="Constraint1"> 
     <xs:selector xpath=".//Planet" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="Continent_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//Continent" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="Country_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//Country" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="County_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//County" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="City_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//City" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="Street_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//Street" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="People_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//People" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="Job_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//Job" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:unique name="Pets_Constraint1" msdata:ConstraintName="Constraint1"> 
     <xs:selector xpath=".//Pets" /> 
     <xs:field xpath="ID" /> 
    </xs:unique> 
    <xs:keyref name="Relation8" refer="People_Constraint1"> 
     <xs:selector xpath=".//Pets" /> 
     <xs:field xpath="PeopleID" /> 
    </xs:keyref> 
    <xs:keyref name="Relation7" refer="People_Constraint1"> 
     <xs:selector xpath=".//Job" /> 
     <xs:field xpath="PeopleID" /> 
    </xs:keyref> 
    <xs:keyref name="Relation6" refer="Street_Constraint1"> 
     <xs:selector xpath=".//People" /> 
     <xs:field xpath="StreetID" /> 
    </xs:keyref> 
    <xs:keyref name="Relation5" refer="City_Constraint1"> 
     <xs:selector xpath=".//Street" /> 
     <xs:field xpath="CityID" /> 
    </xs:keyref> 
    <xs:keyref name="Relation4" refer="County_Constraint1"> 
     <xs:selector xpath=".//City" /> 
     <xs:field xpath="CountyID" /> 
    </xs:keyref> 
    <xs:keyref name="Relation3" refer="Country_Constraint1"> 
     <xs:selector xpath=".//County" /> 
     <xs:field xpath="CountryID" /> 
    </xs:keyref> 
    <xs:keyref name="Relation2" refer="Continent_Constraint1"> 
     <xs:selector xpath=".//Country" /> 
     <xs:field xpath="ContinentID" /> 
    </xs:keyref> 
    <xs:keyref name="Relation1" refer="Constraint1"> 
     <xs:selector xpath=".//Continent" /> 
     <xs:field xpath="PlanetID" /> 
    </xs:keyref> 
    </xs:element> 
</xs:schema> 

A niektóre kod, który generuje przypadek testowy

private void CreateRows(Int32 MaxBaseRows, Int32 MaxChildRows) 
    { 
     dataSet1.Clear(); 
     Int32 RowCount = 0; 
     Random R = new Random(); 
     foreach (DataTable DT in dataSet1.Tables) 
     { 
      Int32 NewCount = R.Next(1, MaxBaseRows); 
      foreach (var FK in DT.Constraints.OfType<ForeignKeyConstraint>()) 
      { 
       NewCount = NewCount * R.Next(1, MaxChildRows); 
      } 
      for (int i = 0; i < NewCount; i++) 
      { 
       DataRow DR = DT.NewRow(); 
       foreach (DataColumn DC in DT.Columns) 
       { 
        if (DC.ColumnName == "ID") 
        { 
         DR[DC] = DT.Rows.Count; 
        } 
        else if (DC.DataType == typeof(Int32)) 
        { 
         Boolean ValueSet = false; 
         foreach (var FK in DT.Constraints.OfType<ForeignKeyConstraint>()) 
         { 
          if (FK.Columns.Contains(DC)) 
          { 
           DR[DC] = R.Next(0, FK.RelatedTable.Rows.Count); 
           ValueSet = true; 
          } 
         } 
         if (!ValueSet) 
         { 
          DR[DC] = R.Next(0, 10000); 
         } 
        } 
        else if (DC.DataType == typeof(String)) 
        { 
         DR[DC] = String.Format("{0}{1}", DT.TableName, DT.Rows.Count); 
        } 
       } 
       DT.Rows.Add(DR); 
       RowCount++; 
      } 
     } 
     label19.Text = RowCount.ToString(); 
     dataSet1.AcceptChanges(); 
    } 


    private void UpdateUsingCascade() 
    { 
     EnableRelations(); 
     GC.Collect(); 
     long Mem = System.GC.GetTotalMemory(false); 
     if (dataSet1.Tables["Planet"].Rows.Count > 0) 
     { 
      dataSet1.Tables["Planet"].Rows[0]["ID"] = new Random().Next(BaseRowCount, BaseRowCount + 10); 
     } 
     Mem = System.GC.GetTotalMemory(false) - Mem; 
     DataSet ds = dataSet1.GetChanges(); 
     Int32 Changes = ds.Tables.OfType<DataTable>().Sum(DT => DT.Rows.Count); 
     label19.Text = Changes.ToString(); 
     label21.Text = Mem.ToString(); 
     dataSet1.AcceptChanges(); 
    } 

    private void UpdateManually() 
    { 
     DisableRelations(); 
     GC.Collect(); 
     long Mem = System.GC.GetTotalMemory(false); 

     DataTable DT = dataSet1.Tables["Planet"]; 
     Int32 ChangeCount = 0; 
     if (DT.Rows.Count > 0) 
     { 
      DataColumn DC = DT.Columns["ID"]; 
      Int32 oldValue = Convert.ToInt32(DT.Rows[0][DC]); 
      DT.Rows[0][DC] = new Random().Next(BaseRowCount + 20,BaseRowCount + 30); 
      Int32 newValue = Convert.ToInt32(DT.Rows[0][DC]); 
      foreach (DataRelation Relation in DT.ChildRelations) 
      { 
       if (Relation.ParentColumns.Contains(DC)) 
       { 
        foreach (DataColumn CC in Relation.ChildColumns) 
        { 
         foreach (DataRow DR in Relation.ChildTable.Rows) 
         { 
          if (Convert.ToInt32(DR[CC]) == oldValue) 
          { 
           DR[CC] = newValue; 
           ChangeCount++; 
           dataSet1.AcceptChanges(); 
           GC.Collect(); 
          } 
         } 
        } 
       } 
      } 
     } 
     Mem = System.GC.GetTotalMemory(false) - Mem; 
     label20.Text = ChangeCount.ToString(); 
     label22.Text = Mem.ToString(); 
     dataSet1.AcceptChanges(); 
    } 

    private void EnableRelations() 
    { 
     dataSet1.EnforceConstraints = true; 
     foreach (DataRelation Relation in dataSet1.Relations) 
     { 
      Relation.ChildKeyConstraint.UpdateRule = Rule.Cascade; 
     } 
    } 

    private void DisableRelations() 
    { 
     dataSet1.EnforceConstraints = false; 
     foreach (DataRelation Relation in dataSet1.Relations) 
     { 
      Relation.ChildKeyConstraint.UpdateRule = Rule.None; 
     } 
    } 
+0

Doskonałe, faktycznie już rozwiązałem problem, ustawiając wartość EnforceConstraints jako false. I aktualizuj inne rekordy ręcznie. Całkiem podobne do tego, co zaproponowałeś. Dziękuję za Twoją odpowiedź! – ABCD

0

SHCJ - należy użyć BufferedStream:

DataSet dataSet = new DataSet(); 
FileStream fileStream = File.OpenRead(pathToYourFile); 
BufferedStream bufferedStream = new BufferedStream(fileStream); 
dataSet.ReadXml(bufferedStream); 

Aktualizacja

Spróbuj tego dla operacji zapisu proszę:

using (XmlWriter xmlWriter = XmlWriter.Create(_pathToYourFile)) 
{ 
    /* write oprations */ 
} 
+0

BufferedStream zużywa ogromną porcję pamięci i generuje wyjątek OutofMemory w środku ReadXml. – ABCD

+0

@SHCJ - Zaktualizowałem moją odpowiedź – MikroDel

+0

Dzieje się tak, gdy aktualizuję ją po I ReadXML. Oczywiście używam już "używania" do zapisu i odczytu. – ABCD

0

Spróbuj:

try 
{ 

    //Logic to load your file 

    var xelmOriginal = new XElement("Root"); 

    for (int i = 0; i < 500000; i++) 
    { 
     var item = new XElement("Item"); 
     item.SetAttributeValue("id", i); 
     xelmOriginal.Add(item); 
    } 

    // Logic to transform each element 

    var xelmRootTransformed = new XElement("Root"); 

    foreach (var element in xelmOriginal.Elements()) 
    { 
     var transformedItem = 
      new XElement("Transformed", 
          element. 
           Attributes() 
           .Single(x => x.Name.LocalName.Equals("id"))); 


     xelmRootTransformed.Add(transformedItem); 
    } 

    //Logic to save your transformed file 
}catch(Exception e) 
{ 

    Console.WriteLine("Failed"); 
    return; 
} 

Console.WriteLine("Success"); 

Kluczowym punktem jest tutaj oddzielenie wejścia i wyjścia. To znaczy. nie przekształcasz pliku i natychmiast go zapisujesz; Zepsujesz wyliczenie.

Zamiast tego, odczytaj plik po jednym elemencie naraz i napisz do pliku tymczasowego jeden element naraz; teoretycznie będziesz mieć aktywny tylko jeden żywy element.

+0

Nie jestem pewien, czy naprawdę rozumiesz mój problem. Nie mam problemów w WriteXml i ReadXml. Zasadniczo mój problem jest uzyskać outofMemory wyjątku podczas procesu aktualizacji kaskadowych w DataSet. To, czego szukam, to sposób na zmniejszenie zużycia pamięci podczas aktualizacji klucza podstawowego (muszę zmienić klucz podstawowy, co jest nieuniknione zgodnie z logiką biznesową). – ABCD

0

Zestawy danych to inteligentne bestie. Mogą nie tylko czytać/zapisywać/wstrzymywać/filtrować dane, ale także ZMIENIAJĄ ŚLEDZENIE, więc późniejsze aktualizacje/zapisy/usuwanie są szybsze (podczas pracy z bazą danych, a nie tylko plikami XML).

Może się zdarzyć, że Twój zestaw danych ma włączone śledzenie zmian, co zmusiłoby go do zapamiętania nie tylko aktualnych danych, ale także tego, jak dane wyglądały wcześniej, oraz w jaki sposób nowe dane odnoszą się do stare. Jeśli po prostu przechowujesz DataSet jako "kontener" dla bieżącego obciążenia, nie potrzebujesz buforowania/zmiany ścieżki - po prostu wyłącz to. Mam na myśli, jeśli to możliwe - nie pamiętam obecnie, czy i jak można to zrobić. Jestem jednak całkiem pewien, że możesz opróżnić zmiany, wywołując funkcję .AcceptChanges() lub dezaktywując stare DS i tworząc nowe DS dla każdej nowej partii danych do załadowania. Ta ostatnia oczywiście NIE pomoże w przypadku OOM rzucanych podczas aktualizacji sekwencyjnych na bieżącej partii. AcceptChanges nie może pomóc, jeśli OOM zostanie zgłoszony w momencie pierwszej aktualizacji PK. Możesz "zaakceptować" zmiany tylko po zakończeniu jednej pełnej operacji, a mimo to nie będzie "międzyczasie", kiedy będziesz mógł je wydać. Ale jeśli OOM zostanie rzucony po kilku zmianach PKs, wtedy wywołanie AcceptChanges po każdym lub po każdym kilku - może pomóc.

Należy pamiętać, że zgaduję. Twój DS nie jest połączony z DB, więc śledzenie zmiany może być domyślnie wyłączone. Ale wątpię w to, pamiętam, że nawet w przypadku pliku XML można poprosić DS o zrzucenie danych wraz z dziennikiem zmian. Myślę, że domyślnie jest włączony.

+0

Dzięki za twój wpis. Właściwie to jestem tego świadomy. Mój problem polega na tym, że otrzymuję wyjątek OutOfMemory, gdy aktualizuję klucz podstawowy, który powoduje pewne aktualizacje kaskadowe. To, czego szukam, to sposób na zmniejszenie zużycia pamięci podczas procesu aktualizacji kaskadowej. – ABCD

Powiązane problemy