Condividi tramite


ASP.NET Web Forms Applications

In ASP.NET Web Forms applications, the recommended approach is to store the container in the global state provided by the Application dictionary object. You can then access the container when required or—even better—use an HTTP module to perform injection on all of the controls in the page automatically.

In general, you should use the Application dictionary object to store the single instance of the container. You may decide to create child containers of the main container and store them in each user Session object, or even for each request, to register your custom types and mappings in the child containers. However, this may reduce application performance, and you should generally avoid creating additional containers if possible.

The following sections describe the techniques and limitations for instantiating the container in ASP.NET applications. They include a basic and simple approach to using the Application object to store the container, followed by the recommended approach that—while more complicated—will perform injection on the controls in your page automatically at run time:

  • The Basic Approach. This is the simplest approach, which may be suitable in small applications where discoverability and testability are less of a concern.
  • Recommended Approach for Dependency Injection. This technique can perform injection on the controls in your page automatically at run time, and provide better discoverability and testability.
  • Limitations and Alternative Approaches. This section describes some of the issues you should be aware of when using the container in ASP.NET applications.

The Basic Approach

The basic approach for initializing the Enterprise Library container, which may be suitable for small and simple applications, is shown here. You can create and populate the container in the Application_Start event of your Global.asax file.

protected void Application_Start(object sender, EventArgs e)
{
  Application.Lock();
  try
  {
    var myContainer = Application["EntLibContainer"] as IUnityContainer;
    if (myContainer == null)
    {
      myContainer = new UnityContainer();
      myContainer.AddExtension(new EnterpriseLibraryCoreExtension());
      // Add your own custom registrations and mappings here as required
      Application["EntLibContainer"] = myContainer;
    }
  }
  finally
  {
    Application.UnLock();
  }
}          
'Usage
Protected Sub Application_Start(sender As Object, e As EventArgs)
  Application.Lock()
  Try
    Dim myContainer = TryCast(Application("EntLibContainer"), IUnityContainer)
    If myContainer Is Nothing Then
      myContainer = New UnityContainer()
      myContainer.AddExtension(New EnterpriseLibraryCoreExtension())
      ' Add your own custom registrations and mappings here as required
      Application("EntLibContainer") = myContainer
    End If
  Finally
    Application.UnLock()
  End Try
End Sub

While the basic approach shown above provides a way for your code to access the container, it has some limitations. It means that you must write code to resolve instances of classes you require, and it reduces the discoverability and testability of your code. A far better approach if you are using the Unity container is to pass each request through the container using the BuildUp method to populate dependencies. This means that you only have to be concerned with applying the appropriate attributes to your controls and classes, or configuring injection registrations and mappings in the container, to inject the required Enterprise Library objects and your custom class instances at run time.

One approach is to create an HTTP module that automatically performs injection for all dependencies when a page loads. To achieve this, you require an HTTP module that executes code during the page initialization process in order to populate dependencies. You also use code in the Application_Start method of Global.asax to create and populate the container, and an extension method for the HttpApplicationState class that exposes the container to your application code. Once you create these items, you only need one line in your application configuration to enable automatic dependency injection for all controls and classes you use in your application.

To prepare an ASP.NET application for automatic dependency injection

  1. Create a new ASP.NET HTTP module class (named, for example, UnityHttpModule ) in your project that captures the PreRequestHandlerExecute event and executes code that walks the complete control tree of the current page request, applying the Unity BuildUp method to each control. For details and an example of the code required, see ASP.NET Dependency Injection HTTP Module.

  2. Edit your Web.config file for the application to add the new HTTP module, placing it before any other HTTP modules so that it loads first. The following extract shows how to add the HTTP module described in the previous step.

    <httpModules>
      <add name="UnityModule" type="Unity.Web.UnityHttpModule, Unity.Web" />
      ... other HTTP modules defined here
    </httpModules>
    
  3. Create a new class that implements an extension method for the ASP.NET HttpApplicationState class that exposes a method to provide access to the container. This method should create a new Unity container and store it in the current Application dictionary if it does not already exist, or just return a reference to it if it does. For details and an example of the code required, see ASP.NET Application State Extension.

  4. Add a global application class file (Global.asax) to your application and add code to the Application_Start event handler in your Global.asax.cs or Global.asax.vb code behind file that accesses the container created by the state extension and loads the special Enterprise Library container extension into it. You can also use this event handler to add your own custom registrations and type mappings to the container. The following shows how you can add registrations for custom types such as IMyService, CustomerService, and DataService.

    protected void Application_Start(object sender, EventArgs e)
    {
      // Create a Unity container and load the Enterprise Library extension.
      IUnityContainer myContainer = Application.GetContainer();
      myContainer.AddExtension(new EnterpriseLibraryCoreExtension());
    
      // Perform any container initialization you require. For example, 
      // register any custom types you require in your application
      // for example, register services and other types to use throughout
      // the code, including any to be managed as singleton instances.
      myContainer.RegisterType<IMyService, CustomerService>();
      myContainer.RegisterType<IMyService, DataService>("CustomerData",
                               new ContainerControlledLifetimeManager());
      myContainer.RegisterInstance("myArray", new string[] {"Item 1", "Item 2" });
    }
    
    'Usage
    Protected Sub Application_Start(sender As Object, e As EventArgs)
    
      ' Create a Unity container and load the Enterprise Library extension.
      Dim myContainer As IUnityContainer = Application.GetContainer()
      myContainer.AddExtension(New EnterpriseLibraryCoreExtension())
    
      ' Perform any container initialization you require. For example, 
      ' Register any custom types you require in your application
      ' for example, register services and other types to use throughout
      ' the code, including any to be managed as singleton instances.
      myContainer.RegisterType(Of IMyService, CustomerService)()
      myContainer.RegisterType(Of IMyService, DataService)("CustomerData", _
                               New ContainerControlledLifetimeManager())
      myContainer.RegisterInstance("myArray", New String() {"Item 1", "Item 2"})
    
    End Sub
    

For information about how you can register types and type mappings in a Unity container, see the topics in the section Configuring Unity in the Unity documentation.

Limitations and Alternative Approaches

The recommended technique described above does not automatically perform constructor injection on controls and classes in the page. It uses the BuildUp method (rather than the Resolve method) of the container, because ASP.NET has already created the control and class instances. This means that automatic constructor injection will not occur.

Instead, you can use property (setter) injection, or create a method for the control or class and define parameters for the types to be resolved then define this as an InjectionMethod using an attribute or a container registration. Effectively, this provides a delayed constructor. Unity will resolve the types of the parameters through the container and call the method. Inside the method, you can store a reference to the resolved object for use in your code.

For more information on how to use property and method call injection, see Injecting Resolved Types into Other Classes and Using Injection Attributes.