ASP MVC Controllers
See Actions for actions and actionfilters.
Injecting Dependencies
Rather than instantiate dependencies (unitOfWork, DbContext) directly in the controller, you can inject them. The DefaultControllerFactory uses Activator.CreateInstance, but if you use DI you can add constructor arguments. Use either:
- an IDependencyResolver (eg with common service locator). Register with DependencyResolver.SetResolver(myDependencyResolver);
- an IControllerActivator (Create(requestContext, type)). Register with an IDependencyResolver.GetService< IControllerActivator>.
Register in global.asax, Application_Start()
var builder = new Autofac.ContainerBuilder();
//registers all controllers with InstancePerDependency
builder.RegisterControllers(typeof(MvcApplication).Assembly);
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
This is Structuremap (v3) with CommonServiceLocator. Note we don't deal properly with disposables here, but for most things (even disposable DbContext) it's fine.
//structuremap
var container = new Container(c => c.AddRegistry<StructureMapDefaultRegistry>());
var structureMapResolver = new StructureMapDependencyResolver(container);
//MVC
DependencyResolver.SetResolver(structureMapResolver);
//WebAPI
GlobalConfiguration.Configuration.DependencyResolver = structureMapResolver;
This is the CommonServiceLocator / IDependencyResolver for StructureMap.
/// <summary>
/// A service locator for StructureMap
/// </summary>
public sealed class StructureMapDependencyResolver : ServiceLocatorImplBase, IDependencyResolver
{
/// <summary>
/// Initializes a new instance of the <see cref="StructureMapDependencyResolver"/> class.
/// </summary>
/// <param name="container">The container.</param>
/// <exception cref="System.ArgumentNullException">container</exception>
public StructureMapDependencyResolver(IContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
Container = container;
}
/// <summary>
/// Gets or sets the container.
/// </summary>
/// <value>
/// The container.
/// </value>
public IContainer Container { get; set; }
/// <summary>
/// Begins the scope. This is called Per Request by MVC.
/// </summary>
/// <returns></returns>
public IDependencyScope BeginScope()
{
var nestedContainer = Container.GetNestedContainer();
return new StructureMapDependencyResolver(nestedContainer);
}
/// <summary>
/// When implemented by inheriting classes, this method will do the actual work of resolving
/// the requested service instance.
/// </summary>
/// <param name="serviceType">Type of instance requested.</param>
/// <param name="key">Name of registered service you want. May be null.</param>
/// <returns>
/// The requested service instance.
/// </returns>
protected override object DoGetInstance(Type serviceType, string key)
{
if (serviceType == null) throw new ArgumentNullException("serviceType");
if (string.IsNullOrEmpty(key))
{
return serviceType.IsAbstract || serviceType.IsInterface
? Container.TryGetInstance(serviceType)
: Container.GetInstance(serviceType);
}
return Container.GetInstance(serviceType, key);
}
/// <summary>
/// When implemented by inheriting classes, this method will do the actual work of
/// resolving all the requested service instances.
/// </summary>
/// <param name="serviceType">Type of service requested.</param>
/// <returns>
/// Sequence of service instance objects.
/// </returns>
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
return Container.GetAllInstances(serviceType).Cast<object>();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
Container.Dispose();
}
/// <summary>
/// Gets the services.
/// </summary>
/// <param name="serviceType">Type of the service.</param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return DoGetAllInstances(serviceType);
}
}
For completeness, a standard StructureMap registry
public class StructureMapDefaultRegistry : Registry
{
/// <summary>
/// Initializes a new instance of the <see cref="StructureMapDefaultRegistry"/> class.
/// </summary>
public StructureMapDefaultRegistry()
{
Scan(
scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.WithDefaultConventions();
scan.ExcludeNamespace("System");
scan.ExcludeNamespace("Microsoft");
scan.ExcludeNamespace("IBM");
scan.RegisterConcreteTypesAgainstTheFirstInterface();
});
}
}
Async Controllers (MVC 3 and before)
using System.Threading.Tasks;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class ProductController : AsyncController
{
//async default timeout is 45 secs (45000)
[AsyncTimeout(60000)] //or [NoAsyncTimeout]
public void DisplayAsync()
{
//tell MVC about number of async operations
AsyncManager.OutstandingOperations.Increment();
Task.Factory.StartNew(() =>
{
//do an operation
var data = RemoteService.LoadProduct();
//set parameters for xCompleted. (Before Decrement!)
AsyncManager.Parameters["data"] = data;
//finally tell MVC it’s finished
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult DisplayCompleted(ProductViewModel data)
{
return View(data);
}
}
}