Ai đó có thể giải thích Microsoft Unity không?


157

Tôi đã đọc các bài viết trên MSDN về Unity (Dependency Injection, Inversion of Control), nhưng tôi nghĩ rằng tôi cần nó được giải thích bằng các thuật ngữ đơn giản (hoặc ví dụ đơn giản). Tôi quen thuộc với mẫu MVPC (chúng tôi sử dụng nó ở đây), nhưng tôi chưa thể thực sự nắm bắt được điều Unity này và tôi nghĩ đó là bước tiếp theo trong thiết kế ứng dụng của chúng tôi.


12
Tôi thích cách nó có cùng tên với "Unity" vì vậy khi tôi tìm kiếm công cụ Unity Game Engine, tôi thấy công nghệ cũ này, thở dài. Tất cả các tên ban nhạc tốt được lấy, tôi đoán.
Tom Schulz

2
@ tom-schulz Công nghệ cũ? nuget.org/packages/Unity - cập nhật lần cuối 5 ngày trước.
Roger Willcocks

Câu trả lời:


174

Unity chỉ là một "container" IoC. Google StructMap và thử nó thay thế. Tôi nghĩ dễ dàng hơn một chút, khi các công cụ IoC mới đối với bạn.

Về cơ bản, nếu bạn hiểu IoC thì bạn hiểu rằng những gì bạn đang làm là đảo ngược điều khiển khi một đối tượng được tạo.

Không có IoC:

public class MyClass
{
   IMyService _myService; 

   public MyClass()
   {
      _myService = new SomeConcreteService();    
   }
}

Với container IoC:

public class MyClass
{
   IMyService _myService; 

   public MyClass(IMyService myService)
   {
      _myService = myService;    
   }
}

Không có IoC, lớp của bạn dựa trên IMyService phải tạo ra một phiên bản cụ thể của dịch vụ để sử dụng. Và điều đó thật tệ vì một số lý do (bạn đã kết hợp lớp của mình với một phiên bản cụ thể của IMyService, bạn không thể kiểm tra đơn vị một cách dễ dàng, bạn không thể thay đổi dễ dàng, v.v.)

Với bộ chứa IoC, bạn "định cấu hình" bộ chứa để giải quyết các phụ thuộc đó cho bạn. Vì vậy, với sơ đồ tiêm dựa trên hàm tạo, bạn chỉ cần chuyển giao diện cho phụ thuộc IMyService vào hàm tạo. Khi bạn tạo MyClass bằng bộ chứa của mình, bộ chứa của bạn sẽ giải quyết phụ thuộc IMyService cho bạn.

Sử dụng StructMap, cấu hình bộ chứa trông như thế này:

StructureMapConfiguration.ForRequestedType<MyClass>().TheDefaultIsConcreteType<MyClass>();
StructureMapConfiguration.ForRequestedType<IMyService>().TheDefaultIsConcreteType<SomeConcreteService>();

Vì vậy, những gì bạn đã làm được nói với bộ chứa, "Khi ai đó yêu cầu IMyService, hãy đưa cho họ một bản sao của dịch vụ someConcittleService." Và bạn cũng đã xác định rằng khi ai đó yêu cầu MyClass, họ sẽ nhận được MyClass cụ thể.

Đó là tất cả một container IoC thực sự làm. Họ có thể làm nhiều hơn, nhưng đó là lực đẩy của nó - họ giải quyết các phụ thuộc cho bạn, vì vậy bạn không phải (và bạn không phải sử dụng từ khóa "mới" trong toàn bộ mã của mình).

Bước cuối cùng: khi bạn tạo MyClass, bạn sẽ làm điều này:

var myClass = ObjectFactory.GetInstance<MyClass>();

Mong rằng sẽ giúp. Gửi thư điện tử cho tôi bất cứ khi nào bạn muốn.


2
Vì vậy, nó giống như một nhà máy, tôi cho rằng? Nếu tôi đang theo dõi điều này một cách chính xác, bạn sẽ không sử dụng <IMyClass> thay vì <MyClass> trong ví dụ cuối cùng chứ? vì vậy nó sẽ là var myClass = ObjectFactory.GetInstance <IMyClass> ()? Cảm ơn sự giúp đỡ của bạn, đây là một khởi đầu tốt cho tôi!
Ryan Abbott

