对 ActionFilter 使用资源库注入时出现对象引用错误

本文关键字:对象引用 错误 注入 ActionFilter 资源库 | 更新日期: 2023-09-27 18:35:53

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

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 属性装饰我的操作方法时,我得到的对象引用未设置为对象的实例。我在OnActionExecute中设置了断点,它告诉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);
} 

对 ActionFilter 使用资源库注入时出现对象引用错误

定义设置

属性注入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;
            }
        }
    }
}

创建自定义结构图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>());