i am facing issue in asp.net mvc custom acitonfilte using structuremap in my "LogAttribute" class i have setter dependency injection which is coming null when executing the "OnActionExecuted" Method of my customfilterclass which is "LogAttribute"
my LogAttribute Class code is
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();
}
}
i creat another class from wheren i am passing value to the setter dependency property
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;
}
}
}
my dependency resolver class code is
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>();
}
}
and my global.ascx code is
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;
}
}
I installed structuremap
and StructureMap.MVC5
NuGet packages.
This added DependencyResolution
folder and a StructuremapMvc.cs
to App_Start
folder.
I then created Filters
folder and added an attribute, an action filter and a filter provider classes to it:
My attribute class - LogActionsAttribute
- is the easiest.
It is just an attribute without any references to any actions:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class LogActionsAttribute : System.Attribute
{
}
Usage on a Controller
:
[LogActions]
public class HomeController : System.Web.Mvc.Controller
{
public ActionResult Index()
{
return View();
}
}
Now I needed to add an action filter that would look for this attribute and do something. In my case, log the incoming parameters and execution result. ILog
is a custom interface with those 2 methods that are used by this filter.
ControllerLoggerFilter
class:
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();
}
}
I now had to tell MVC that this filter needed to be in the pipeline. Since it is built by structuremap container, I needed a custom 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));
}
}
Last thing to do: register my classes in my IoC.cs
.
This is the only place I had to modify in the files that were created when I installed NuGet packages.
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>();
});
}
}
Sources:
Mark Seemann (and whole bunch other places)
Javier G. Lozano
eric.sowell
K. Scott Allen
Dear friends i solve my question it was just [SetterProperty] missing from the setter property in my LogAttribute Class
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();
}
}
now its working :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With