如何提高类型化DataSet的创建性能
如何提高类型化 DataSet 创建的性能。
引言
在 .NET 环境中用于传递给 N 层应用程序的常见对象是 DataSet
对象。 此外,为了简化 DataSet
对象的使用,我们通常将其定义为强类型 DataSet
(使用 XML 架构)。
一个常见的场景是在一层中创建(实例化)类型化的 DataSet
,然后将其传递给其他层以进行进一步的逻辑实现。 创建类型化的 DataSet
实际上非常昂贵。 我惊讶地意识到(使用各种分析工具),大约 15%-20% 的应用程序时间浪费在类型化的 DataSet
的构造函数上。
一个建议的解决方案
大多数应用程序使用以下流程:一个新创建的 DataSet
被填充数据(由用户或来自数据库),用一些逻辑更新,保存到数据库,然后最终被丢弃。 然后循环重复。
(有关在需要多个此类 DataSet
实例的情况下可以执行的操作的更多信息,请参阅 备注 部分。)
如果 DataSet
仅创建一次,则会显着提高性能。 因此,建议的解决方案如下:创建一次所需的类型化 DataSet
,将其保存到缓存中,并在需要时,在使用 DataSet.Clear()
方法清除其数据后立即返回它。 这样本质上更快吗? 是的,我使用各种分析器进行了测试,DataSet.Clear()
方法比类型化的 DataSet
的构造函数快 2-10 倍 (!!!)。
下面是用于类型化 DataSet
代理的源代码,该代理控制所需类型化 DataSet
的创建。
namespace TypedDSProxy
{
///<summary>
/// Controls creation of typed DataSet.
/// Singleton.
///</summary>
public class DSProxy
{
///<summary>
/// Initial size of typed DS cache.
///</summary>
private const int InitialCacheSize = 2;
///<summary>
/// Typed DS cache.
///</summary>
private Hashtable cache;
///<summary>
/// Instance variable for Singleton pattern.
///</summary>
private static DSProxy DSProxyInstance;
///<summary>
/// Default Constructor.
///</summary>
private DSProxy()
{
cache = new Hashtable(InitialCacheSize);
}
///<summary>
/// Instance method for Singleton pattern.
///</summary>
///<returns>DSProxy</returns>
public static DSProxy Instance()
{
if(DSProxyInstance == null)
DSProxyInstance = new DSProxy();
return DSProxyInstance;
}
///<summary>
/// Gets the namespace from the given type.
///(The namespace derived from the beginning
/// of the type till the first "." char.)
///</summary>
///<param name="dsType">The string representation
/// of typed DS's type.</param>
///<returns>Typed DS's namespace.</returns>
private string GetNamespace(string dsType)
{
try
{
return dsType.Substring(0, dsType.IndexOf("."));
}
catch(Exception e)
{
// write e.Message to log.
...
}
}
///<summary>
/// Returns an empty typed DataSet according to a given type.
///</summary>
///<param name="dsType">The string representation
/// of typed DS's type.</param>
///<returns>Empty typed DS.</returns>
public DataSet GetDS(string dsType)
{
try
{
DataSet ds;
// if the DataSet wasn't created.
if(cache[dsType] == null)
{
// create it using its assembly.
Assembly asm = Assembly.Load(GetNamespace(dsType));
ds = (DataSet)asm.CreateInstance(dsType, true);
cache.Add(dsType, ds);
}
else
{
// else clear it.
ds = (DataSet)cache[dsType];
ds.Clear();
}
return ds;
}
catch(Exception e)
{
// write e.Message to log.
...
}
}
}
}
客户端使用类型化 DataSet
代理,如下所示
class clsClient
{
...
public void SomeOperationWithTypedDS()
{
OrdersDS ds =
(OrdersDS)DSProxy.Instance().GetDS(typeof(OrdersDS).ToString());
ds.Orders.AddOrdersRow("a", 1, System.DateTime.Now,
System.DateTime.Now, System.DateTime.Now, 1,
decimal.MinValue, "a", "a", "a", "a", "a", "a");
}
...
}
备注
- 建议的解决方案不是线程安全的。 如果线程安全是一个问题,可以使用类型化
DataSet
的池。(对象池也解决了需要多个类型化DataSet
对象的情况。)关于托管对象池的一篇好文章可以在这里找到:使用托管代码进行对象池 - 作者:Scott Sherer。(当线程安全不是问题并且仍然需要多个类型化DataSet
对象时,可以使用一个更简单的池 - 无对象上下文。) - 这里使用反射是为了避免在创建所需类型化
DataSet
的类型时进行切换。另一种方法是使用
ConstructorInfo
对象,该对象可以从DataSet
的Type
中提取以创建实例。 使用ConstructorInfo.Invoke()
方法来完成此任务。 - 非常欢迎任何建议、改进和错误报告。