3
Theo một cách nào đó, nó giống như một nhà máy, vâng. Một nhà máy chính cho ứng dụng của bạn. Nhưng nó có thể được cấu hình để trả về nhiều loại khác nhau, bao gồm cả singletons. Đối với giao diện cho MyClass - nếu đó là đối tượng kinh doanh, tôi sẽ không trích xuất giao diện. Đối với mọi thứ khác, tôi thường sẽ.
Chris Holmes

nếu bạn chỉ gọi ObjectFactory.GetInstance <MyClass> (); và bạn đã không cấu hình someConcittleClass? Bạn sẽ nhận được và lỗi trong trường hợp đó?
RayLovless

1
@Ray: Nó phụ thuộc vào container. Một số container được viết để theo mặc định, chúng sử dụng quy ước đặt tên, sao cho nếu một lớp có tên MyClass và giao diện được đặt tên là IMyInterface, thì container sẽ tự động định cấu hình lớp đó cho giao diện đó. Vì vậy, trong trường hợp đó, nếu bạn không cấu hình thủ công, "quy ước" mặc định của container sẽ tiếp tục. Tuy nhiên, nếu lớp và giao diện của bạn không tuân theo quy ước và bạn không định cấu hình vùng chứa cho lớp đó, thì có, bạn sẽ gặp lỗi khi chạy.
Chris Holmes

1
@saravanan Tôi nghĩ rằng StructMap hiện có một quy ước dựa trên tên. Tôi không chắc chắn; chúng tôi đã không sử dụng nó trong một thời gian dài (tôi đã viết một tùy chỉnh cho doanh nghiệp của mình; nó sử dụng quy ước cùng tên cho các giao diện và các lớp).
Chris Holmes

39

Tôi chỉ xem 30 phút Unity Dependency Injection IoC Screencast của David Hayden và cảm thấy đó là một lời giải thích tốt với các ví dụ. Đây là một đoạn trong ghi chú của chương trình:

Bản ghi hình cho thấy một số cách sử dụng phổ biến của Unity IoC, chẳng hạn như:

  • Tạo các loại không có trong container
  • Đăng ký và giải quyết TypeMappings
  • Đăng ký và giải quyết TypeMappings được đặt tên
  • Singletons, LifetimeManager và ContainerControlledLifetimeManager
  • Đăng ký trường hợp hiện có
  • Tiêm phụ thuộc vào các trường hợp hiện có
  • Phổ biến UnityContainer qua App.config / Web.config
  • Chỉ định phụ thuộc thông qua API tiêm thay vì thuộc tính phụ thuộc
  • Sử dụng Container lồng nhau (Cha mẹ-Con)

32

Unity là một thư viện giống như nhiều thư viện khác cho phép bạn lấy một thể hiện của loại được yêu cầu mà không phải tự tạo. Vì vậy, đưa ra.

public interface ICalculator
{
    void Add(int a, int b);
}

public class Calculator : ICalculator
{
    public void Add(int a, int b)
    {
        return a + b;
    }
}

Bạn sẽ sử dụng một thư viện như Unity để đăng ký Máy tính để được trả về khi loại ICalculator được yêu cầu còn gọi là IoC (Inversion of Control) (ví dụ này là lý thuyết, không đúng về mặt kỹ thuật).

IoCLlibrary.Register<ICalculator>.Return<Calculator>();

Vì vậy, bây giờ khi bạn muốn có một phiên bản của ICalculator, bạn chỉ cần ...

Calculator calc = IoCLibrary.Resolve<ICalculator>();

Các thư viện IoC thường có thể được cấu hình để giữ một singleton hoặc tạo một thể hiện mới mỗi khi bạn giải quyết một loại.

Bây giờ hãy giả sử bạn có một lớp học dựa trên ICalculator để có mặt bạn có thể có ..

public class BankingSystem
{
    public BankingSystem(ICalculator calc)
    {
        _calc = calc;
    }

    private ICalculator _calc;
}

Và bạn có thể thiết lập thư viện để đưa một đối tượng vào hàm tạo khi nó được tạo.

Vì vậy, DI hoặc Dependency Injection có nghĩa là tiêm bất kỳ đối tượng nào khác có thể yêu cầu.


nên là ICalculator calc = IoCL Library.Resolve <ICalculator> ();
Shukhrat Raimov


10

