Integrated StructureMap container for the MvcServiceLocator in ASP.NET MVC 3

ASP.NET MVC 3 just released a few days ago. In this release, Microsoft team published some features very cool. I really love that, because we shall code easier. See release note for more information. And in this post, I would like to explore about MvcServiceLocator, one of features very interesting in this release. So why I want to know about that? Because in the past project in codeplex http://nma.codeplex.com, I used to CommonServiceLocator from Microsoft, and I must to customize it for integrated with StructureMap. Now, I don’t need making like that, because ASP.NET MVC team has already integrated it into core of ASP.NET MVC 3. That enough to talking, I only care about coding because I don’t want to talk more but not practice.

When ASP.NET MVC 3 released, I read the series articles from Brad Wilson http://bradwilson.typepad.com/blog/2010/07/service-location-pt1-introduction.html. It is really useful articles for me. And he mentioned to MvcServiceLocator in ASP.NET MVC 3, and implemented the custom ServiceLocator for Unity. And after few hours to searching on internet, I also found the article from Maarten Balliauw about integrated the custom ServiceLocator for MEF. I really like the StructureMap, so in this post, I will pay attention to custom the ServiceLocator for StructureMap.

The first thing, you need to start is download the StructureMap from http://structuremap.github.com, and download the ASP.NET MVC 3 at http://go.microsoft.com/fwlink/?LinkID=157073 . After enough tools, we shall start for my post.

To save time for all of you, I will list the agenda as below:

  • Build the StructureMapControllerFactory
  • Build StructureMapServiceLocator
  • Build the Registry of StructureMap
  • Build the heart of application
  • Repository for Persistence Ignorance
  • Adding information to Global.asax (request from Itzik)
  • Controller for getting request from user
  • Build the View

Build the StructureMapControllerFactory

public class StructureMapControllerFactory : IControllerFactory
    {
        private readonly IContainer _container;
        private readonly IControllerFactory _innerFactory;

        /// <summary>
        /// Initializes a new instance of the <see cref="StructureMapControllerFactory"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        public StructureMapControllerFactory(IContainer container)
            : this(container, new DefaultControllerFactory())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StructureMapControllerFactory"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="innerFactory">The inner factory.</param>
        public StructureMapControllerFactory(IContainer container, IControllerFactory innerFactory)
        {
            _container = container;
            _innerFactory = innerFactory;
        }

        /// <summary>
        /// Creates the specified controller by using the specified request context.
        /// </summary>
        /// <param name="requestContext">The request context.</param>
        /// <param name="controllerName">The name of the controller.</param>
        /// <returns>The controller.</returns>
        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            try
            {
                return _container.GetInstance<IController>(controllerName.ToLowerInvariant());
            }
            catch (Exception)
            {
                return _innerFactory.CreateController(requestContext, controllerName);
            }
        }

        /// <summary>
        /// Releases the specified controller.
        /// </summary>
        /// <param name="controller">The controller.</param>
        public void ReleaseController(IController controller)
        {
            GC.SuppressFinalize(controller);
        }
    }

