两种方法提升subsonic ORMapping的速度

 

Subsonic是一个轻量级的ORMapping工具,比较适合短平快项目的开发。在和ado.net相比后发现 ado.net的速度是subsonic的10倍多,取10万条记录并封装成实体,ado.net 的平均速度是subsonic3 的 9倍。这个成本有点太高了。

 

SubSonic (milsecond)

ADO.net (milisecond)

subsonic/ADO.net

10078

1140


8.840351





10078

1109


9.087466





10062

1140


8.826316





9953

1140


8.730702





10625

1125


9.444444





10062

1109


9.073039





10000

1109


9.017133





10000

1109


9.017133





10000

1125


8.888889





10265

1156


8.879758









101123

11262


8.979133

 

 

经过多次debug,最后发现封装成实体的过程中,每一行记录的每一列都会调用ProviderFactory的 new DbDataProvider(connectionString, providerName), 于是用dictionary缓存provider。

 

复制代码
代码
private static Dictionary<string, IDataProvider> SingleDProviderList;

public static IDataProvider GetProvider(string connectionString, string providerName)
{
bool providerExist;
if (!SingleDProviderList.Instance.ContainsKey(connectionString))
{
tempProvider
= new DbDataProvider(connectionString, providerName);
SingleDProviderList.Instance.Add(connectionString, tempProvider);
}
providerExist
= SingleDProviderList.Instance.TryGetValue(connectionString, out tempProvider);
return tempProvider;
}
复制代码

 

 

这个改变让mapping的速度提升了3秒。

 

6倍多的差异还是有点高,因此继续想办法。 经过30多分钟的debug, 发现DataBase.cs 文件中的Load()

 

 

复制代码
代码
/// <summary>
/// Coerces an IDataReader to try and load an object using name/property matching
/// </summary>
public static void Load<T>(this IDataReader rdr, T item, List<string> ColumnNames) //mike added ColumnNames
{
Type iType
= typeof(T);

PropertyInfo[] cachedProps
= iType.GetProperties();
FieldInfo[] cachedFields
= iType.GetFields();

PropertyInfo currentProp;
FieldInfo currentField
= null;

// var typeObj = Activator.CreateInstance(iType);

for (int i = 0; i < rdr.FieldCount; i++)
{
string pName = rdr.GetName(i);
currentProp
= cachedProps.SingleOrDefault(x => x.Name.Equals(pName, StringComparison.InvariantCultureIgnoreCase));

//mike if the property is null and ColumnNames has data then look in ColumnNames for match
if (currentProp == null && ColumnNames != null && ColumnNames.Count > i)
{
currentProp
= cachedProps.First(x => x.Name == ColumnNames[i]);
}

//if the property is null, likely it's a Field
if (currentProp == null)
currentField
= cachedFields.SingleOrDefault(x => x.Name.Equals(pName, StringComparison.InvariantCultureIgnoreCase));

if (currentProp != null && !DBNull.Value.Equals(rdr.GetValue(i)))
{
Type valueType
= rdr.GetValue(i).GetType();
if (valueType == typeof(Boolean))
{
string value = rdr.GetValue(i).ToString();
currentProp.SetValue(item, value
== "1" || value == "True", null);
}
else if (currentProp.PropertyType == typeof(Guid))
{
currentProp.SetValue(item, rdr.GetGuid(i),
null);
}
else if (Objects.IsNullableEnum(currentProp.PropertyType))
{
var nullEnumObjectValue
= Enum.ToObject(Nullable.GetUnderlyingType(currentProp.PropertyType), rdr.GetValue(i));
currentProp.SetValue(item, nullEnumObjectValue,
null);
}
else if (currentProp.PropertyType.IsEnum)
{
var enumValue
= Enum.ToObject(currentProp.PropertyType, rdr.GetValue(i));
currentProp.SetValue(item, enumValue,
null);
}
else
{

var val
= rdr.GetValue(i);
var valType
= val.GetType();
//try to assign it
if (currentProp.PropertyType.IsAssignableFrom(valueType))
{
currentProp.SetValue(item, val,
null);
}
else
{
currentProp.SetValue(item, val.ChangeTypeTo(currentProp.PropertyType),
null);
}
}
}
else if (currentField != null && !DBNull.Value.Equals(rdr.GetValue(i)))
{
Type valueType
= rdr.GetValue(i).GetType();
if (valueType == typeof(Boolean))
{
string value = rdr.GetValue(i).ToString();
currentField.SetValue(item, value
== "1" || value == "True");
}
else if (currentField.FieldType == typeof(Guid))
{
currentField.SetValue(item, rdr.GetGuid(i));
}
else if (Objects.IsNullableEnum(currentField.FieldType))
{
var nullEnumObjectValue
= Enum.ToObject(Nullable.GetUnderlyingType(currentField.FieldType), rdr.GetValue(i));
currentField.SetValue(item, nullEnumObjectValue);
}
else
currentField.SetValue(item, rdr.GetValue(i).ChangeTypeTo(valueType));
}
}
if (item is IActiveRecord) {
var arItem
= (IActiveRecord)item;
arItem.SetIsLoaded(
true);
arItem.SetIsNew(
false);

}
}
复制代码