Đoàn kết là một IoC. Quan điểm của IoC là trừu tượng hóa hệ thống phụ thuộc giữa các loại bên ngoài các loại. Điều này có một vài lợi thế. Trước hết, nó được thực hiện tập trung, có nghĩa là bạn không phải thay đổi nhiều mã khi phụ thuộc thay đổi (có thể là trường hợp đối với các bài kiểm tra đơn vị).

Hơn nữa, nếu việc nối dây được thực hiện bằng cách sử dụng dữ liệu cấu hình thay vì mã, bạn thực sự có thể tua lại các phụ thuộc sau khi triển khai và do đó thay đổi hành vi của ứng dụng mà không thay đổi mã.


5

MSDN có Hướng dẫn của Nhà phát triển về Tiêm phụ thuộc bằng cách sử dụng Unity có thể hữu ích.

Hướng dẫn của Nhà phát triển bắt đầu với những điều cơ bản về tiêm phụ thuộc là gì và tiếp tục với các ví dụ về cách sử dụng Unity cho tiêm phụ thuộc. Kể từ tháng 2 năm 2014, Hướng dẫn dành cho nhà phát triển bao gồm Unity 3.0, được phát hành vào tháng 4 năm 2013.


1

Tôi đang trình bày hầu hết các ví dụ về Dependency Injection trong ASP.NET Web API 2

public interface IShape
{
    string Name { get; set; }
}

public class NoShape : IShape
{
    public string Name { get; set; } = "I have No Shape";
}

public class Circle : IShape
{
    public string Name { get; set; } = "Circle";
}

public class Rectangle : IShape
{
    public Rectangle(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; } = "Rectangle";
}

Trong cơ chế tự động tiêm DIAutoV2Controll.cs được sử dụng

[RoutePrefix("api/v2/DIAutoExample")]
public class DIAutoV2Controller : ApiController
{
    private string ConstructorInjected;
    private string MethodInjected1;
    private string MethodInjected2;
    private string MethodInjected3;

    [Dependency]
    public IShape NoShape { get; set; }

    [Dependency("Circle")]
    public IShape ShapeCircle { get; set; }

    [Dependency("Rectangle")]
    public IShape ShapeRectangle { get; set; }

    [Dependency("PiValueExample1")]
    public double PiValue { get; set; }

    [InjectionConstructor]
    public DIAutoV2Controller([Dependency("Circle")]IShape shape1, [Dependency("Rectangle")]IShape shape2, IShape shape3)
    {
        this.ConstructorInjected = shape1.Name + " & " + shape2.Name + " & " + shape3.Name;
    }

    [NonAction]
    [InjectionMethod]
    public void Initialize()
    {
        this.MethodInjected1 = "Default Initialize done";
    }

    [NonAction]
    [InjectionMethod]
    public void Initialize2([Dependency("Circle")]IShape shape1)
    {
        this.MethodInjected2 = shape1.Name;
    }

    [NonAction]
    [InjectionMethod]
    public void Initialize3(IShape shape1)
    {
        this.MethodInjected3 = shape1.Name;
    }

    [HttpGet]
    [Route("constructorinjection")]
    public string constructorinjection()
    {
        return "Constructor Injected: " + this.ConstructorInjected;
    }

    [HttpGet]
    [Route("GetNoShape")]
    public string GetNoShape()
    {
        return "Property Injected: " + this.NoShape.Name;
    }

    [HttpGet]
    [Route("GetShapeCircle")]
    public string GetShapeCircle()
    {
        return "Property Injected: " + this.ShapeCircle.Name;
    }

    [HttpGet]
    [Route("GetShapeRectangle")]
    public string GetShapeRectangle()
    {
        return "Property Injected: " + this.ShapeRectangle.Name;
    }

    [HttpGet]
    [Route("GetPiValue")]
    public string GetPiValue()
    {
        return "Property Injected: " + this.PiValue;
    }

    [HttpGet]
    [Route("MethodInjected1")]
    public string InjectionMethod1()
    {
        return "Method Injected: " + this.MethodInjected1;
    }

    [HttpGet]
    [Route("MethodInjected2")]
    public string InjectionMethod2()
    {
        return "Method Injected: " + this.MethodInjected2;
    }

    [HttpGet]
    [Route("MethodInjected3")]
    public string InjectionMethod3()
    {
        return "Method Injected: " + this.MethodInjected3;
    }
}

Trong DIV2Controll.cs, mọi thứ sẽ được chèn từ lớp Trình giải quyết cấu hình phụ thuộc

