LINQ To SQL和ORM的理解淺析
LINQ To SQL和ORM的理解是什么呢,這將會是我們掌握LINQ技術(shù)的關(guān)鍵,那么對于LINQ To SQL具體的是什么含義,那么操作我們是需要明確的呢?那么下面我們就向你講述一下關(guān)于LINQ To SQL和ORM的相關(guān)理解。
有N個用LINQ的理由,也有N個不用LINQ的理由。在完善框架的時候,電信的一位技術(shù)總監(jiān)跟我說:我不建議你做框架,那是微軟等大公司的事,我們主要是用框架,你現(xiàn)在寫ORM,現(xiàn)在的DotNet3.5 已經(jīng)處于Alpha測試階段了,你的ORM還有多大的用處。我當(dāng)時一下子迷惘了,趕快找資料,從Lamda表達(dá)式開始學(xué)起,對于我來說難度還是不少的(是在考驗(yàn)我的數(shù)學(xué)呀)。最終還是放棄了。
LINQ To SQL和ORM的理解1、
沒有LINQ的源代碼,不知道清楚中間的數(shù)據(jù)是如何處理的,特別是緩存是如何處理的。
LINQ To SQL和ORM的理解2、
用LINQ翻譯到存儲過程/Sql語句效率很低的,對于復(fù)雜的對效率有一定的要求的,還是要寫存儲過程。而且翻譯出來的存儲過程/Sql語句的怎么緩存,第二次執(zhí)行的時候還要重新翻譯,不能很好的利用緩存。
LINQ To SQL和ORM的理解3、工具的支持。
這一點(diǎn)是非常重要。如果沒有有力工具的支持,用ORM簡直就是惡夢。LINQ的工具支持總的來說,現(xiàn)在在VS2008里的支持我感覺還不是很友好。至少有一點(diǎn),我們通常在表的備注里面寫下這個字段的注釋,很多情況下對應(yīng)到界面上就是這個字段的抬頭,這個工具不能直接生成到項(xiàng)目里去,因?yàn)槲覀儾荒芸刂?,再者,現(xiàn)在的項(xiàng)目都不是由一個人完成,如果一個類有幾十個字段,很難記住解釋,自己的工具可以加入注釋,方便開發(fā)。
LINQ To SQL和ORM的理解4、與業(yè)務(wù)層的綜合。
通常ORM映射就是把對象與關(guān)系表之間形成映射關(guān)系,映射的目標(biāo)是為了方便業(yè)務(wù)層的邏輯處理,如果不能與業(yè)務(wù)層無縫的結(jié)合起來,那么對于項(xiàng)目的開發(fā),也未必有多少好處。至少在的ORM里面,與的我業(yè)務(wù)層可以非常友好的集成在一起。
LINQ To SQL和ORM的理解5、多數(shù)據(jù)源的支持。
對于我來說,這點(diǎn)也是非常關(guān)鍵的。我說的多數(shù)據(jù)源是指一個項(xiàng)目里面,有多個數(shù)據(jù)庫,比如:基礎(chǔ)數(shù)據(jù)、客戶管理、業(yè)務(wù)運(yùn)作、權(quán)限管理等,這些都是相對獨(dú)立的模塊,開發(fā)的時候,可以把這幾個作為單獨(dú)的項(xiàng)目開發(fā),各自有自己獨(dú)立的數(shù)據(jù)庫,而且在多個項(xiàng)目里面,這些模塊都是非常相似,像權(quán)限模塊基本上是通用的,沒有必要再重復(fù)開發(fā)。如果能夠做到數(shù)據(jù)庫層分離,則新項(xiàng)目開發(fā)的時候,可以達(dá)到更快的速度。當(dāng)然也可以做到很容易的分合,把兩個數(shù)據(jù)庫合并成一個,或者把一個數(shù)據(jù)庫分開成兩個,只涉及到配置文件的更改,在開發(fā)的時候可以不關(guān)心的。
LINQ To SQL和ORM的理解6、對于可變查詢的處理。
也就是相當(dāng)于拼接字符串了??赡軙f,這個是LINQ的強(qiáng)項(xiàng),但是,如果對于一個分布式的項(xiàng)目呢?又應(yīng)該如何處理。知道ORM的朋友應(yīng)該都知道IBatis.net 和NHibnate ,I家的參數(shù)處理是非常的方便的,N家呢就沒有I的直觀,但是對于面向?qū)ο蟮奶幚鞩比N家好像就有太多的不足了。對于遠(yuǎn)程處理要能夠通過參數(shù)傳遞這些只對映射層公開的參數(shù),這點(diǎn)對于程序的某些地方可能是至關(guān)重要的影響。我的ORM里面,隨便起了一個名字,叫通用查詢,業(yè)務(wù)邏輯里用的查詢通常會寫在存儲過程里,可以通過工具直接生成業(yè)務(wù)層代碼里的函數(shù)。
LINQ To SQL和ORM的理解7、對于泛形、繼承的支持與處理。
泛形,可以減少很多代碼,同時,還可以減少很多人為的錯誤。
因?yàn)槿说木偸怯邢薜?,你把精力花在一個地方,那么其他方面肯定關(guān)注的少了,我也一樣??赡苁俏野丫^多的放在我的ORM里面,忽略了其他的方面,請大家能夠給出批評指正。接下來,把我的ORM映射部分做一詳細(xì)的介紹
LINQ To SQL和ORM的理解之ORM映射主要分為以下幾個方面:
LINQ To SQL和ORM的理解1、對象、參數(shù)翻譯
a)LINQ To SQL和ORM的理解之對象的表達(dá)。對象的映射,我采用的是自定義屬性方法。
#p#
- namespace LG.Common.Enities
 - {
 - using System;
 - using System.Collections.Generic;
 - using System.Text;
 - using System.Data;
 - using System.Data.SqlClient;
 - using Unie2e.Common;
 - using Unie2e.ORM;
 - using Unie2e.ORM.Mapping;
 - [System.SerializableAttribute()]
 - [Unie2e.ORM.Mapping.TableAttribute(
 - "City", MapFileName="CityCustParam.Xml")]
 - public sealed class CityEntity :
 - Unie2e.ORM.Mapping.Entity
 - {
 - /**//// <summary>
 - /// 映射字段的總長度
 - /// </summary>
 - private const int length = 5;
 
LINQ To SQL和ORM的理解之映射字段
- #region 映射字段
 - /**//// <summary>
 - /// 城市Id
 - /// </summary>
 - [Unie2e.ORM.Mapping.FieldAttribute(
 - "City", "CityId", true, SqlDbType.
 - UniqueIdentifier, 0, 0, 0, "城市Id")]
 - public Guid CityId = System.Guid.NewGuid();
 - /**//// <summary>
 - /// 省份Id
 - /// </summary>
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "ProvinceId", false, SqlDbType.UniqueIdentifier,
 - 0, 0, 0, "省份Id")]
 - public Guid ProvinceId = System.Guid.Empty;
 - /**//// <summary>
 - /// 城市代碼
 - /// </summary>
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityCode", false, SqlDbType.VarChar,
 - 50, 0, 0, "城市代碼")]
 - public String CityCode;
 - /**//// <summary>
 - /// 城市名稱
 - /// </summary>
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityName", false, SqlDbType.VarChar,
 - 50, 0, 0, "城市名稱")]
 - public String CityName;
 - /**//// <summary>
 - /// 備注
 - /// </summary>
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityComment", false, SqlDbType.VarChar,
 - 200, 0, 0, "備注")]
 - public String CityComment;
 - #endregion
 
