C# 数据集级联和内存消耗

C# 数据集级联和内存消耗,c#,.net,memory,memory-management,dataset,C#,.net,Memory,Memory Management,Dataset,我有一个应用程序,它使用DataSet.WriteXML导出数据,使用DataSet.ReadXML导入数据。在导入过程中,作为应用程序逻辑的一部分,我需要更改某些主键 当有超过500K条记录时,它会成功地写入XML并从XML中读取。一旦我更改主键,它将等待一段时间并抛出OutOfMemory异常。我认为原因是,它必须进行大量的级联更新。我在主键更改期间尝试了BeginEdit和EndEdit,但在这种情况下EndEdit仍然失败 据我所知,数据集还将以前的一些数据保存在内存中。有没有办法以消耗

我有一个应用程序,它使用DataSet.WriteXML导出数据,使用DataSet.ReadXML导入数据。在导入过程中,作为应用程序逻辑的一部分,我需要更改某些主键

当有超过500K条记录时,它会成功地写入XML并从XML中读取。一旦我更改主键,它将等待一段时间并抛出OutOfMemory异常。我认为原因是,它必须进行大量的级联更新。我在主键更改期间尝试了BeginEdit和EndEdit,但在这种情况下EndEdit仍然失败


据我所知,数据集还将以前的一些数据保存在内存中。有没有办法以消耗最少内存的方式优化数据集更新操作?

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 */
}
试试这个:

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");
试试看
{
//加载文件的逻辑
var xelmOriginal=新的XElement(“根”);
对于(int i=0;i<500000;i++)
{
var项目=新项目(“项目”);
项目.SetAttributeValue(“id”,i);
添加(项目);
}
//转换每个元素的逻辑
var xelmRootTransformed=新XElement(“根”);
foreach(xelmOriginal.Elements()中的var元素)
{
变量转换项=
新XElement(“已转换”,
元素。
属性()
.Single(x=>x.Name.LocalName.Equals(“id”);
xelmRootTransformed.Add(transformedItem);
}
//保存转换文件的逻辑
}捕获(例外e)
{
控制台写入线(“失败”);
返回;
}
Console.WriteLine(“成功”);
这里的关键点是要将输入和输出分开。也就是说,您不会转换文件并立即写入;你会搞砸你的枚举


相反,一次读取一个元素,一次写入一个临时输出元素;理论上,只有一个活动元素处于活动状态

数据集是智能动物。它们不仅可以读取/写入/保留/过滤数据,还可以进行更改跟踪,因此以后的更新/写入/删除速度更快(在处理数据库时,而不仅仅是XML文件)

您的数据集可能已经打开了更改跟踪,这将迫使它不仅始终记住当前数据是什么,而且还要记住数据以前的样子,以及新数据与旧数据之间的关系。如果您只是将数据集作为当前工作负载的“容器”,则不需要缓存/变更跟踪,只需将其关闭即可。我的意思是,如果有可能的话——我不记得现在是否能做到,也不记得如何做到。但是,我很确定,您可以通过调用.AcceptChanges()或为要加载的每批新数据插入旧DS并创建新DS来刷新更改。当然,对于在当前批处理的连续更新过程中抛出的oom,后者将没有帮助。如果在第一次PK更新时抛出OOM,则AcceptChanges无法帮助您。只有在一次完整操作结束后,您才能“接受”更改,即使如此,也不会有“同时”可以发出更改。但是,如果OOM是在几个更改PKs之后抛出的,那么在每个更改之后或者在每个更改之后调用AcceptChanges可能会有所帮助


请注意,我在猜测。您的DS未连接到DB,因此默认情况下更改跟踪可能处于关闭状态。但我对此表示怀疑,我记得即使对于XML文件,您也可以要求DS转储数据并附上更改日志。。我认为默认情况下它是打开的。

如果您需要更多的控制,那么您需要删除数据集提供给您的一些功能。减少级联引起的内存的一种方法是简单的不级联。使用表架构手动更新表ID

这样做的想法是,您可以控制哪些行被更新,在任何时候接受更改,强制gcmid更新或者您可能想要控制的任何其他内容

我创建了一个简单的测试场景,它显示了我的意思:

模式:

<?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;
        }
    }
private void CreateRows(Int32 MaxBaseRows、Int32 MaxChildRows)
{
dataSet1.Clear();
Int32行计数=0;
随机R=新随机();
foreach(dataSet1.Tables中的DataTable DT)
{
Int32 NewCount=R.Next(1,MaxBaseRows);
foreach(DT.Constraints.OfType()中的var FK)
{
NewCount=NewCount*R.Next(1,MaxChildRo