[RoutePrefix("api/v2/DIExample")]
public class DIV2Controller : ApiController
{
    private string ConstructorInjected;
    private string MethodInjected1;
    private string MethodInjected2;
    public string MyPropertyName { get; set; }
    public double PiValue1 { get; set; }
    public double PiValue2 { get; set; }
    public IShape Shape { get; set; }

    // MethodInjected
    [NonAction]
    public void Initialize()
    {
        this.MethodInjected1 = "Default Initialize done";
    }

    // MethodInjected
    [NonAction]
    public void Initialize2(string myproperty1, IShape shape1, string myproperty2, IShape shape2)
    {
        this.MethodInjected2 = myproperty1 + " & " + shape1.Name + " & " + myproperty2 + " & " + shape2.Name;
    }

    public DIV2Controller(string myproperty1, IShape shape1, string myproperty2, IShape shape2)
    {
        this.ConstructorInjected = myproperty1 + " & " + shape1.Name + " & " + myproperty2 + " & " + shape2.Name;
    }

    [HttpGet]
    [Route("constructorinjection")]
    public string constructorinjection()
    {
        return "Constructor Injected: " + this.ConstructorInjected;
    }

    [HttpGet]
    [Route("PropertyInjected")]
    public string InjectionProperty()
    {
        return "Property Injected: " + this.MyPropertyName;
    }

    [HttpGet]
    [Route("GetPiValue1")]
    public string GetPiValue1()
    {
        return "Property Injected: " + this.PiValue1;
    }

    [HttpGet]
    [Route("GetPiValue2")]
    public string GetPiValue2()
    {
        return "Property Injected: " + this.PiValue2;
    }

    [HttpGet]
    [Route("GetShape")]
    public string GetShape()
    {
        return "Property Injected: " + this.Shape.Name;
    }

    [HttpGet]
    [Route("MethodInjected1")]
    public string InjectionMethod1()
    {
        return "Method Injected: " + this.MethodInjected1;
    }

    [HttpGet]
    [Route("MethodInjected2")]
    public string InjectionMethod2()
    {
        return "Method Injected: " + this.MethodInjected2;
    }
}

Định cấu hình Bộ giải quyết phụ thuộc

public static void Register(HttpConfiguration config)
{
    var container = new UnityContainer();
    RegisterInterfaces(container);
    config.DependencyResolver = new UnityResolver(container);

    // Other Web API configuration not shown.
}

private static void RegisterInterfaces(UnityContainer container)
{
    var dbContext = new SchoolDbContext();
    // Registration with constructor injection
    container.RegisterType<IStudentRepository, StudentRepository>(new InjectionConstructor(dbContext));
    container.RegisterType<ICourseRepository, CourseRepository>(new InjectionConstructor(dbContext));

    // Set constant/default value of Pi = 3.141 
    container.RegisterInstance<double>("PiValueExample1", 3.141);
    container.RegisterInstance<double>("PiValueExample2", 3.14);

    // without a name
    container.RegisterInstance<IShape>(new NoShape());

    // with circle name
    container.RegisterType<IShape, Circle>("Circle", new InjectionProperty("Name", "I am Circle"));

    // with rectangle name
    container.RegisterType<IShape, Rectangle>("Rectangle", new InjectionConstructor("I am Rectangle"));

    // Complex type like Constructor, Property and method injection
    container.RegisterType<DIV2Controller, DIV2Controller>(
        new InjectionConstructor("Constructor Value1", container.Resolve<IShape>("Circle"), "Constructor Value2", container.Resolve<IShape>()),
        new InjectionMethod("Initialize"),
        new InjectionMethod("Initialize2", "Value1", container.Resolve<IShape>("Circle"), "Value2", container.Resolve<IShape>()),
        new InjectionProperty("MyPropertyName", "Property Value"),
        new InjectionProperty("PiValue1", container.Resolve<double>("PiValueExample1")),
        new InjectionProperty("Shape", container.Resolve<IShape>("Rectangle")),
        new InjectionProperty("PiValue2", container.Resolve<double>("PiValueExample2")));
}

Đây không phải là một câu trả lời đặc biệt hữu ích vì một số lý do. Đây là một ví dụ phức tạp không cần thiết có quá nhiều mã có ích trong việc đưa ra một lời giải thích đơn giản về IOC. Ngoài ra, mã không được ghi lại rõ ràng ở những nơi bạn thực sự cần nó.
Dan Atkinson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.