LINQ To SQL和ORM的理解之重載基類函數(shù)
- public override void Initialize(object[] parameters)
 - {
 - if ((length != parameters.Length))
 - {
 - throw new E2EException("參數(shù)個數(shù)與字段數(shù)不相等");
 - }
 - CityId = ((System.Guid)(parameters[0]));
 - ProvinceId = ((System.Guid)(parameters[1]));
 - CityCode = ((string)(parameters[2]));
 - CityName = ((string)(parameters[3]));
 - CityComment = ((string)(parameters[4]));
 - }
 - public override object[] ToArray()
 - {
 - object[] objs = new object[5];
 - objs[0] = CityId;
 - objs[1] = ProvinceId;
 - objs[2] = CityCode;
 - objs[3] = CityName;
 - objs[4] = CityComment;
 - return objs;
 - }
 - #endregion
 - }
 - [System.SerializableAttribute()]
 - [Unie2e.ORM.Mapping.TableAttribute("City")]
 - public abstract class CityFindParam<T> : EntityParam<T> where T: IEntity
 - {
 - /**//// <summary>
 - /// 映射字段的總長度
 - /// </summary>
 - private const int length = 6;
 
LINQ To SQL和ORM的理解之映射字段
- [Unie2e.ORM.Mapping.FieldAttribute(
 - "City", "Method", SqlDbType.VarChar, 50, 0, 0, "")]
 - public String Method;
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityId", SqlDbType.UniqueIdentifier, 0, 0, 0, "")]
 - public Guid CityId = System.Guid.Empty;
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "ProvinceId", SqlDbType.UniqueIdentifier, 0, 0, 0, "")]
 - public Guid ProvinceId = System.Guid.Empty;
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityCode", SqlDbType.VarChar, 50, 0, 0, "")]
 - public String CityCode;
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityName", SqlDbType.VarChar, 50, 0, 0, "")]
 - public String CityName;
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityComment", SqlDbType.VarChar, 200, 0, 0, "")]
 - public String CityComment;
 - #endregion
 
