Unity.MVC 对自定义 ActionFilter 的依赖注入
Dependency Injection on Custom ActionFilter with Unity.MVC
我在使用自定义操作过滤器和依赖项注入时遇到问题。这是我的代码:
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));
}
错误
The current type,
GameCommerce.Infrastructure.Services.Visitors.IVisitorService, is an
interface and cannot be constructed. Are you missing a type mapping?
您是否考虑过将您的属性拆分为本文中讨论的 "passive" 属性?
http://blog.ploeh.dk/2014/06/13/passive-attributes/
从您的实现来看,您正在使用 属性 注入,因为构造函数注入不可用,因为您只能将常量和文字传递给属性。但是您确实应该使用构造函数注入,因为您的依赖项是必需的,并且如果不引用 IVisitorService 将无法工作。 (属性 注入通常表示可选依赖项。)
如果您遵循本文中的示例,您将创建一个很好的关注点分离并缓解您在使用 IoC 框架时遇到的问题。
问题是 MVC 没有使用您在 UnityConfig.cs 中的 RegisterTypes
方法中设置的容器。在 UnityConfig.cs 中的 RegisterTypes
末尾,您将 DependencyResolver.Current
设置为您在该方法中创建的容器,但稍后在 UnityMvcActivator.cs,DependencyResolver.Current
被 UnityConfig.GetConfiguredContainer()
覆盖,其中不包含您的注册。
我建议您使用 Unity.Mvc
包中提供的 UnityConfig.cs class,而不要修改 RegisterTypes
方法如下:
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>();
}
这样 DependencyResolver.Current
只会设置一次,在 UnityMvcActivator.cs.
我在使用自定义操作过滤器和依赖项注入时遇到问题。这是我的代码:
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));
}
错误
The current type, GameCommerce.Infrastructure.Services.Visitors.IVisitorService, is an interface and cannot be constructed. Are you missing a type mapping?
您是否考虑过将您的属性拆分为本文中讨论的 "passive" 属性?
http://blog.ploeh.dk/2014/06/13/passive-attributes/
从您的实现来看,您正在使用 属性 注入,因为构造函数注入不可用,因为您只能将常量和文字传递给属性。但是您确实应该使用构造函数注入,因为您的依赖项是必需的,并且如果不引用 IVisitorService 将无法工作。 (属性 注入通常表示可选依赖项。)
如果您遵循本文中的示例,您将创建一个很好的关注点分离并缓解您在使用 IoC 框架时遇到的问题。
问题是 MVC 没有使用您在 UnityConfig.cs 中的 RegisterTypes
方法中设置的容器。在 UnityConfig.cs 中的 RegisterTypes
末尾,您将 DependencyResolver.Current
设置为您在该方法中创建的容器,但稍后在 UnityMvcActivator.cs,DependencyResolver.Current
被 UnityConfig.GetConfiguredContainer()
覆盖,其中不包含您的注册。
我建议您使用 Unity.Mvc
包中提供的 UnityConfig.cs class,而不要修改 RegisterTypes
方法如下:
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>();
}
这样 DependencyResolver.Current
只会设置一次,在 UnityMvcActivator.cs.