2010-10-30 42 views
28

Tôi đang sử dụng Microsoft Unity. Tôi có giao diện ICustomerService và việc triển khai CustomerService. Tôi có thể đăng ký họ cho container Unity sử dụng đoạn mã sau:Microsoft Unity. Làm thế nào để xác định một tham số nhất định trong constructor?

container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager()); 

Nếu CustomerService có một tham số nhất định trong constructor của nó (ví dụ ISomeService1), tôi sử dụng đoạn mã sau (Tôi cần phải xác định SomeService1):

container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1())); 

Không có vấn đề gì ở đây.

Sự cố xuất hiện khi lớp CustomerService có hai tham số (không phải một tham số như trong ví dụ trước) trong hàm tạo (ví dụ: ISomeService1ISomeService2). Nó hoạt động tốt khi tôi đang sử dụng mã sau: container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1(), new SomeService2()));

Vấn đề là tôi không muốn chỉ định SomeService2() cho tham số thứ hai. Tôi muốn chỉ định tham số đầu tiên - SomeService1(). Nhưng tôi nhận được lỗi mà tôi cần phải xác định không có hoặc cả hai tham số.

Làm cách nào tôi có thể chỉ định tham số đầu tiên của hàm tạo?

+0

Hi and.maz, bạn đã nhận được bất kỳ giải pháp mà bạn không cần phải cung cấp các thông số khác gõ là tốt. một cái gì đó giống như giá trị quan trọng, nơi chúng tôi có thể chỉ định tên của nhà xây dựng và giá trị – rdhaundiyal

Trả lời

0

Bạn có thể đặt mặc định tham số thứ hai cho hàm tạo (ví dụ: =null) hoặc cung cấp một hàm tạo tham số duy nhất ngoài hai hàm tạo tham số bằng cách nạp chồng nó.

57

Câu trả lời hay nhất của bạn là sử dụng vùng chứa.

Những gì bạn đang làm đang nói "khi tạo loại này, hãy sử dụng trường hợp cụ thể này của đối tượng". Điều này không tận dụng khả năng của container để xây dựng cá thể cho bạn. Thay vào đó, bạn nên đăng ký IService1 và IService2 trong vùng chứa. Sau đó, yêu cầu thùng chứa giải quyết những phụ thuộc đó cho bạn.

Nó trông giống như sau:

container.RegisterType<IService1, SomeService1>(); 
container.RegisterType<IService2, SomeService2>(); 

Điều này không là nói với container "bất cứ khi nào có một sự phụ thuộc của loại IService1, mới lên một đối tượng mới của loại SomeService1 và nhường lời rằng" và tương tự cho IService2 .

Vì vậy, tiếp theo, bạn cần phải thông báo cho bộ chứa những gì cần làm về ICustomerService. Trong hầu hết các tính tổng quát, bạn muốn làm điều này:

container.RegisterType<ICustomerService, CustomerService>(
    // Note, don't need to explicitly say transient, that's the default 
    new InjectionConstructor(new ResolvedParameter<IService1>(), 
     new ResolvedParameter<IService2>())); 

này cho container: khi giải quyết ICustomerService, mới lập một thể hiện của hậu mãi bằng cách sử dụng constructor mà mất IService1 và IService2. Để nhận các thông số đó, hãy gọi lại vào vùng chứa để giải quyết các loại đó.

Đây là chi tiết một chút và trường hợp thông thường, do đó, có một số phím tắt. Trước hết, bạn có thể vượt qua một đối tượng Loại thay vì làm ResolvedParameter mới, như vậy:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), typeof (IService2))); 

Là viết tắt khác, nếu hậu mãi chỉ có một nhà xây dựng, hoặc nếu bạn muốn gọi là một trong đó mất lớn nhất danh sách tham số, bạn có thể rời khỏi tệp InjectionConstructor hoàn toàn, vì đó là hàm tạo mà vùng chứa sẽ chọn trong trường hợp không có cấu hình khác.