LINQ To SQL和ORM的理解之重載基類函數(shù)
- public override object[] ToArray()
 - {
 - object[] objs = new object[6];
 - objs[0] = Method;
 - objs[1] = CityId;
 - objs[2] = ProvinceId;
 - objs[3] = CityCode;
 - objs[4] = CityName;
 - objs[5] = CityComment;
 - return objs;
 - }
 - public override void Initialize(object[] parameters)
 - {
 - if ((length != parameters.Length))
 - {
 - throw new E2EException("參數(shù)個數(shù)與字段數(shù)不相等");
 - }
 - Method = ((String)(parameters[0]));
 - CityId = ((Guid)(parameters[1]));
 - ProvinceId = ((Guid)(parameters[2]));
 - CityCode = ((String)(parameters[3]));
 - CityName = ((String)(parameters[4]));
 - CityComment = ((String)(parameters[5]));
 - }
 - #endregion
 - }
 - [System.SerializableAttribute()]
 - [Unie2e.ORM.Mapping.TableAttribute("City")]
 - public sealed class CityFindParam : CityFindParam<CityEntity>
 - {
 - }
 - [System.SerializableAttribute()]
 - [Unie2e.ORM.Mapping.TableAttribute("City")]
 - public abstract class CityActionParam<T> :
 - EntityParam<T> where T: IEntity
 - {
 - /**//// <summary>
 - /// 映射字段的總長度
 - /// </summary>
 - private const int length = 2;
 - 映射字段#region 映射字段
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "Method", SqlDbType.VarChar, 50, 0, 0, "")]
 - public String Method;
 - [Unie2e.ORM.Mapping.FieldAttribute("City",
 - "CityId", SqlDbType.UniqueIdentifier, 0, 0, 0, "")]
 - public Guid CityId = System.Guid.Empty;
 - #endregion
 
