Настраиваемый атрибут ActionFilterAttribute in. Net Core 3.1: невозможно разрешить службу для типа 'System.String' - PullRequest
2 голосов
/ 06 марта 2020

Я переношу свое приложение. Net Core 2.2 до версии 3.1.

У меня есть следующий фильтр действий:

public class MyFilterAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly int _num;
    private readonly string _type;

    public MyFilterAttribute(string name, int num, string type)
    {
        _name = name;
        _num = num;
        _type = type;
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        ...
        await base.OnActionExecutionAsync(context, next);
    }
}

Я зарегистрировал этот фильтр в файле Startup.cs вот так:

services.AddTransient<MyFilterAttribute>();

И я использую фильтр внутри своих контроллеров вот так:

public class MyController : Controller
{
    [MyFilter("some-name", 5000, "some-type")]
    public async Task MyAction()
    {
        ...
    }
}

Этот фильтр отлично работал в моем приложении. Net Core 2.2. Однако теперь, когда я пытаюсь перейти на версию 3.1, я получаю следующее исключение:

Возникло исключение: CLR / System.AggregateException В Microsoft возникло необработанное исключение типа «System.AggregateException». Extensions.DependencyInjection.dll: «Некоторые службы не могут быть построены». Обнаружены внутренние исключения. Дополнительные сведения см. В разделе $ исключение в окне переменных. Внутреннее исключение
System.InvalidOperationException: невозможно разрешить службу для типа «System.String» при попытке активировать «my_app.Filters.MyFilterAttribute». в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites (Тип serviceType, Тип instanceType типа, CallSiteChain, параметры CallSiteChain, ParameterInfo [], Boolean throwIfCallSiteNotFound. реализацияType, CallSiteChain (CallSiteChain) в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact (дескриптор ServiceDescriptor, тип serviceType, слот службы CallSiteChain, ячейка Int32) Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.ValidateService (дескриптор ServiceDescriptor)

Я попытался добавить фильтр глобально, как это:

services.AddControllersWithViews(config =>
{
    config.Filters.Add<MyFilterAttribute>();
})

Но у меня то же самое ошибка.

Что-то изменилось. Net Core читает пользовательские фильтры. Net Core 3.1?

Я не вижу ничего уместного в документации по критическим изменениям миграции.

Ответы [ 2 ]

1 голос
/ 06 марта 2020

В соответствии с этим (https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.1) все ваши фильтры действий разрешаются с помощью DI, что означает, что ваши 2 строковых параметра также будут разрешены. И это вызвало у вас ошибку.

Возможно, вы захотите использовать другой тип фильтра, который не нужно разрешать с помощью DI.

Посмотрите на 2 типа фильтров на

public class AddHeaderAttribute : ResultFilterAttribute
public class MyActionFilterAttribute : ActionFilterAttribute
0 голосов
/ 08 марта 2020

Ответы Алекса и Нкоси (/ comments) верны, проблема с первой реализацией заключалась в том, что фильтр был построен в механизме внедрения зависимостей, и DI не знал, как обращаться с строковыми аргументами.

Честно говоря, я не знаю, как это работает, но у меня есть приложение. Net Core 2.2, работающее в производстве, с фильтрами, реализованными таким образом.

В любом случае, я изменил фильтр на TypeFilterAttribute и теперь он снова работает в. Net Core 3.1 (нет необходимости регистрировать его в DI):

public class MyFilterAttribute : TypeFilterAttribute
{
    public MyFilterAttribute(params object[] arguments) : base(typeof(MyFilterAttributeImpl))
    {
        Arguments = new object[] { arguments };
    }

    private class MyFilterAttributeImpl : ActionFilterAttribute
    {
        private readonly string _name;
        private readonly int _num;
        private readonly string _type;

        public MyFilterAttribute(object[] arguments)
        {
            _name = (string)arguments[0];
            _num = (int)arguments[1];
            _type = (string)arguments[2];
        }

        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            ...
            await base.OnActionExecutionAsync(context, next);
        }
    }
}

С этой реализацией, где мы сначала определяем TypeFilterAttribute и вызываем ActionFilterAttribute внутри него мы можем вызвать фильтр с красивой аннотацией:

[MyFilter("some-name", 5000, "some-type")]

вместо:

[TypeFilter(typeof(MyFilterAttribute), Arguments = new object[] { "some-name", 5000, "some-type" })]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...