先上測試結(jié)果:
//測試1000次針對同一個表同一個字段更新,比Ormlite平均快2.34倍 //生成SQL+ExecuteNonQuery Ormlite 倍數(shù) //6513ms 15158ms 2.327 //6461ms 15303ms 2.369 //6624ms 15424ms 2.329 //6591ms 15468ms 2.347 //測試1000次針對同一個表同一個字段更新,比EF平均快1.68倍 //生成SQL+ExecuteNonQuery EF 倍數(shù) //6622ms 11113ms 1.678 //6577ms 11117ms 1.690 //6756ms 11258ms 1.666 //6603ms 11184ms 1.694
不管出于什么原因,有時候框架人員摒棄了NH或EF,而使用原生數(shù)據(jù)庫訪問對象。
為了優(yōu)美的編程,用上我寫的輕量級動態(tài)生成更新SQL擴展方法吧
還記得在EF中只更新修改過的字段時,我們這么寫:
var e = db.Entry(d); e.State = EntityState.Unchanged; d.UploadTime = DateTime.Now; e.目的是,使生成的更新SQL只更新UploadTime字段,否則會更新所有字段,那么就需要先查詢一遍再更新。既然提到數(shù)據(jù)倉儲,我們更新一個實體在數(shù)據(jù)層最好只有一個方法,但是一個更新的SQL能滿足要求嗎?
出于以上目的,我寫了這樣的輕量級擴展方法:
/// <summary> /// /// </summary> public static class SqlStringEx { #region 靜態(tài)私有成員 private static object _obj = new object(); private static Dictionary<int, PropertyInfo[]> propertiesCache = new Dictionary<int, PropertyInfo[]>(); /// <summary> /// 從緩存中獲取屬性信息 /// </summary> /// <param name="t"></param> /// <returns></returns> private static PropertyInfo[] GetPropertiesFromCache(Type t) { var code = t.GetHashCode(); if (!propertiesCache.ContainsKey(code)) { lock (_obj) { if (!propertiesCache.ContainsKey(code)) { propertiesCache.Add(code, t.GetProperties()); } } } return propertiesCache[code]; } #endregion /// <summary> /// 根據(jù)實體模型的賦值情況,動態(tài)生成更新SQL。避免要先獲取再保存,或者避免按需寫多個更新SQL /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="tableName">數(shù)據(jù)表名稱</param> /// <param name="conditionColumnName">更新時條件列名稱,該列名需包含在parameters中</param> /// <param name="parameters"></param> /// <returns></returns> public static string GenerateUpdateSql<T>(this T t, string tableName, string conditionColumnName, out List<SqlParameter> parameters) where T : new() { parameters = new List<SqlParameter>(); StringBuilder res = new StringBuilder(); res.AppendFormat("update {0} set ", tableName); var props = GetPropertiesFromCache(t.GetType()); string wherePart = null; foreach (PropertyInfo item in props) { var propVal = item.GetValue(t, null); var hasValue = propVal != null && !propVal.Equals(0); if (hasValue) { var filedname = item.GetFieldName(); parameters.Add(new SqlParameter { Value = propVal, ParameterName = filedname }); if (filedname.Equals(conditionColumnName.ToLower())) { wherePart = string.Format(" where {0}=@{0} ", filedname); } else { res.AppendFormat(" {0}=@{0} ,", filedname); } } } res.Remove(res.Length - 1, 1); res.Append(wherePart); return res.ToString(); } }其中的GetFieldName()方法見上一篇
有了這個方法,數(shù)據(jù)層一個實體就只需要一個更新的方法,也不需要自己去拼接更新的SQL了。
List<SqlParameter> parameters =null; var saveSql = model.GenerateUpdateSql("TableName", "ID", out parameters);注意,model的屬性的默認值分兩種,值類型默認值為0,引用類型和非空值類型的默認值是null,當(dāng)model屬性的值不是默認值的時候會參與值的更新。saveSql就是我們要更新的SQL,parameters是對應(yīng)更新的參數(shù)。ID參數(shù)也可以是別的列,但在model中作為條件的列的值不能為默認值。
有沒有很方便,歡迎拋磚。
雖然重復(fù)造輪子,但方便性是不是可以彌補一切。
性能要求高的,請慎重考慮,因為內(nèi)部使用了反射。但是性能總的來說比Ormlite更快,執(zhí)行1000次更新,本擴展方法耗時6513毫秒,而Ormlite耗時15158毫秒。
EF相比Ormlite更快:
新聞熱點
疑難解答