2008-12-04 32 views
70

Tôi đã dành thời gian xem bài viết của Phil Haack trên Grouping Controllers những nội dung rất thú vị.Cấu trúc trình cắm thêm cho ASP.NET MVC

Hiện tại tôi đang cố gắng tìm hiểu xem liệu có thể sử dụng cùng một ý tưởng để tạo kiến ​​trúc plug-in/mô-đun cho dự án mà tôi đang làm việc hay không.

Vì vậy, câu hỏi của tôi là: Có thể có các khu vực trong bài viết của Phil được chia thành nhiều dự án không?

Tôi có thể thấy rằng không gian tên sẽ tự hoạt động, nhưng tôi lo ngại về lượt xem kết thúc ở đúng vị trí. Có điều gì đó có thể được sắp xếp với các quy tắc xây dựng không?

Giả sử rằng ở trên là có thể với nhiều dự án trong một giải pháp duy nhất, không ai có ý tưởng nào về cách tốt nhất để làm cho nó có thể với một giải pháp riêng biệt và mã hóa cho một bộ được xác định trước của giao diện? Di chuyển từ một Khu vực đến một trình cắm thêm.

Tôi có một số kinh nghiệm với kiến ​​trúc trình cắm nhưng không phải khối lượng nên mọi hướng dẫn trong lĩnh vực này sẽ hữu ích.

Trả lời

51

Tôi đã làm bằng chứng khái niệm cách đây vài tuần khi tôi đặt một chồng thành phần hoàn chỉnh: một lớp mô hình, một lớp điều khiển và các khung nhìn liên quan của chúng thành một DLL, thêm/tinh chỉnh one of the examples của các lớp VirtualPathProvider. vì vậy họ sẽ giải quyết những vấn đề đó trong DLL một cách thích hợp.

Cuối cùng, tôi vừa bỏ DLL vào một ứng dụng MVC được cấu hình thích hợp và nó hoạt động giống như khi nó là một phần của ứng dụng MVC ngay từ đầu. Tôi đã đẩy nó thêm một chút và nó làm việc với 5 trong số các plugin mini-MVC nhỏ này tốt. Rõ ràng, bạn phải xem tài liệu tham khảo của bạn và cấu hình phụ thuộc khi xáo trộn tất cả xung quanh, nhưng nó đã làm việc.

Bài tập được nhắm vào chức năng plugin cho nền tảng dựa trên MVC mà tôi đang xây dựng cho khách hàng. Có một bộ điều khiển và chế độ xem lõi được tăng cường bởi nhiều tùy chọn hơn trong mỗi trường hợp của trang web. Chúng tôi sẽ làm cho những bit tùy chọn vào các plugin DLL mô-đun này. Càng xa càng tốt.

Tôi đã viết tổng quan về mẫu thử nghiệm và sample solution for ASP.NET MVC plugins trên trang web của mình.

EDIT: 4 năm sau, tôi đã làm khá một vài ứng dụng ASP.NET MVC có bổ trợ và không còn sử dụng phương pháp tôi mô tả ở trên. Tại thời điểm này, tôi chạy tất cả các plugin của mình thông qua MEF và không đặt bộ điều khiển vào các plugin. Thay vào đó, tôi làm cho bộ điều khiển chung sử dụng thông tin định tuyến để chọn các plugin MEF và giao công việc cho plugin, v.v. Chỉ cần nghĩ rằng tôi sẽ thêm vì câu trả lời này được đánh một chút công bằng.

+1

liên kết của bạn không làm việc tôi đã hy vọng để xem những gì bạn xây dựng, tôi có vấn đề tương tự với dự án của tôi mà tôi muốn thực hiện dự án pluggable vì vậy tôi có thể thêm/xóa chức năng theo yêu cầu. giống như cách mọi người làm trong wordpress. – Alok

3

Tôi đoán có thể rời khỏi chế độ xem của bạn trong các dự án trình cắm.

