使用ActionFilter的setter注入时获取对象引用错误

时间:2015-01-02 21:15:49

标签: c# asp.net-mvc nullreferenceexception structuremap actionfilterattribute

我已创建自定义操作过滤器属性以记录错误并将日志保存到数据库中:

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);
    }
} 

所以,我还创建了自定义过滤器提供程序,用于使用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;
        }
    }
}

然后我在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)));

一切都应该没问题,但是当我使用Log属性修饰我的动作方法时,我得到对象引用未设置为对象的实例。我在OnActionExecuted中设置了断点,它告诉LogActionService是空:

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);
} 

1 个答案:

答案 0 :(得分:2)

定义StructureMapFilterProvider以设置属性注入:

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;
            }
        }
    }
}

创建我们的自定义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>();
                });
            });
        }
    }
}

然后有必要删除旧的FilterAttributeFilterProvider并将其替换为新的StructureMapFilterProvider(在Application_Start方法中):

var filterProvider = FilterProviders.Providers.Single(provider => provider is FilterAttributeFilterProvider);
FilterProviders.Providers.Remove(filterProvider);
FilterProviders.Providers.Add(SmObjectFactory.Container.GetInstance<StructureMapFilterProvider>());