2013-01-23 31 views
5

Sử dụng Autofac, tôi có thể đăng ký một lớp học để giải quyết đối với một giao diện sử dụng tiêm sở hữu, sử dụng đoạn mã sau:Làm thế nào để tiêm tên phụ thuộc như một tham số constructor

builder.RegisterType<Log4NetAdapter>() 
     .As<ILogger>() 
     .PropertiesAutowired() 
     .InstancePerDependency(); 

Tuy nhiên, lớp Log4NetAdapter tôi có một tham số constructor yêu cầu tên của lớp gọi. Bằng cách này, tôi có thể ghi lại các sự kiện dựa trên tên của lớp gọi.

public class Log4NetAdapter : ILogger 
{ 
    private readonly ILog _logger; 

    public Log4NetAdapter(string logName) 
    { 
     _logger = LogManager.GetLogger(logName); 
    } 

    ... 
} 

Làm thế nào tôi có thể tiêm tên (ví dụ: typeof(dependency).Name) của sự phụ thuộc vào lớp constructor tài sản tiêm cho rằng mỗi phụ thuộc sẽ có Log4NetAdapter dụ riêng của mình?

+0

Khi bạn cần tên của lớp trong logger nó thường là một dấu hiệu của việc quá nhiều đăng nhập vào ứng dụng. Cân nhắc xem xét kỹ thiết kế ứng dụng của bạn. [Câu trả lời SO này chi tiết] (http://stackoverflow.com/a/9915056/264697) về quá nhiều ghi nhật ký. – Steven

+1

@Steven Đó là một bài viết tuyệt vời và có, có một khả năng tôi cần phải sửa đổi cách tôi đăng nhập. – Digbyswift

Trả lời

3

Cập nhật: Xây dựng trên mẫu LogInjectionModulehow Autofac does property injection, tôi đã mở rộng mô-đun để thực hiện cả hàm tạo và thuộc tính.

Lưu ý: Tôi đã sửa loại được chuyển đến LogManager trong OnComponentPreparing để sử dụng loại khai báo. Điều này làm cho ví dụ Resolve<Func<Service>> sử dụng loại nhật ký chính xác.

using System.Linq; 
    using log4net; 

    public class LogInjectionModule : Module 
    { 
     protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration) 
     { 
      registration.Preparing += OnComponentPreparing; 
      registration.Activating += OnComponentActivating; 
     } 

     private static void OnComponentActivating(object sender, ActivatingEventArgs<object> e) 
     { 
      InjectLogProperties(e.Context, e.Instance, false); 
     } 

     private static void OnComponentPreparing(object sender, PreparingEventArgs e) 
     { 
      e.Parameters = e.Parameters.Union(new[] 
       { 
        new ResolvedParameter(
         (p, i) => p.ParameterType == typeof(ILog), 
         (p, i) => LogManager.GetLogger(p.Member.DeclaringType)) 
       }); 
     } 

     private static void InjectLogProperties(IComponentContext context, object instance, bool overrideSetValues) 
     { 
      if (context == null) throw new ArgumentNullException("context"); 
      if (instance == null) throw new ArgumentNullException("instance"); 

      var instanceType = instance.GetType(); 
      var properties = instanceType 
       .GetProperties(BindingFlags.Public | BindingFlags.Instance) 
       .Where(pi => pi.CanWrite && pi.PropertyType == typeof(ILog)); 

      foreach (var property in properties) 
      { 
       if (property.GetIndexParameters().Length != 0) 
        continue; 

       var accessors = property.GetAccessors(false); 
       if (accessors.Length == 1 && accessors[0].ReturnType != typeof(void)) 
        continue; 

       if (!overrideSetValues && 
        accessors.Length == 2 && 
        (property.GetValue(instance, null) != null)) 
        continue; 

       ILog propertyValue = LogManager.GetLogger(instanceType); 
       property.SetValue(instance, propertyValue, null); 
      } 
     } 
    } 

Về cách sử dụng các mô-đun, đây là một ví dụ:

public class Service 
{ 
    public Service(ILog log) { ... } 
} 

var cb = new ContainerBuilder(); 
cb.RegisterModule<LogInjectionModule>(); 
cb.RegisterType<Service>(); 
var c = cb.Build(); 

var service = c.Resolve<Service>(); 
+0

Bạn có thể muốn cung cấp một số chi tiết từ liên kết để tránh câu trả lời này trở nên chết nếu liên kết bị chết. – Guvante

+0

@Peter Tôi nghĩ rằng giải pháp tại liên kết chỉ đề cập đến việc xây dựng tiêm, không phải là việc tiêm tài sản. – Digbyswift

+0

@Digbyswift - đúng vậy. Nó không được rõ ràng từ câu hỏi của bạn rằng người nhận ILog yêu cầu tiêm tài sản. Bất kỳ lý do nào không sử dụng tiêm ctor? –

1

Bạn chỉ sử dụng logName để giải quyết hiệu quả theo tên ILog, vậy tại sao không chỉ tiêm ILog?

public class Log4NetAdapter : ILogger 
{ 
    private readonly ILog _logger; 

    public Log4NetAdapter(ILog logger) 
    { 
     _logger = logger; 
    } 

    ... 
} 

OK, vì vậy bây giờ tôi đã chuyển vấn đề một chút, nhưng tôi cũng đã làm điều này ít kết hợp với các lớp khác, cụ thể là LogManager.

Vì vậy, nếu tôi được sử dụng thống nhất, sau đó tôi sẽ làm điều này để đảm bảo tôi nhận được logger phải:

var childContainer = container.CreateChildContainer(); 
childContainer.RegisterInstance<ILog>(LogManager.GetLogger(logName)); 
var adaptor = childContainer.Resolve<Log4NetAdapter>(); 

Container con ngăn cản bất kỳ mã nhận được quyền truy cập khác để ILog đó. Bạn có thể làm điều này cao như bạn muốn, tôi không biết nhiều hơn về mã của bạn.

+0

Cảm ơn, nhưng câu trả lời của bạn không cho tôi biết nơi tôi nhận được tham số 'logName' từ - trừ khi tôi đã bỏ lỡ một cái gì đó. Ngoài ra, nó là ví dụ cụ thể bằng cách sử dụng bộ điều hợp tôi quan tâm, nhưng tôi thấy quan điểm của bạn. – Digbyswift

+0

Tại một số điểm bạn sẽ biết 'logName', tôi không thể giúp bạn nếu không, nhưng đó là điểm bạn nên đăng ký nó như là' ILog' trước khi giải quyết 'Log4NetAdapter' của bạn nhưng tôi không có thêm mã nào nữa tiếp tục vì vậy tôi không thể tư vấn thêm nữa. Hình như bạn có một giải pháp mặc dù từ Peter. – weston

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