【问题标题】:Asp.Net MVC 5 Custom Action Filter With StructureMap带有 StructureMap 的 Asp.Net MVC 5 自定义动作过滤器
【发布时间】:2014-04-30 11:01:52
【问题描述】:

我在使用结构映射的 asp.net mvc 自定义 acitonfilte 中遇到问题 在我的“LogAttribute”类中,我有 setter 依赖注入,当执行我的 customfilterclass 的“OnActionExecuted”方法时,它是“LogAttribute”

我的 LogAttribute 类代码是

    public class LogAttribute : ActionFilterAttribute
{
    public ApplicationDbContext Context { get; set; }
    public string Description { get; set; }
    public LogAttribute(string description)
    {
        Description = description;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var userId = filterContext.HttpContext.User.Identity.GetUserId();
        var user = Context.Users.Find(userId); **i am getting error here the Context is coming null here** 
        Context.Logs.Add(new Log(user, filterContext.ActionDescriptor.ActionName,
                                filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
                                Description
                                )
                        );
        Context.SaveChanges();
    }
}

我创建了另一个类,从那里我将值传递给 setter 依赖属性

    public class StructureMapFilterProvider : FilterAttributeFilterProvider 
{
    private readonly Func<IContainer> _container;
    public StructureMapFilterProvider(Func<IContainer> container)
    {
        _container = container;
    }

    public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        var filters = base.GetFilters(controllerContext, actionDescriptor);
        var container = _container();
        foreach (var filter in filters)
        {
            container.BuildUp(filter.Instance);
            yield return filter;
        }
    }
}

我的依赖解析器类代码是

public class StructureMapDependencyResolver : IDependencyResolver
{
    private readonly Func<IContainer> _containerFactory;
    public StructureMapDependencyResolver(Func<IContainer> containerFactory)
    {
        _containerFactory = containerFactory;
    }
    public object GetService(Type serviceType)
    {
        if (serviceType == null)
        {
            return null;
        }
        var container = _containerFactory();

        return serviceType.IsAbstract || serviceType.IsInterface
            ? container.TryGetInstance(serviceType)
            : container.GetInstance(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _containerFactory().GetAllInstances(serviceType).Cast<object>();
    }
}

我的 global.ascx 代码是

public class MvcApplication : System.Web.HttpApplication
{

    public IContainer Container
    {
        get
        {
            return (IContainer)HttpContext.Current.Items["_Container"];
        }
        set
        {
            HttpContext.Current.Items["_Container"] = value;
        }
    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        DependencyResolver.SetResolver(new StructureMapDependencyResolver(() => Container ?? ObjectFactory.Container));

        ObjectFactory.Configure(cfg =>
           {
               cfg.Scan(Scan =>
               {
                   Scan.TheCallingAssembly();
                   Scan.WithDefaultConventions();
                   Scan.With(new ControllerConfiguration());
               });
               cfg.For<IFilterProvider>().Use(new StructureMapFilterProvider(() => Container ?? ObjectFactory.Container));
               cfg.For<IUserStore<ApplicationUser>>()
                  .Use<UserStore<ApplicationUser>>();
               cfg.For<DbContext>()
                  .Use(() => new ApplicationDbContext());
               cfg.SetAllProperties(x =>
                x.Matching(p =>
                     p.DeclaringType.CanBeCastTo(typeof(ActionFilterAttribute)) &&
                     p.DeclaringType.Namespace.StartsWith("TestingSturctureMap") &&
                     p.PropertyType.IsPrimitive &&
                     p.PropertyType != typeof(string)));

           });
    }

    public void Application_BeginRequest()
    {
        Container = ObjectFactory.Container.GetNestedContainer();
    }

    public void Application_EndRequest()
    {
        Container.Dispose();
        Container = null;
    }
}

【问题讨论】:

    标签: asp.net asp.net-mvc custom-action-filter


    【解决方案1】:

    我安装了structuremap 和StructureMap.MVC5 NuGet 包。
    这将DependencyResolution 文件夹和StructuremapMvc.cs 添加到App_Start 文件夹。
    然后我创建了Filters 文件夹,并为其添加了一个属性、一个操作过滤器和一个过滤器提供程序类:

    我的属性类 - LogActionsAttribute - 是最简单的。 它只是一个属性,没有任何操作的引用:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public class LogActionsAttribute : System.Attribute
    {
    }
    

