Adakah yang bisa menjelaskan Microsoft Unity?

157

Saya telah membaca artikel di MSDN tentang Persatuan (Injeksi Ketergantungan, Pembalikan Kontrol), tapi saya pikir saya perlu dijelaskan dalam istilah sederhana (atau contoh sederhana). Saya akrab dengan pola MVPC (kami menggunakannya di sini), tapi saya belum bisa memahami hal Unity ini, dan saya pikir ini adalah langkah selanjutnya dalam desain aplikasi kami.

Ryan Abbott
sumber
12
Saya suka bagaimana ini memiliki nama yang sama dengan "Unity" jadi ketika saya mencari hal-hal Unity Game Engine saya melihat teknologi lama ini, desah. Semua nama band yang bagus diambil, kurasa.
Tom Schulz
2
@ tom-schulz Teknologi lama? nuget.org/packages/Unity - terakhir diperbarui 5 hari yang lalu.
Roger Willcocks

Jawaban:

174

Unity hanyalah "wadah" IOC. Google StructureMap dan coba saja. Sedikit lebih mudah untuk grok, saya pikir, ketika hal-hal IoC baru bagi Anda.

Pada dasarnya, jika Anda memahami IoC maka Anda memahami bahwa apa yang Anda lakukan adalah membalikkan kontrol ketika sebuah objek dibuat.

Tanpa IOC:

public class MyClass
{
   IMyService _myService; 

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

Dengan wadah IoC:

public class MyClass
{
   IMyService _myService; 

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

Tanpa IoC, kelas Anda yang bergantung pada IMyService harus membuat versi konkret layanan untuk digunakan. Dan itu buruk karena sejumlah alasan (Anda telah menggabungkan kelas Anda ke versi konkret khusus dari IMyService, Anda tidak dapat mengujinya dengan mudah, Anda tidak dapat mengubahnya dengan mudah, dll.)

Dengan wadah IoC Anda "mengkonfigurasi" wadah untuk menyelesaikan dependensi tersebut untuk Anda. Jadi dengan skema injeksi berbasis konstruktor, Anda cukup meneruskan antarmuka ke ketergantungan IMyService ke dalam konstruktor. Saat Anda membuat MyClass dengan wadah Anda, wadah Anda akan menyelesaikan ketergantungan IMyService untuk Anda.

Menggunakan StructureMap, mengonfigurasi wadah terlihat seperti ini:

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

Jadi yang Anda lakukan adalah memberi tahu kontainer, "Ketika seseorang meminta IMyService, beri mereka salinan SomeConcreteService." Dan Anda juga telah menentukan bahwa ketika seseorang meminta MyClass, mereka mendapatkan MyClass konkret.

Itu semua benar-benar wadah IoC. Mereka dapat melakukan lebih banyak, tetapi itulah dorongannya - mereka menyelesaikan dependensi untuk Anda, jadi Anda tidak perlu (dan Anda tidak harus menggunakan kata kunci "baru" di seluruh kode Anda).

Langkah terakhir: ketika Anda membuat MyClass Anda, Anda akan melakukan ini:

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

Semoga itu bisa membantu. Silakan kirim email kepada saya.

Chris Holmes
sumber
2
Jadi itu seperti pabrik, saya kira? Jika saya mengikuti ini dengan benar, tidakkah Anda akan menggunakan <IMyClass> alih-alih <MyClass> dalam contoh terakhir? jadi itu akan var myClass = ObjectFactory.GetInstance <IMyClass> ()? Terima kasih atas bantuan Anda, ini adalah awal yang baik untuk saya!
Ryan Abbott
3
Di satu sisi, itu seperti sebuah pabrik, ya. Pabrik utama untuk aplikasi Anda. Tetapi dapat dikonfigurasi untuk mengembalikan banyak jenis yang berbeda, termasuk lajang. Adapun antarmuka ke MyClass - jika itu objek bisnis, saya tidak akan mengekstrak antarmuka. Untuk yang lainnya, saya biasanya akan.
Chris Holmes
bagaimana jika Anda hanya memanggil ObjectFactory.GetInstance <MyClass> (); dan Anda tidak Mengkonfigurasi SomeConcreteClass? Apakah Anda akan mendapatkan kesalahan dalam kasus itu?
RayLoveless
1
@ Ray: Itu tergantung pada wadah. Beberapa kontainer ditulis sehingga, secara default, mereka menggunakan konvensi penamaan, sehingga jika kelas bernama MyClass dan antarmuka bernama IMyInterface, wadah akan secara otomatis mengkonfigurasi kelas itu untuk antarmuka itu. Jadi dalam hal itu, jika Anda tidak mengkonfigurasinya secara manual, "konvensi" default dari wadah mengambilnya. Namun, jika kelas dan antarmuka Anda tidak mengikuti konvensi dan Anda tidak mengkonfigurasi wadah untuk kelas itu, maka ya, Anda mendapatkan kesalahan saat runtime.
Chris Holmes
1
@saravanan Saya pikir StructureMap melakukan konvensi berbasis nama sekarang. Saya tidak yakin; kami belum menggunakannya dalam waktu yang lama (saya menulis yang khusus untuk bisnis kami; menggunakan konvensi dengan nama yang sama untuk antarmuka dan kelas).
Chris Holmes
39

Saya hanya menonton 30 menit Unity Dependency Injection IoC Screencast oleh David Hayden dan merasa itu adalah penjelasan yang baik dengan contoh-contoh. Berikut cuplikan dari catatan acara:

Screencast menunjukkan beberapa penggunaan umum dari Unity IoC, seperti:

  • Membuat Jenis Tidak Dalam Wadah
  • Mendaftarkan dan Menyelesaikan Pemetaan Type
  • Mendaftarkan dan Memecahkan TypeMappings Bernama
  • Singletons, LifetimeManagers, dan ContainerControlledLifetimeManager
  • Mendaftarkan Instansi yang Ada
  • Menyuntikkan Ketergantungan ke Mesin Virtual yang Ada
  • Mengisi UnityContainer melalui App.config / Web.config
  • Menentukan Dependensi melalui API Injeksi sebagai lawan dari Atribut Dependensi
  • Menggunakan Wadah Bersarang (Orangtua-Anak)
Kevin Hakanson
sumber
32

Unity adalah perpustakaan seperti banyak perpustakaan lain yang memungkinkan Anda untuk mendapatkan instance dari tipe yang diminta tanpa harus membuatnya sendiri. Jadi diberikan.

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

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

Anda akan menggunakan perpustakaan seperti Unity untuk mendaftarkan Kalkulator yang akan dikembalikan ketika jenis ICalculator diminta alias IOC (Inversion of Control) (contoh ini teoretis, tidak benar secara teknis).

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

Jadi sekarang ketika Anda ingin contoh ICalculator Anda hanya ...

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

Pustaka IoC biasanya dapat dikonfigurasi untuk memegang singleton atau membuat instance baru setiap kali Anda menyelesaikan suatu tipe.

Sekarang katakanlah Anda memiliki kelas yang bergantung pada ICalculator untuk hadir, Anda dapat memiliki ..

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

    private ICalculator _calc;
}

Dan Anda bisa mengatur pustaka untuk menyuntikkan objek ke konstruktor saat itu dibuat.

Jadi DI atau Dependency Injection berarti menyuntikkan benda apa pun yang mungkin diperlukan orang lain.

Chad Moran
sumber
harus ICalculator calc = IoCLibrary.Resolve <ICalculator> ();
Shukhrat Raimov
10

Unity adalah IOC. Maksud dari IOC adalah untuk abstrak kabel ketergantungan antara tipe di luar tipe itu sendiri. Ini memiliki beberapa keunggulan. Pertama-tama, ini dilakukan secara terpusat yang berarti Anda tidak perlu mengubah banyak kode ketika dependensi berubah (yang mungkin merupakan kasus untuk unit test).

Selain itu, jika pengkabelan dilakukan menggunakan data konfigurasi alih-alih kode, Anda benar-benar dapat mengatur ulang dependensi setelah penyebaran dan dengan demikian mengubah perilaku aplikasi tanpa mengubah kode.

Brian Rasmussen
sumber
5

MSDN memiliki Panduan Pengembang untuk Injeksi Ketergantungan Menggunakan Unity yang mungkin berguna.

Panduan Pengembang dimulai dengan dasar-dasar apa itu injeksi dependensi, dan berlanjut dengan contoh bagaimana menggunakan Unity untuk injeksi dependensi. Pada Februari 2014, Panduan Pengembang mencakup Unity 3.0, yang dirilis pada April 2013.

Simon Tewsi
sumber
1

Saya membahas sebagian besar contoh Ketergantungan Injeksi di 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";
}

Dalam mekanisme Injeksi Otomatis DIAutoV2Controller.cs digunakan

[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;
    }
}

Dalam DIV2Controller.cs semuanya akan disuntikkan dari kelas Dependency Configuration Resolver

[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;
    }
}

Mengkonfigurasi Penyelesai Ketergantungan

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")));
}
Narottam Goyal
sumber
Ini bukan jawaban yang sangat berguna karena sejumlah alasan. Ini adalah contoh rumit yang tidak perlu yang memiliki terlalu banyak kode untuk berguna dalam menawarkan penjelasan sederhana tentang IOC. Selain itu, kode tidak didokumentasikan dengan jelas di tempat-tempat di mana Anda benar-benar membutuhkannya.
Dan Atkinson