Đó là ý tưởng của tôi: bạn cần một ViewEngine có thể gọi plugin (có thể thông qua giao diện) và yêu cầu chế độ xem (IView). Các plugin sau đó sẽ nhanh chóng xem không thông qua url của nó (như một ViewEngine bình thường không - /Views/Shared/View.asp) nhưng thông qua tên của nó xem) ví dụ thông qua phản ánh hoặc DI/IoC container).

Các trở về quan điểm trong các plugin có thể tôi thậm chí hardcoded (ví dụ đơn giản sau):

public IView GetView(string viewName) 
{ 
    switch (viewName) 
    { 
     case "Namespace.View1": 
      return new View1(); 
     case "Namespace.View2": 
      return new View2(); 
     ... 
    } 
} 

... Đây chỉ là một ý tưởng nhưng tôi hy vọng nó có thể làm việc hay chỉ là một nguồn cảm hứng tốt.

4

Vì vậy, tôi đã chơi một chút với ví dụ từ J Wynia ở trên. Rất cám ơn cho btw đó.

Tôi đã thay đổi mọi thứ để phần mở rộng của VirtualPathProvider sử dụng một hàm dựng tĩnh để tạo danh sách tất cả các tài nguyên có sẵn kết thúc bằng .aspx trong các dll khác nhau trong hệ thống. Thật là mất thời gian nhưng chỉ chúng tôi mới làm điều đó một lần.

Nó có thể là một tổng lạm dụng cách mà VirtualFiles có nghĩa vụ phải được sử dụng như cũng ;-)

bạn kết thúc với một:

private static IDictionary resourceVirtualFile;

với chuỗi là đường dẫn ảo.

mã bên dưới đưa ra một số giả định về không gian tên của tệp .aspx nhưng nó sẽ hoạt động trong các trường hợp đơn giản. Điều tuyệt vời này là bạn không phải tạo ra các đường dẫn xem phức tạp mà chúng được tạo ra từ tên tài nguyên.

class ResourceVirtualFile : VirtualFile 
{ 
    string path; 
    string assemblyName; 
    string resourceName; 

    public ResourceVirtualFile(
     string virtualPath, 
     string AssemblyName, 
     string ResourceName) 
     : base(virtualPath) 
    { 
     path = VirtualPathUtility.ToAppRelative(virtualPath); 
     assemblyName = AssemblyName; 
     resourceName = ResourceName; 
    } 

    public override Stream Open() 
    { 
     assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName + ".dll"); 

     Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyName); 
     if (assembly != null) 
     { 
      Stream resourceStream = assembly.GetManifestResourceStream(resourceName); 
      if (resourceStream == null) 
       throw new ArgumentException("Cannot find resource: " + resourceName); 
      return resourceStream; 
     } 
     throw new ArgumentException("Cannot find assembly: " + assemblyName); 
    } 

    //todo: Neaten this up 
    private static string CreateVirtualPath(string AssemblyName, string ResourceName) 
    { 
     string path = ResourceName.Substring(AssemblyName.Length); 
     path = path.Replace(".aspx", "").Replace(".", "/"); 
     return string.Format("~{0}.aspx", path); 
    } 

    public static IDictionary<string, VirtualFile> FindAllResources() 
    { 
     Dictionary<string, VirtualFile> files = new Dictionary<string, VirtualFile>(); 

     //list all of the bin files 
     string[] assemblyFilePaths = Directory.GetFiles(HttpRuntime.BinDirectory, "*.dll"); 
     foreach (string assemblyFilePath in assemblyFilePaths) 
     { 
      string assemblyName = Path.GetFileNameWithoutExtension(assemblyFilePath); 
      Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFilePath); 

      //go through each one and get all of the resources that end in aspx 
      string[] resourceNames = assembly.GetManifestResourceNames(); 

      foreach (string resourceName in resourceNames) 
      { 
       if (resourceName.EndsWith(".aspx")) 
       { 
        string virtualPath = CreateVirtualPath(assemblyName, resourceName); 
        files.Add(virtualPath, new ResourceVirtualFile(virtualPath, assemblyName, resourceName)); 
       } 
      } 
     } 

     return files; 
    } 
} 

