使用Mono.Cecil輔助ASP.NET MVC使用dynamic類型Model

使用Mono.Cecil輔助ASP.NET MVC使用dynamic類型Model

作者: 老趙 發布時間: 2011-10-20 17:10 閱讀: 6759 次 推薦: 2 原文鏈接 [收藏]

  這也是之前在珠三角技術沙龍上的示例之一,解決的是在ASP.NET MVC使用dynamic類型Model時遇到的一個真實問題。C# 4編譯器支持dynamic類型,因此在編寫頁面模板的時候自然就可以把它作為視圖的Model類型。表現層的需求很容易改變,因此dynamic類型的Model可以減少我們反復修改強類型Model的麻煩,再配合匿名類型的使用,可謂是動靜相宜,如魚得水。不過,如果把一個匿名類型直接作為Model交給視圖去使用,在默認情況下會拋出異常。我們可以用Mono.Cecil來改變這一情況。

  在視圖中使用dynamic類型Model

  我們先來重現這個問題。創建一個使用C# 4的ASP.NET MVC網站,添加如下的Controller,其中把匿名類型作為視圖Model:

public class HomeController : Controller
{
public ActionResult Index(string title = “<<Default>>")
{
return View(new { Title = title });
}
}

  并定義一個Index.aspx作為視圖模板,Model類型作為dynamic,并用到Title:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>"%>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Index</title>
</head>
<body>
<h1><%: Model.Title %></h1>
</body>
</html>

  按理來說,這么做應該一切正常,但是運行之后便會提示說Model上找不到Title成員:

  這又是什么原因呢?

  訪問級別與成員

  在C# 4出現之前,我們也完全可以構造一個Model類型作為視圖的模型,例如:

public class IndexModel
{
public string Title { get; set; }
}

  使用這種做法便完全可以正常運行通過了。那么為什么具體類型能夠正常工作,而匿名類型卻失敗了呢?“按常理推斷”它們不都是普通的類型,然后訪問它們的屬性嗎?我們用ILSpy查看使用匿名類型編譯后的結果,可以發現匿名類型與上面的IndexModel有一個重要的不同之處:

  由于是“匿名類型”,顯然它的訪問級別應該是internal的,這樣它就能對外“隱藏”起來了。但是這就給ASP.NET MVC的視圖帶來了麻煩。因為ASP.NET MVC的視圖會在運行時動態地編譯aspx為額外的dll,因此它是無法訪問到Controller所在程序集的internal成員的。經試驗,如果我們將之前的IndexModel的訪問級別修改為internal便會得到相同的結果。

  額外提一句,類似的代碼在Mono下卻可以運行通過。這意味著在動態訪問對象成員的時候,Mono和.NET在訪問級別方面的檢查是有所不同的。雖然在這個情景里Mono更方便,但理論上說,.NET的做法實則更合理。

  使用NuGet安裝Mono.Cecil

  Mono.Cecil是Mono的組件之一,用來編輯.NET程序集文件。我們可以用它來打探一個.NET程序集內部的結構,就像反射那樣,只不過并不需要將程序集加載進來,Mono.Cecil只是讀取文件物理內容而已。例如,上圖所用的ILSpy便用到了Mono.Cecil。更重要的是,Mono.Cecil可以修改并保存程序集,這便可以讓我們實現各種奇形怪狀的要求。像這篇文章所提到的,只不過是小試牛刀而已。

  Mono和.NET是二進制兼容的,因此我們可以直接把Mono下的Mono.Cecil.dll復制并引用到.NET程序里。不過這么做還是麻煩了,如今在.NET平臺上使用各種組件已經有更方便的做法:使用包管理器。.NET平臺下的包管理器叫做NuGet,是由SubText的作者,后來被微軟聘用作ASP.NET MVC程序經理的Phil Haack帶頭開發的開源項目。NuGet提供了Visual Studio的擴展,同時也有基于PowerShell的命令行。這里我們就從Visual Studio的擴展開始使用吧。

  創建一個名為PublicAnonymous的控制臺項目,并選擇Reference – Manage NuGet Packages:

  搜索Mono.Cecil,并安裝即可:

  NuGet會自動處理組件之間的依賴及項目的配置,您也可以自己把玩一番。

  使用Mono.Cecil修改程序集

  有了Mono.Cecil我們便可以修改程序集了,只需數行代碼:

static void Main(string[] args)
{
var asmFile = args[0];
Console.WriteLine("Making anonymous types public for '{0}'.", asmFile);

var asmDef = AssemblyDefinition.ReadAssembly(asmFile, new ReaderParameters
{
ReadSymbols = true
});

var anonymousTypes = asmDef.Modules
.SelectMany(m => m.Types)
.Where(t => t.Name.Contains("<>f__AnonymousType"));

foreach (var type in anonymousTypes)
{
type.IsPublic = true;
}

asmDef.Write(asmFile, new WriterParameters
{
WriteSymbols = true
});
}

  首先,從參數中獲取需要修改的程序集名稱,找到所有的匿名類型,并將其訪問級別設為Public后保存。保存的時候將WriteSymbols參數設為true,這樣它也會同時修改pdb文件——這很重要,否則修改后的程序集無法和pdb文件內容相對應,便無法調試了。換句話說,Mono.Cecil也能正確處理pdb文件。

  最后,只要在ASP.NET MVC網站編譯時使用這個項目即可,只需配置一下它的Post Build事件:

  再次編譯并運行程序,即可得到正確結果。再拿ILSpy來檢查一番:

  總結

  在沙龍上,有朋友問我怎么樣可以成為一個高級.NET技術人員。我不知道“如何成為”,但我想,了解整個生態環境的發展情況,了解.NET的優勢及不足,甚至能夠了解相關領域其他技術方向的發展態勢,應該是優秀.NET程序員的特質之一吧。

  而Mono便是.NET生態環境的重要組成部分。

2
0

請先登錄 提交中…

標簽:MVC Mono dynamic

文章列表

C#多線程學習(一) 多線程的相關概念

C#多線程學習(一) 多線程的相關概念

作者: 鋼鋼 來源: 博客園 發布時間: 2008-09-20 18:42 閱讀: 114920 次 推薦: 63 原文鏈接 [收藏]
摘要:關于線程的相關概念

系列文章導航:

C#多線程學習(一) 多線程的相關概念

C#多線程學習(二) 如何操縱一個線程

C#多線程學習(三) 生產者和消費者

C#多線程學習(四) 多線程的自動管理(線程池)

C#多線程學習(五) 多線程的自動管理(定時器)

C#多線程學習(六) 互斥對象

什么是進程?

當一個程序開始運行時,它就是一個進程,進程包括運行中的程序和程序所使用到的內存和系統資源。 而一個進程又是由多個線程所組成的。

什么是線程?

線程是程序中的一個執行流,每個線程都有自己的專有寄存器(棧指針、程序計數器等),但代碼區是共享的,即不同的線程可以執行同樣的函數。

什么是多線程?

多線程是指程序中包含多個執行流,即在一個程序中可以同時運行多個不同的線程來執行不同的任務,也就是說允許單個程序創建多個并行執行的線程來完成各自的任務。

多線程的好處:

可以提高CPU的利用率。在多線程程序中,一個線程必須等待的時候,CPU可以運行其它的線程而不是等待,這樣就大大提高了程序的效率。

多線程的不利方面:

線程也是程序,所以線程需要占用內存,線程越多占用內存也越多; 多線程需要協調和管理,所以需要CPU時間跟蹤線程; 線程之間對共享資源的訪問會相互影響,必須解決競用共享資源的問題; 線程太多會導致控制太復雜,最終可能造成很多Bug;

接下來將對C#編程中的多線程機制進行探討。為了省去創建GUI那些繁瑣的步驟,更清晰地逼近線程的本質,接下來的所有程序都是控制臺程序,程序最后的Console.ReadLine()是為了使程序中途停下來,以便看清楚執行過程中的輸出。

任何程序在執行時,至少有一個主線程。

一個直觀印象的線程示例:

using System;
using System.Threading;

namespace ThreadTest
{
  class RunIt
  {
    [STAThread]
    static void Main(string[] args)
    {
      Thread.CurrentThread.Name="System Thread";//給當前線程起名為"System Thread"
Console.WriteLine(Thread.CurrentThread.Name+"'Status:"+Thread.CurrentThread.ThreadState);
      Console.ReadLine();
    }
  }
}

輸出如下:

System Thread’s Status:Running

在這里,我們通過Thread類的靜態屬性CurrentThread獲取了當前執行的線程,對其Name屬性賦值“System Thread”,最后還輸出了它的當前狀態(ThreadState)。

所謂靜態屬性,就是這個類所有對象所公有的屬性,不管你創建了多少個這個類的實例,但是類的靜態屬性在內存中只有一個。很容易理解CurrentThread為什么是靜態的——雖然有多個線程同時存在,但是在某一個時刻,CPU只能執行其中一個。

在程序的頭部,我們使用了如下命名空間:

using System;

using System.Threading;

在.net framework class library中,所有與多線程機制應用相關的類都是放在System.Threading命名空間中的。如果你想在你的應用程序中使用多線程,就必須包含這個類。

我們通過其中提供的Thread類來創建和控制線程,ThreadPool類用于管理線程池等。 (此外還提供解決了線程執行安排,死鎖,線程間通訊等實際問題的機制。)

Thread類有幾個至關重要的方法,描述如下:

Start():啟動線程;

Sleep(int):靜態方法,暫停當前線程指定的毫秒數;

Abort():通常使用該方法來終止一個線程;

Suspend():該方法并不終止未完成的線程,它僅僅掛起線程,以后還可恢復;

Resume():恢復被Suspend()方法掛起的線程的執行。

63
2

請先登錄 提交中…

標簽:C# Thread 線程

文章列表

Linq To Sql進階系列(一)從映射講起

Linq To Sql進階系列(一)從映射講起

作者: Tom Song 來源: 博客園 發布時間: 2008-09-27 11:46 閱讀: 25807 次 推薦: 3 原文鏈接 [收藏]

[1] Linq To Sql進階系列(一)從映射講起
[2] Linq To Sql進階系列(一)從映射講起
[3] Linq To Sql進階系列(一)從映射講起

系列文章導航:

Linq To Sql進階系列(一)從映射講起

Linq To Sql進階系列(二)M:M關系

Linq To Sql進階系列(三)CUD和Log

Linq To Sql進階系列(四)User Define Function篇

Linq To Sql進階系列(五)Store Procedure篇

Linq To Sql進階系列(六)用object的動態查詢與保存log篇

Linq To Sql進階系列(七)動態查詢續及CLR與SQL在某些細節上的差別

本系列,或多或少,直接或間接依賴入門系列知識。但,依然追求獨立成章。因本文作者水平有限,文中錯誤難免,敬請讀者指出并諒解。本系列將會和入門并存。

案例
某君被邀為一超市設計數據庫,用來存儲數據。該君根據該超市中實際出現的對象,設計了Customer, Employee,Order, Product等表,用來保存相應的客戶,員工,訂單,貨品等。太好了,該君很有oo的思想嗎。

如果,你被要求用類及對象,來描述該關系型數據,你該如何做呢?在linq推出之前,ADO.NET被用來做數據訪問層。而后,程序員需要自己去編寫事務邏輯層中所出現的類。比如,Customer, Employee,Order, Product等。然后,程序員組裝所需的sql語句,通過ADO.NET,將返回的記錄,來初始化Customer等類的對象。在這里,你已經自己動手將Customer表和Customer類關聯了起來。從Linq To Sql的設計來看,它主要是為了解決data!=objects 的問題而產生的。現在,有了Table和Class之間的映射,數據和對象之間就可以有一個一一對應的關系了。
在Linq To Sql之前,在java領域有Hibernate,在net領域有NHibernate技術,來實現object/relational 持久和查詢服務。無論是Hibernate還是NHibernate,其配置復雜,上手時間長,已經不能適應快速開發的需要。而Linq To Sql的推出,恰恰彌補了它們的缺點,徹底降低了程序開發門檻。

Linq
Linq是Language Integrated Query的縮寫。Linq To Sql是linq中的一部分,其與ADO.NET Orcas的關系入下。

繼續>>下一頁
[第1頁][第2頁][第3頁]

3
0

請先登錄 提交中…

標簽:Linq To Sql 映射

文章列表

C#多線程學習(二) 如何操縱一個線程

C#多線程學習(二) 如何操縱一個線程

作者: 鋼鋼 來源: 博客園 發布時間: 2008-09-20 19:03 閱讀: 120979 次 推薦: 27 原文鏈接 [收藏]

[1] C#多線程學習(二) 如何操縱一個線程
[2] C#多線程學習(二) 如何操縱一個線程

系列文章導航:

C#多線程學習(一) 多線程的相關概念

C#多線程學習(二) 如何操縱一個線程

C#多線程學習(三) 生產者和消費者

C#多線程學習(四) 多線程的自動管理(線程池)

C#多線程學習(五) 多線程的自動管理(定時器)

C#多線程學習(六) 互斥對象

下面我們就動手來創建一個線程,使用Thread類創建線程時,只需提供線程入口即可。(線程入口使程序知道該讓這個線程干什么事)

在C#中,線程入口是通過ThreadStart代理(delegate)來提供的,你可以把ThreadStart理解為一個函數指針,指向線程要執行的函數,當調用Thread.Start()方法后,線程就開始執行ThreadStart所代表或者說指向的函數。

打開你的VS.net,新建一個控制臺應用程序(Console Application),編寫完全控制一個線程的代碼示例:

using System;
using System.Threading;

namespace ThreadTest
{
public class Alpha
{
public void Beta()
{
while (true)
{
Console.WriteLine("Alpha.Beta is running in its own thread.");
}
}
};

public class Simple
{
public static int Main()
{
  Console.WriteLine("Thread Start/Stop/Join Sample");
Alpha oAlpha = new Alpha();
   file://這里創建一個線程,使之執行Alpha類的Beta()方法
   Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));
   oThread.Start();
   while (!oThread.IsAlive)
   Thread.Sleep(1);
   oThread.Abort();
   oThread.Join();
   Console.WriteLine();
   Console.WriteLine("Alpha.Beta has finished");
   try
   {
     Console.WriteLine("Try to restart the Alpha.Beta thread");
     oThread.Start();
   }
   catch (ThreadStateException)
   {
     Console.Write("ThreadStateException trying to restart Alpha.Beta. ");
     Console.WriteLine("Expected since aborted threads cannot be restarted.");
     Console.ReadLine();
   }
   return 0;
   }
}
}

這段程序包含兩個類Alpha和Simple,在創建線程oThread時我們用指向Alpha.Beta()方法的初始化了ThreadStart代理(delegate)對象,當我們創建的線程oThread調用oThread.Start()方法啟動時,實際上程序運行的是Alpha.Beta()方法:

Alpha oAlpha = new Alpha();

Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));

oThread.Start();

然后在Main()函數的while循環中,我們使用靜態方法Thread.Sleep()讓主線程停了1ms,這段時間CPU轉向執行線程oThread。然后我們試圖用Thread.Abort()方法終止線程oThread,注意后面的oThread.Join(),Thread.Join()方法使主線程等待,直到oThread線程結束。你可以給Thread.Join()方法指定一個int型的參數作為等待的最長時間。之后,我們試圖用Thread.Start()方法重新啟動線程oThread,但是顯然Abort()方法帶來的后果是不可恢復的終止線程,所以最后程序會拋出ThreadStateException異常。

繼續>>下一頁
[第1頁][第2頁]

27
9

請先登錄 提交中…

標簽:C# Thread 線程

文章列表

