两种方法提升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 2010-11-22 17:52  黑头  阅读(671)  评论(2编辑  收藏  举报

导航