#p#
LINQ To SQL和ORM的理解之重載基類函數(shù)
- public override object[] ToArray()
 - {
 - object[] objs = new object[2];
 - objs[0] = Method;
 - objs[1] = CityId;
 - return objs;
 - }
 - public override void Initialize(object[] parameters)
 - {
 - if ((length != parameters.Length))
 - {
 - throw new E2EException("參數(shù)個數(shù)與字段數(shù)不相等");
 - }
 - Method = ((String)(parameters[0]));
 - CityId = ((Guid)(parameters[1]));
 - }
 - #endregion
 - }
 - [System.SerializableAttribute()]
 - [Unie2e.ORM.Mapping.TableAttribute("City")]
 - public sealed class CityActionParam :
 - CityActionParam<CityEntity>
 - {
 - }
 - }
 - [Unie2e.ORM.Mapping.TableAttribute("City",
 - MapFileName="CityCustParam.Xml")]
 
給出了映射對象與數(shù)據(jù)庫表或者視圖的關(guān)系,MapFileName給出了所對應(yīng)的通用查詢的配置文件,當(dāng)然也可以把Sql語句寫在配置文件里面,實(shí)現(xiàn)查詢之外的其他動作處理。因?yàn)楣ぞ邲]有對其他生成代碼函數(shù)的支持,所以通常還是通過工具來生成存儲過程來處理。
在Entity里面,有兩上重載的函數(shù):public override void Initialize(object[] parameters)和public override object[] ToArray(),這兩個函數(shù)是為了加速數(shù)據(jù)加載的。底層用的是DataReader讀取數(shù)據(jù),盡可能少的通過反射加載數(shù)據(jù),實(shí)現(xiàn)數(shù)據(jù)的快速從數(shù)據(jù)庫加載到對象里面,因?yàn)檫@些代碼都是通過工具生成的,不會出現(xiàn)錯誤,比較要注意的是用工具寫存儲過程的時候要注意一點(diǎn)就可以了。
- [Unie2e.ORM.Mapping.FieldAttribute("City", "CityId",
 - true, SqlDbType.UniqueIdentifier, 0, 0, 0, "城市Id")]
 
給出了對象Field與表或視圖的列的映射關(guān)系。***一個是對于注釋,對于BO的業(yè)務(wù)數(shù)據(jù)會生成Summary。最終會生成到界面的表述,以及列表的配置文件的頭。
除了Entity 外,還有CityFindParam<T> ,CityFindParam,CityActionParam<T>,CityActionParam這幾個類,CityFindParam<T> 主要是為了實(shí)現(xiàn)可變返回?cái)?shù)據(jù),默認(rèn)情況下返回的是完整表的數(shù)據(jù),有時候,數(shù)據(jù)庫表或視圖的字段很多,比較重要的地方,可能只會要求返回其中的部分字段,用在這方面處理。CityActionParam,主要是針對批量更新等特殊處理,對個別地方進(jìn)行性能優(yōu)化處理。
b)LINQ To SQL和ORM的理解之對象翻譯
對象翻譯就是把對象屬性翻譯到成相對應(yīng)的存儲過程的調(diào)用。我的ORM映射主要是對象參數(shù)處理。添加、刪除、修改都是對完整的對象的映射,對于特別的處理是通過Action處理的。
- SqlParameter[] BuilderSqlParameters(Type type,SQLActionType actionType)
 - {
 - FieldInfo[] infos = type.GetFields();
 - SqlParameter[] parameters =
 - new SqlParameter[infos.Length];
 - for (int i = 0; i < infos.Length; i++)
 - {
 - object[] objs = infos[i].GetCustomAttributes(typeof(FieldAttribute), false);
 - FieldAttribute attr = objs[0] as FieldAttribute;
 - SqlParameter sp;
 - if (attr.Length != 0)
 - sp = new SqlParameter("@" + attr.ColumnName, attr.SqlDbType, attr.Length);
 - else
 - {
 - sp = new SqlParameter("@" + attr.ColumnName, attr.SqlDbType);
 - }
 - if (attr.IsKey&&actionType== SQLActionType .Insert)
 - sp.Direction = ParameterDirection.InputOutput;
 - parameters[i]=sp;
 - }
 - }
 - return parameters;
 - }
 