    在Controller 上使用:

    [LogActions]
    public class HomeController : System.Web.Mvc.Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
    

    现在我需要添加一个动作过滤器来寻找这个属性并做一些事情。在我的例子中,记录传入的参数和执行结果。 ILog 是一个自定义接口,其中包含此过滤器使用的这两种方法。

    ControllerLoggerFilter类:

    public class ControllerLoggerFilter : IActionFilter
    {
        private readonly ILog _log;
        private string _request;
    
        public ControllerLoggerFilter(ILog log)
        {
            _log = log;
        }
    
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (ApplyBehavior(filterContext))
            {
                ActionExecuting(filterContext);
            }
        }
    
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (ApplyBehavior(filterContext))
            {
                ActionExecuted(filterContext);
            }
        }
    
        private void ActionExecuting(ControllerContext c)
        {
            if (c == null || c.HttpContext == null)
                return;
    
            _log.LogInput(c.HttpContext.Request);
            _request = c.HttpContext.Request;
        }
    
        private void ActionExecuted(ControllerContext c)
        {
            if (c.HttpContext.Response == null)
                return;
    
            _log.LogOutput(_request, c.HttpContext.Response);
        }
    
        private static bool ApplyBehavior(ActionExecutingContext filterContext)
        {
            return
                filterContext.ActionDescriptor.ControllerDescriptor
    .GetCustomAttributes(typeof (LogActionsAttribute),
                    false).Any();
        }
    
        private static bool ApplyBehavior(ActionExecutedContext filterContext)
        {
            return
                filterContext.ActionDescriptor.ControllerDescriptor
    .GetCustomAttributes(typeof (LogActionsAttribute),
                    false).Any();
        }
    }
    

    我现在必须告诉 MVC,这个过滤器需要在管道中。由于它是由结构映射容器构建的,我需要一个自定义的IFilterProvider:

    public class MvcInjectableFilterProvider : IFilterProvider
    {
        private readonly IEnumerable<Filter> _list;
    
        public MvcInjectableFilterProvider(IContainer container)
        {
            _list = GetContainerFilters(container);
        }
    
        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return _list;
        }
    
        private static IEnumerable<Filter> GetContainerFilters(IContainer container)
        {
            return
                container.GetAllInstances<IActionFilter>()
                    .Select(instance => new Filter(instance, FilterScope.Action, null));
        }
    }
    

    最后一件事:在我的IoC.cs 中注册我的课程。
    这是我在安装 NuGet 包时创建的文件中唯一需要修改的地方。

    public static class IoC {
        public static IContainer Initialize() {
            return new Container(c =>
            {
                c.AddRegistry<DefaultRegistry>();
                // added these lines:
                c.For<ILog>().Use<Log>();
                c.For<IActionFilter>().Use<ControllerLoggerFilter>();
                c.For<IFilterProvider>().Use<MvcInjectableFilterProvider>();
            });
        }
    }
    

    来源:
    Mark Seemann(以及其他地方)
    Javier G. Lozano
    eric.sowell
    K. Scott Allen

    【讨论】:

      【解决方案2】:

      亲爱的朋友们,我解决了我的问题,我的 LogAttribute 类的 setter 属性中缺少 [SetterProperty]

          public class LogAttribute : ActionFilterAttribute
      {
          [SetterProperty]
          public ApplicationDbContext Context { get; set; }
          public string Description { get; set; }
          public LogAttribute(string description)
          {
              Description = description;
          }
      
          public override void OnActionExecuted(ActionExecutedContext filterContext)
          {
              var userId = filterContext.HttpContext.User.Identity.GetUserId();
              var user = Context.Users.Find(userId);
              Context.Logs.Add(new Log(user, filterContext.ActionDescriptor.ActionName,
                                      filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
                                      Description
                                      )
                              );
              Context.SaveChanges();
          }
      }
      

      现在它的工作:)

      【讨论】:

        【解决方案3】:

        在 ActionFilters 中使用 setter 属性是一个非常糟糕的主意! 同一个 actionFilter 实例可以在多个请求之间共享。结果不同的请求(线程)将获得对 ApplicationDbContext 的相同引用。

        Are ActionFilterAttributes reused across threads? How does that work?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-04-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多