Sau đó bạn có thể làm một cái gì đó như thế này trong VirtualPathProvider mở rộng:

private bool IsExtended(string virtualPath) 
    { 
     String checkPath = VirtualPathUtility.ToAppRelative(virtualPath); 
     return resourceVirtualFile.ContainsKey(checkPath); 
    } 

    public override bool FileExists(string virtualPath) 
    { 
     return (IsExtended(virtualPath) || base.FileExists(virtualPath)); 
    } 

    public override VirtualFile GetFile(string virtualPath) 
    { 
     string withTilda = string.Format("~{0}", virtualPath); 

     if (resourceVirtualFile.ContainsKey(withTilda)) 
      return resourceVirtualFile[withTilda]; 

     return base.GetFile(virtualPath); 
    } 
14

Tôi đang thực sự làm việc trên một khuôn khổ mở rộng để sử dụng trên đầu trang của ASP.NET MVC. Khung mở rộng của tôi dựa trên vùng chứa Ioc nổi tiếng: Structuremap.

Trường hợp sử dụng mà tôi đang cố gắng thực hiện rất đơn giản: tạo một ứng dụng cần có một số chức năng cơ bản có thể được mở rộng cho mọi khách hàng (= nhiều người thuê). Chỉ nên có một phiên bản của ứng dụng được lưu trữ nhưng trường hợp này có thể được điều chỉnh cho mọi khách hàng mà không thực hiện bất kỳ thay đổi nào đối với trang web cốt lõi.

Tôi lấy cảm hứng từ bài viết về đa chủ đề được viết bởi Ayende Rahien: http://ayende.com/Blog/archive/2008/08/16/Multi-Tenancy--Approaches-and-Applicability.aspx Một nguồn cảm hứng khác là cuốn sách của Eric Evans về Thiết kế điều khiển tên miền. Khung mở rộng của tôi dựa trên mẫu kho lưu trữ và khái niệm về tập hợp gốc. Để có thể sử dụng khung, ứng dụng lưu trữ sẽ được xây dựng xung quanh các kho lưu trữ và các đối tượng miền. Các bộ điều khiển, kho lưu trữ hoặc các đối tượng miền được liên kết tại thời gian chạy bởi ExtensionFactory.

Trình cắm chỉ đơn giản là một asselmbly chứa Bộ điều khiển hoặc Kho lưu trữ hoặc Đối tượng miền tôn trọng quy ước đặt tên cụ thể. Quy ước đặt tên rất đơn giản, mỗi lớp nên được tiền tố bởi customerID ví dụ: AdventureworksHomeController.

Để mở rộng ứng dụng, bạn sao chép một trình cắm thêm trong thư mục tiện ích mở rộng của ứng dụng. Khi người dùng yêu cầu một trang trong thư mục gốc của khách hàng, ví dụ: http://multitenant-site.com/[customerID]/[controller]/[action] kiểm tra khung nếu có trình cắm cho khách hàng cụ thể đó và khởi tạo các lớp trình cắm tùy chỉnh nếu không nó sẽ tải mặc định một lần. Các lớp tùy chỉnh có thể là Controllers - Repositories hoặc Domain Objects. Cách tiếp cận này cho phép mở rộng một ứng dụng ở tất cả các cấp, từ cơ sở dữ liệu đến giao diện người dùng, thông qua mô hình miền, các kho lưu trữ.