***次執(zhí)行的時候,遍歷所有字段,根據(jù)FieldAttribute 生成調(diào)用存儲過程的Command,并緩存,第二次執(zhí)行的時候,直接從緩存里取出Command 的Clone()。此處調(diào)用Clone ,返回的是Command的DeepCopy?。?!也是微軟在2.0里面新添加的函數(shù),我估計(jì)新加的函數(shù)的用處也就是在這里了,極大的提高了處理速度。接下來就是存儲過程的賦值了。
- void AssignParameterValues(object instance, SqlCommand cmd)
 - {
 - SqlParameterCollection sps = cmd.Parameters;
 - IEntity idb =instance as IEntity;
 - if (idb == null)
 - throw new Exception("存儲過程賦值出錯,要具有IEntity接口");
 - object[] values = idb.ToArray();
 - if (values.Length != sps.Count )
 - throw new Exception("參數(shù)個數(shù)不一致");
 - for (int i = 0; i < values.Length; i++)
 - {
 - object val = values[i];
 - //對于時間的空值等,這里還會報(bào)錯。一般要求要有初始化。
 - sps[i].Value =null == val ?System.DBNull.Value:val;
 - }
 - }
 
剛剛介紹了在Entity和ParamEntity里面有兩個重載的虛函數(shù)函數(shù),其中一個是:ToArray(),在這里用到了。如果是通過反射得到實(shí)例的值,那效率實(shí)在是太低了,這里用了IEntity的ToArray(),因?yàn)橛成涞捻樞蛭恢枚际枪潭ǖ?,所以可以通過數(shù)組的方式實(shí)現(xiàn)。這也是工具的重要的地方,靠手工處理,錯誤的幾率太高了。
現(xiàn)在存儲過程是可以正常調(diào)用了,正常的 增、改、刪,是可以處理了,下面介紹取數(shù)據(jù)。
#p#
c)LINQ To SQL和ORM的理解之?dāng)?shù)據(jù)讀取加載
數(shù)據(jù)加載主要是通過DataReader實(shí)現(xiàn)的。
- object GetFindResult(IDataReader reader, Type targetType)
 - {
 - Type target = targetType;
 - object[] fields = new object[reader.FieldCount];
 - IEntity t = (IEntity)Activator.CreateInstance(target);
 - Type constructed = typeof(ORMCollection<>).
 - MakeGenericType(target);
 - IList list = (IList)Activator.CreateInstance(constructed);
 - while (reader.Read())
 - {
 - for (int i = 0; i < fields.Length; i++)
 - {
 - fields[i] = reader.IsDBNull(i) ? null : reader[i];
 - }
 - t.Initialize(fields);
 - list.Add(t.Clone());
 - }
 - return list;
 - }
 - object GetFindResult(IDataReader reader,
 - Type targetType, int currentpage, int pagesize)
 
