26

Tôi đang cố gắng làm việc ra một cách để sử dụng tiêm phụ thuộc với ASP.NET Web Forms điều khiển.Làm thế nào để sử dụng Dependency Injection với ASP.NET Web Forms

Tôi đã nhận được rất nhiều điều khiển tạo ra các kho trực tiếp, và sử dụng những tiếp cận và liên kết với dữ liệu, vv

Tôi đang tìm kiếm một mẫu nơi tôi có thể vượt qua kho để điều khiển bên ngoài (IOC), do đó Các điều khiển của tôi vẫn không biết về cách các kho lưu trữ được xây dựng và chúng đến từ đâu…

Tôi không muốn phụ thuộc vào container IoC từ các điều khiển của mình, do đó tôi chỉ muốn có thể xây dựng các điều khiển với hàm tạo hoặc tiêm tài sản.

(Và chỉ để những thứ phức tạp, các điều khiển này đang được xây dựng và được đặt trên trang của CMS trong thời gian chạy!)

Bất kỳ suy nghĩ nào?

Trả lời

30

Bạn có thể sử dụng chức năng tiêm xây dựng tự động bằng cách thay thế mặc định PageHandlerFactory bằng tùy chỉnh. Bằng cách này bạn có thể sử dụng một constructor quá tải để tải các phụ thuộc. Trang của bạn có thể trông như thế này:

public partial class HomePage : System.Web.UI.Page 
{ 
    private readonly IDependency dependency; 

    public HomePage(IDependency dependency) 
    { 
     this.dependency = dependency; 
    } 

    // Do note this protected ctor. You need it for this to work. 
    protected HomePage() { } 
} 

Cấu hình tùy chỉnh mà PageHandlerFactory thể được thực hiện trong web.config như sau:

<?xml version="1.0"?> 
<configuration> 
    <system.web> 
    <httpHandlers> 
     <add verb="*" path="*.aspx" 
     type="YourApp.CustomPageHandlerFactory, YourApp"/> 
    </httpHandlers> 
    </system.web> 
</configuration> 

CustomPageHandlerFactory của bạn có thể trông như thế này:

public class CustomPageHandlerFactory : PageHandlerFactory 
{ 
    private static object GetInstance(Type type) 
    { 
     // TODO: Get instance using your favorite DI library. 
     // for instance using the Common Service Locator: 
     return Microsoft.Practices.ServiceLocation 
      .ServiceLocator.Current.GetInstance(type); 
    } 

    public override IHttpHandler GetHandler(HttpContext cxt, 
     string type, string vPath, string path) 
    { 
     var page = base.GetHandler(cxt, type, vPath, path); 

     if (page != null) 
     { 
      // Magic happens here ;-) 
      InjectDependencies(page); 
     } 

     return page; 
    } 

