7

DataSet.WriteXML を使用してデータをエクスポートし、DataSet.ReadXML を使用してデータをインポートするアプリケーションがあります。インポート プロセス中に、アプリケーション ロジックの一部として特定の主キーを変更する必要があります。

500K を超えるレコードがある場合、XML への書き込みと XML からの読み取りが正常に行われます。主キーを変更すると、しばらく待機して OutOfMemory 例外がスローされます。私が信じている理由は、多くのカスケード更新を行う必要があるためです。主キーの変更中に BeginEdit と EndEdit を試しましたが、その場合でも EndEdit で失敗します。

私が理解しているように、DataSets は以前のデータの一部もメモリに保持します。最小限のメモリを消費する方法で DataSet 更新操作を最適化する方法はありますか?

4

4 に答える 4

1

より詳細な制御が必要な場合は、データセットが提供する機能の一部を削除する必要があります。カスケードによって引き起こされるメモリを削減する1つの方法は、単純な「カスケードしない」です。テーブルスキーマを使用して、テーブルIDを手動で更新します。

次に、更新する行を制御したり、任意の時点でAcceptChangesを制御したり、GCの中間更新を強制したり、その他の制御したいものを制御したりできます。

私は、私が何を意味するかを示す簡単なテストシナリオを作成しました。

ここに画像の説明を入力してください

スキーマ:

<?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;
        }
    }
于 2013-03-06T12:24:02.687 に答える
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");

ここでの重要なポイントは、入力と出力を分離していることです。つまり、ファイルを変換してすぐに書き込むわけではありません。あなたはあなたの列挙を台無しにするでしょう。

代わりに、ファイルを一度に 1 要素ずつ読み取り、一度に 1 要素ずつ一時出力に書き込みます。理論的には、アクティブな要素は 1 つだけです。

于 2013-02-28T13:03:24.280 に答える
0

SHCJ - 使用する必要がありますBufferedStream:

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

アップデート

書き込み操作にこれを試してください:

using (XmlWriter xmlWriter = XmlWriter.Create(_pathToYourFile))
{
   /* write oprations */
}
于 2013-02-28T07:27:26.597 に答える
0

DataSet は知的な獣です。データの読み取り/書き込み/保持/フィルタリングだけでなく、CHANGE TRACKING も実行できるため、後で更新/書き込み/削除が高速になります (XML ファイルだけでなく、データベースを操作する場合)。

DataSet で変更の追跡がオンになっている可能性があります。これにより、現在のデータが何であるかだけでなく、以前のデータがどのように見えたか、新しいデータが古いデータにどのように関連しているかを常に記憶する必要があります。DataSet を現在のワークロードの「コンテナー」として保持する場合は、キャッシュ/変更追跡は必要ありません。無効にするだけです。つまり、可能であれば、それが可能かどうか、またどのように可能かは今のところ覚えていません。ただし、.AcceptChanges() を呼び出すか、古い DS を破棄して、新しいデータ バッチごとに新しい DS を作成することで、変更をフラッシュできると確信しています。もちろん、後者は、現在のバッチの順次更新中にスローされた OOM には役立ちません。最初の PK 更新の時点で OOM がスローされた場合、AcceptChanges は役に立ちません。「受け入れる」ことができます 1 回の完全な操作が終了した後にのみ変更されますが、それでも発行できる「その間」はありません。ただし、いくつかの変更 PK の後に OOM がスローされる場合は、それぞれの変更後、またはいくつかの変更ごとに AcceptChanges を呼び出すと役立つ場合があります。

推測ですのでご了承ください。DS が DB に接続されていないため、デフォルトで変更追跡オフになっている可能性があります。XML ファイルの場合でも、変更ログを伴うデータのダンプを DS に要求できることを覚えています。デフォルトではオンになっていると思います。

于 2013-02-28T13:17:07.367 に答える