这段代码对每行记录的每一列进行mapping,这是没有必要的,借鉴以往项目的mapping方法, 改写代码为

 

 

复制代码
代码
/// <summary>
/// Coerces an IDataReader to try and load an object using name/property matching
/// </summary>
public static void Load<T>(this IDataReader rdr, T item, List<string> ColumnNames) //mike added ColumnNames
{
Type iType
= typeof(T);

PropertyInfo[] cachedProps
= iType.GetProperties();
FieldInfo[] cachedFields
= iType.GetFields();

PropertyInfo currentProp;
FieldInfo currentField
= null;

// var typeObj = Activator.CreateInstance(iType);
if (item.GetType().GetInterface("IORMapping") != null)
((IORMapping)item).ORMapping(rdr);
else
{
#region origional mapping
for (int i = 0; i < rdr.FieldCount; i++)
{
string pName = rdr.GetName(i);
currentProp
= cachedProps.SingleOrDefault(x => x.Name.Equals(pName, StringComparison.InvariantCultureIgnoreCase));

//mike if the property is null and ColumnNames has data then look in ColumnNames for match
if (currentProp == null && ColumnNames != null && ColumnNames.Count > i)
{
currentProp
= cachedProps.First(x => x.Name == ColumnNames[i]);
}

//if the property is null, likely it's a Field
if (currentProp == null)
currentField
= cachedFields.SingleOrDefault(x => x.Name.Equals(pName, StringComparison.InvariantCultureIgnoreCase));

if (currentProp != null && !DBNull.Value.Equals(rdr.GetValue(i)))
{
Type valueType
= rdr.GetValue(i).GetType();
if (valueType == typeof(Boolean))
{
string value = rdr.GetValue(i).ToString();
currentProp.SetValue(item, value
== "1" || value == "True", null);
}
else if (currentProp.PropertyType == typeof(Guid))
{
currentProp.SetValue(item, rdr.GetGuid(i),
null);
}
else if (Objects.IsNullableEnum(currentProp.PropertyType))
{
var nullEnumObjectValue
= Enum.ToObject(Nullable.GetUnderlyingType(currentProp.PropertyType), rdr.GetValue(i));
currentProp.SetValue(item, nullEnumObjectValue,
null);
}
else if (currentProp.PropertyType.IsEnum)
{
var enumValue
= Enum.ToObject(currentProp.PropertyType, rdr.GetValue(i));
currentProp.SetValue(item, enumValue,
null);
}
else
{

var val
= rdr.GetValue(i);
var valType
= val.GetType();
//try to assign it
if (currentProp.PropertyType.IsAssignableFrom(valueType))
{
currentProp.SetValue(item, val,
null);
}
else
{
currentProp.SetValue(item, val.ChangeTypeTo(currentProp.PropertyType),
null);
}
}
}
else if (currentField != null && !DBNull.Value.Equals(rdr.GetValue(i)))
{
Type valueType
= rdr.GetValue(i).GetType();
if (valueType == typeof(Boolean))
{
string value = rdr.GetValue(i).ToString();
currentField.SetValue(item, value
== "1" || value == "True");
}
else if (currentField.FieldType == typeof(Guid))
{
currentField.SetValue(item, rdr.GetGuid(i));
}
else if (Objects.IsNullableEnum(currentField.FieldType))
{
var nullEnumObjectValue
= Enum.ToObject(Nullable.GetUnderlyingType(currentField.FieldType), rdr.GetValue(i));
currentField.SetValue(item, nullEnumObjectValue);
}
else
currentField.SetValue(item, rdr.GetValue(i).ChangeTypeTo(valueType));
}
}
#endregion

}
if (item is IActiveRecord) {
var arItem
= (IActiveRecord)item;
arItem.SetIsLoaded(
true);
arItem.SetIsNew(
false);

}
}
复制代码

如果类实现了IORMapping接口,则用ORMapping 方法转换。

 

 

IORMapping接口定义为:

public interface IORMapping
{
void ORMapping(IDataRecord dataRecord);
}

 

 

 

修改ActiveRecord.tt, 为每个类添加IORMapping的继承,及ORMapping方法的实现

 

 

