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