Khi bạn muốn mở rộng một số tính năng hiện có, bạn tạo một trình cắm thêm một assembly có chứa các lớp con của ứng dụng cốt lõi. Khi bạn đã tạo các chức năng hoàn toàn mới, bạn thêm bộ điều khiển mới bên trong trình cắm. Các bộ điều khiển này sẽ được tải bởi khung MVC khi url tương ứng được yêu cầu.Nếu bạn muốn mở rộng giao diện người dùng, bạn có thể tạo chế độ xem mới bên trong thư mục tiện ích mở rộng và tham chiếu chế độ xem bằng trình điều khiển mới hoặc phân lớp .Để sửa đổi hành vi hiện tại, bạn có thể tạo kho lưu trữ mới hoặc đối tượng miền hoặc phân loại phụ thoát. Trách nhiệm khung công tác là xác định đối tượng điều khiển/kho/tên miền nào cần được tải cho một khách hàng cụ thể.
Tôi khuyên bạn nên xem sơ đồ cấu trúc (http://structuremap.sourceforge.net/Default.htm) và đặc biệt là ở các tính năng của Registry DSL http://structuremap.sourceforge.net/RegistryDSL.htm.

Đây là mã tôi sử dụng ở lần khởi động của các ứng dụng để đăng ký tất cả plug-in controllers/kho hoặc các đối tượng miền:

protected void ScanControllersAndRepositoriesFromPath(string path) 
     { 
      this.Scan(o => 
      { 
       o.AssembliesFromPath(path); 
       o.AddAllTypesOf<SaasController>().NameBy(type => type.Name.Replace("Controller", "")); 
       o.AddAllTypesOf<IRepository>().NameBy(type => type.Name.Replace("Repository", "")); 
       o.AddAllTypesOf<IDomainFactory>().NameBy(type => type.Name.Replace("DomainFactory", "")); 
      }); 
     } 

Tôi cũng sử dụng một ExtensionFactory kế thừa từ các System.Web.MVC. DefaultControllerFactory. Nhà máy này chịu trách nhiệm tải các đối tượng mở rộng (bộ điều khiển/đăng ký hoặc đối tượng miền). Bạn có thể cắm các nhà máy của riêng bạn bằng cách đăng ký cho họ lúc khởi động trong file Global.asax:

protected void Application_Start() 
     { 
      ControllerBuilder.Current.SetControllerFactory(
       new ExtensionControllerFactory() 
       ); 
     } 

khuôn khổ này là một trang web mẫu hoạt động đầy đủ có thể được tìm thấy trên: http://code.google.com/p/multimvc/

+2

Đây thực sự là điều thú vị, tôi thích ý tưởng về chức năng quá tải cho những người thuê khác nhau. Bài viết của Ayende rất thú vị. –

0

[đăng như một trả lời bởi vì tôi không thể nhận xét]

Giải pháp tuyệt vời - Tôi đã sử dụng phương pháp này bởi J Wynia và nhận nó để hiển thị chế độ xem từ một hội đồng riêng biệt. Tuy nhiên, phương pháp này chỉ xuất hiện để chỉ hiển thị chế độ xem. Bộ điều khiển trong plugin có vẻ không được hỗ trợ, đúng không? Ví dụ: nếu chế độ xem từ plugin đã đăng lại, bộ điều khiển chế độ xem đó trong plugin sẽ không được gọi là. Thay vào đó, nó sẽ được chuyển đến bộ điều khiển trong ứng dụng gốc MVC. Tôi có hiểu điều này một cách chính xác hoặc có cách giải quyết cho vấn đề này không?

+0

Bạn có thể đăng ký các tuyến đường cục bộ với plugin, chỉ cần một số cách để định cấu hình chúng. Bạn có thể có thể sử dụng StructureMap để tránh rất nhiều lộn xộn xung quanh với sự phản ánh. –

+0

Bỏ qua nhận xét/câu trả lời của tôi. Tôi đã làm điều đó sai, nhưng làm cho nó hoạt động theo cách mà nó được dự định. Hoạt động tuyệt vời! Xin lỗi vì tiếng ồn! – tbehunin

Các vấn đề liên quan