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;
}
}
Very bad idea to have setter properties in ActionFilters! The same actionFilter instance could be shared among multiple requests. As result different requests (threads) will obtain the same reference to ApplicationDbContext.
Are ActionFilterAttributes reused across threads? How does that work?
I installed
structuremap
andStructureMap.MVC5
NuGet packages.This added
DependencyResolution
folder and aStructuremapMvc.cs
toApp_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:Usage on a
Controller
: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: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
: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.
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
now its working :)