數(shù)據(jù)加載提供了2個函數(shù),一個是帶分頁一個是不帶分頁。IEntity提供的幾個接口在這里都用了,也主要是為了這里準(zhǔn)備的。也主要是在此實(shí)現(xiàn)了數(shù)據(jù)的快速加載。主要體現(xiàn)在兩個上面,一個是t.Initialize(fields),Initialize 在上面是介紹了,不用通過反射初始化及賦值,再者是 t.Clone(),用的也是DeepCopy,不是用Activator 創(chuàng)建新的對象實(shí)例,因?yàn)锳ctivator 構(gòu)造對象的時候速度跟DeepCopy相比,要慢了很多。
還有一個是我自封的通用查詢,也就是通過MapFileName 找到配置文件,把Sql配置文件加載進(jìn)來,翻譯成Command加到緩存里面,后期的處理與上面的一樣,在這里就不再過多的介紹了,只把關(guān)鍵代碼列出來。
- string BuildFind(FieldInfo[] pis, FinderParam finderParam,
 - out SqlParameter[] parameters)
 - {
 - ORMCollection<FinderParamItem> items =
 - finderParam.ParamItemCollection;
 - if (items==null)
 - throw new E2EException("ParamItemCollection參數(shù)不能為空");
 - StringBuilder sb = new StringBuilder();
 - List<SqlParameter> parameterList = new List<SqlParameter>();
 - foreach (FinderParamItem var in items)
 - {
 - FieldInfo pi = Array.Find(pis, delegate(FieldInfo obj) {
 - return obj.Name.Equals(var.PropertyName,
 - StringComparison.OrdinalIgnoreCase); });
 - if (pi == null)
 - throw new E2EException("不存在參數(shù):"+var.PropertyName);
 - object[] attribs = pi.GetCustomAttributes(
 - typeof(FieldAttribute), false);
 - if (attribs.Length == 0)
 - continue;
 - FieldAttribute attr = attribs[0] as FieldAttribute;
 - string typeName = pi.FieldType.Name;
 - if (sb.Length != 0)
 - {
 - switch (var.JoinKey)
 - {
 - case JoinKey.與:
 - sb.Append(" AND ");
 - break;
 - case JoinKey.或:
 - sb.Append(" OR ");
 - break;
 - default:
 - break;
 - }
 - }
 - sb.Append(" (");
 - sb.Append(attr.TableName+"."+attr.ColumnName);
 - switch (var.ComparisonKey)
 - {
 - case ComparisonKey.等于:
 - sb.Append("=");
 - break;
 - case ComparisonKey.不等于:
 - sb.Append("<>");
 - break;
 - case ComparisonKey.大于:
 - sb.Append(">");
 - break;
 - case ComparisonKey.小于:
 - sb.Append("<");
 - break;
 - case ComparisonKey.大于等于:
 - sb.Append(">=");
 - break;
 - case ComparisonKey.小于等于:
 - sb.Append("<=");
 - break;
 - case ComparisonKey.包含:
 - sb.Append(" like ");
 - break;
 - case ComparisonKey.不含:
 - sb.Append("<>");
 - break;
 - default:
 - break;
 - }
 - sb.Append("@" + pi.Name + ")");
 - SqlParameter param = new SqlParameter();
 - param.ParameterName = "@" + pi.Name;
 - param.SqlDbType = attr.SqlDbType;
 - param.Size = attr.Length;
 - param.Scale = (byte)attr.XScale;
 - param.Precision = (byte)attr.XPrec;
 - param.IsNullable = true;
 - parameterList.Add(param);
 - sb.Append(" ");
 - }
 - if (sb.Length > 0)
 - sb.Insert(0, " WHERE ");
 - parameters = parameterList.ToArray();
 - return sb.ToString();
 - }
 
到此,基本ORM 映射的處理已經(jīng)介紹完了,映射的代碼都是通過工具生成的,不用人工去處理,曾考慮直接生成Dll的,包括存儲過程,都是工具與之配套的,至少不用寫千篇一率的Insert、Update、Delete、FindById這些枯燥的存儲過程了,如果是通過外鍵關(guān)聯(lián)的,相關(guān)的存儲過程也是自動生成的,盡可能減少人工處理過程。(建立的時候可以把外鍵全部進(jìn)來,代碼生成完之后,再把外鍵去掉,偷工減料的做法,偶經(jīng)常做 ^_^)
當(dāng)然,這里只是ORM 處理,還沒有把業(yè)務(wù)邏輯層關(guān)聯(lián)起來,大家最關(guān)注的還是業(yè)務(wù)邏輯處理,這才是核心,我只所以不想更換ORM,部分是由于我的邏輯層的組織。
原文來自:http://www.cnblogs.com/dreamstec/archive/2008/02/06/1065343.html
關(guān)于LINQ To SQL和ORM的理解的相關(guān)內(nèi)容就向你介紹到這里,希望對你了解和學(xué)習(xí)LINQ To SQL和ORM的理解有所幫助。
【編輯推薦】















 
 
 
 
 
 
 