Build StructureMapServiceLocator

    public class StructureMapServiceLocator : IMvcServiceLocator
    {
        const string HttpContextKey = "__StructureMapServiceLocator_Container";
        private readonly IMvcServiceLocator _defaultLocator;
        private readonly IContainer _container;

        /// <summary>
        /// Initializes a new instance of the <see cref="StructureMapServiceLocator"/> class.
        /// </summary>
        public StructureMapServiceLocator()
        {
            var structureMapServiceLocator = MvcServiceLocator.Current as StructureMapServiceLocator;
            if (structureMapServiceLocator != null)
            {
                _container = structureMapServiceLocator.Container;
            }

            _defaultLocator = MvcServiceLocator.Default;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StructureMapServiceLocator"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        public StructureMapServiceLocator(IContainer container)
            : this(container, MvcServiceLocator.Default)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StructureMapServiceLocator"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="defaultLocator">The default locator.</param>
        public StructureMapServiceLocator(IContainer container, IMvcServiceLocator defaultLocator)
        {
            _container = container;
            _defaultLocator = defaultLocator;
        }

        /// <summary>
        /// Gets the container.
        /// </summary>
        /// <value>The container.</value>
        protected Container Container
        {
            get
            {
                if (!HttpContext.Current.Items.Contains(HttpContextKey))
                {
                    HttpContext.Current.Items.Add(HttpContextKeyContainer);
                }

                return (Container)HttpContext.Current.Items[HttpContextKey];
            }
        }

        /// <summary>
        /// Releases the specified instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        public void Release(object instance)
        {
            if (instance != null)
            {
                GC.SuppressFinalize(instance);
            }
        }

        /// <summary>
        /// Resolves the specified service type.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        private object Resolve(Type serviceType, string key = null)
        {
            var instance = _container.GetInstance(serviceType);
            if (instance != null)
            {
                return instance;
            }

            var dedaultInstance = _defaultLocator.GetInstance(serviceType, key);
            if (dedaultInstance != null)
            {
                return dedaultInstance;
            }

            throw new ActivationException(string.Format("Could not resolve service type {0}.", serviceType.FullName));
        }

        /// <summary>
        /// Resolves all.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns></returns>
        private IEnumerable<objectResolveAll(Type serviceType)
        {
            var instances = _container.GetAllInstances(serviceType).Cast<IEnumerable<object>>();
            if (instances.Count() > 0)
            {
                return instances;
            }

            var defaultInstances = _defaultLocator.GetAllInstances(serviceType);
            if (defaultInstances != null)
            {
                return defaultInstances;
            }

            throw new ActivationException(string.Format("Could not resolve service type {0}.", serviceType.FullName));
        }

        /// <summary>
        /// Gets all instances.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns></returns>
        public IEnumerable<objectGetAllInstances(Type serviceType)
        {
            return ResolveAll(serviceType);
        }

        /// <summary>
        /// Gets all instances.
        /// </summary>
        /// <typeparam name="TService">The type of the service.</typeparam>
        /// <returns></returns>
        public IEnumerable<TServiceGetAllInstances<TService>()
        {
            var instances = ResolveAll(typeof(TService));
            return from TService instance in instances select instance;
        }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public object GetInstance(Type serviceType, string key)
        {
            return Resolve(serviceType, key);
        }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns></returns>
        public object GetInstance(Type serviceType)
        {
            return Resolve(serviceType);
        }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <typeparam name="TService">The type of the service.</typeparam>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public TService GetInstance<TService>(string key)
        {
            return (TService)Resolve(typeof(TService), key);
        }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <typeparam name="TService">The type of the service.</typeparam>
        /// <returns></returns>
        public TService GetInstance<TService>()
        {
            return (TService)Resolve(typeof(TService));
        }

        /// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <param name="serviceType">An object that specifies the type of service object to get.</param>
        /// <returns>
        /// A service object of type <paramref name="serviceType"/>.-or- null if there is no service object of type <paramref name="serviceType"/>.
        /// </returns>
        public object GetService(Type serviceType)
        {
            return Resolve(serviceType);
        }
    }

Build the Registry of StructureMap

    public class CustomRegistry : Registry
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CustomRegistry"/> class.
        /// </summary>
        public CustomRegistry()
        {
            // These need for MvcServiceLocator in ASP.NET MVC 3
            For<IMvcServiceLocator>().Singleton().Use<StructureMapServiceLocator>();
            For<IControllerFactory>().Singleton().Use<DefaultControllerFactory>();

            // register all services here
            For<ICategoryDataProvider>().Use<CategoryDataProvider>();
            For<ICategoryRepository>().Use<StubCategoryRepository>();
            For<HomeController>().Use<HomeController>();
        }
    }

