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

Getting object reference error when using setter injection for ActionFilter

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

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

正在定义 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>());