Search code examples
c#asp.net-mvcunity-container

Get an instance of an interface from Unity


I am trying to setup a custom authenticaion filter in my MVC application, but I need to have a reference to my user service, in the authentication filter. I setup my services with Unity like so:

    private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
    {
        var container = new UnityContainer();
        RegisterTypes(container);
        return container;
    });

    public static IUnityContainer GetConfiguredContainer()
    {
        return container.Value;
    }

    public static void RegisterTypes(IUnityContainer container)
    {

        // container.LoadConfiguration();

        // TODO: Register your types here
        container.RegisterType<IUserService, UserService>();

    }

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(GERPWeb.App_Start.UnityWebActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(GERPWeb.App_Start.UnityWebActivator), "Shutdown")]

namespace APP.App_Start
{
/// <summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary>
public static class UnityWebActivator
{
    /// <summary>Integrates Unity when the application starts.</summary>
    public static void Start() 
    {
        var container = UnityConfig.GetConfiguredContainer();

        FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
        FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        // TODO: Uncomment if you want to use PerRequestLifetimeManager
        // Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
    }

    /// <summary>Disposes the Unity container when the application is shut down.</summary>
    public static void Shutdown()
    {
        var container = UnityConfig.GetConfiguredContainer();
        container.Dispose();
    }
}

This works fine everywhere that I can have a constructor, like so:

public class UserController : BaseController
{

    private readonly IUserService _userService;

    public CustomerController(IUserService userService)
    {
        _userService = userService;

    }

However I cannot have one on my authentication filter:

public class BasicAuthorization : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        //see if we can skip the authorization
        bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) ||
                                 filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
                                     typeof(AllowAnonymousAttribute), true);

        if (!skipAuthorization)
        {
            //var userService = ServiceLocator.Current.GetInstance<IUserService>();
            //get instance of user service here


            base.OnAuthorization(filterContext);
        }
    }
}

Then in my FilterConfig.cs:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new BasicAuthorization());
        filters.Add(new HandleErrorAttribute());
    }

I found a reference to something called service locator and attempted to use it to the best of my ability, but I think I'm missing something as I get ServiceLocationProvider must be set. on that line, and all the google links are for WPF.

What am I missing here?


Solution

  • You have already set up the DependencyResolver which is basically follows a service locator pattern.

    public class BasicAuthorization : AuthorizeAttribute {
        public override void OnAuthorization(AuthorizationContext filterContext) {
            //see if we can skip the authorization
            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) ||
                                     filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
                                         typeof(AllowAnonymousAttribute), true);
    
            if (!skipAuthorization) {
                //Using dependency resolver here
                var userService = (IUserService) DependencyResolver.Current.GetService(typeof(IUserService));
    
    
                base.OnAuthorization(filterContext);
            }
        }
    }
    

    You could create an extension method to allow for a cleaner resolution of the instance

    public static T GetService<T>(this IDependencyResolver container) {
        return (T)container.GetService(typeof(T));
    }
    

    Which would allow for

    var userService = DependencyResolver.Current.GetService<IUserService>();