使用LINQ Expression構建Query Object

使用LINQ Expression構建Query Object

作者: dax.net 來源: 博客園 發布時間: 2011-01-24 11:42 閱讀: 4021 次 推薦: 0 原文鏈接 [收藏]

  這個問題來源于Apworks應用開發框架的設計。由于命令與查詢職責的分離,使得基于CQRS體系結構風格的應用系統的外部存儲系統的結構變得簡單起來:在“命令”部分,簡單地說,只需要 Event Store和Snapshot Store來保存Domain Model;而“查詢”部分,則又是基于事件派送與偵聽的系統集成。之前我也提到過,“查詢”部分由于不牽涉到Domain Model,于是,它的設計應該隨意性很大:不需要受到Domain Model的牽制,例如,我們可以根據UI所需的數據源結構進行“查詢”庫的設計。Greg Young在他的“CQRS Documents”一文中也提到了這樣一些相關話題:就“查詢”部分而言,雖然也存在“阻抗失衡”效應,但是事件模型與關系模型之間的這種效應,要遠遠小于對象模型與關系模型之間的“阻抗失衡”效應。這是因為,事件模型本身沒有結構,它僅僅表述“該對關系模型做哪些操作”這樣的概念。在設計上,Greg Young建議,采用一種非正規的方式設計“查詢”數據庫,以便盡量減少讀取數據時所需的JOIN操作,比如可以選用基于第一范式(1NF)的關系模型。這是一種反范式模型,雖然Greg Young并沒有建議根據UI所需的數據源結構進行設計,但思想是相同的:基于事件而不拘泥于對象模型本身。由此引申出來的另一個好處就是外部存儲系統架構的隨意性:你可以選用任何存儲技術和存儲媒介,這又給基于系統架構的性能優化提供了便利。因為并非所有的存儲架構都支持“表”、“字段”、“存儲過程”、“JOIN”這些概念。
  根據上面的簡單分析,我們得到一個結論:通常情況下,或許基于CQRS體系結構風格的應用系統更多的是采用的“平整”的外部存儲結構,簡而言之,就是一個數據訪問對象(DAO)對應一張數據表。這也是我所設計的Apworks應用開發框架中默認支持的一種存儲結構。有讀過Apworks源代碼的朋友會發現,在Apworks.Events.Storage命名空間下,有兩個定制的DAO:DomainEventDataObject,用于表述領域事件的數據結構,以及SnapshotDataObject,用于表述快照的數據結構,與之相對應的就是數據庫中的兩張表:DomainEvents和 Snapshots。雖然結構變得這么簡單,但是映射關系總還是需要維護的:最簡單的就是需要在對象類型名稱與數據表名之間,以及對象屬性與數據表字段之間建立起映射關系。在Apworks中,這種映射關系是由Apworks.Storage.IStorageMappingResolver接口完成的。有關這個接口的內容不是本文討論的重點,暫且不深入分析了。
  至此,也許你不會接受我上面的討論,認為“基于UI設計數據庫結構”或者“采用1NF、反范式設計數據庫結構”是無法接受的,那么,接下來的討論可能對你來說意義也不大了。因為下面的問題是以上面的描述為基礎的:一個數據訪問對象對應一張數據表。不過即使你不認同我的觀點,我也建議你繼續看完本文。
  Query Object模式

  雖然只是簡單的映射,但畢竟不能忽略這樣的映射關系。Apworks作為一個應用開發框架,需要提供方便的整合接口,以便今后能夠根據不同的客戶需求進行擴展。例如在存儲部分,數據的增刪改查(CRUD)是基于數據訪問對象(DAO)的,這樣做的一個好處是能夠對外部存儲系統進行抽象,使得訪問存儲系統的部分能夠無需關系存儲系統的細節問題。客戶有可能選擇SQL Server、Oracle、MySQL等關系型數據庫作為存儲系統,也可以選擇其它的非關系型數據庫作為存儲系統,因此,我們的設計不能僅僅局限于關系型數據庫,我們需要同時考慮其它形式的數據存儲產品以便將來能夠方便地集成新的存儲方案。假設我們要設計一個針對 DomainEventDataObject的“查詢”功能,我們需要考慮的問題可能會有(但不一定僅限于):
  * 需要查詢對象的哪些屬性(或者說與DomainEventDataObject相對應的數據表的哪些字段)
  * 需要根據什么樣的條件進行查詢
  * 查詢是否需要排序
  * 是否只查結果集中的任意一條記錄,還是要返回所有的記錄
  在Apworks框架的Alpha版本中,查詢的方法定義在Apworks.Storage.IStorage接口中。比如,根據給定的查詢條件和排序方式,對指定DAO進行查詢的方法定義如下:

/// <summary>
/// Gets a list of ordered objects from storage by given selection criteria and order.
/// </summary>
/// <typeparam name="T">The type of the object to get.</typeparam>
/// <param name="criteria">The <c>PropertyBag</c> instance which contains the criteria.</param>
/// <param name="orders">The <c>PropertyBag</c> instance which contains the ordering fields.</param>
/// <param name="sortOrder">The sort order.</param>
/// <returns>A list of ordered objects.</returns>
IEnumerable<T> Select<T>(PropertyBag criteria, PropertyBag orders, SortOrder sortOrder)
where T : class, new();

  這個方法是個泛型方法,泛型類型就是DAO的類型。它接受三個參數:前兩個是用于指定查詢條件和排序字段的PropertyBag,最后一個是指定排序方式的SortOrder。之所以采用PropertyBag,而不是接受SQL字符串,原因不僅是因為框架本身需要在今后能夠方便地支持非關系型數據庫,而且更重要的是,雖然SQL已經成為一種業界標準,但實際上不同的關系型數據庫產品對SQL的支持和實現方式也有所不同,有些關系型數據庫產品或許只支持SQL的一些子集,如果單純地把SQL字符串作為Select方法的參數,明顯是不合理的。事實上,Apworks.Storage.IStorage實現了Query Object模式[MF, PoEAA],Martin Fowler在他的PoEAA(《企業應用架構模式》)中有以下幾點可以供讀者參考:
  * “編程語言是可以支持SQL語句的,但大多數開發人員對此不太熟悉。而且,你需要了解數據庫設計方案以便構造出查詢。可以通過創建特殊的、隱藏了 SQL內部參數化方法的查詢器方法避免這一點。但這樣難以構造更多的特別查詢。而且,如果數據庫設計方案改變,就會需要復制到SQL語句中”
  * “查詢對象的一個共同特征是,它能夠利用內存對象的語言而不是用數據庫方案來描述查詢。這意味著我可以使用對象和域名,而不是表和列名。如果對象和數據庫具有相同的結構,這一點就不重要,如果兩者有差異,查詢對象就很有用。為了實現這樣的視角變化,查詢對象需要知道數據庫結構怎樣映射到對象結構,這一功能實際上要用到元數據映射”【daxnet注:上面提到過,在Apworks框架中,這個元數據映射的實現,就是 IStorageMappingResolver】
  * “為了描述任意的查詢,你需要一個靈活的查詢對象。然而,應用程序經常能用遠少于SQL全部功能的操作來完成這一任務,在此情況下,你的查詢對象就會比較簡單。它不能代表任何東西,但它可以滿足特定的需要。此外,當需要更多功能而進行功能擴充時,通常不會比從零開始創建一個全能的查詢對象更麻煩。因此,應該為當前需求創建一個功能最小化的查詢對象,并隨著需求的增加改進這個查詢對象”
  以上三點讓我很有感觸,特別是第三點。目前基于PropertyBag的設計,只能夠支持以AND連接的查詢條件,比如,類似“WHERE a=va AND b=vb AND c=vc…”這樣的查詢,雖然在Apworks Alpha版本中,這樣的查詢已經夠用了,但它不具備擴展性,基于關系型數據庫的存儲設計Apworks.Storage.RdbmsStorage已經將這種邏輯寫死了,倘若我們需要一個復雜的查詢,這種方式不僅沒法勝任,而且沒法擴展。PropertyBag應該要退休了。
  在下一個版本的Apworks中,我使用.NET中的LINQ Expression代替了PropertyBag,并引入了一個WhereClauseBuilder的對象,專門根據LINQ Expression,針對關系型數據庫產生WHERE子句。使用LINQ Expression的好處有:
  * LINQ Expression是.NET下的一種查詢標準,多數存儲系統產品能夠提供針對LINQ Expression的查詢解決方案,即使不提供,也可以自己定制Provider,雖然麻煩一點,但總歸是可以實現的
  * LINQ Expression能夠完美地“利用內存對象的語言而不是用數據庫方案”來描述查詢,語言集成的特性,為開發人員帶來了更多的便捷
  * Apworks中,Specification是基于LINQ Expression的,于是,Apworks.Storage.IStorage就能夠實現基于Specification的查詢,實現接口統一
  于是技術問題來了:如何將LINQ Expression轉換成WHERE子句,以便Apworks.Storage.IStorage的類(Query Objects)能夠使用這個WHERE子句構造出SQL語句,進而通過ADO.NET直接訪問數據庫?Apworks選用的是Expression Visitor的方案:使用Expression Visitor遍歷表達式樹(Expression Tree)然后產生WHERE子句。在討論Expression Visitor之前,讓我們回顧一下對象結構以及Visitor模式。
  Visitor模式
  網上面有關Visitor模式的文章太多了,還有相當一部分討論的比較深入透徹,我也就不多說了。總之,Visitor模式在處理較復雜的對象結構時會顯得十分自然:它能夠遍歷結構中的每一個對象,然后針對不同的對象類型作不同的處理。這就看上去像是為這些對象擴展了一些方法一樣。之前,我有用過 Visitor模式來驗證程序配置節點的合理性,當節點的類型增加后,只需要擴展Visitor即可實現新的驗證邏輯,非常方便。模式歸模式,不同的應用場景,實現方式還是有所不同的。經典的Visitor例子,通常都是利用了函數的重載(多態性),并結合了Composite模式來說明問題,但實際上 Visitor并非一定需要使用函數重載,也不是僅能用在Composite上。Expression Visitor的實現方式,就與這經典的Visitor案例有所不同。
  Expression Visitor
  在System.Linq.Expressions命名空間下,有一個ExpressionVisitor的抽象類,我們只需要繼承這個抽象類,并重寫其中的某些Visit方法,即可實現WHERE子句的生成。在這里我不打算繼續去細究ExpressionVisitor是如何遍歷表達式樹的,我還是描述一下實現WHERE子句生成的幾個細節問題。
  1. 支持哪些運算?

  LINQ Expression的類型有85種,但并不是SQL中會支持到所有的這85種類型。目前Apworks打算支持常用的條件運算,比如:大于、大于等于、小于、小于等于、不等于、等于這幾種,打算支持常用的邏輯運算:AND、OR、NOT
  2. 支持哪些方法(函數)?
