【问题标题】:Can someone explain Microsoft Unity?有人可以解释 Microsoft Unity 吗?
【发布时间】:2009-03-03 22:54:15
【问题描述】:

我一直在阅读 MSDN 上关于 Unity(依赖注入、控制反转)的文章,但我认为我需要用简单的术语(或简单的示例)对其进行解释。我熟悉 MVPC 模式(我们在这里使用它),但我还不能真正掌握 Unity 的东西,我认为这是我们应用程序设计的下一步。

【问题讨论】:

  • 我喜欢它与“Unity”同名的方式,所以当我搜索 Unity 游戏引擎的东西时,我看到了这个老技术,叹息。我猜所有好乐队的名字都被取走了。
  • @tom-schulz 老技术? nuget.org/packages/Unity - 最后一次更新是 5 天前。

标签: c# dependency-injection inversion-of-control unity-container


【解决方案1】:

Unity 只是一个 IoC“容器”。谷歌 StructureMap 并尝试一下。我认为,当 IoC 对你来说是新的东西时,更容易理解。

基本上,如果您了解 IoC,那么您就会明白您正在做的事情是在创建对象时反转控件。

没有 IoC:

public class MyClass
{
   IMyService _myService; 

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

使用 IoC 容器:

public class MyClass
{
   IMyService _myService; 

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

如果没有 IoC,则依赖 IMyService 的类必须更新要使用的服务的具体版本。这很糟糕,原因有很多(你已经将你的类耦合到 IMyService 的特定具体版本,你不能轻易地对其进行单元测试,你不能轻易地改变它等等)

使用 IoC 容器,您可以“配置”容器来为您解决这些依赖关系。因此,使用基于构造函数的注入方案,您只需将 IMyService 依赖项的接口传递给构造函数即可。当您使用容器创建 MyClass 时,您的容器将为您解析 IMyService 依赖项。

使用 StructureMap,配置容器如下所示:

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

所以你所做的就是告诉容器,“当有人请求 IMyService 时,给他们一份 SomeConcreteService 的副本。”而且您还指定当有人请求 MyClass 时,他们会得到一个具体的 MyClass。

这就是 IoC 容器的全部功能。他们可以做得更多,但这就是它的主旨——他们为您解决依赖关系,因此您不必这样做(而且您不必在整个代码中使用“new”关键字)。

最后一步:当你创建你的 MyClass 时,你会这样做:

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

希望对您有所帮助。随时给我发电子邮件。

【讨论】:

  • 所以我想它就像一个工厂?如果我正确地遵循了这一点,您不会在最后一个示例中使用 而不是 吗?所以它会是 var myClass = ObjectFactory.GetInstance()?感谢您的帮助,这对我来说是一个好的开始!
  • 在某种程度上,它就像一个工厂,是的。适合您应用的主工厂。但它可以配置为返回许多不同的类型,包括单例。至于 MyClass 的接口——如果它是一个业务对象,我不会提取接口。对于其他一切,我通常会。
  • 如果你只调用 ObjectFactory.GetInstance();你没有配置 SomeConcreteClass?在这种情况下你会得到错误吗?
  • @Ray:这取决于容器。某些容器的编写默认情况下,它们使用命名约定,这样如果一个类被命名为 MyClass 并且接口被命名为 IMyInterface,容器将自动为该接口配置该类。所以在这种情况下,如果你不手动配置它,容器的默认“约定”无论如何都会选择它。但是,如果您的类和接口不遵循约定并且您没有为该类配置容器,那么是的,您会在运行时收到错误。
  • @saravanan 我认为 StructureMap 现在做了一个基于名称的约定。我不确定;我们已经很久没有使用它了(我为我们的业务编写了一个自定义的;它对接口和类使用同名约定)。
【解决方案2】:

我刚刚观看了 David Hayden 的 30 分钟 Unity Dependency Injection IoC 截屏视频,觉得这是一个很好的示例解释。这是演出笔记中的一个 sn-p:

截屏视频展示了 Unity IoC 的几种常见用法,例如:

  • 创建不在容器中的类型
  • 注册和解析类型映射
  • 注册和解析命名类型映射
  • 单例、LifetimeManager 和 ContainerControlledLifetimeManager
  • 注册现有实例
  • 将依赖项注入现有实例
  • 通过 App.config / Web.config 填充 UnityContainer
  • 通过注入 API 指定依赖项,而不是依赖项属性
  • 使用嵌套(父子)容器

【讨论】:

    【解决方案3】:

    Unity 是一个与许多其他库一样的库,它允许您获取所请求类型的实例,而无需自己创建它。所以给定了。

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

    您可以使用 Unity 之类的库来注册 Calculator,以便在请求 ICalculator 类型时返回,即 IoC(控制反转)(此示例是理论上的,在技术上不正确)。

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

    所以现在当您想要一个 ICalculator 实例时,您只需...

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

    IoC 库通常可以配置为在每次解析类型时保存单例或创建新实例。

    现在假设你有一个依赖于 ICalculator 的类,你可以拥有..

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

    您可以设置库以在创建对象时将其注入构造函数。

    所以 DI 或依赖注入意味着注入另一个可能需要的任何对象。

    【讨论】:

    • 应该是 ICalculator calc = IoCLibrary.Resolve();
    【解决方案4】:

    Unity 是一个 IoC。 IoC 的重点是在类型本身之外抽象类型之间的依赖关系。这有几个优点。首先,它是集中完成的,这意味着当依赖关系发生变化时,您不必更改大量代码(单元测试可能就是这种情况)。

    此外,如果使用配置数据而不是代码完成连接,您实际上可以在部署后重新连接依赖项,从而在不更改代码的情况下更改应用程序的行为。

    【讨论】:

    • 它有几个优点和several disadvantages。甜的。我要感谢你们那个时代的所有开发人员让年轻一代处理这些事情:) 超级有帮助
    【解决方案5】:

    MSDN 有一个可能有用的Developer's Guide to Dependency Injection Using Unity

    开发者指南从什么是依赖注入的基础开始,然后是如何使用 Unity 进行依赖注入的示例。截至 2014 年 2 月,开发人员指南涵盖了 2013 年 4 月发布的 Unity 3.0。

    【讨论】:

      【解决方案6】:

      我将介绍 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";
      }
      

      在DIAutoV2Controller.cs中使用了自动注入机制

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

      在 DIV2Controller.cs 中,所有内容都将从依赖配置解析器类中注入

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

      配置依赖解析器

      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")));
      }
      

      【讨论】:

      • 这不是一个特别有用的答案,原因有很多。这是一个不必要的复杂示例,其中包含太多代码,无法用于提供 IOC 的简单解释。除此之外,代码并没有在您实际需要的地方清楚地记录。
      猜你喜欢
      • 2012-05-29
      • 2010-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多