【发布时间】:2010-08-05 19:27:52
【问题描述】:
这是我向here 提出的 LINQ to DB2 问题的下一步。
按照zb_z 的回答,我对DB_Linq 的代码进行了一些探索,并设法添加了有效的DB2 支持。 (它现在仍处于起步阶段,还没有准备好回馈项目。)概念证明效果很好,实际上非常令人兴奋。但是,我在此过程中遇到了另一个问题。
事实证明,我们的 DB2 数据库很大。 8,306 张大桌子。因此生成的代码超过 520 万行代码。在一个文件中。不用说,Visual Studio 并不太关心它 :)
所以我进一步修改了生成器,将每个表类吐出到自己的文件中。这给我留下了 8,307 个文件(数据上下文和每个表一个,它使用表属性扩展数据上下文)。 Visual Studio 仍然不喜欢它,这是可以理解的,所以我将代码生成和编译打包在一个脚本中,然后运行它以输出一个 DLL 供我的项目使用。
一个 36 MB 的 DLL。
现在,搜索了一下性能,我找到了this SO question(它本身引用了this one),我已经关注了答案和链接,看看他们在说什么。所以这让我想知道是否可能是同一命名空间中存在超过 8,000 个类是导致明显性能问题的罪魁祸首。
我的性能测试是编写一个小控制台应用程序,它初始化数据上下文,使用 LINQ 抓取数据,打印出行数,使用经典 ADO 抓取数据,然后打印出另一个行数。每个语句都包含一个时间戳。添加更多查询以进行测试等始终会产生相同的性能。 LINQ 代码需要几秒钟才能运行,而 ADO 会在眨眼之间填充数据集。
所以我想这最终会成为一个有点开放式(而且冗长,抱歉)的问题。有人对在这里加快性能有任何想法吗?有什么简单的调整或我可以应用的设计注意事项?
编辑
需要注意的几点:
- 如果我将代码生成限制为表的子集(例如 200 个),那么它的运行速度要快得多。
- 在调试器中单步执行,时间长度花费在
var foo = from t in bank1.TMX9800F where t.T9ADDEP > 0 select t.T9ADDEP行上,当我在调试器中展开属性以枚举结果时(或让它转到执行 .Count() 的下一行) 那么这部分根本不需要时间。
编辑
我无法发布整个生成的 DLL,但这里是测试应用的代码:
static void Main(string[] args)
{
Console.WriteLine(string.Format("{0}: Process Started", DateTime.Now.ToLongTimeString()));
// Initialize your data contexts
var bank1 = new BNKPRD01(new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString));
var bank6 = new BNKPRD06(new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString));
Console.WriteLine(string.Format("{0}: Data contexts initialized", DateTime.Now.ToLongTimeString()));
var foo = from t in bank1.TMX9800F where t.T9ADDEP > 0 select t; // <- runs slow
Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD01 test table", DateTime.Now.ToLongTimeString(), foo.Count().ToString()));
var baz = from t in bank6.TMX9800F where t.T9ADDEP > 0 select t; // <- runs slow
Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD06 test table", DateTime.Now.ToLongTimeString(), baz.Count().ToString()));
var ds = new DataSet();
using (var conn = new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT * FROM BNKPRD01.TMX9800F WHERE T9ADDEP > 0";
new IBM.Data.DB2.iSeries.iDB2DataAdapter(cmd).Fill(ds);
}
}
Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD01 test table", DateTime.Now.ToLongTimeString(), ds.Tables[0].Rows.Count.ToString()));
ds = new DataSet();
using (var conn = new iDB2Connection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT * FROM BNKPRD06.TMX9800F WHERE T9ADDEP > 0";
new IBM.Data.DB2.iSeries.iDB2DataAdapter(cmd).Fill(ds);
}
}
Console.WriteLine(string.Format("{0}: {1} records found in BNKPRD06 test table", DateTime.Now.ToLongTimeString(), ds.Tables[0].Rows.Count.ToString()));
Console.WriteLine("Press return to exit.");
Console.ReadLine();
}
也许我遗漏了一些明显的东西,或者我没有了解 LINQ 的某些东西?
编辑
在下面与 Jon 和 Brian 讨论后,我进一步了解了在创建 LINQ 查询时调用的 DB_Linq 代码并遇到了漫长的步骤:
public override IEnumerable<MetaTable> GetTables()
{
const BindingFlags scope = BindingFlags.GetField |
BindingFlags.GetProperty | BindingFlags.Static |
BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public;
var seen = new HashSet<Type>();
foreach (var info in _ContextType.GetMembers(scope))
{
// Only look for Fields & Properties.
if (info.MemberType != MemberTypes.Field && info.MemberType != MemberTypes.Property)
continue;
Type memberType = info.GetMemberType();
if (memberType == null || !memberType.IsGenericType ||
memberType.GetGenericTypeDefinition() != typeof(Table<>))
continue;
var tableType = memberType.GetGenericArguments()[0];
if (tableType.IsGenericParameter)
continue;
if (seen.Contains(tableType))
continue;
seen.Add(tableType);
MetaTable metaTable;
if (_Tables.TryGetValue(tableType, out metaTable))
yield return metaTable;
else
yield return AddTableType(tableType);
}
}
该循环迭代了 16,718 次。
【问题讨论】:
-
做 BNKPRD??类将所有表枚举为字段/属性?
-
@Brian:我认为你正在做一些事情。我只是在调试器中进一步进入 DB_Linq 代码,并遇到了一个我将添加到问题中的方法......
-
嗯,到目前为止,我还不能重现类似的东西(但我保持简单,所以在我的情况下没有 LINQ 提供程序)。您添加的代码确实使用了反射,这可能会带来很大的开销。您是否尝试过分析应用程序?尝试使用例如分析它200、1000 和 2000 表,看看会弹出什么数字。
-
@Brian:绝对是 GetTables() 方法中的迭代。我可能会好好研究一下,看看我是否可以调整它以满足我在这个项目中的需求。如果我可以在该方法调用中及其周围获得更多上下文,我可能能够获得我需要的确切成员而无需迭代。无论哪种方式,都会很有趣。
标签: .net linq performance dll db2