2013-02-25 2 views
7

У меня есть приложение, которое использует DataSet.WriteXML для экспорта данных и DataSet.ReadXML для импорта данных. Во время процесса импорта мне нужно изменить некоторые первичные ключи как часть логики приложения.Каскад DataSet и потребление памяти

При записи более 500 тыс. Записей он записывается в XML и успешно читается из XML. После изменения первичного ключа он ждет некоторое время и выбрасывает исключение OutOfMemory. Причина, по моему мнению, заключается в том, что она должна делать много каскадных обновлений. Я попробовал BeginEdit и EndEdit во время изменения первичного ключа, но в этом случае все еще не работает в EndEdit.

Как я понял, DataSets сохраняет некоторые предыдущие данные также в памяти. Есть ли способ оптимизировать операции обновления DataSet таким образом, чтобы он потреблял минимальную память?

+0

Это было бы больше работы, но вы считали, что записываете все данные во временную базу данных tabl es и выполнить повторную нумерацию на уровне temp db? 500k - это много строк для хранения в памяти. Сколько разных таблиц в вашем наборе данных? – tgolisch

+0

@tgolisch: У меня около 70 разных таблиц. Можно ли создать temp db? Я не знаю временную базу данных. Не могли бы вы указать мне пример? Благодарю. – ABCD

+0

Я думаю, что у вас есть некоторые проблемы с дизайном с вашим приложением. Я имею в виду «записи 500K, ...»)) Может быть, вы разместите дополнительную информацию - код - чтобы описать вашу проблему? – MikroDel

ответ

1

Если вам нужно больше контроля, вам нужно будет удалить некоторые функции, которые предоставляет набор данных. Один из способов уменьшения памяти, вызванный каскадами, - это просто не каскад. Обновите идентификатор таблиц вручную, используя схему таблиц.

Идея заключается в том, что вы можете контролировать, какие строки обновлены, AcceptChanges в любой момент, принудительно выполнить GC-обновление или что-то еще, что вы, возможно, захотите контролировать.

Я создал простой сценарий тест, который показывает, что я имею в виду:

enter image description here

Схема:

<?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> 

И некоторый код, который генерирует тестовый случай

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

Отлично, на самом деле я уже решил проблему, установив EnforceConstraints false. И обновите другие записи вручную. Совсем похоже на то, что вы предложили. Спасибо за ваш ответ! – ABCD

0

SHCJ - вы должны использовать BufferedStream:

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

Update

Попробуйте это для операций записи, пожалуйста:

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

BufferedStream потребляет огромный кусок памяти, и он исключает исключение из памяти в середине ReadXml. – ABCD

+0

@SHCJ - я обновил свой ответ – MikroDel

+0

Это происходит, когда я обновляю его после чтения ReadXML. Конечно, я уже использую «использование» для записи и чтения. – ABCD

0

Попробуйте это:

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

Ключевым моментом здесь является разделение ввода и вывода. То есть вы не преобразовываете файл и сразу пишете ему; Вы испортите свое перечисление.

Вместо этого прочитайте свой файл по одному элементу за раз и напишите на временный вывод по одному элементу за раз; в теории у вас будет только один активный элемент.

+0

Я не уверен, действительно ли вы понимаете мою проблему. У меня нет проблем с WriteXml и ReadXml. В основном моя проблема заключается в том, что я получаю исключение outofMemory в процессе каскадного обновления в DataSet. То, что мне нужно, - это способ уменьшить потребление памяти при обновлении первичного ключа (мне нужно изменить первичный ключ, что неизбежно в соответствии с бизнес-логикой). – ABCD

0

DataSets являются интеллектуальными животными. Они могут не только читать/записывать/удерживать/фильтровать данные, но также выполнять CHANGE TRACKING, поэтому более поздние обновления/записи/удаления выполняются быстрее (при работе с базой данных, а не только с файлами XML).

Возможно, произошло, что ваш DataSet включил отслеживание изменений, что заставило бы его всегда помнить не только то, что является текущими данными, но и как данные выглядели раньше, и как новые данные, связанные с Старые. Если вы просто держите DataSet в качестве «контейнера» для текущей рабочей нагрузки, вам не нужно кэширование/changetracking - просто отключите его. Я имею в виду, если это возможно - я не помню сейчас, если и как это можно сделать. Тем не менее, я уверен, что вы можете сбросить изменения, вызвав .AcceptChanges() или путем опрокидывания старых DS и создания новых DS для каждой новой партии загружаемых данных. Последнее, конечно, НЕ поможет для OOM, брошенных во время последовательных обновлений текущей партии. AcceptChanges не может помочь, если OOM брошен в момент самого первого обновления PK. Вы можете «принять» изменения только после завершения одной полной операции, и даже в этом случае «не будет», когда вы сможете ее опубликовать. Но, если OOM бросается после нескольких изменений PK, то вызов AcceptChanges после каждого из них или после каждых нескольких - может помочь.

Обратите внимание, что я предполагаю. Ваш DS не подключен к БД, поэтому отслеживание изменений может быть отключено по умолчанию. Но я сомневаюсь, что, я помню, что даже для XML-файла вы можете попросить DS сбрасывать данные, сопровождаемые журналом изменений. Я думаю, что он включен по умолчанию.

+0

Спасибо за сообщение. Я действительно об этом знаю. Моя проблема заключается в том, что я получаю OutOfMemory Exception при обновлении первичного ключа, который вызывает некоторые каскадные обновления. То, что я ищу, - это способ уменьшить потребление памяти в процессе каскадного обновления. – ABCD

Смежные вопросы