目前Apworks支持的方法僅有三種:object.Equals、string.StartsWith和 string.EndsWith。object.Equals將被翻譯成“object = value”,string.StartsWith和string.EndsWith將被翻譯成“LIKE”子句
  3. 支持內聯函數和變量?
目前僅支持變量,不支持內聯函數。
比如:可以用下面的方式來指定Expression:

int a = GetAge();
Expression<Func<Employee, bool>> expr = p => p.Age.Equals(a);

  而不能使用下面的方式來指定Expression:

Expression<Func<Employee, bool>> expr = p => p.Age.Equals(GetAge());

  4. 支持擴展?
  當然,只需要繼承已有的ExpressionVisitor類,并重寫其中某些方法即可
  在當前的Apworks版本中,Apworks.Storage.Builders命名空間下定義了針對關系型數據庫的 IWhereClauseBuilder接口,以及一個抽象實現:Apworks.Storage.Builders.WhereClauseBuilder類,它不僅實現了IWhereClauseBuilder 接口,同時繼承于System.Linq.Expressions.ExpressionVisitor抽象類,因此,WHERE子句生成的主體邏輯都在這個類中。SqlWhereClauseBuilder類繼承WhereClauseBuilder類,以便實現特定于SQL Server語法的WHERE子句生成器。
  由于Apworks.Storage.Builders.WhereClauseBuilder類的源代碼比較長,我就不貼在這里了,讀者朋友請【點擊此處】查看該類的全部源代碼。
  與規約(Specification)整合
  在《EntityFramework之領域驅動設計實踐(十):規約模式》一文中,我提出了基于.NET的規約模式的實現方式,為了迎合.NET對LINQ Expression的支持,規約模式的實現也采用了LINQ Expression,而原來的IsSatisfiedfiedBy方法則改為直接使用LINQ Expression來獲得結果:

public interface ISpecification<T>
{
bool IsSatisfiedBy(T obj);
Expression<Func<T, bool>> Expression { get; }
}

public abstract class Specification<T> : ISpecification<T>
{
#region ISpecification Members
public virtual bool IsSatisfiedBy(T obj)
{
return this.Expression.Compile()(obj);
}
public abstract Expression<Func<T, bool>> Expression { get; }
#endregion
}

  回過頭來考察Select方法,原本第一個參數是用Expression<Func<T, bool>>類型代替PropertyBag的,現在則可以直接使用ISpecification接口了,于是,我們的Query Object可以使用規約模式來支持數據查詢了。

IEnumerable<T> Select<T>(ISpecification<T> specification, PropertyBag orders, SortOrder sortOrder)
where T : class, new();

  執行過程與客戶端調用示例
  基于上面的討論,Select方法的定義,已經從使用PropertyBag作為查詢條件,轉變為使用ISpecification接口。注意:orders參數仍然使用PropertyBag,因為目前不打算支持基于表達式的排序條件:
  在Apworks.Storage.RdbmsStorage中,使用WhereClauseBuilder.BuildWhereClause方法,根據LINQ Expression生成WHERE子句,進而產生SQL語句并使用ADO.NET訪問關系型數據庫:

public IEnumerable<T> Select<T>(ISpecification<T> specification, PropertyBag orders, Storage.SortOrder sortOrder)
where T : class, new()
{
try
{
Expression<Func<T, bool>> expression = null;
WhereClauseBuildResult whereBuildResult = null;
string sql = string.Format("SELECT {0} FROM {1}",
GetFieldNameList<T>(), GetTableName<T>());
if (specification != null)
{
expression = specification.GetExpression();
whereBuildResult = GetWhereClauseBuilder<T>().BuildWhereClause(expression);
sql += " WHERE " + whereBuildResult.WhereClause;
}
if (orders != null && sortOrder != Storage.SortOrder.Unspecified)
{
sql += " ORDER BY " + GetOrderByFieldList<T>(orders);
switch (sortOrder)
{
case Storage.SortOrder.Ascending:
sql += " ASC";
break;
case Storage.SortOrder.Descending:
sql += " DESC";
break;
default: break;
}
}
using (DbCommand command = CreateCommand(sql))
{
if (command.Connection == null)
command.Connection = Connection;
if (Transaction != null)
command.Transaction = Transaction;
if (specification != null)
{
command.Parameters.Clear();
var parameters = GetSelectCriteriaDbParameterList<T>(whereBuildResult.ParameterValues);
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
}
DbDataReader reader = command.ExecuteReader();
List<T> ret = new List<T>();
while (reader.Read())
{
ret.Add(CreateFromReader<T>(reader));
}
reader.Close(); // Very important: reader MUST be closed !!!
return ret;
}
}
catch (ExpressionParseException)
{
throw;
}
catch (InfrastructureException)
{
throw;
}
catch (Exception ex)
{
throw ExceptionManager.HandleExceptionAndRethrow<StorageException>(ex,
Resources.EX_SELECT_FROM_STORAGE_FAIL,
typeof(T).AssemblyQualifiedName,
specification != null ? specification.ToString() : "NULL",
orders != null ? orders.ToString() : "NULL",
sortOrder);
}
}

  下面這個方法將根據Aggregate Root的類型與ID,返回與之相關的所有Domain Events:

public virtual IEnumerable<IDomainEvent> LoadEvents(Type aggregateRootType, long id)
{
try
{
PropertyBag sort = new PropertyBag();
sort.AddSort<long>("Version");
var aggregateRootTypeName = aggregateRootType.AssemblyQualifiedName;
ISpecification<DomainEventDataObject> specification = Specification<DomainEventDataObject>
.Eval(p => p.AggregateRootId == id && p.AggregateRootType == aggregateRootTypeName);
return Select<DomainEventDataObject>(specification, sort, Apworks.Storage.SortOrder.Ascending)
.Select(p => p.ToEntity());
}
catch { throw; }
}

0
0

請先登錄 提交中…

標簽:LINQ

文章列表

C#多線程學習(四) 多線程的自動管理(線程池)

C#多線程學習(四) 多線程的自動管理(線程池)

作者: 鋼鋼 來源: 博客園 發布時間: 2008-09-20 19:26 閱讀: 42074 次 推薦: 16 原文鏈接 [收藏]

系列文章導航:

C#多線程學習(一) 多線程的相關概念

C#多線程學習(二) 如何操縱一個線程

C#多線程學習(三) 生產者和消費者

C#多線程學習(四) 多線程的自動管理(線程池)

C#多線程學習(五) 多線程的自動管理(定時器)

C#多線程學習(六) 互斥對象

在多線程的程序中,經常會出現兩種情況:

一種情況: 應用程序中,線程把大部分的時間花費在等待狀態,等待某個事件發生,然后才能給予響應

這一般使用ThreadPool(線程池)來解決;

另一種情況:線程平時都處于休眠狀態,只是周期性地被喚醒

這一般使用Timer(定時器)來解決;

ThreadPool類提供一個由系統維護的線程池(可以看作一個線程的容器),該容器需要 Windows 2000 以上系統支持,因為其中某些方法調用了只有高版本的Windows才有的API函數。

將線程安放在線程池里,需使用ThreadPool.QueueUserWorkItem()方法,該方法的原型如下:

//將一個線程放進線程池,該線程的Start()方法將調用WaitCallback代理對象代表的函數

public static bool QueueUserWorkItem(WaitCallback);

//重載的方法如下,參數object將傳遞給WaitCallback所代表的方法

public static bool QueueUserWorkItem(WaitCallback, object);

ThreadPool類是一個靜態類,你不能也不必要生成它的對象。而且一旦使用該方法在線程池中添加了一個項目,那么該項目將是無法取消的。

在這里你無需自己建立線程,只需把你要做的工作寫成函數,然后作為參數傳遞給ThreadPool.QueueUserWorkItem()方法就行了,傳遞的方法就是依靠WaitCallback代理對象,而線程的建立、管理、運行等工作都是由系統自動完成的,你無須考慮那些復雜的細節問題。

ThreadPool 的用法:

首先程序創建了一個ManualResetEvent對象,該對象就像一個信號燈,可以利用它的信號來通知其它線程。

本例中,當線程池中所有線程工作都完成以后,ManualResetEvent對象將被設置為有信號,從而通知主線程繼續運行。

ManualResetEvent對象有幾個重要的方法:

初始化該對象時,用戶可以指定其默認的狀態(有信號/無信號);

在初始化以后,該對象將保持原來的狀態不變,直到它的Reset()或者Set()方法被調用:

Reset()方法:將其設置為無信號狀態;

Set()方法:將其設置為有信號狀態。

WaitOne()方法:使當前線程掛起,直到ManualResetEvent對象處于有信號狀態,此時該線程將被激活。然后,程序將向線程池中添加工作項,這些以函數形式提供的工作項被系統用來初始化自動建立的線程。當所有的線程都運行完了以后,ManualResetEvent.Set()方法被調用,因為調用了ManualResetEvent.WaitOne()方法而處在等待狀態的主線程將接收到這個信號,于是它接著往下執行,完成后邊的工作。

ThreadPool 的用法示例:

Code
using System;
using System.Collections;
using System.Threading;

namespace ThreadExample
{
//這是用來保存信息的數據結構,將作為參數被傳遞
public class SomeState
{
   public int Cookie;
   public SomeState(int iCookie)
   {
Cookie = iCookie;
   }
}

public class Alpha
{
  public Hashtable HashCount;
  public ManualResetEvent eventX;
  public static int iCount = 0;
  public static int iMaxCount = 0;
  
public Alpha(int MaxCount)
  {
  HashCount = new Hashtable(MaxCount);
  iMaxCount = MaxCount;
  }

  //線程池里的線程將調用Beta()方法
  public void Beta(Object state)
  {
   //輸出當前線程的hash編碼值和Cookie的值
  Console.WriteLine(" {0} {1} :", Thread.CurrentThread.GetHashCode(),((SomeState)state).Cookie);
Console.WriteLine("HashCount.Count=={0}, Thread.CurrentThread.GetHashCode()=={1}", HashCount.Count, Thread.CurrentThread.GetHashCode());
lock (HashCount)
{
     //如果當前的Hash表中沒有當前線程的Hash值,則添加之
     if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode()))
     HashCount.Add (Thread.CurrentThread.GetHashCode(), 0);
     HashCount[Thread.CurrentThread.GetHashCode()] =
((int)HashCount[Thread.CurrentThread.GetHashCode()])+1;
   }
int iX = 2000;
Thread.Sleep(iX);
//Interlocked.Increment()操作是一個原子操作,具體請看下面說明
Interlocked.Increment(ref iCount);

if (iCount == iMaxCount)
{
    Console.WriteLine();
     Console.WriteLine("Setting eventX ");
     eventX.Set();
  }
   }
}

public class SimplePool
{
public static int Main(string[] args)
{
Console.WriteLine("Thread Pool Sample:");
bool W2K = false;
int MaxCount = 10;//允許線程池中運行最多10個線程
//新建ManualResetEvent對象并且初始化為無信號狀態
ManualResetEvent eventX = new ManualResetEvent(false);
Console.WriteLine("Queuing {0} items to Thread Pool", MaxCount);
Alpha oAlpha = new Alpha(MaxCount);
//創建工作項
//注意初始化oAlpha對象的eventX屬性
oAlpha.eventX = eventX;
Console.WriteLine("Queue to Thread Pool 0");
try
{
//將工作項裝入線程池
//這里要用到Windows 2000以上版本才有的API,所以可能出現NotSupportException異常
ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta), new SomeState(0));
W2K = true;
}
catch (NotSupportedException)
{
Console.WriteLine("These API's may fail when called on a non-Windows 2000 system.");
W2K = false;
}
if (W2K)//如果當前系統支持ThreadPool的方法.
{
for (int iItem=1;iItem < MaxCount;iItem++)
{
//插入隊列元素
Console.WriteLine("Queue to Thread Pool {0}", iItem);
ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta), new SomeState(iItem));
}
Console.WriteLine("Waiting for Thread Pool to drain");
//等待事件的完成,即線程調用ManualResetEvent.Set()方法
eventX.WaitOne(Timeout.Infinite,true);
//WaitOne()方法使調用它的線程等待直到eventX.Set()方法被調用
Console.WriteLine("Thread Pool has been drained (Event fired)");
Console.WriteLine();
Console.WriteLine("Load across threads");
foreach(object o in oAlpha.HashCount.Keys)
Console.WriteLine("{0} {1}", o, oAlpha.HashCount[o]);
}
Console.ReadLine();
return 0;
}
}
}
}

程序中應該引起注意的地方:

SomeState類是一個保存信息的數據結構,它在程序中作為參數被傳遞給每一個線程,因為你需要把一些有用的信息封裝起來提供給線程,而這種方式是非常有效的。

