1

I've created custom action filter attribute for logging error and save logs into database:

public class LogAttribute : ActionFilterAttribute
{
    public string Description { get; set; }
    public IUnitOfWork Uow { get; set; }
    public ILogActionService LogActionService { get; set; }

    public IApplicationUserManager ApplicationUserManager { get; set; }
    public LogAttribute(string desciption)
    {
        Description = desciption;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        //var userId = filterContext.HttpContext.User.Identity.GetUserId();
        //var user = await ApplicationUserManager.FindByIdAsync(2);
        var model = new LogAction(filterContext.ActionDescriptor.ActionName,
            filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, Description);
        LogActionService.AddLog(model);
        Uow.SaveAllChanges();
        base.OnActionExecuted(filterContext);
    }
} 

So, I also created custom filter provider for injecting my dependency using StructureMap:

public class StructureMapFilterProvider : FilterAttributeFilterProvider
{
    private 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;
        }
    }
}

Then I've registered that in Global.asax:

cfg.For<IFilterProvider>().Use(new StructureMapFilterProvider(() =>  SmObjectFactory.Container));
cfg.Policies.SetAllProperties(x => x.Matching(p => p.DeclaringType.CanBeCastTo(typeof(ActionFilterAttribute)) &&
    p.DeclaringType.Namespace.StartsWith("MyNamespace") && 
    !p.PropertyType.IsPrimitive &&
    p.PropertyType != typeof(string)));

Everything should be ok, But when I decorate my action method with Log attribute, I get Object reference not set to an instance of an object. I set break point in OnActionExecuted it tells that LogActionService is null:

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
    //var userId = filterContext.HttpContext.User.Identity.GetUserId();
    //var user = await ApplicationUserManager.FindByIdAsync(2);
    var model = new LogAction(filterContext.ActionDescriptor.ActionName,
        filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, Description);
    LogActionService.AddLog(model);
    Uow.SaveAllChanges();
    base.OnActionExecuted(filterContext);
} 
John Saunders
  • 157,405
  • 24
  • 229
  • 388
Sirwan Afifi
  • 10,031
  • 11
  • 54
  • 104
  • I assume `UoW` and `ApplicationUserManager` are also null? – Rhumborl Jan 02 '15 at 21:58
  • @Rhumborl Yes the are null. – Sirwan Afifi Jan 03 '15 at 06:11
  • 1
    Unlike forum sites, we don't use "Thanks", or "Any help appreciated", or signatures on [so]. See "[Should 'Hi', 'thanks,' taglines, and salutations be removed from posts?](http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be-removed-from-posts). BTW, it's "Thanks in advance", not "Thanks in advanced". – John Saunders Jan 24 '15 at 00:44

1 Answers1

2

Defining StructureMapFilterProvider to setup property injection:

using StructureMap;
using System.Collections.Generic;
using System.Web.Mvc;

namespace DI06.CustomFilters
{
    public class StructureMapFilterProvider : FilterAttributeFilterProvider
    {
        private readonly IContainer _container;
        public StructureMapFilterProvider(IContainer container)
        {
            _container = container;
        }

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

Creating our custom StructureMap Container:

using System;
using System.Threading;
using DI06.Services;
using DI06.Services.Contracts;
using StructureMap;

namespace DI06.IocConfig
{
    public static class SmObjectFactory
    {
        private static readonly Lazy<Container> _containerBuilder =
            new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);

        public static IContainer Container
        {
            get { return _containerBuilder.Value; }
        }

        private static Container defaultContainer()
        {
            return new Container(x =>
            {
                x.For<ILogActionService>().Use<LogActionService>();

                x.Policies.SetAllProperties(y =>
                {
                    y.OfType<ILogActionService>();
                });
            });
        }
    }
}

And then it's necessary to remove the old FilterAttributeFilterProvider and replace it with the new StructureMapFilterProvider (in Application_Start method):

var filterProvider = FilterProviders.Providers.Single(provider => provider is FilterAttributeFilterProvider);
FilterProviders.Providers.Remove(filterProvider);
FilterProviders.Providers.Add(SmObjectFactory.Container.GetInstance<StructureMapFilterProvider>());
VahidN
  • 16,098
  • 7
  • 63
  • 110