container.RegisterType<ICustomerService, CustomerService>(); 

Biểu mẫu bạn đang sử dụng thường được sử dụng khi bạn muốn một giá trị cụ thể được truyền cho tham số hàm tạo thay vì giải quyết dịch vụ qua vùng chứa.

Để trả lời câu hỏi ban đầu của bạn - tốt, bạn không thể làm chính xác những gì bạn đã nói. Tham số hàm tạo cần một giá trị của một số loại. Bạn có thể đặt bất cứ điều gì khác trong đó bạn muốn, mặc dù - null thường hoạt động.

Lưu ý bạn cũng có thể kết hợp hai biểu mẫu. Ví dụ, nếu bạn muốn giải quyết IService1 và vượt qua null cho tham số IService2, làm điều này:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), null)); 

* EDIT *

Dựa trên những nhận xét dưới đây, những gì bạn thực sự muốn là tính năng khác - tên đăng ký.

Về cơ bản, bạn có hai triển khai IService1 và một trong IService2. Vì vậy, những gì bạn có thể làm là đăng ký cả hai, và sau đó nói với các container mà một trong những sử dụng.

Trước hết, phải đăng ký thi thứ hai, bạn cần phải cung cấp một cái tên rõ ràng:

container.RegisterType<IService1, OtherService1Impl>("other"); 

Sau đó, bạn có thể nói với các container để giải quyết IService1 nhưng sử dụng tên. Đây là lý do chính mà loại ResolvedParameter tồn tại. Vì bạn chỉ muốn mặc định cho IService2, bạn có thể sử dụng typeof() làm viết tắt. Bạn vẫn cần phải xác định cả hai loại cho các tham số, nhưng bạn không cần một giá trị cụ thể. Nếu mà làm cho bất kỳ ý nghĩa.

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new ResolvedParameter<IService1>("other"), typeof(IService2)); 

Điều đó sẽ làm những gì bạn cần.

+1

Chris, cảm ơn. Nhưng vấn đề là tôi có hai triển khai IService1. Hãy đặt tên cho chúng là Service1Impl1 và Service1Impl2. Cả hai đều được sử dụng trong giải pháp. CustomerManager nên sử dụng Service1Impl1 và OrderManager nên sử dụng Service1Impl2. Đó là lý do tại sao tôi cần phải chỉ định Service1Impl1 làm tham số đầu tiên của CustomerService. Nhưng tôi không muốn chỉ định tham số thứ hai vì chỉ có một triển khai được sử dụng của IService2 và nó đã được đăng ký bằng cách sử dụng mã nguồn sau: container.RegisterType (); –

+0

Tôi sẽ chỉnh sửa câu trả lời của mình, nó sẽ yêu cầu chi tiết hơn một chút so với tôi có thể phù hợp trong nhận xét. –

+1

Câu trả lời tuyệt vời Chris. –

11

Là một thay thế của Chris Tavares' câu trả lời, bạn có thể cho container giải quyết chỉ tham số thứ hai:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new SomeService1(), new ResolvedParameter<IService2>()); 
2

Bạn có thể sử dụng các thùng chứa phân cấp. Đăng ký triển khai chung trong vùng chứa cha, cá thể này sẽ giải quyết nếu được giải quyết thông qua thùng chứa chính. Sau đó tạo thùng chứa con và đăng ký triển khai thay thế trong vùng chứa con. Việc triển khai này sẽ giải quyết Nếu được giải quyết thông qua vùng chứa con, nghĩa là đăng ký trong vùng chứa con sẽ ghi đè đăng ký trong vùng chứa chính.

Dưới đây là ví dụ:

public interface IService {} 

public interface IOtherService {} 

// Standard implementation of IService 
public class StandardService : IService {} 

// Alternative implementaion of IService 
public class SpecialService : IService {} 

public class OtherService : IOtherService {} 