程序出現的InterLocked類也是專為多線程程序而存在的,它提供了一些有用的原子操作。

原子操作:就是在多線程程序中,如果這個線程調用這個操作修改一個變量,那么其他線程就不能修改這個變量了,這跟lock關鍵字在本質上是一樣的。

輸出的結果
Thread Pool Sample:
Queuing 10 items to Thread Pool
Queue to Thread Pool 0
Queue to Thread Pool 1
Queue to Thread Pool 2
Queue to Thread Pool 3
Queue to Thread Pool 4
Queue to Thread Pool 5
2 0 :
HashCount.Count==0, Thread.CurrentThread.GetHashCode()==2
Queue to Thread Pool 6
Queue to Thread Pool 7
Queue to Thread Pool 8
Queue to Thread Pool 9
Waiting for Thread Pool to drain
4 1 :
HashCount.Count==1, Thread.CurrentThread.GetHashCode()==4
6 2 :
HashCount.Count==1, Thread.CurrentThread.GetHashCode()==6
7 3 :
HashCount.Count==1, Thread.CurrentThread.GetHashCode()==7
2 4 :
HashCount.Count==1, Thread.CurrentThread.GetHashCode()==2
8 5 :
HashCount.Count==2, Thread.CurrentThread.GetHashCode()==8
9 6 :
HashCount.Count==2, Thread.CurrentThread.GetHashCode()==9
10 7 :
HashCount.Count==2, Thread.CurrentThread.GetHashCode()==10
11 8 :
HashCount.Count==2, Thread.CurrentThread.GetHashCode()==11
4 9 :
HashCount.Count==2, Thread.CurrentThread.GetHashCode()==4

Setting eventX
Thread Pool has been drained (Event fired)

Load across threads
11 1
10 1
9 1
8 1
7 1
6 1
4 2
2 2

我們應該徹底地分析上面的程序,把握住線程池的本質,理解它存在的意義是什么,這樣才能得心應手地使用它。

16
1

請先登錄 提交中…

標簽:C# Thread 多線程 線程池

文章列表

維護LINQ to SQL多對多表間關系

維護LINQ to SQL多對多表間關系

作者: sunny段段 來源: 博客園 發布時間: 2010-08-24 16:27 閱讀: 3832 次 推薦: 0 原文鏈接 [收藏]
摘要:項目開發中,經常會碰到維護多對多(many to many)關系表間關系的操作,例如為人員配置角色、為人員配置部門、為產品配置類別等。如果沒有經過程序設計而直接進行開發,將會過多地關注其細節問題。

在項目開發中,經常會碰到維護多對多(many to many)關系表間關系的操作,例如為人員配置角色、為人員配置部門、為產品配置類別等。如果沒有經過程序設計而直接進行開發,將會過多地關注其細節問題,如:應刪除那些數據、應添加哪些數據、應保留哪些數據等,導致開發效率降低。

  名詞解釋

在本文開始之前,首先以用戶-用戶角色-角色表為例,聲明三個概念:

clip_image004

l 主表:如果為用戶配置角色,那么用戶就是主表;如果為角色配置用戶,那么角色就是主表。

l 從表:如果為用戶配置角色,那么角色就是從表。

l 關系表:記錄用戶與角色表間關系的表。

  行為描述

經過總結,發現其行為有統一的地方:傳遞主表對象與從表對象集合->獲取現有關系->對比出要刪除的關系->對比出要添加的關系->提交更改。

在下面的文章中,將一步步介紹完成的過程。

  測試用例

使用ASP.NET網站程序,新建如下圖所示的頁面:

clip_image006

在左側的用戶選擇DropDownList中,選擇一個現有用戶后,會在右側的CheckBoxList中顯示其具有的角色。在進行完配置后,可以點擊上方的”保存”LinkButton進行保存。

建表腳本下載

下面我將4以種情況,來展示這個示例。在每個操作完成后,我們執行以下腳本,來查看操作是否成功:

SELECT * FROM [dbo].[UsersInRoles]

開始測試前:

clip_image008

添加一個關系

在本次操作中,將為馬六配置一個“Java程序員”的角色。

clip_image010

點擊保存后,查看數據庫數據:

clip_image012

可以看到,已經成功添加一個關系。

添加兩個關系,其中一個將新建,一個不做處理

在本次操作中,將為馬六配置 “Java程序員 + .NET程序員”的角色。

clip_image014

點擊保存后,查看數據庫數據:

clip_image016

可以看到數據庫中的關系數據變為了兩條,并且RoleID為“63B04…”的關系數據的主鍵“064AB…”并沒有發生改變,這說明我并沒有做“全部刪除,再全部添加”的暴力型操作。

添加兩個關系,其中一個將新建,一個不做處理,一個將刪除

在本次操作中,將為馬六配置 “Java程序員 + 項目經理”的角色。

clip_image018

點擊保存后,查看數據庫數據:

clip_image020

可以看到RoleID為“71DFA…”的關系被刪除了,而且新建了一個RoleID為“3F45B…”的關系,而RoleID為“63B04…”的關系沒有發生改變。操作成功完成。

刪除所有關系

在本次操作中,將馬六的全部角色置空。

clip_image022

點擊保存后,查看數據庫數據:

clip_image024

可以看到用戶的關系被全部刪除了。至此,測試工作告一段落,開始介紹功能是如何完成的。

在點擊保存按鈕時,要做的就是將選中的用戶,以及為其配置的角色保存到數據庫中。有過這樣的開發經驗的開發人員會知道這個過程還是十分繁瑣的,在下面的代碼中,我將使用設計后的方式來完成這個操作:

/// <summary>

/// 點擊保存按鈕時的操作

/// </summary>

protected void btnSave_Click(object sender, EventArgs arg)

{

Users user = DataContext.Users.SingleOrDefault(e => e.UserID == new Guid(ddlUsers.SelectedValue));

List<Roles> roles = new List<Roles>();

foreach (ListItem item in cblUserRoles.Items)

{

if (item.Selected)

{

Roles role = DataContext.Roles.SingleOrDefault(e => e.RoleID == new Guid(item.Value));

if (role != null)

{

roles.Add(role);

}

}

}

LinqM2MProvider.Execute(user, roles, true);

}

可以看到,除了封裝參數(用戶對象,角色對象集合)的常規操作外,只調用了一個方法:LinqM2MProvider.Execute(user, roles, true)。那么這個LinqM2MProvider是什么呢?答案是一個接口,下面的代碼介紹了這個接口的構造方法:

/// <summary>

/// 管理用戶-用戶角色-角色之間關系的提供程序

/// </summary>

public ILinqM2MProvider<Users, UsersInRoles, Roles> LinqM2MProvider

{

get

{

var provider = new LinqM2MProvider<Users, UsersInRoles, Roles>()

{

DataContext = this.DataContext,

GetRelationHandler = (m, s) => DataContext.UsersInRoles.SingleOrDefault(e => e.UserID == m.UserID && e.RoleID == s.RoleID),

RelationSetHandler = m => m.UsersInRoles,

CreateRelationHandler = (m, s) => new UsersInRoles()

{

UserInRoleID = Guid.NewGuid(),

Users = m,

Roles = s

}

};

return provider;

}

}

可以看到ILinqM2MProvider接口是一個泛型接口,它的三個泛型類型是用戶-用戶角色-角色,就跟本文“名詞解釋”段落中的圖片所顯示關系一致。使用它需要設置四個屬性:

l DataContext

l GetRelationHandler

l RelationSetHandler

l CreateRelationHandler

那么這個四個屬性分別代表什么含義呢?下文將進行詳細的說明。

  接口ILinqM2MProvider<M, R, S>

/// <summary>

/// 維護LINQ to SQL的多對多關聯關系

/// </summary>

/// <typeparam name="M">主表類型</typeparam>

/// <typeparam name="R">關系表類型</typeparam>

