I've been reading the articles on MSDN about Unity (Dependency Injection, Inversion of Control), but I think I need it explained in simple terms (or simple examples). I'm familiar with the MVPC pattern (we use it here), but I just can't really grasp this Unity thing yet, and I think it's the next step in our application design.
Unity is just an IoC "container". Google StructureMap and try it out instead. A bit easier to grok, I think, when the IoC stuff is new to you.
Basically, if you understand IoC then you understand that what you're doing is inverting the control for when an object gets created.
Without IoC:
public class MyClass
{
IMyService _myService;
public MyClass()
{
_myService = new SomeConcreteService();
}
}
With IoC container:
public class MyClass
{
IMyService _myService;
public MyClass(IMyService myService)
{
_myService = myService;
}
}
Without IoC, your class that relies on the IMyService has to new-up a concrete version of the service to use. And that is bad for a number of reasons (you've coupled your class to a specific concrete version of the IMyService, you can't unit test it easily, you can't change it easily, etc.)
With an IoC container you "configure" the container to resolve those dependencies for you. So with a constructor-based injection scheme, you just pass the interface to the IMyService dependency into the constructor. When you create the MyClass with your container, your container will resolve the IMyService dependency for you.
Using StructureMap, configuring the container looks like this:
StructureMapConfiguration.ForRequestedType<MyClass>().TheDefaultIsConcreteType<MyClass>();
StructureMapConfiguration.ForRequestedType<IMyService>().TheDefaultIsConcreteType<SomeConcreteService>();
So what you've done is told the container, "When someone requests the IMyService, give them a copy of the SomeConcreteService." And you've also specified that when someone asks for a MyClass, they get a concrete MyClass.
That's all an IoC container really does. They can do more, but that's the thrust of it - they resolve dependencies for you, so you don't have to (and you don't have to use the "new" keyword throughout your code).
Final step: when you create your MyClass, you would do this:
var myClass = ObjectFactory.GetInstance<MyClass>();
I just watched the 30 minute Unity Dependency Injection IoC Screencast by David Hayden and felt that was a good explaination with examples. Here is a snippet from the show notes:
The screencast shows several common usages of the Unity IoC, such as:
- Creating Types Not In Container
- Registering and Resolving TypeMappings
- Registering and Resolving Named TypeMappings
- Singletons, LifetimeManagers, and the ContainerControlledLifetimeManager
- Registering Existing Instances
- Injecting Dependencies into Existing Instances
- Populating the UnityContainer via App.config / Web.config
- Specifying Dependencies via Injection API as opposed to Dependency Attributes
- Using Nested ( Parent-Child ) Containers
Unity is a library like many others that allows you to get an instance of a requested type without having to create it yourself. So given.
public interface ICalculator
{
void Add(int a, int b);
}
public class Calculator : ICalculator
{
public void Add(int a, int b)
{
return a + b;
}
}
You would use a library like Unity to register Calculator to be returned when the type ICalculator is requested aka IoC (Inversion of Control) (this example is theoretical, not technically correct).
IoCLlibrary.Register<ICalculator>.Return<Calculator>();
So now when you want an instance of an ICalculator you just...
Calculator calc = IoCLibrary.Resolve<ICalculator>();
IoC libraries can usually be configured to either hold a singleton or create a new instance every time you resolve a type.
Now let's say you have a class that relies on an ICalculator to be present you could have..
public class BankingSystem
{
public BankingSystem(ICalculator calc)
{
_calc = calc;
}
private ICalculator _calc;
}
And you can setup the library to inject a object into the constructor when it's created.
So DI or Dependency Injection means to inject any object another might require.
Unity is an IoC. The point of IoC is to abstract the wiring of dependencies between types outside of the types themselves. This has a couple of advantages. First of all, it is done centrally which means you don't have to change a lot of code when dependencies change (which may be the case for unit tests).
Furthermore, if the wiring is done using configuration data instead of code, you can actually rewire the dependencies after deployment and thus change the behavior of the application without changing the code.
MSDN has a Developer's Guide to Dependency Injection Using Unity that may be useful.
The Developer's Guide starts with the basics of what dependency injection is, and continues with examples of how to use Unity for dependency injection. As of the February 2014 the Developer's Guide covers Unity 3.0, which was released in April 2013.
I am covering most of the examples of Dependency Injection in 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";
}
In DIAutoV2Controller.cs Auto Injection mechanism is used
[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;
}
}
In DIV2Controller.cs everything will be injected from the Dependency Configuration Resolver class
[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;
}
}
Configuring the Dependency Resolver
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")));
}
© 2022 - 2024 — McMap. All rights reserved.