简体   繁体   中英

Dependency Injection on Custom ActionFilter with Unity.MVC

I'm having a problem with custom action filter and dependency injection. Here is my code:

PageCount.cs

public class PageCount : ActionFilterAttribute
{
    [Dependency]
    public IVisitorService _visitorService { get; set; }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        _visitorService.AddVisitorCount();
        base.OnResultExecuted(filterContext);
    }
}

IVisitorService.cs

public partial interface IVisitorService
{
    int GetVisitorsCount();
    void AddVisitorCount();
}

VisitorService.cs

public partial class VisitorService : IVisitorService
{
    private readonly IRepository<Visitor> _visitorRepository;

    public VisitorService(IRepository<Visitor> visitorRepository)
    {
        _visitorRepository = visitorRepository;
    }

    public int GetVisitorsCount()
    {
        Visitor v = _visitorRepository.Get(1);

        return v.VisitCount;
    }

    public void AddVisitorCount()
    {
        Visitor v = _visitorRepository.Get(1);
        v.VisitCount += 1;
        _visitorRepository.Update(v);
    }
}

UnityConfig.cs

public static void RegisterComponents()
{
    var container = new UnityContainer();

    // identity
    container.RegisterType<DbContext, ApplicationDbContext>(new HierarchicalLifetimeManager());
    container.RegisterType<UserManager<ApplicationUser>>(new HierarchicalLifetimeManager());
    container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(new InjectionConstructor(new ApplicationDbContext()));
    container.RegisterType<AccountController>(new InjectionConstructor());
        container.RegisterType<IAuthenticationManager>(new InjectionFactory( o => HttpContext.Current.GetOwinContext().Authentication));

    container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
    container.RegisterType<IGameService, GameService>();
    container.RegisterType<IGenreService, GenreService>();
    container.RegisterType<IVisitorService, VisitorService>();

    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}

UnityMvcActivator.cs

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));
}


Error

The current type, GameCommerce.Infrastructure.Services.Visitors.IVisitorService, is an interface and cannot be constructed. Are you missing a type mapping?

The problem is that MVC isn't using the container that you setup in your RegisterTypes method in UnityConfig.cs . At the end of RegisterTypes in UnityConfig.cs , you set DependencyResolver.Current to a container that you've created within the method, but later on in UnityMvcActivator.cs , DependencyResolver.Current is overwritten with UnityConfig.GetConfiguredContainer() , which does not contain your registrations.

I recommend that you use the UnityConfig.cs class provided with the Unity.Mvc package without your modifications to the RegisterTypes method as follows:

UnityConfig.cs

public class UnityConfig
{
    #region Unity Container
    private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
    {
        var container = new UnityContainer();
        RegisterTypes(container);
        return container;
    });

    /// <summary>
    /// Gets the configured Unity container.
    /// </summary>
    public static IUnityContainer GetConfiguredContainer()
    {
        return container.Value;
    }
    #endregion

    /// <summary>Registers the type mappings with the Unity container.</summary>
    /// <param name="container">The unity container to configure.</param>
    /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 
    /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
    public static void RegisterTypes(IUnityContainer container)
    {
        // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
        // container.LoadConfiguration();

        // TODO: Register your types here
        container.RegisterType<DbContext, ApplicationDbContext>(new HierarchicalLifetimeManager());
        container.RegisterType<UserManager<ApplicationUser>>(new HierarchicalLifetimeManager());
        container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(new InjectionConstructor(new ApplicationDbContext()));
        container.RegisterType<AccountController>(new InjectionConstructor());
        container.RegisterType<IAuthenticationManager>(new InjectionFactory( o => HttpContext.Current.GetOwinContext().Authentication));

        container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
        container.RegisterType<IGameService, GameService>();
        container.RegisterType<IGenreService, GenreService>();
        container.RegisterType<IVisitorService, VisitorService>();
    }

This way DependencyResolver.Current will only get set once, in UnityMvcActivator.cs .

Have you thought about splitting your attribute into a "passive" attribute as discussed in this article?

http://blog.ploeh.dk/2014/06/13/passive-attributes/

From your implementation, you are using property injection since constructor injection is unavailable because you can only pass constants and literals to an attribute. But you really should be using constructor injection because your dependency is required and the won't work without the reference to IVisitorService. (Property injection commonly denotes an optional dependency.)

If you follow the example in the article, you'd create a nice separation of concerns and ease the issue you are having with the IoC framework.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM