1.1 Thực thi assembly ở miềnứngdụng khác V V Bạn cần thực thi một assembly ở một miềnứngdụng khác với miềnứngdụng hiện hành. # # Gọi phương thức ExecuteAssembly của đối tượng AppDomain đại diện cho miềnứng dụng, và chỉ định tên của assembly cần thực thi. Nếu bạn có một assembly khả-thực-thi và muốn nạp để thực thi nó trong một miềnứng dụng, phương thức ExecuteAssembly sẽ giúp bạn. Phương thức ExecuteAssembly có bốn dạng thức khác nhau. Dạng thức đơn giản nhất chỉ nhận vào một kiểu string chứa tên c ủa assembly cần thực thi; bạn có thể chỉ định một file cục bộ hay một URL. Một dạng thức khác cho phép bạn chỉ định chứng cứ (evidence) cho assembly (xem mục 13.10) và các đối số để truyền đến điểm nhập của assembly (tương đương với các đối số dòng lệnh). Phương thức ExecuteAssembly nạp assembly được chỉ định và thực thi phương thức được định nghĩa trong siêu d ữ liệu là điểm nhập của assembly (thường là phương thức Main). Nếu assembly được chỉ định không có khả năng thực thi, ExecuteAssembly sẽ ném ngoại lệ System.Runtime.InteropServices.COMException. Bộ thực thi không thực thi assembly trong một tiểu trình mới, vì thế quyền kiểm soát sẽ không trả về cho đến khi quá trình thực thi của assembly kết thúc. Do ExecuteAssembly nạp một assembly bằng tên riêng phần (chỉ có tên file), CLR sẽ không dùng GAC hay probing để tìm assembly (xem mục 3.5 để bi ết thêm chi tiết). Ví dụ dưới đây trình bày cách sử dụng phương thức ExecuteAssembly để nạp và thực thi một assembly. Lớp ExecuteAssemblyExample tạo một AppDomain và thực thi chính nó trong AppDomain bằng phương thức ExecuteAssembly. Kết quả là có hai bản sao của ExecuteAssemblyExample được nạp vào hai miềnứngdụng khác nhau. using System; public class ExecuteAssemblyExample { public static void Main(string[] args) { // Nếu assembly đang thực thi trong một AppDomain // có tên thân thiện là "NewAppDomain" // thì không tạo AppDomain mới. Điều này sẽ // tránh một vòng lặp vô tận tạo AppDomain. if (AppDomain.CurrentDomain.FriendlyName != "NewAppDomain") { // Tạo miềnứngdụng mới có tên là "NewAppDomain". AppDomain domain = AppDomain.CreateDomain("NewAppDomain"); // Thực thi assembly này trong AppDomain mới và // truyền mảng các đối số dòng lệnh. domain.ExecuteAssembly("ExecuteAssemblyExample.exe", null, args); } // Hiển thị các đối số dòng lệnh lên màn hình // cùng với tên thân thiện của AppDomain. foreach (string s in args) { Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + " : " + s); } } } 1.2 Thể hiện hóa một kiểu trong miềnứngdụng khác V V Bạn cần thể hiện hóa một kiểu trong một miềnứngdụng khác với miềnứngdụng hiện hành. # # Gọi phương thức CreateInstance hay CreateInstanceFrom của đối tượng AppDomain đại diện cho miềnứngdụng đích. Việc sử dụng phương thức ExecuteAssembly (đã được thảo luận trong mục 3.6) không mấy khó khăn; nhưng khi phát triển các ứngdụng phức tạp có sử dụng nhiều miềnứng dụng, chắc chắn bạn muốn kiểm soát quá trình nạp các assembly, tạo các kiểu dữ liệu, và triệu g ọi các thành viên của đối tượng bên trong miềnứng dụng. Các phương thức CreateInstance và CreateInstanceFrom cung cấp nhiều phiên bản nạp chồng giúp bạn kiểm soát quá trình tạo đối tượng. Các phiên bản đơn giản nhất sử dụng phương thức khởi dựng mặc định của kiểu, nhưng cả hai phương thức này đều thiết đặt các phiên bản cho phép bạn cung cấp đối số để sử dụng bấ t kỳ phương thức khởi dựng nào. Phương thức CreateInstance nạp một assembly có tên xác định vào miềnứngdụng bằng quá trình đã được mô tả cho phương thức Assembly.Load trong mục 3.5. Sau đó, CreateInstance tạo đối tượng cho kiểu và trả về một tham chiếu đến đối tượng mới được đóng gói trong ObjectHandle (được mô tả trong mục 3.3). Tương tự như thế đối với phương thức CreateInstanceFrom; tuy nhiên, CreateInstanceFrom nạp assembly vào mi ền ứngdụng bằng quá trình đã được mô tả cho phương thức Assembly.LoadFrom trong mục 3.5. Í AppDomain cũng cung cấp hai phương thức rất tiện lợi có tên là CreateInstanceAndUnwrap và CreateInstanceFromAndUnwrap, chúng sẽ tự động khôi phục tham chiếu đến đối tượng đã được tạo từ đối tượng ObjectHandle; bạn phải ép đối tượng trả về cho đúng kiểu trước khi sử dụng. Nếu bạn sử dụng CreateInstance hay CreateInstanceFrom để tạo đối tượng kiểu MBV trong một miềnứngdụng khác, đối tượ ng sẽ được tạo nhưng tham chiếu trả về sẽ không chỉ đến đối tượng đó. Do cách thức đối tượng MBV vượt qua biên miềnứng dụng, tham chiếu này sẽ chỉ đến một bản sao của đối tượng được tạo tự động trong miềnứngdụng cục bộ. Chỉ khi bạn tạo một kiểu MBR thì tham chiếu trả về mớ i chỉ đến đối tượng trong miềnứngdụng khác (xem mục 3.2 để biết thêm chi tiết về kiểu MBV và MBR). Kỹ thuật chung để đơn giản hóa việc quản lý các miềnứngdụng là sử dụng lớp điều khiển (controller class). Một lớp điều khiển là một kiểu MBR tùy biến. Bạn hãy tạo một miềnứngdụng rồi t ạo đối tượng lớp điều khiển trong miềnứngdụng này bằng phương thức CreateInstance. Lớp điều khiển hiện thực các chức năng cần thiết cho ứngdụng để thao tác miềnứngdụngvà các nội dung của nó. Các chức năng này có thể bao gồm: nạp assembly, tạo thêm miềnứng dụng, dọn dẹp trước khi xóa miềnứng dụng, hay liệt kê các phần tử chương trình (bạn không thể thực hiện ở bên ngoài miềnứng dụng). Ví dụ dưới đây trình bày cách sử dụng một lớp điều khiển có tên là PluginManager. Khi đã được tạo trong một miềnứng dụng, PluginManager cho phép bạn tạo đối tượng của các lớp có hiện thực giao diện IPlugin, chạy vàdừng các plug-in đó, và trả về danh sách các plug-in hiện được nạp. using System; using System.Reflection; using System.Collections; using System.Collections.Specialized; // Giao diện chung cho tất cả các plug-in. public interface IPlugin { void Start(); void Stop(); } // Một hiện thực đơn giản cho giao diện Iplugin // để minh họa lớp điều khiển PluginManager. public class SimplePlugin : IPlugin { public void Start() { Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + ": SimplePlugin starting ."); } public void Stop() { Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + ": SimplePlugin stopping ."); } } // Lớp điều khiển, quản lý việc nạp và thao tác // các plug-in trong miềnứngdụng của nó. public class PluginManager : MarshalByRefObject { // ListDictionary giữ tham chiếu đến các plug-in. private ListDictionary plugins = new ListDictionary(); // Phương thức khởi dựng mặc định. public PluginManager() {} // Phương thức khởi dựng nhận danh sách các plug-in. public PluginManager(ListDictionary pluginList) { // Nạp các plug-in đã được chỉ định. foreach (string plugin in pluginList.Keys) { this.LoadPlugin((string)pluginList[plugin], plugin); } } // Nạp assembly và tạo plug-in được chỉ định. public bool LoadPlugin(string assemblyName, string pluginName) { try { // Nạp assembly. Assembly assembly = Assembly.Load(assemblyName); // Tạo plug-in mới. IPlugin plugin = (IPlugin)assembly.CreateInstance(pluginName, true); if (plugin != null) { // Thêm plug-in mới vào ListDictionary. plugins[pluginName] = plugin; return true; } else { return false; } } catch { return false; } } public void StartPlugin(string plugin) { // Lấy một plug-in từ ListDictionary và // gọi phương thức Start. ((IPlugin)plugins[plugin]).Start(); } public void StopPlugin(string plugin) { // Lấy một plug-in từ ListDictionary và // gọi phương thức Stop. ((IPlugin)plugins[plugin]).Stop(); } public ArrayList GetPluginList() { // Trả về danh sách các plug-in. return new ArrayList(plugins.Keys); } } public class CreateInstanceExample { public static void Main() { // Tạo một miềnứngdụng mới. AppDomain domain1 = AppDomain.CreateDomain("NewAppDomain1"); // Tạo một PluginManager trong miềnứngdụng mới // bằng phương thức khởi dựng mặc định. PluginManager manager1 = (PluginManager)domain1.CreateInstanceAndUnwrap( "CreateInstanceExample", "PluginManager"); // Nạp một plug-in mới vào NewAppDomain1. manager1.LoadPlugin("CreateInstanceExample", "SimplePlugin"); // Chạy vàdừng plug-in trong NewAppDomain1. manager1.StartPlugin("SimplePlugin"); manager1.StopPlugin("SimplePlugin"); // Tạo một miềnứngdụng mới. AppDomain domain2 = AppDomain.CreateDomain("NewAppDomain2"); // Tạo một ListDictionary chứa các plug-in. ListDictionary pluginList = new ListDictionary(); pluginList["SimplePlugin"] = "CreateInstanceExample"; // Tạo một PluginManager trong miềnứngdụng mới // và chỉ định danh sách các plug-in. PluginManager manager2 = (PluginManager)domain1.CreateInstanceAndUnwrap( "CreateInstanceExample", "PluginManager", true, 0, null, new object[] {pluginList}, null, null, null); // Hiển thị các plug-in đã được nạp vào NewAppDomain2. Console.WriteLine("Plugins in NewAppDomain2:"); foreach (string s in manager2.GetPluginList()) { Console.WriteLine(" - " + s); } // Nhấn Enter để thoát. Console.ReadLine(); } } 1.3 Truyền dữliệu giữa các miềnứngdụng V V Bạn cần một cơ chế đơn giản để truyền dữliệu trạng thái hay cấu hình giữa các miềnứng dụng. # # Dùng các phương thức SetData và GetData của lớp AppDomain. Dữliệu có thể được truyền qua các miềnứngdụng như đối số hay trị trả về khi bạn cho gọi các thành viên của các đối tượng hiện có trong các miềnứng dụng. Việc truyền dữliệu qua các miềnứngdụng được thực hiện dễ dàng giống như truyền dữliệu trong cùng một miềnứng dụng. Mọ i miềnứngdụng đều duy trì một vùng đệm dữliệu (data cache) chứa một tập các cặp “tên/giá trị”. Hầu hết nội dung của vùng đệm dữliệuphản ánh các thiết lập cấu hình của miềnứng dụng, như các giá trị từ đối tượng AppDomainSetup được cung cấp trong quá trình tạo miềnứngdụng (xem mục 3.1). Vùng đệm dữliệu này có thể được sử dụng để trao đổi dữliệu giữa các miềnứngdụng hay lưu trữ các giá trị tạm thời dùng trong cùng một miềnứng dụng. Phương thức SetData thực hiện việc kết hợp một khóa dạng chuỗi với một đối tượng và lưu trữ nó vào vùng đệm dữliệu của miềnứng dụng. Phương thức GetData thực hiện công việc ngược lại là lấy lạ i đối tượng từ vùng đệm dữliệu thông qua khóa. Nếu mã lệnh trong một miềnứngdụng gọi phương thức SetData hay GetData để truy xuất vùng đệm dữliệu của miềnứngdụng khác, thì đối tượng dữliệu phải hỗ trợ ngữ nghĩa marshal-by-value hay marshal-by-reference, nếu không thì ngoại lệ System.Runtime.Serialization.SerializationException sẽ bị ném (xem mục 3.3 để biết thêm chi tiết về cách truyền đối tượng qua các miềnứng dụng). Đoạn mã sau trình bày cách sử dụng phương thức SetData và GetData để truyền một System.Collections.ArrayList giữa hai miềnứng dụng. using System; using System.Reflection; using System.Collections; public class ListModifier { public ListModifier () { // Nhận danh sách từ đệm dữ liệu. ArrayList list = (ArrayList)AppDomain.CurrentDomain.GetData("People"); // Thay đổi danh sách. list.Add("Tam"); } } public class PassDataExample { public static void Main() { // Tạo một miềnứngdụng mới. AppDomain domain = AppDomain.CreateDomain("Test"); // Tạo một ArrayList và thêm thông tin vào. ArrayList list = new ArrayList(); list.Add("Phuong"); list.Add("Phong"); list.Add("Nam"); // Đặt danh sách vào vùng đệm dữliệu của miềnứngdụng mới. domain.SetData("People", list); // Tạo một ListModifier trong miềnứngdụng mới // sẽ thay đổi nội dung của list trong vùng đệm dữ liệu. domain.CreateInstance("03-08", "ListModifier"); // Nhận lại danh sách và hiển thị nội dung của nó. foreach (string s in (ArrayList)domain.GetData("People")) { Console.WriteLine(s); } // Nhấn Enter để thoát. Console.ReadLine(); } . các miền ứng dụng. Việc truyền dữ liệu qua các miền ứng dụng được thực hiện dễ dàng giống như truyền dữ liệu trong cùng một miền ứng dụng. Mọ i miền ứng dụng. } } 1.3 Truyền dữ liệu giữa các miền ứng dụng V V Bạn cần một cơ chế đơn giản để truyền dữ liệu trạng thái hay cấu hình giữa các miền ứng dụng. # # Dùng