public class Consumer 
{ 
    public Consumer(IService service, IOtherService otherService) 
    {} 
} 

private void Test() 
{ 
    IUnityContainer parent = new UnityContainer() 
     .RegisterType<IService, StandardService>() 
     .RegisterType<IOtherService, OtherService>(); 

    // Here standardWay is initialized with StandardService as IService and OtherService as IOtherService 
    Consumer standardWay = parent.Resolve<Consumer>(); 

    // We construct child container and override IService registration 
    IUnityContainer child = parent.CreateChildContainer() 
     .RegisterType<IService, SpecialService>(); 

    // And here specialWay is initialized with SpecialService as IService and still OtherService as IOtherService 
    Consumer specialWay = child.Resolve<Consumer>(); 

    // Profit! 
} 

Xin lưu ý rằng việc sử dụng các thùng chứa hệ thống phân cấp, bạn có thể không biết gì về số lượng các thông số trong constructor, và đó là tuyệt vời, bởi vì chừng nào bạn đang bị ràng buộc để các thông số đếm và loại của họ, bạn không thể sử dụng toàn bộ sức mạnh của IoC. Bạn càng biết càng ít càng tốt.

6

Chris Tavares đưa ra câu trả lời hay với nhiều thông tin.

Nếu bạn có nhiều tham số để tiêm thường thì đây là các giao diện hoặc các trường hợp có thể được giải quyết bằng Unity (sử dụng các kỹ thuật differnet). Nhưng điều gì sẽ xảy ra nếu bạn chỉ muốn cung cấp một Thông số không thể được giải quyết tự động, ví dụ: một chuỗi cho một tên tập tin?

Bây giờ, bạn phải cung cấp tất cả typeof(IMyProvider) và một chuỗi hoặc ví dụ.Nhưng phải trung thực chỉ cung cấp các loại có thể được thực hiện bởi Unity, bởi vì Unity đã là một chiến lược để chọn ctor tốt nhất.

Vì vậy, tôi mã hóa một sự thay thế cho InjectionConstructor:

using System; 
using System.Collections.Generic; 
using System.Diagnostics.CodeAnalysis; 
using System.Globalization; 
using System.Linq; 
using System.Reflection; 
using Microsoft.Practices.ObjectBuilder2; 
using Microsoft.Practices.Unity; 
using Microsoft.Practices.Unity.ObjectBuilder; 
using Microsoft.Practices.Unity.Utility; 

namespace Microsoft.Practices.Unity 
{ 
    /// <summary> 
    /// A class that holds the collection of information for a constructor, 
    /// so that the container can be configured to call this constructor. 
    /// This Class is similar to InjectionConstructor, but you need not provide 
    /// all Parameters, just the ones you want to override or which cannot be resolved automatically 
    /// The given params are used in given order if type matches 
    /// </summary> 
    public class InjectionConstructorRelaxed : InjectionMember 
    { 
     private List<InjectionParameterValue> _parameterValues; 

     /// <summary> 
     /// Create a new instance of <see cref="InjectionConstructor"/> that looks 
     /// for a constructor with the given set of parameters. 
     /// </summary> 
     /// <param name="parameterValues">The values for the parameters, that will 
     /// be converted to <see cref="InjectionParameterValue"/> objects.</param> 
     public InjectionConstructorRelaxed(params object[] parameterValues) 
     { 
      _parameterValues = InjectionParameterValue.ToParameters(parameterValues).ToList(); 
     } 