Build the heart of application

    public class Category : EntityBase
    {
        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public virtual string Name { getset; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Category"/> class.
        /// </summary>
        public Category()
        {
        }
    }

Repository for Persistence Ignorance

    public interface ICategoryRepository : ICommandRepository<Category>, IQueryRepository<Category>
    {
    }
    public class StubCategoryRepository : ICategoryRepository
    {
        private ICategoryDataProvider _categoryDataProvider;

        /// <summary>
        /// Initializes a new instance of the <see cref="StubCategoryRepository"/> class.
        /// </summary>
        /// <param name="categoryDataProvider">The category data provider.</param>
        public StubCategoryRepository(ICategoryDataProvider categoryDataProvider)
        {
            _categoryDataProvider = categoryDataProvider;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StubCategoryRepository"/> class.
        /// </summary>
        public StubCategoryRepository()
            : this(new CategoryDataProvider())
        {
        }

        /// <summary>
        /// Gets the by id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public Category GetById(int id)
        {
            DBC.Contract.Assert(_categoryDataProvider != null"CategoryDataProvider is null");

            return _categoryDataProvider.GetCategoryList().SingleOrDefault(x => x.Id == id);
        }

        
        /// <summary>
        /// Gets all.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<CategoryGetAll()
        {
            DBC.Contract.Assert(_categoryDataProvider != null"CategoryDataProvider is null");

            return _categoryDataProvider.GetCategoryList();
        }

        .....
    }
Adding information to Global.asax
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            ObjectFactory.Initialize(x => x.AddRegistry(new CustomRegistry()));
            MvcServiceLocator.SetCurrent(new StructureMapServiceLocator(ObjectFactory.ContainerMvcServiceLocator.Default));
            var factory = new StructureMapControllerFactory(ObjectFactory.Container);
            ControllerBuilder.Current.SetControllerFactory(factory);
        }

Controller for getting request from user

    public class HomeController : Controller
    {
        /// <summary>
        /// Gets or sets the category repository.
        /// </summary>
        /// <value>The category repository.</value>
        private ICategoryRepository CategoryRepository { getset; }

        /// <summary>
        /// Initializes a new instance of the <see cref="HomeController"/> class.
        /// </summary>
        public HomeController()
            : this(MvcServiceLocator.Current.GetInstance(typeof(ICategoryRepository)) as StubCategoryRepository)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HomeController"/> class.
        /// </summary>
        /// <param name="categoryRepository">The category repository.</param>
        public HomeController(ICategoryRepository categoryRepository)
        {
            CategoryRepository = categoryRepository;
        }

        /// <summary>
        /// Indexes this instance.
        /// </summary>
        /// <returns></returns>
        public ActionResult Index()
        {
            ViewModel.Message = "List of categories";

            Contract.Assert(CategoryRepository != null"CategoryRepository is null");

            return View(CategoryRepository.GetAll());
        }

        /// <summary>
        /// Details the specified id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public ActionResult Detail(int id)
        {
            ViewModel.Message = "List of categories";

            Contract.Assert(CategoryRepository != null"CategoryRepository is null");

            return View(CategoryRepository.GetById(id));
        }
        ................ 
 }

Build the View

Index.cshtml

@inherits System.Web.Mvc.WebViewPage<IList<CustomStructureMapServiceLocator.Models.Entity.Category>>

@{
    View.Title = "Category List Page";
    LayoutPage = "~/Views/Shared/_Layout.cshtml";
}

<h2>@View.Message</h2>
<ul>
    @foreach(var c in Model) {
      <li>
         @Html.ActionLink(c.Name, "Detail", new { id = c.Id })
      </li>
    }
</ul>
Detail.cshtml
@inherits System.Web.Mvc.WebViewPage<CustomStructureMapServiceLocator.Models.Entity.Category>

@{
View.Title = "Category Detail";
LayoutPage = "~/Views/Shared/_Layout.cshtml";
}

<h2>Detail information for [@Model.Name]</h2>

<ul>
<li>Id: @Model.Id</li>
<li>Name: @Model.Name</li>
<li>Is deleted: @Model.IsDelete</li>
<li>Created date: @Model.CreatedDate</li>
<li>Additional information: @Model.Description</li>
</ul>
 
I hope this post will enjoy for the fan of StructureMap. Goodbye and enjoy your coding.

7 Comments

Comments have been disabled for this content.