访问 DAO 性能问题
Access DAO Performance issue
我正在使用 Microsoft.Office.Interop.Access.Dao.DBEngine
将数据写入现有的 accdb
模板。这是由某个class在某个程序集中完成的。
现在我正在观察两种情况:当我在 xunit(1.9.2 和 VS runner 2.0.1)中开始我的例程(调试构建)时,测试作为 TE.ProcessHost.Managed.exe 中的 32 位进程它需要大约一分钟完成。在 32 位模式下从控制台应用程序启动它作为发布版本需要超过 12 分钟。
我只是实例化一个 new DbEngine()
,然后每个 table 调用 OpenTable(name)
进行填充,每行调用 table.Update() 进行插入(没有更新,仅插入)。程序集引用 Microsoft.Office.interop.access.dao.dll
版本 15.0.4420.1017 (Access 2010)。
我正在寻找从哪里开始挖掘这些巨大差异的原因的线索。
编辑:
基本上,它是从 SQL-Server 到访问数据库的复制作业,因此它首先通过 ADO 从 SQL-Server 读取数据,然后将其插入 accdb。像这样(不是确切的代码):
foreach(var tableName in tables)
{
readSqlIntoArray(tablename, tableData);
var daoTable = daoDb.OpenTable(tableName);
foreach(var row in tableData)
{
// ... add new record and copy data
daoTable.Update(); // this is the expensive call in console app
}
}
单元测试只是为复制作业创建参数,创建相关对象并启动作业。控制台应用程序做同样的事情。分析、计时和调试总是导致 table.Update()
成为成本最高的调用。 SQL 读数显示没有差异,因此排除了问题的原因。
这里问的原因其实是,我需要一个想法,我可以在那里进一步研究,因为代码本身没有显示出明显的差异。
调用方法(运行程序与控制台应用程序)中没有反射、泛型、不安全代码或隐藏的工件,可以解释这种行为,因为它们都只构建运行时参数并调用作业。我什至通过字符比较了这些参数。
所以我想知道,如果控制台应用程序和 VS 测试运行器之间存在一些 'environmental difference',因为我在这里处理 COM 对象。
更新 2:
这些天我有时间再次调查这个问题。
所以我添加了计时测量来比较各个步骤。从 SQL 服务器获取数据是在同样的时间内完成的。有趣的部分又来了:
foreach(var tableName in tables)
{
readSqlIntoArray(tablename, tableData);
var daoTable = daoDb.OpenTable(tableName);
foreach(var row in tableData)
{
var rowArray = row.ItemArray;
// because of type conversions this loop is necessary
for (int i = 0; i < rowArray.Length; i++)
{
var srcValue = rowArray[i];
if (srcValue.GetType() == typeof(TimeSpan))
{
// TimeSpan cannot be automatically converted and would cause exception
tabl.Fields[i].Value = ((TimeSpan)srcValue).ToString(@"hh\:mm");
}
else if (srcValue.GetType() == typeof(Guid))
{
// Guid cannot be automatically converted and would cause exception
// so wrap it as string
tabl.Fields[i].Value = ((Guid)srcValue).ToString();
}
else
{
// even this assignment is taking longer in console app
// than in testrunner (te.processhost.managed.exe)
tabl.Fields[i].Value = srcValue;
}
}
daoTable.Update();
}
}
看起来,table 行的字段分配似乎表现不同,尽管它是完全相同的代码行。在调试器中,我看不到底层 COM 对象在测试和控制台中是否始终属于同一类型。
有人在托管应用程序中的 COM 对象方面有过这样的经验吗?
一种方法可能是利用 MSAccess 文件中的直通查询从 SQL 服务器检索数据,然后使用插入查询将数据复制到本地访问 table(s)。由于没有用户代码,因此很想知道是否存在性能差异。如果不是,则可能是外部问题,如磁盘 IO、索引或网络问题(假设 SQL 服务器在另一台计算机上)。
在分析问题并询问 COM 对象时,我发现了简单而明确的原因:应用程序的单元状态。如果不设置为 STA,COM 对象需要复杂的编组,并将不正确的调用打包到代理中,这是非常昂贵的。
用 [STAThread]
装饰控制台应用程序是唯一必要的事情。我想,visual studio 测试运行器默认情况下会自动启动单线程,而普通应用程序则不会。
COM 及其与 STA 的亲和力是我一直在寻找的关键。
我正在使用 Microsoft.Office.Interop.Access.Dao.DBEngine
将数据写入现有的 accdb
模板。这是由某个class在某个程序集中完成的。
现在我正在观察两种情况:当我在 xunit(1.9.2 和 VS runner 2.0.1)中开始我的例程(调试构建)时,测试作为 TE.ProcessHost.Managed.exe 中的 32 位进程它需要大约一分钟完成。在 32 位模式下从控制台应用程序启动它作为发布版本需要超过 12 分钟。
我只是实例化一个 new DbEngine()
,然后每个 table 调用 OpenTable(name)
进行填充,每行调用 table.Update() 进行插入(没有更新,仅插入)。程序集引用 Microsoft.Office.interop.access.dao.dll
版本 15.0.4420.1017 (Access 2010)。
我正在寻找从哪里开始挖掘这些巨大差异的原因的线索。
编辑: 基本上,它是从 SQL-Server 到访问数据库的复制作业,因此它首先通过 ADO 从 SQL-Server 读取数据,然后将其插入 accdb。像这样(不是确切的代码):
foreach(var tableName in tables)
{
readSqlIntoArray(tablename, tableData);
var daoTable = daoDb.OpenTable(tableName);
foreach(var row in tableData)
{
// ... add new record and copy data
daoTable.Update(); // this is the expensive call in console app
}
}
单元测试只是为复制作业创建参数,创建相关对象并启动作业。控制台应用程序做同样的事情。分析、计时和调试总是导致 table.Update()
成为成本最高的调用。 SQL 读数显示没有差异,因此排除了问题的原因。
这里问的原因其实是,我需要一个想法,我可以在那里进一步研究,因为代码本身没有显示出明显的差异。
调用方法(运行程序与控制台应用程序)中没有反射、泛型、不安全代码或隐藏的工件,可以解释这种行为,因为它们都只构建运行时参数并调用作业。我什至通过字符比较了这些参数。 所以我想知道,如果控制台应用程序和 VS 测试运行器之间存在一些 'environmental difference',因为我在这里处理 COM 对象。
更新 2:
这些天我有时间再次调查这个问题。 所以我添加了计时测量来比较各个步骤。从 SQL 服务器获取数据是在同样的时间内完成的。有趣的部分又来了:
foreach(var tableName in tables)
{
readSqlIntoArray(tablename, tableData);
var daoTable = daoDb.OpenTable(tableName);
foreach(var row in tableData)
{
var rowArray = row.ItemArray;
// because of type conversions this loop is necessary
for (int i = 0; i < rowArray.Length; i++)
{
var srcValue = rowArray[i];
if (srcValue.GetType() == typeof(TimeSpan))
{
// TimeSpan cannot be automatically converted and would cause exception
tabl.Fields[i].Value = ((TimeSpan)srcValue).ToString(@"hh\:mm");
}
else if (srcValue.GetType() == typeof(Guid))
{
// Guid cannot be automatically converted and would cause exception
// so wrap it as string
tabl.Fields[i].Value = ((Guid)srcValue).ToString();
}
else
{
// even this assignment is taking longer in console app
// than in testrunner (te.processhost.managed.exe)
tabl.Fields[i].Value = srcValue;
}
}
daoTable.Update();
}
}
看起来,table 行的字段分配似乎表现不同,尽管它是完全相同的代码行。在调试器中,我看不到底层 COM 对象在测试和控制台中是否始终属于同一类型。 有人在托管应用程序中的 COM 对象方面有过这样的经验吗?
一种方法可能是利用 MSAccess 文件中的直通查询从 SQL 服务器检索数据,然后使用插入查询将数据复制到本地访问 table(s)。由于没有用户代码,因此很想知道是否存在性能差异。如果不是,则可能是外部问题,如磁盘 IO、索引或网络问题(假设 SQL 服务器在另一台计算机上)。
在分析问题并询问 COM 对象时,我发现了简单而明确的原因:应用程序的单元状态。如果不设置为 STA,COM 对象需要复杂的编组,并将不正确的调用打包到代理中,这是非常昂贵的。
用 [STAThread]
装饰控制台应用程序是唯一必要的事情。我想,visual studio 测试运行器默认情况下会自动启动单线程,而普通应用程序则不会。
COM 及其与 STA 的亲和力是我一直在寻找的关键。