/// <typeparam name="S">從表類型</typeparam>

/// <remarks>

/// Sunny D.D at 2010-8-20

/// sunny19788989@gmail.com

/// </remarks>

public interface ILinqM2MProvider<M, R, S>

where M : class

where R : class

where S : class

{

/// <summary>

/// LINQ to SQL入口

/// </summary>

DataContext DataContext { get; set; }

/// <summary>

/// 描述如何根據主表對象和從表對象獲取中間關系表對象的行為

/// </summary>

Func<M, S, R> GetRelationHandler { get; set; }

/// <summary>

/// 描述如何根據主表對象和從表對象創建中間關系表對象的行為

/// </summary>

Func<M, S, R> CreateRelationHandler { get; set; }

/// <summary>

/// 描述如何根據主表對象獲取獲取中間關系表對象集合

/// </summary>

Func<M, EntitySet<R>> RelationSetHandler { get; set; }

/// <summary>

/// 獲取將要刪除的關系

/// </summary>

/// <param name="master">主表對象</param>

/// <param name="slaves">從表對象</param>

/// <returns></returns>

IEnumerable<R> GetDeleting(M master, IEnumerable<S> slaves);

/// <summary>

/// 獲取將要新建的關系

/// </summary>

/// <param name="master">主表對象</param>

/// <param name="slaves">從表對象</param>

/// <returns></returns>

IEnumerable<R> GetAdding(M master, IEnumerable<S> slaves);

/// <summary>

/// 執行操作。默認在方法結束時不將變更提交至數據庫,需要顯式提交變更。

/// </summary>

/// <param name="master">主表對象</param>

/// <param name="slaves">從表對象</param>

void Execute(M master, IEnumerable<S> slaves);

/// <summary>

/// 執行操作

/// </summary>

/// <param name="master">主表對象</param>

/// <param name="slaves">從表對象</param>

/// <param name="isSubmitChanges">是否在方法結束時將變更提交至數據庫</param>

void Execute(M master, IEnumerable<S> slaves, bool isSubmitChanges);

}

  DataContext屬性

  類型:System.Data.Linq.DataContext

  RelationSetHandler屬性

  描述如何根據主表對象獲取獲取中間關系表對象集合。在LINQ to SQL架構中,一個表的外鍵對象集合是用EntitySet來表示的,EntitySet中的元素代表著主鍵表關聯的外建表的條目。在本文的“測試用例”部分中,是這樣賦值的:RelationSetHandler = m => m.UsersInRoles

  GetRelationHandler屬性

  描述如何根據主表對象和從表對象獲取中間關系表對象的行為。在本文中,就是根據用戶ID與角色ID獲取一個UsersInRoles對象。它的作用是確定有多少個中間表對象需要被操作。當然,最后提交至數據庫的元素,是與RelationSetHandler操作結果中包含的元素進行比對后才被執行的。在本文的“測試用例”部分中,是這樣賦值的:

  GetRelationHandler = (m, s) => DataContext.UsersInRoles.SingleOrDefault(e => e.UserID == m.UserID && e.RoleID == s.RoleID)

  CreateRelationHandler屬性

  描述如何根據主表對象和從表對象創建中間關系表對象的行為。在傳遞的從表集合中,發現有需要新建的關系時,就要用到這個操作。在本文的“測試用例”部分中,是這樣賦值的:

CreateRelationHandler = (m, s) => new UsersInRoles()

{

UserInRoleID = Guid.NewGuid(),

Users = m,

Roles = s

}

  GetDeleting方法:獲取將要刪除的關系。

  GetAdding方法:獲取將要新建的關系。

  Execute方法:執行操作。

  接口的實現LinqM2MProvider<M, R, S>

至于接口的實現,各位肯定都有自己的方式,在這里我就不詳細說明了,本文給出一個實現僅供參考。點擊下載

  總結

  使用本文中介紹的方式來管理多對多表間關系,就可以不關注操作到底是進行添加關系、刪除關系、還是更改關系了,開發人員需要做的只是將三個委托GetRelationHandler、CreateRelationHandler、RelationSetHandler構造好,并傳遞正確的參數(主表對象、從表集合)即可,從而不再關注操作細節,提高開發效率。

  本文資源下載

建表腳本:下載

頁面代碼:UsersInRolesDemo.aspx UsersInRolesDemo.aspx.cs

接口ILinqM2MProvider:下載

實現LinqM2MProvider:下載

如果鏈接打不開,請配置hosts文件:

209.85.225.101 docs.google.com

74.125.127.100 writely.google.com

74.125.127.139 spreadsheets.google.com

0
0

請先登錄 提交中…

標簽:LINQ LINQ to SQL

文章列表

Linq to SQL T4 代碼生成器 (二)訪問設計器中的 Table 對象

Linq to SQL T4 代碼生成器 (二)訪問設計器中的 Table 對象

作者: 麥舒 來源: 博客園 發布時間: 2010-07-23 13:31 閱讀: 1925 次 推薦: 1 原文鏈接 [收藏]

  在上一篇文章中,介紹了如何訪問 DataContext 對象,下面接著來講解一下如何訪問設計器中的表對象,并生成生體類代碼。從 Northwind 數據庫中拖一個表到設計器中。拖出來后,記得保存 dbml 文件,否則是無法訪問到這個表的。 在這里拖的是 Catories 表,如下圖所示:

  我們可以通過訪問 DataContext.Tables 來訪拖放到設計器中的表。代碼如下:

<# foreach(ITable table in DataContext.Tables){

}#>

  現在再來看看關于 ITable 的對象成員:

  其中 Member 這個屬性,指的是在 data context 實例中的成員名稱,例如對于 Categories 表來說,這個 Member 就是 Categories。

  Name 屬性指的是該表的名稱,而 Type 指就是該表的映射類。我們都知道,在 Linq to SQL 中,一個表可以映射成到一個類(或者多個繼承類中),Type 屬性就是用來訪問這些映謝類。新建一個 DataClasses.tt 模版文件,復制下面的代碼:

<#@ template inherits="ModelingTextTransformation" language="C#" debug="true" hostspecific="True"#>
<#@ QuickCode processor="DbmlProcessor" requires="ModelFile='Northwind.dbml'"#>
<#@ output extension=".cs" #>
<#@ import namespace = "System.Text.RegularExpressions" #>

using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace <#= DataContext.ContextNamespace #>
{
<# foreach(ITable table in DataContext.Tables){ #>
[Table(Name="<#= table.Name #>")]
public class <#= table.Type.Name #>
{
}
<# } #>
}

  按保存后生成的代碼如下:

using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace DecodeDemo
{
[Table(Name="dbo.Categories")]
public class Category
{
}
}

  在這里可以看到,已經可以生成實體類了,當然,還有屬性沒有生成。(這個我們放在下一單文章中講解)

  現在來看一下如何生成繼承類。從 ToolBox 工具欄中拖一個 DataClass 對象到調計器中,然后命名為 MyCatory,并繼承于 Category。

  通過訪問 Type.SubTypes 成員來對于訪問繼承類。代碼:

<# foreach(ITable table in DataContext.Tables){ #>
<#}#>

  完整的模版代碼如下:

<#@ template inherits="ModelingTextTransformation" language="C#" debug="true" hostspecific="True"#>
<#@ QuickCode processor="DbmlProcessor" requires="ModelFile='Northwind.dbml'"#>
<#@ output extension=".cs" #>
<#@ import namespace = "System.Text.RegularExpressions" #>

using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace <#= DataContext.ContextNamespace #>
{
public partial class <#= DataContext.Name #> : ALinq.DataContext
{
public <#= DataContext.Name #>(string connection) :
base(connection)
{
}

<# foreach(ITable table in DataContext.Tables){ #>
public Table<<#= table.Type.Name #>> <#= table.Member #>
{
get
{
return this.GetTable<<#= table.Type.Name #>>();
}
}
<# } #>
}
}

  生成的代碼如下:

代碼

using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace DecodeDemo
{
public partial class DataClasses1DataContext : ALinq.DataContext
{
public DataClasses1DataContext(string connection) :
base(connection)
{
}

public Table<Category> Categories
{
get
{
return this.GetTable<Category>();
}
}
}
}

  先講到這里吧,下一篇再講解屬性的生成。

  代碼下載:http://files.cnblogs.com/ansiboy/ConsoleApplication2.zip

1
0

請先登錄 提交中…

標簽:C# LINQ to SQL

文章列表

Linq to SQL T4 代碼生成器 (-)訪問 DataContext 對象

Linq to SQL T4 代碼生成器 (-)訪問 DataContext 對象

作者: 麥舒 來源: 博客園 發布時間: 2010-07-23 13:33 閱讀: 2376 次 推薦: 1 原文鏈接 [收藏]

這個工具,是在一個項目中提取出來的,現在免費提供給大家使用。

本文介紹的 Linq to SQL T4 代碼生成器有如下特點:

1、支持 dbml 文件。能夠訪問 dbml 設計器中的對象。

2、可能通多修改模版來生成代碼。

3、可以生成多個文件。比如:一個類一個文件。

準備:

1、T4 代碼編輯器(goole 即可)。盡管不是必須,但是為了方便代碼的編輯,還是建議安裝一個。

2、下載安裝 Decode_VS2008 (必須)。

開始:

1、創建一個項目。然后運行 LicenseFileGenerator.exe 生成一個名為 deco.lic 的授權文件,并添加到項目中。

其中 Assembly 為項目生成文件的名稱,Company 為項目中的公司名稱,如果沒有,可以不填。在這里生的 assembly 文件名為 ConsoleApplication1.exe,因此要 Assembly 文件本框中填入的是 ConsoleApplication1.exe ,而并非 ConsoleApplication1,公司名稱為空,因此可以不填。

點擊 Generate 按鈕后在當前路徑中生成一個 deco.lic 文件。請把該文件添加到項目中去。

2、創建名為 Northwind.dbml 的文件,并禁用原來的代碼生成器。選項 Northwind.dbml 文件。把 Custom Tool 選項清空(默認為 MSLinqToSQLGenerator)。

3、創建一個名為 Northwind.tt 的文件。打開 Northwind.tt 文件進行編輯。然后按保存生成代碼。

內容如下:

<#@ template inherits="ModelingTextTransformation" language="C#" debug="true" hostspecific="True"#>
<#@ QuickCode processor="DbmlProcessor" requires="ModelFile='Northwind.dbml'"#>
<#@ output extension=".cs" #>
<#@ import namespace = "System.Text.RegularExpressions" #>

using ALinq;
using ALinq.Mapping;

namespace <#= DataContext.ContextNamespace #>
{
public partial class <#= DataContext.Name #> : ALinq.DataContext
{
public <#= DataContext.Name #>(string connection) :
base(connection)
{
}

<# foreach(ITable table in DataContext.Tables){ #>
public Table<<#= table.Type.Name #>> <#= table.Member #>
{
get
{
return this.GetTable<<#= table.Type.Name #>>();
}
}
<# } #>
}
}

下來解釋一下上面的代碼。我們現在要關注的 DataContext 對象。這個對象很重要,Dbml 設計器的對象,都是通過 DataContext 對象來進行訪問。你可以把它和 Dbml 設計器的中的 DataContext 對應起來。

關于是 DataContext 的成員,請參考:http://www.alinq.org/document/decode.htm

示例代碼:http://files.cnblogs.com/ansiboy/ConsoleApplication1.zip

完整范例代碼:http://files.cnblogs.com/ansiboy/JDataDemo_0.9.zip

1
0

請先登錄 提交中…

標簽:C# LINQ to SQL

文章列表

分布式緩存系統Memcached簡介與實踐

分布式緩存系統Memcached簡介與實踐

作者: 戲水 來源: 博客園 發布時間: 2009-07-19 10:05 閱讀: 10900 次 推薦: 3 原文鏈接 [收藏]

  緣起: 在數據驅動的web開發中,經常要重復從數據庫中取出相同的數據,這種重復極大的增加了數據庫負載。緩存是解決這個問題的好辦法。但是ASP.NET中的雖然已經可以實現對頁面局部進行緩存,但還是不夠靈活。此時Memcached或許是你想要的。
  Memcached是什么?
  Memcached是由Danga Interactive開發的,高性能的,分布式的內存對象緩存系統,用于在動態應用中減少數據庫負載,提升訪問速度。
  Memcached能緩存什么?
  通過在內存里維護一個統一的巨大的hash表,Memcached能夠用來存儲各種格式的數據,包括圖像、視頻、文件以及數據庫檢索的結果等。
  Memcached快么?
  非常快。Memcached使用了libevent(如果可以的話,在linux下使用epoll)來均衡任何數量的打開鏈接,使用非阻塞的網絡I/O,對內部對象實現引用計數(因此,針對多樣的客戶端,對象可以處在多樣的狀態), 使用自己的頁塊分配器和哈希表, 因此虛擬內存不會產生碎片并且虛擬內存分配的時間復雜度可以保證為O(1)。
  Danga Interactive為提升Danga Interactive的速度研發了Memcached。目前,LiveJournal.com每天已經在向一百萬用戶提供多達兩千萬次的頁面訪問。而這些,是由一個由web服務器和數據庫服務器組成的集群完成的。Memcached幾乎完全放棄了任何數據都從數據庫讀取的方式,同時,它還縮短了用戶查看頁面的速度、更好的資源分配方式,以及Memcache失效時對數據庫的訪問速度。
  Memcached的特點
  Memcached的緩存是一種分布式的,可以讓不同主機上的多個用戶同時訪問, 因此解決了共享內存只能單機應用的局限,更不會出現使用數據庫做類似事情的時候,磁盤開銷和阻塞的發生。
  Memcached的使用
  一、
Memcached服務器端的安裝 (此處將其作為系統服務安裝)
  下載文件:memcached 1.2.1 for Win32 binaries (Dec 23, 2006)
  1. 解壓縮文件到c:\memcached
  2. 命令行輸入 ‘c:\memcached\memcached.exe -d install’
  3. 命令行輸入 ‘c:\memcached\memcached.exe -d start’ ,該命令啟動 Memcached ,默認監聽端口為 11211
  通過 memcached.exe -h 可以查看其幫助
  二、.NET memcached client library
  下載文件:https://sourceforge.net/projects/memcacheddotnet/

  里面有.net1.1 和 .net2.0的兩種版本 還有一個不錯的例子。

  三、應用

  1. 將Commons.dll,ICSharpCode.SharpZipLib.dll,log4net.dll,Memcached.ClientLibrary.dll 等放到bin目錄
  2. 引用Memcached.ClientLibrary.dll
  3. 代碼

1 namespace Memcached.MemcachedBench
2 {
3 using System;
4 using System.Collections;
5
6 using Memcached.ClientLibrary;
7
8 public class MemcachedBench
9 {
10 [STAThread]
11 public static void Main(String[] args)
12 {
13 string[] serverlist = { “10.0.0.131:11211″, “10.0.0.132:11211″ };
14
15 //初始化池
16 SockIOPool pool = SockIOPool.GetInstance();
17 pool.SetServers(serverlist);
18
19 pool.InitConnections = 3;
20 pool.MinConnections = 3;
21 pool.MaxConnections = 5;
22
23 pool.SocketConnectTimeout = 1000;
24 pool.SocketTimeout = 3000;
25
26 pool.MaintenanceSleep = 30;
27 pool.Failover = true;
28
29 pool.Nagle = false;
30 pool.Initialize();
31
32 // 獲得客戶端實例
33 MemcachedClient mc = new MemcachedClient();
34 mc.EnableCompression = false;
35
36 Console.WriteLine(“