     /// <summary> 
     /// Add policies to the <paramref name="policies"/> to configure the 
     /// container to call this constructor with the appropriate parameter values. 
     /// </summary> 
     /// <param name="serviceType">Interface registered, ignored in this implementation.</param> 
     /// <param name="implementationType">Type to register.</param> 
     /// <param name="name">Name used to resolve the type object.</param> 
     /// <param name="policies">Policy list to add policies to.</param> 
     public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies) 
     { 
      ConstructorInfo ctor = FindExactMatchingConstructor(implementationType); 
      if (ctor == null) 
      { 
       //if exact matching ctor not found, use the longest one and try to adjust the parameters. 
       //use given Params if type matches otherwise use the type to advise Unity to resolve later 
       ctor = FindLongestConstructor(implementationType); 
       if (ctor != null) 
       { 
        //adjust parameters 
        var newParams = new List<InjectionParameterValue>(); 
        foreach (var parameter in ctor.GetParameters()) 
        { 
         var injectionParameterValue = 
          _parameterValues.FirstOrDefault(value => value.MatchesType(parameter.ParameterType)); 
         if (injectionParameterValue != null) 
         { 
          newParams.Add(injectionParameterValue); 
          _parameterValues.Remove(injectionParameterValue); 
         } 
         else 
          newParams.Add(InjectionParameterValue.ToParameter(parameter.ParameterType)); 
        } 
        _parameterValues = newParams; 
       } 
       else 
       { 
        throw new InvalidOperationException(
         string.Format(
          CultureInfo.CurrentCulture, 
          "No constructor found for type {0}.", 
          implementationType.GetTypeInfo().Name)); 
       } 
      } 
      policies.Set<IConstructorSelectorPolicy>(
       new SpecifiedConstructorSelectorPolicy(ctor, _parameterValues.ToArray()), 
       new NamedTypeBuildKey(implementationType, name)); 
     } 



     private ConstructorInfo FindExactMatchingConstructor(Type typeToCreate) 
     { 
      var matcher = new ParameterMatcher(_parameterValues); 
      var typeToCreateReflector = new ReflectionHelper(typeToCreate); 

      foreach (ConstructorInfo ctor in typeToCreateReflector.InstanceConstructors) 
      { 
       if (matcher.Matches(ctor.GetParameters())) 
       { 
        return ctor; 
       } 
      } 

      return null; 
     } 

     private static ConstructorInfo FindLongestConstructor(Type typeToConstruct) 
     { 
      ReflectionHelper typeToConstructReflector = new ReflectionHelper(typeToConstruct); 

      ConstructorInfo[] constructors = typeToConstructReflector.InstanceConstructors.ToArray(); 
      Array.Sort(constructors, new ConstructorLengthComparer()); 

      switch (constructors.Length) 
      { 
       case 0: 
        return null; 

       case 1: 
        return constructors[0]; 

       default: 
        int paramLength = constructors[0].GetParameters().Length; 
        if (constructors[1].GetParameters().Length == paramLength) 
        { 
         throw new InvalidOperationException(
          string.Format(
           CultureInfo.CurrentCulture, 
           "The type {0} has multiple constructors of length {1}. Unable to disambiguate.", 
           typeToConstruct.GetTypeInfo().Name, 
           paramLength)); 
        } 
        return constructors[0]; 
      } 
     } 
     private class ConstructorLengthComparer : IComparer<ConstructorInfo> 
     { 
      /// <summary> 
      /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. 
      /// </summary> 
      /// <param name="y">The second object to compare.</param> 
      /// <param name="x">The first object to compare.</param> 
      /// <returns> 
      /// Value Condition Less than zero is less than y. Zero equals y. Greater than zero is greater than y. 
      /// </returns> 
      [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class")] 
      public int Compare(ConstructorInfo x, ConstructorInfo y) 
      { 
       Guard.ArgumentNotNull(x, "x"); 
       Guard.ArgumentNotNull(y, "y"); 

       return y.GetParameters().Length - x.GetParameters().Length; 
      } 
     } 
    } 
} 

Cách sử dụng:

container.RegisterType(new TransientLifetimeManager(), new InjectionConstructorRelaxed(
    new SomeService1("with special options") 
    //, new SomeService2() //not needed, normal unity resolving used 
    //equivalent to: , typeof(SomeService2) 
    )); 
Các vấn đề liên quan