Sunday, December 15, 2013

Building Unity.WebForms (Part 4 of 4)

Injecting dependencies into your Pages, Controls, and Classes

Now that our types have been registered with the container, it is time to see how they get into our pages, controls, and types.

Injecting into Types

When injection into plain old C# classes, we can use any of the mechanisms supported by Unity: Construction, Property, or Method. In the Sample web application we will use the preferred method, Constructor injection, for the Service1 and Service2 classes as follows:

public class Service1
{
public string SayHello()
{
return string.Format( "Hello from Service 1 [Object ID = {0}]", GetHashCode() );
}
}

public class Service2
{
private Service1 _service1;

public Service2( Service1 svc1 )
{
_service1 = svc1;
}

public string SayHello()
{
return string.Format( "{0} (Called from Service2 [Object ID = {1}])", _service1.SayHello(), GetHashCode() );
}
}

Service1 is a basic class with no external dependencies while the Service2 class depends upon the Service1 class. By specifying the dependency in the constructor, the unity container will perform automatic Constructor Injection when resolving the type. The calls to GetHashCode() will show the effect the Lifetime Manager (specified when registering the types with the container) types will have on each service when used.

Injecting into Pages and static User Controls

As mentioned earlier, the ASP.NET pipeline manages the creation of Pages and we cannot override that behavior. As such, we cannot use constructor injection since the ASP.NET runtime knows nothing about Unity. That is why we created the UnityHttpModule which allows us to perform dependency resolution prior to any life-cycle events being called using Property Injection. As the name implies, we simply need place a [Dependency] attribute on any Property we wish to be injected as follows:


[Dependency]
public Service1 InjectedService1 { get; set; }

[Dependency]
public Service2 InjectedService2 { get; set; }

When the BuildUp method of the Unity container is called for a Page and/or User Control that contains the [Dependency] attribute, it will resolve that type of the property and call the setter with the resolved type.

Injecting into dynamic User Controls (or On-Demand injection)

Not all User Controls are added to a page at design time. There are many cases where controls are dynamically added to a page at runtime in response to a certain condition. When this is required, then you will need to ensure that the control gets it dependencies resolved by manually calling the BuildUp method for the newly added User Control as follows:


// dynamically load the new control
InjectedControl newControl = LoadControl("InjectedControl.ascx") as InjectedControl;
// 'buildup' the new control
Container.BuildUp( newControl );
// add the injected control into the page hierarchy
DynamicInjectedControl.Controls.Add( newControl );

To get access to the Container in order to perform the BuildUp, there are 2 options:

  • Add an injected property of type IUnityContainer to the containing Page. This will tell Unity to inject itself into the page.
  • Use the Unity.WebForms.GetChildContainer() extension method. Anywhere on the Page, simply say Context.GetChildContainer().BuildUp(); to build up your new control/object.

Results

When running the sample web application included in the source, the default web page displayed will show the following:

Notice how the hashcode for Service1 is the same for all calls while the hashcode for Service2 changes with each invocation? This is due to the Lifetime Manager used when registering the service with the container. Service1 was registered with the Hierarchical lifetime manager, which means as long as the container is still valid, you will receive the same object instance and any child containers will maintain their own instance of the type. Service2 was declared without specifying a lifetime manager so it received the default of 'transient', or per-call, meaning every call to the container to resolve a type results in a new object.

That's it!