<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      happyhippy

      這個世界的問題在于聰明人充滿疑惑,而傻子們堅信不疑。--羅素

          如果允許在UI層直接訪問Linq to Sql的DataContext,可以省去很多問題,譬如在處理多表join的時候,我們使用var來定義L2S查詢,讓IDE自動推斷變量的具體類型(IQueryable<匿名類型>),并提供友好的智能提示;而且可以充分應用L2S的延遲加載特性,來進行動態查詢。但如果我們希望將業務邏輯放在一個獨立的層中(譬如封裝在遠程的WCF應用中),又希望在邏輯層應用Linq to sql,則情況就比較復雜了;由于我們只能使用var(IQueryable<匿名類型>),而var只能定義方法(Method)范圍中聲明的變量,出了方法(Method)之后IDE就不認得它了;在這種對IQueryable<匿名類型>一無所知的情況下,又希望能在開發時也能應用上IDE的智能感應,我們該怎么定義層之間交互的數據傳輸載體呢?又如何對它進行動態查詢呢?

           內容比較多,分上下兩篇,上篇介紹查詢返回自定義實體,下篇介紹動態查詢。

       

           下面來看一個示例(以NorthWind數據庫為示例),現在我們要在界面上展示某個用戶什么時間訂購了哪些產品。

      L2S_NorthWind

          如果允許在UI層直接訪問DataContext,我們可以這樣來寫:

         1: using (NorthWindDataContext context = new NorthWindDataContext())
         2: {
         3:     var query0 = from C in context.Customers
         4:                 join O in context.Orders
         5:                     on C.CustomerID equals O.CustomerID
         6:                 join OD in context.Order_Details
         7:                     on O.OrderID equals OD.OrderID
         8:                 join P in context.Products
         9:                     on OD.ProductID equals P.ProductID
        10:                 select new
        11:                 {
        12:                     C.CustomerID,
        13:                     C.CompanyName,
        14:                     C.ContactName,
        15:                     C.Address,
        16:                     O.OrderDate,
        17:                     P.ProductName
        18:                 };
        19:     gridView.DataSource = query0.ToList();
        20:     gridView.DataBind();
        21: }

          這里只查詢需要顯示的列,避免返回不必要的列。查詢返回的是一個泛型匿名對象集合,由于綁定操作與查詢操作在同一個方法內,所以IDE會自動幫忙推斷var的對象類型。但如果要將查詢邏輯封裝在遠程的WCF中,我們該用啥作為層之間交互的數據傳輸載體呢?List<???>,里面的“???”該是啥呢?

          以下是我嘗試過的幾種方案和走過的彎路。

       

      1. 擴展默認實體定義

          從上面的代碼中可以看到,我們需要返回的屬性信息主要來源于Customers實體,下面來嘗試下能否在該實體的定義中直接附加字段OrderDate和ProductName:

         1: partial class Customers
         2: {
         3:     public DateTime OrderDate {get;set;}
         4:     public string ProductName { get; set; }
         5: }

          然后這樣來寫查詢,看看能不能欺騙L2S來自動匹配這新增的兩個屬性:

         1: public List<Customers> GetOrderInfo(string customerID)
         2: {
         3:     using (NorthWindDataContext context = new NorthWindDataContext())
         4:     {
         5:         var query1 = from C in context.Customers
         6:                     join O in context.Orders
         7:                         on C.CustomerID equals O.CustomerID
         8:                     join OD in context.Order_Details
         9:                         on O.OrderID equals OD.OrderID
        10:                     join P in context.Products
        11:                         on OD.ProductID equals P.ProductID
        12:                     where C.CustomerID == customerID
        13:                     select C;  //直接返回實體
        14:  
        15:         //或者這樣
        16:         var query2 = from C in context.Customers
        17:                     join O in context.Orders
        18:                         on C.CustomerID equals O.CustomerID
        19:                     join OD in context.Order_Details
        20:                         on O.OrderID equals OD.OrderID
        21:                     join P in context.Products
        22:                         on OD.ProductID equals P.ProductID
        23:                     where C.CustomerID == customerID
        24:                     select new Customers  //顯示構造實體
      構造實體
        25:                     {
        26:                         CustomerID = C.CustomerID,
        27:                         CompanyName = C.CompanyName,
        28:                         ContactName = C.ContactName,
        29:                         Address = C.Address,
        30:                         OrderDate = O.OrderDate,
        31:                         ProductName = P.ProductName
        32:                     };
        33:         return query1.ToList(); //query2.ToList()
        34:     }
        35: }

          很遺憾的是,query1查詢執行的結果,沒有取得我們需要的數據:

      L2S_Search_Partial

          而query2也拋出了NotSupportedException:不允許在查詢中顯式構造實體類型“TestLINQ.Customers”。

           看來,這種方法行不通。

       

      2. 使用Translate來返回自定義實體

          在老趙的這篇文章中:《在LINQ to SQL中使用Translate方法以及修改查詢用SQL》,里面提出了一種方法來來砍掉那些不需要加載的信息,且可以繼續使用LINQ to SQL進行查詢。

          這里借鑒下里面的思路,看看在增加屬性的情況下,結果會怎樣:

         1: public List<Customers> GetOrderInfo(string customerID)
         2: {
         3:     using (NorthWindDataContext context = new NorthWindDataContext())
         4:     {
         5:         var query3 = query0;
         6:         return context.ExecuteQuery<Customers>(query);
         7:     }
         8: }

      說明:
      (1) 這里的Customers類型定義,繼續用上一節中的對實體類的擴展;
      (2) DataContext.ExcuteQuery<T>(IQuery query)方法,使用的老趙的DataContext擴展;
      (3) 為避免L2S查詢占用太多的版面,前面對每個查詢都進行了編號,query0, query1, query2….,下面如果需要用到同樣的查詢時,直接引用前面的查詢,以節省版面和突出重點。

          很遺憾的是,這次希望又落空了。返回的結果中,OrderDate和ProductName依然為空。

          老趙只提供了砍掉不需要的字段的方法,把增加字段的方法自己留著了/:)

          另外補充一點,這里對老趙提供的方法做了一點兒改進:如果調用OpenConnection時打開了新的連接,則需要在用完后關閉該連接,下面是代碼:

         1: public static List<T> ExecuteQuery<T>(this DataContext dataContext, IQueryable query)
         2: {
         3:     using (DbCommand command = dataContext.GetCommand(query))
         4:     {
         5:         bool openNewConnecion = false;
         6:         try
         7:         {
         8:             openNewConnecion = dataContext.OpenConnection();
         9:             using (DbDataReader reader = command.ExecuteReader())
        10:             {
        11:                 return dataContext.Translate<T>(reader).ToList();
        12:             }
        13:         }
        14:         finally
        15:         {
        16:             if (openNewConnecion) //如果打開了新的連接,則需要手動Close
        17:                 dataContext.Connection.Close();
        18:         }
        19:     }
        20: }
        21:  
        22: /// <summary>
        23: /// 打開連接
        24: /// </summary>
        25: /// <param name="dataContext"></param>
        26: /// <returns>是否打開了新的連接(這個返回值可能容易讓人誤解,汗...)</returns>
        27: private static bool OpenConnection(this DataContext dataContext)
        28: {
        29:     if (dataContext.Connection.State == ConnectionState.Closed)
        30:     {
        31:         dataContext.Connection.Open();
        32:         return true; 
        33:     }
        34:     return false;
        35: }

       

      3. 執行TSQL

          使用DataContext自帶的ExcuteQuery<T>方法:

         1: public List<Customers> GetOrderInfo(string customerID)
         2: {
         3:     using (NorthWindDataContext context = new NorthWindDataContext())
         4:     {
         5:         string sql = @"SELECT C.CustomerID, C.CompanyName, C.ContactName, C.[Address], O.OrderDate, P.ProductName 
         6:  dbo.Customers AS C
         7:  dbo.Orders AS O
         8: ON O.CustomerID = C.CustomerID
         9:  dbo.[Order Details] AS OD
        10: ON OD.OrderID = O.OrderID
        11:  dbo.Products AS P
        12: ON P.ProductID = OD.ProductID
        13: E  C.CustomerID={0}";
        14:         return context.ExecuteQuery<Customers>(sql, customerID).ToList();
        15:     }
        16: }

          結果跟第二節中的結果相同,又失敗了……

          補充,MSDN上關于Translate和ExcuteQuery對查詢結果進行轉換的描述如下:

      • 1. 使查詢結果中的列與對象中的字段和屬性相匹配的算法如下所示:

        • 1.1 如果字段或屬性映射到特定列名稱,則結果集中應包含該列名稱。

        • 1.2 如果未映射字段或屬性,則結果集中應包含其名稱與該字段或屬性相同的列。

        • 1.3 通過先查找區分大小寫的匹配來執行比較。如果未找到匹配項,則會繼續搜索不區分大小寫的匹配項。

      • 2. 如果同時滿足下列所有條件,則該查詢應當返回(除延遲加載的對象外的)對象的所有跟蹤的字段和屬性:

      否則會引發異常。

          我愣是看了好多遍,還是沒有搞明白,為啥將結果集轉換到對象集合時L2S把我增加的字段給拋棄了……

       

      4. 繼承默認實體定義

          既然不讓我在L2S生成的默認實體上直接進行擴展,那我可以派生一個實體并添加我們需要的字段嗎?

         1: public class CustomerExt : Customers
         2: {
         3:     public DateTime? OrderDate {get;set;}
         4:     public string ProductName { get; set; }
         5: }

          然后在業務邏輯層里面這樣寫:

         1: public List<CustomerExt> GetOrderInfo(string customerID)
         2: {
         3:     using (NorthWindDataContext context = new NorthWindDataContext())
         4:     {
         5:         var query4 = query0
         6:         return context.ExecuteQuery<CustomerExt>(query).ToList();
         7:     }
         8: }

          遺憾的是,程序執行到dataContext.Translate<T>(reader).ToList()時,又出錯了,拋出了InvalidOperationException異常:

      未處理 System.InvalidOperationException
        Message="類型為“TestLINQ.Customers”的數據成員“System.String CustomerID”不是類型“CustomerExt”的映射的一部分。該成員是否位于繼承層次結構根節點的上方?"
        Source="System.Data.Linq"
        StackTrace:
             在 System.Data.Linq.SqlClient.SqlBinder.Visitor.GetRequiredInheritanceDataMember(MetaType type, MemberInfo mi)
             在 System.Data.Linq.SqlClient.SqlBinder.Visitor.AccessMember(SqlMember m, SqlExpression expo)
             在 System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitMember(SqlMember m)
             在 System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
             在 System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr)
             在 System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitNew(SqlNew sox)
             在 System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
             在 System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr)
             在 System.Data.Linq.SqlClient.SqlVisitor.VisitUserQuery(SqlUserQuery suq)
             在 System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitUserQuery(SqlUserQuery suq)
             在 System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
             在 System.Data.Linq.SqlClient.SqlBinder.Bind(SqlNode node)
             在 System.Data.Linq.SqlClient.SqlProvider.BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection`1 parentParameters, SqlNodeAnnotations annotations)
             在 System.Data.Linq.SqlClient.SqlProvider.GetDefaultFactory(MetaType rowType)
             在 System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Translate(Type elementType, DbDataReader reader)
             在 System.Data.Linq.DataContext.Translate(Type elementType, DbDataReader reader)
             在 System.Data.Linq.DataContext.Translate[TResult](DbDataReader reader)
             在 TestLINQ.DataContextExtensions.ExecuteQuery[T](DataContext dataContext, DbCommand command) 位置 D:\04.Other\WinForm\TestLINQ\DataContextExtensions.cs:行號 74
             在 TestLINQ.DataContextExtensions.ExecuteQuery[T](DataContext dataContext, IQueryable query, Boolean withNoLock) 位置 D:\04.Other\WinForm\TestLINQ\DataContextExtensions.cs:行號 53
             在 TestLINQ.DataContextExtensions.ExecuteQuery[T](DataContext dataContext, IQueryable query) 位置 D:\04.Other\WinForm\TestLINQ\DataContextExtensions.cs:行號 28
             在 TestLINQ.Program.GetOrderInfo(String customerID) 位置 D:\04.Other\WinForm\TestLINQ\Class1.cs:行號 49
             在 TestLINQ.Program.Main(String[] args) 位置 D:\04.Other\WinForm\TestLINQ\Class1.cs:行號 21
             在 System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
             在 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
             在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
             在 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
             在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
             在 System.Threading.ThreadHelper.ThreadStart()
        InnerException: 

          回過頭來看看L2S中的繼承,MSDN說法如下:若要在 LINQ 中執行繼承映射,您必須在繼承層次結構的根類中指定屬性 (Attribute) 和屬性 (Attribute) 的屬性 (Property)。(FROM MSDN: 映射繼承層次結構 (LINQ to SQL))

          看得我有點兒暈暈的....如果我不想修改L2S幫我生成的類型定義文件,則需要通過partial類對默認生成的Customers進行擴展:擴展一個屬性作為鑒別器值?
           好像挺繞的,我最終還是沒有嘗試成功……

       

           上面啰嗦了這么多廢話,是我使用L2S過程中走過的一些彎路,列出來供大家參考,避免重蹈我的覆轍。

      ---------------------------------------------------------------------------------------------------------------

      --------------------------我是華麗的分割線(happyhippy.cnblogs.com)-------------------------------

      ---------------------------------------------------------------------------------------------------------------

       

      5. 顯式自定義實體

           在上面一節嘗試使用繼承時,查看錯誤堆棧信息,最后定位到GetRequiredInheritanceDataMember這里,這是在訪問基類成員時出錯了。于是我起了個邪惡的念頭,把基類拋棄掉,顯式再定義一個實體看看:

         1: public class CustomerOrderDetial
         2: {
         3:     public string CustomerID { get; set; }
         4:     public string CompanyName { get; set; }
         5:     public string ContactName { get; set; }
         6:     public string Address { get; set; }
         7:     public DateTime? OrderDate { get; set; }
         8:     public string ProductName { get; set; }
         9: }
        10:  
        11: public List<CustomerOrderDetial> GetOrderInfo(string customerID)
        12: {
        13:     using (NorthWindDataContext context = new NorthWindDataContext())
        14:     {
        15:         var query5 = query0
        16:         return context.ExecuteQuery<CustomerOrderDetial>(query5).ToList();
        17:     }
        18: }

           這次運行通過了,而且得到了我們想要的結果,Congratulations!

      L2S_Search_UserDefineType
           但是,這樣操作的話,每次我們都要去手工編寫代碼,將我們需要的字段封裝成一個實體類型。

           結合上面第3節中的結論,我推測Translate和ExcuteQuery是按照下列邏輯來將結果集轉換成對象集合的:

         1: if(實體是由Table影射的實體)
         2: {
         3:     轉換時,只匹配標記為[Column]的屬性
         4: }
         5: else //顯式自定義實體(參考下面第4節)
         6: {
         7:    轉換時,根據屬性名與結果集中的列名進行匹配
         8: }

       

      6. 使用視圖/存儲過程/自定義函數

          另一種方法是使用視圖、或存儲過程、或自定義函數,讓L2S設計器或者SqlMeta工具將視圖映射成實體,或生成調用存儲過程和自定義函數的代碼。
          可以參考MSDN:存儲過程 (LINQ to SQL)。使用自定義函數過程與存儲過程差不錯,使用視圖的過程與表差不多,具體可以看MSDN中介紹,及L2S生成的源代碼,這里就不啰嗦了。

          然而,視圖、存儲過程、自定義函數也不是萬金油。就拿本文的例子來說,我們的應用場景是“查詢客戶什么時間訂了哪些產品”,于是我們定義了一個視圖來關聯相關的四張表;但一個應用系統中,往往會有很多場景;各種場景相互之間很相似,但又有不同,譬如“查詢客戶什么時間訂了哪些公司生產的哪些產品”、“查詢客戶什么時間訂了哪些雇員銷售的哪些產品”,我們又該怎么處理呢?為每個場景定制一個視圖?還是做一個聰明的大視圖,把所有關聯的表都join起來?
          使用前者的結果可能會是,試圖的數量呈爆炸式增長;
          使用后者的結果可能會是:聰明反被聰明誤,性能不是一般地差。

       

      7. 自定義對象轉換器

          前面的兩種方法雖然都可行,但用起來還是有點兒麻煩,能不能簡單一點兒呢?

           在使用LINQ之前,我們經常使用Ado.Net從數據庫中取得一個數據集(DataSet或者DataTable),然后再根據列名稱與對象的屬性名進行匹配,將數據集轉換成對象集合List<T>。在本節中,我將參考這個思路,自定義一個對象轉換器。

           LINQ中,有一個擴展方法IEnumerable.Cast<TResult>,實現了從IEnumerable到IEnumerable<TResult>的轉換,里面實現的是遍歷源集合,然后將里面的元素進強制類型轉換TResult類型,最后返回IEnumerable<TResult>。但這里,我們要實現的是,將IEnumerable<匿名類型>轉換成IEnumerable<命名類型>,使用該轉換器的代碼示例如下圖所示:

      L2S_ObjectConverter

          下面是執行結果(其中CustomerExt使用第4節中的實體定義,繼承自Customers):L2S_ObjectConverterResult

           使用起來還算比較清爽;當然,也有不足之處,性能怎樣是一個考慮點,還有就是如上面的運行結果截圖,一些被我們坎掉的字段也會顯示出來;雖然這些額外字段的值都為空,但考慮下列情況:UI層取得的結果是List<CustomerExt>,但他怎么知道CustomerExt中哪些字段可以用,哪些字段被閹割了呢?答案是:源代碼前面沒有秘密,只有看底層的源代碼了-.-

          下面來看下這個對象轉換器的源代碼:

         1: public static class ObjectConverter
         2: {
         3:     private class CommonProperty
         4:     {
         5:         public PropertyInfo SourceProperty { get; set; }
         6:         public PropertyInfo TargetProperty { get; set; }
         7:     }
         8:  
         9:     public static List<TResult> ConvertTo<TResult>(this IEnumerable source)
        10:         where TResult : new()
        11:     {
        12:         if (source == null) //啥都不用干
        13:             return null;
        14:  
        15:         if (source is IEnumerable<TResult>)
        16:             return source.Cast<TResult>().ToList();//源類型于目標類型一致,可以直接轉換
        17:  
        18:         List<TResult> result = new List<TResult>();
        19:         bool hasGetElementType = false;
        20:         IEnumerable<CommonProperty> commonProperties = null; //公共屬性(按屬性名稱進行匹配)
        21:  
        22:         foreach (var s in source)
        23:         {
        24:             if (!hasGetElementType) //訪問第一個元素時,取得屬性對應關系;后續的元素就不用再重新計算了
        25:             {
        26:                 if (s is TResult) //如果源類型是目標類型的子類,可以直接Cast<T>擴展方法
        27:                 {
        28:                     return source.Cast<TResult>().ToList();
        29:                 }
        30:                 commonProperties = GetCommonProperties(s.GetType(), typeof(TResult));
        31:                 hasGetElementType = true;
        32:             }
        33:  
        34:             TResult t = new TResult();
        35:             foreach (CommonProperty commonProperty in commonProperties) //逐個屬性拷貝
        36:             {
        37:                 object value = commonProperty.SourceProperty.GetValue(s, null);
        38:                 commonProperty.TargetProperty.SetValue(t, value, null);
        39:             }
        40:             result.Add(t);
        41:         }
        42:  
        43:         return result;
        44:     }
        45:  
        46:     private static IEnumerable<CommonProperty> GetCommonProperties(Type sourceType, Type targetType)
        47:     {
        48:         PropertyInfo[] sourceTypeProperties = sourceType.GetProperties();//獲取源對象所有屬性
        49:         PropertyInfo[] targetTypeProperties = targetType.GetProperties(); //獲取目標對象所有屬性
        50:         return from SP in sourceTypeProperties
        51:                join TP in targetTypeProperties
        52:                   on SP.Name.ToLower() equals TP.Name.ToLower() //根據屬性名進行對應(不區分大小寫)
        53:                select new CommonProperty
        54:                {
        55:                    SourceProperty = SP,
        56:                    TargetProperty = TP
        57:                };
        58:     }
        59: }

          源代碼前沒有秘密,里面就是實現了最簡單的轉換:將源對象集合中的元素逐個轉換成目標對象。

          關于這段代碼的一點補充說明(下面的源類型和目標類型,是指泛型中的T,而不是IEnumerable<T>):
      (1). 如果源類型于目標類型一致,或者源類型是目標類型的子類,則可以不用逐個元素遍歷了,直接調用IEnumerable的擴展方法Cast<T>()即可;用Reflector看了下其源代碼實現,里面比較繞,不知道性能咋樣,暫時不管了,用著先,而且這樣很省事兒。
          另外List<T>也提供了一個ConvertAll<TOutput>(Converter<T, TOutput> converter)方法,可以自己定義一個對象轉換器方法,然后傳給Converter<T, TOutput>委托;但這里用不上該方法,原因如下:
          a. 看其源代碼實現,可以發現其就是遍歷集合循環執行Converter委托,這樣不便于進行優化(參考下面的第(2)點);
          b. 雖然我可以實現一個Converter<T, TOutput>,但在外面該怎樣調用呢?因為query的類型是IQueryable<匿名類型>,所以在調用時,我們根本不知道該傳啥進去。

      (2). 如果不滿足(1),則需要逐個元素進行轉換。由于在進入foreach(上面代碼的第22行)之前,還不知道源類型是什么類型,因此將GetCommonProperties方法放到循環中;但如果源集合中有100個元素,而循環中每次都來執行這個方法,合計執行100次,這樣會顯得很傻X,因此里面加了點控制,只在處理第一個元素時調用該方法,然后將屬性匹配結果緩存下來(使用局部變量commonProperties進行緩存),從而避免每次都做無用功。

      (3). 執行返回的結果時List<TResult>,也即是執行此方法時,如果傳進來的是IQueryable<T>,則會立即進行計算。

      (4). 這里面還有繼續優化的余地:如果有100個用戶同時在執行這個查詢請求,則每個請求里面都在進行執行GetCommonProperties函數,然后各自進行著反射(取得“特定匿名類型”和CustomerExt類型的屬性集合)和屬性匹配(取得“特定匿名類型”和CustomerExt類型的公共屬性)運算,這樣又會顯得傻X了。對于一個普通的已經部署完畢的應用系統,其中的實體類型定義是恒定的(不考慮動態編譯的情況;對于匿名類型,在編譯時,編譯器會為其創建類型定義),而且類型之間的轉換關系也是恒定的,因此我們可以這些信息緩存下來,避免每次請求都執行重復計算。下面是一個最簡單的屬性緩存器,采用靜態變量來保存計算過的信息,直接替換上面的GetCommonProperties方法即可:

         1: private static class PropertyCache
         2: {
         3:     private static object syncProperty = new object();
         4:     private static object syncCommon = new object();
         5:  
         6:     private static Dictionary<Type, PropertyInfo[]> PropertyDictionary =
         7:         new Dictionary<Type, PropertyInfo[]>(); //緩存類型的PropertyInfo數組
         8:     private static Dictionary<string, IEnumerable<CommonProperty>> CommonPropertyDictionary =
         9:         new Dictionary<string, IEnumerable<CommonProperty>>(); //緩存兩種類型的公共屬性對應關系
        10:  
        11:     private static PropertyInfo[] GetPropertyInfoArray(Type type)
        12:     {
        13:         if (!PropertyCache.PropertyDictionary.ContainsKey(type))
        14:         {
        15:             lock (syncProperty)
        16:             {
        17:                 if (!PropertyCache.PropertyDictionary.ContainsKey(type)) //雙重檢查
        18:                 {
        19:                     PropertyInfo[] properties = type.GetProperties();
        20:                     PropertyCache.PropertyDictionary.Add(type, properties); //Type是單例的(Singleton),可以直接作為Key
        21:                 }
        22:             }
        23:         }
        24:         return PropertyCache.PropertyDictionary[type];
        25:     }
        26:  
        27:     public static IEnumerable<CommonProperty> GetCommonProperties(Type sourceType, Type targetType)
        28:     {
        29:         string key = sourceType.ToString() + targetType.ToString();
        30:         if (!PropertyCache.CommonPropertyDictionary.ContainsKey(key))
        31:         {
        32:             lock (syncCommon)
        33:             {
        34:                 if (!PropertyCache.CommonPropertyDictionary.ContainsKey(key)) //雙重檢查
        35:                 {
        36:                     PropertyInfo[] sourceTypeProperties = GetPropertyInfoArray(sourceType);//獲取源對象所有屬性
        37:                     PropertyInfo[] targetTypeProperties = GetPropertyInfoArray(targetType);//獲取目標對象所有屬性
        38:                     IEnumerable<CommonProperty> commonProperties = from SP in sourceTypeProperties
        39:                                                                    join TP in targetTypeProperties
        40:   on SP.Name.ToLower() equals TP.Name.ToLower()
        41:                                                                    select new CommonProperty
        42:                                                                    {
        43:                                                                        SourceProperty = SP,
        44:                                                                        TargetProperty = TP
        45:                                                                    };
        46:                     PropertyCache.CommonPropertyDictionary.Add(key, commonProperties);
        47:                 }
        48:             }
        49:         }
        50:         return PropertyCache.CommonPropertyDictionary[key];
        51:     }
        52: }

       

      8. Something Others

            上面第7節中,看起來好像解決了文章標題所提出的問題,但這種方式也可能是個陷阱。

            其中使用了CustomerExt,其繼承自L2S生成的默認實體Customers,這樣帶來的一個好處就是可以復用Customers中的屬性定義,而不必像第5節中一樣,重新定義一套。但是從繼承的語義上來講,繼承體現的是一種IS-A的關系,因此套用過來的話就是這樣:“客戶什么時間訂購哪些商品”是一個“客戶”!???這是啥?幼兒園沒畢業吧?打回去重讀……

           在某些場景下,我們可以應用繼承,譬如NorthWind數據庫中有張表dbo.Contacts記錄用戶的聯系信息,則我們可以對Customer或者Employee進行擴展,添加聯系信息;而對于本文所舉的這個例子,繼承是被濫用了。當然,本文的重點是Linq to Sql,而不是OO,因此,這里就請各位看官不要追究我的錯誤了………我先原諒我自己,愿主也原諒我吧,阿彌陀佛。。。

           為了將功補過,這里引入一點Entity Framework的東西,下面這個截圖來自《Linq in Action》:

      EF_LinqInAction

           在Linq to Sql中,我們只能將表或者視圖影射成實體定義,且這種影射是1對1影射。從上圖可以看到,在EF中,可以建立一個概念模型,將多個表影射到一個實體定義;于是,整個世界清靜了……

           我也只是撇了一眼,還沒有用過EF,不知道自己理解的對不對;這里只是做個引子,有興趣的話,各位可以自己研究研究,記得把研究結果分享給我/:)

       

      最有來個總結(由于個人認知的局限性,這些結論可能不一定正確):

        可行性 缺點
      擴展默認實體定義 --
      使用Translate來返回自定義實體 --
      執行TSQL返回自定義實體 --
      繼承默認實體定義 --
      顯式自定義實體 麻煩,要自己Code,定義新的實體類型
      使用視圖/存儲過程/自定義函數 不夠靈活,無法為每個應用場景都去訂制視圖
      自定義對象轉換器 繼承關系可能會被濫用;返回的實體集合是個黑盒子,上層可能不知道實體的哪些屬性可用,哪些不可用
      Entity Framework 貌似可行 --
      posted on 2010-01-25 13:55  Silent Void  閱讀(7114)  評論(16)    收藏  舉報

      主站蜘蛛池模板: 三人成全免费观看电视剧高清| 国产av剧情md精品麻豆| 国产AV影片麻豆精品传媒| 四虎网址| 中文字幕亚洲无线码A| 亚洲国产aⅴ成人精品无吗 | 亚洲一区中文字幕人妻| 国产无套精品一区二区三区 | 男女猛烈激情xx00免费视频| 午夜福利激情一区二区三区| 日本老熟女一二三区视频| 人妻丰满熟妇无码区免费| 另类专区一区二区三区| 国产热A欧美热A在线视频| 又黄又硬又湿又刺激视频免费| 国产对白叫床清晰在线播放| 国产精品无码无片在线观看3d| 国产精品99区一区二区三| 亚洲国产综合自在线另类| 亚洲欧美偷国产日韩| 亚洲精品成人无限看| 免费99精品国产人妻自在现线| 国精偷拍一区二区三区| 精品国产一区二区在线视| 亚洲av永久无码精品漫画| 国产成人av乱码在线观看| 国产成人综合久久亚洲av| 东方四虎在线观看av| 美女内射毛片在线看3d| 麻豆国产97在线 | 欧美| 中文字幕国产精品自拍| 精品久久久久久国产| 国产一区二三区日韩精品| 精品国产中文字幕第一页| 无码伊人66久久大杳蕉网站谷歌| 欧美日本激情| 国产成人亚洲综合91精品| 日韩毛片在线视频x| 国内精品久久久久影视| 欧美人与动牲交a免费| 国产日韩一区二区天美麻豆|