复制代码
代码
public void ORMapping(IDataRecord dataRecord)
{
SubSonic.IReadRecord readRecord
= SubSonic.SqlReadRecord.GetIReadRecord();
readRecord.DataRecord
= dataRecord;
<#
foreach(Column col in tbl.Columns)
{
#
>
<#=col.Name#> = readRecord.get_<#=col.SysType.ToLower().Replace("[]","s")#>("<#=col.Name#>",null);
<#}#>
}
复制代码

 

 

定义接口IReadRecord

 

复制代码
代码
namespace SubSonic
{
/// <summary>
/// 从IDataRecord中读取数据的接口
/// field 字段名
/// def 默认值
/// </summary>
public interface IReadRecord
{
/// <summary>
/// DataRecord接口
/// </summary>
IDataRecord DataRecord { get; set; }
string get_string(string field);
string get_string(string field, string def);
Int32 get_int(
string field, Int32? def);
Int64 get_long(
string field, Int32? def);
bool get_bool(string field, bool? def);
double get_double(string field, double? def);
DateTime get_datetime(
string field, DateTime? def);
Decimal get_decimal(
string field, Decimal? def);
Guid get_guid(
string field, Guid? def);
byte? get_byte(string field, byte? def);
byte[] get_bytes(string field, byte[] def);
short? get_short(string field, short? def);
}
}
复制代码

 

为sqlserver数据库实现接口IReadRecord

复制代码
代码

namespace SubSonic
{
/// <summary>
/// 实现IReadRecord接口,从IDataRecord中读取数据
/// </summary>
public class SqlReadRecord : IReadRecord
{
private static SqlReadRecord sqlReadRecord = null;

private IDataRecord dataRecord = null;

public SqlReadRecord()
{

}

public SqlReadRecord(IDataRecord dataRecord)
{
this.dataRecord = dataRecord;
}

public static IReadRecord GetIReadRecord()
{
if (sqlReadRecord == null)
sqlReadRecord
= new SqlReadRecord();

return (IReadRecord)sqlReadRecord;
}

public IDataRecord DataRecord
{
get { return dataRecord; }
set { dataRecord = value; }
}

#region public methods
public string get_string(string AField)
{
try
{
return dataRecord[AField].ToString();
}
catch (Exception)
{
return "";
}
}
public string get_string(string AField, string ADef)
{
return get_string(AField);
}

public Int32 get_int(string AField, Int32? ADef)
{
try
{
return Convert.ToInt32(get_string(AField));
}
catch
{
return ADef == null ? 0 : ADef.Value;
}
}
public Int64 get_long(string AField, int? ADef)
{
try
{
return Convert.ToInt64(get_string(AField));
}
catch
{
return ADef == null ? 0 : ADef.Value;
}
}
public bool get_bool(string AField)
{
try
{
return Convert.ToBoolean(get_string(AField));
}
catch
{
return get_bool(AField, false);
}
}

public bool get_bool(string AField, bool? ADef)
{
try
{
return Convert.ToBoolean(get_string(AField));
}
catch
{
return ADef == null ? false : ADef.Value;
}
}

public double get_double(string AField, double? ADef)
{
try
{
return Convert.ToDouble(get_string(AField));
}
catch
{
return ADef == null ? Convert.ToDouble("0") : ADef.Value;
}
}

public DateTime get_datetime(string AField, DateTime? ADef)
{
try
{
return Convert.ToDateTime(get_string(AField));
}
catch
{
return ADef == null ? DateTime.Now : ADef.Value;
}
}

public Decimal get_decimal(string AField, Decimal? ADef)
{
try
{
return Convert.ToDecimal(get_string(AField));
}
catch
{
return ADef == null ? Convert.ToDecimal(0M) : ADef.Value;
}
}

public Guid get_guid(string AField, Guid? ADef)
{
try
{
return new Guid(get_string(AField));
}
catch
{
return new Guid();
}
}

//reload method
public Decimal get_decimal(string AField)
{
return get_decimal(AField, null);
}
public Guid get_guid(string AField)
{
return get_guid(AField, Guid.NewGuid());
}
public byte? get_byte(string AField, byte? ADef)
{
try
{
return Convert.ToByte(dataRecord[AField]);
}
catch
{
return ADef == null ? Convert.ToByte(' ') : ADef.Value;
}
}
public byte[] get_bytes(string AField, byte[] ADef)
{
System.Text.UTF8Encoding encoding
= new System.Text.UTF8Encoding();
try
{
return encoding.GetBytes(get_string(AField));
}
catch
{
return ADef == null ? encoding.GetBytes("") : ADef;
}
}
public short? get_short(string AField, short? ADef)
{
try
{
return short.Parse(get_string(AField));
}
catch
{
return ADef == null ? 0 : ADef;
}
}
#endregion
}
}
复制代码

 

经过以上处理,Ado.net 和subsonic相比,装箱的操作速度相差3.4倍左右。 尽管还是不够理想, 但是相比原来已经有了比较大的提升。

 

代码比较生硬,希望大家多提意见!

posted on   黑头  阅读(680)  评论(2编辑  收藏  举报

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
< 2010年11月 >
31 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 1 2 3 4
5 6 7 8 9 10 11

导航

统计

点击右上角即可分享
微信分享提示