    private static void InjectDependencies(object page) 
    { 
     Type pageType = page.GetType().BaseType; 

     var ctor = GetInjectableCtor(pageType); 

     if (ctor != null) 
     { 
      object[] arguments = (
       from parameter in ctor.GetParameters() 
       select GetInstance(parameter.ParameterType) 
       .ToArray(); 

      ctor.Invoke(page, arguments); 
     } 
    } 

    private static ConstructorInfo GetInjectableCtor(
     Type type) 
    { 
     var overloadedPublicConstructors = (
      from constructor in type.GetConstructors() 
      where constructor.GetParameters().Length > 0 
      select constructor).ToArray(); 

     if (overloadedPublicConstructors.Length == 0) 
     { 
      return null; 
     } 

     if (overloadedPublicConstructors.Length == 1) 
     { 
      return overloadedPublicConstructors[0]; 
     } 

     throw new Exception(string.Format(
      "The type {0} has multiple public " + 
      "ctors and can't be initialized.", type)); 
    } 
} 

Nhược điểm là điều này chỉ hoạt động khi chạy bên bạn trong Full Trust. Bạn có thể đọc thêm về nó here. Nhưng lưu ý rằng việc phát triển các ứng dụng ASP.NET trong một phần tin cậy seems a lost cause.

+0

Xin chào Steven, tôi có một cái gì đó như thế này được thực hiện trong dự án của tôi và nó hoạt động thực sự tốt. Nhưng tôi đang đối mặt với một vấn đề bây giờ. Nó được mô tả ở đây "http://stackoverflow.com/questions/15692499/page-routing-in-asp-net-4-0-extensionless-url-versus-pagehandlerfactory-asp". Bạn có thể vui lòng xem và có thể chia sẻ một số ý kiến? –

+0

Castle Windsor. Nevermind, tôi đã giải quyết nó bằng cách giải quyết từ Boostrapper trong các trang. Nó không phải là mát mẻ, nhưng hey, nó làm việc và vẫn có vẻ tốt. –

+0

Tôi tìm thấy bài viết tuyệt vời này http://www.codemag.com/Article/1210031 (Tôi nghĩ rằng được liên kết từ một câu trả lời SO khác, nhưng bây giờ tôi không thể tìm thấy cái nào) bao gồm mã ví dụ khác liên quan đến giải pháp trên và Ngoài ra, thật thú vị, cho thấy cách Microsoft Extensibility Framework (MEF) có thể giúp bạn giải quyết vấn đề này và các vấn đề tiêm phụ thuộc tương tự theo cách rất hữu ích và không chuẩn. –

3

Cách tốt nhất là phải có một lớp cơ sở cho các điều khiển như:

public class PartialView : UserControl 
{ 
    protected override void OnInit(System.EventArgs e) 
    { 
     ObjectFactory.BuildUp(this); 
     base.OnInit(e); 
    } 
} 

Điều đó sẽ tiêm bất kỳ kiểm soát được thừa kế từ lớp cơ sở đó (sử dụng StructureMap). Kết hợp điều đó với một cấu hình bất động sản dựa, bạn sẽ có thể có các điều khiển như:

public partial class AdminHeader : PartialView 
{ 
    IMyRepository Repository{get;set;} 
} 

Cập nhật 1: Nếu bạn không thể có các điều khiển kế thừa, có lẽ là CMS có một cái móc ngay sau khi tạo các điều khiển , trong đó bạn có thể gọi cho BuildUp. Ngoài ra nếu CMS cho phép bạn móc một cái gì đó để lấy ví dụ bạn có thể sử dụng constructor dựa trên tiêm, nhưng tôi thích BuildUp trên kịch bản cụ thể này như asp.net không có một móc cho việc này.

+0

Cảm ơn đã phản ứng. Bên hoàn hảo của tôi muốn các điều khiển không có sự phụ thuộc vào khung ObjectFactory, tức là sự tiêm phụ thuộc thuần túy. Rõ ràng điều này ngụ ý một cái gì đó bên ngoài, tạo ra các điều khiển. – Schneider

+0

Re: Cập nhật 1. Tôi sẽ có một poke xung quanh trong CMS và xem nếu tôi có thể tìm thấy bất cứ điều gì. Tôi đoán một vấn đề với constructor dựa trên tiêm trong ASP.NET là các điều khiển trở thành 'undesignable' tại thời điểm đó. Trừ khi người thiết kế biết cách xây dựng chúng. – Schneider

1

Bạn cũng có thể tạo một số cá thể đơn lẻ trong sự kiện Global_asart của Application_Start và để chúng có sẵn dưới dạng thuộc tính chỉ đọc tĩnh công khai.

4

Autofac supports tiêm phụ thuộc khá kín đáo trong ASP.NET WebForms. Sự hiểu biết của tôi là nó chỉ móc vào vòng đời trang ASP.NET bằng cách sử dụng một mô-đun http và tiêm tài sản. Điều duy nhất là đối với các điều khiển tôi không nghĩ điều này xảy ra cho đến sau sự kiện Init.

0

Đây là một giải pháp Gần đây tôi đã sử dụng để tránh hooking vào các đường ống dẫn (tôi thấy rằng lẫn lộn tất cả mọi người mà nhìn vào mã của tôi trong tương lai, nhưng có, tôi nhìn thấy lợi ích của nó cũng):

public static class TemplateControlExtensions 
{ 
    static readonly PerRequestObjectManager perRequestObjectManager = new PerRequestObjectManager(); 

    private static WIIIPDataContext GetDataContext(this TemplateControl templateControl) 
    { 
     var dataContext = (WIIIPDataContext) perRequestObjectManager.GetValue("DataContext"); 

     if (dataContext == null) 
     { 
      dataContext = new WIIIPDataContext(); 
      perRequestObjectManager.SetValue("DataContext", dataContext); 
     } 

     return dataContext; 
    } 

    public static IMailer GetMailer(this TemplateControl templateControl) 
    { 
     return (IMailer)IoC.Container.Resolve(typeof(IMailer)); 
    } 

    public static T Query<T>(this TemplateControl templateControl, Query<T> query) 
    { 
     query.DataContext = GetDataContext(templateControl); 
     return query.GetQuery(); 
    } 

    public static void ExecuteCommand(this TemplateControl templateControl, Command command) 
    { 
     command.DataContext = GetDataContext(templateControl); 
     command.Execute(); 
    } 

    private class PerRequestObjectManager 
    { 
     public object GetValue(string key) 
     { 
      if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key)) 
       return HttpContext.Current.Items[key]; 
      else 
       return null; 
     } 

     public void SetValue(string key, object newValue) 
     { 
      if (HttpContext.Current != null) 
       HttpContext.Current.Items[key] = newValue; 
     } 
    } 
} 

Điều này cho thấy cách bạn có thể tạo trình quản lý thời gian sống của chính bạn khá dễ dàng cũng như móc vào một thùng chứa IoC nếu bạn muốn. Oh, và tôi cũng đang sử dụng một cấu trúc truy vấn/lệnh đó là loại không liên quan, nhưng thêm về lý do đằng sau đó có thể được tìm thấy ở đây:

Limit your abstractions: Refactoring toward reduced abstractions

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