Некоторые службы не могут быть созданы с использованием библиотеки: NetCore.AutoRegisterDi

Я разрабатываю API-интерфейсы asp.net core 3.1 на основе GraphQL. Я использовал приведенную ниже справочную статью для настройки автоматической конфигурации DI на уровне API и использовал пакет nuget: NetCore.AutoRegisterDi

https://www.thereformedprogrammer.net/asp-net-core-fast-and-automatic-dependency-injection-setup/

Вот детали кода:

Код:

Startup.cs:

public virtual void ConfigureServices(IServiceCollection services) => services
                      .AddGraphQLResolvers()
                      .AddProjectRepositories();

ProjectServiceCollectionExtensions.cs

public static class ProjectServiceCollectionExtensions
{
    public static IServiceCollection AddProjectRepositories(this IServiceCollection services) => 
        services.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(CommonService)))
                .Where(c => c.Name.EndsWith("Persistence"))
                .AsPublicImplementedInterfaces(ServiceLifetime.Scoped);

    public static IServiceCollection AddGraphQLResolvers(this IServiceCollection services) =>
       services
           .AddScoped<ICountriesResolver, CountriesResolver>()
           .AddScoped<ICountryGroupsResolver, CountryGroupsResolver>()
           .AddScoped<IDisclaimerResolver, DisclaimerResolver>();
}

В приведенном выше примере CommonService является частью уровня обслуживания, который заканчивается постоянством.

CountryResolver.cs

public class CountriesResolver : Resolver, ICountriesResolver
{
    private readonly ICountryService _countryService;
    private readonly IHttpContextAccessor _accessor;
    private readonly IDataLoaderContextAccessor _dataLoaderContextAccessor;
    public CountriesResolver(ICountryService countryService, IHttpContextAccessor accessor, IDataLoaderContextAccessor dataLoaderContextAccessor)
    {
        _countryService = countryService ?? throw new ArgumentNullException(nameof(countryService));
        _accessor = accessor;
        _dataLoaderContextAccessor = dataLoaderContextAccessor;
    }

    public void Resolve(GraphQLQuery graphQLQuery)
    {
        var language = _accessor.HttpContext.Items["language"] as LanguageDTO;
        graphQLQuery.FieldAsync<ResponseGraphType<CountryResultType>>("countriesresponse", arguments: new QueryArguments(new QueryArgument<IdGraphType>{Name = "pageNo", Description = "page number"}, new QueryArgument<IdGraphType>{Name = "pageSize", Description = "page size"}), resolve: async context =>
        {
            var pageNo = context.GetArgument<int>("pageNo") == 0 ? 1 : context.GetArgument<int>("pageNo");
            var pageSize = context.GetArgument<int>("pageSize") == 0 ? 100 : context.GetArgument<int>("pageSize");
            if (language != null)
            {
                var loader = _dataLoaderContextAccessor.Context.GetOrAddLoader("GetAllCountries", () => _countryService.GetAllCountriesAsync(language, pageNo, pageSize));
                var list = await context.TryAsyncResolve(async c => await loader.LoadAsync());
                return Response(list);
            }

            return null;
        }

        , description: "All Countries data");
    }
}

ICommonService.cs

using Author.Query.Persistence.DTO;
using System.Threading.Tasks;

namespace Author.Query.Persistence.Interfaces
{
    public interface ICommonService
    {
        LanguageDTO GetLanguageFromLocale(string locale);
        Task<LanguageDTO> GetLanguageFromLocaleAsync(string locale);
    }
}

CommonService.cs

namespace Author.Query.Persistence
{
    public class CommonService : ICommonService
    {
        private readonly AppDbContext _dbContext;
        private readonly IOptions<AppSettings> _appSettings;
        private readonly IMapper _mapper;
        private readonly ICacheService<Languages, LanguageDTO> _cacheService;
        public CommonService(AppDbContext dbContext, IOptions<AppSettings> appSettings, IMapper mapper, ICacheService<Languages, LanguageDTO> cacheService)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _appSettings = appSettings;
            _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
            _cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService));
        }

        //public Languages GetLanguageFromLocale(string locale)
        public LanguageDTO GetLanguageFromLocale(string locale)
        {
            return GetLanguagesByFilter(GetFilterValues(locale, true).ToArray());
        }
    }
}

ICountryService.cs

namespace Author.Query.Persistence.Interfaces
{
    public interface ICountryService
    {
        Task<CountryResult> GetAllCountriesAsync(LanguageDTO language, int pageNo, int pageSize);
        Task<CountryDTO> GetCountryAsync(LanguageDTO language, int countryId);
    }
}

CountryService.cs

namespace Author.Query.Persistence
{
    public class CountryService : ICountryService
    {
        private readonly AppDbContext _dbContext;
        private readonly IOptions<AppSettings> _appSettings;
        private readonly ICacheService<Images, ImageDTO> _cacheService;
        public CountryService(TaxathandDbContext dbContext, IOptions<AppSettings> appSettings, ICacheService<Images, ImageDTO> cacheService)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService));
            _appSettings = appSettings;
        }

        public async Task<CountryResult> GetAllCountriesAsync(LanguageDTO language, int pageNo, int pageSize)
        {
            var localeLangId = language.LanguageId;
            var dftLanguageId = int.Parse(_appSettings.Value.DefaultLanguageId);
            // By default pick the localLanguage value
            var countries = await GetAllCountriesDataAsync(localeLangId, pageNo, pageSize);
            // If localLanguage data is not available then pull the data based on default language
            if (countries.Countries.Count == 0)
            {
                countries = await GetAllCountriesDataAsync(dftLanguageId, pageNo, pageSize);
            }

            return countries;
        }

        public async Task<CountryDTO> GetCountryAsync(LanguageDTO language, int countryId)
        {
            var localeLangId = language.LanguageId;
            var dftLanguageId = int.Parse(_appSettings.Value.DefaultLanguageId);
            //var country = new CountryDTO();
            // By default pick the localLanguage value
            var country = await GetCountryDetailsAsync(countryId, localeLangId);
            // If localLanguage data is not available then pull the data based on default language
            if (country == null)
            {
                country = await GetCountryDetailsAsync(countryId, dftLanguageId);
            }

            return country;
        }

        private async Task<CountryDTO> GetCountryDetailsAsync(int countryId, int languageId)
        {
            var images = await _cacheService.GetAllAsync("imagesCacheKey");
            var country = await _dbContext.Countries.AsNoTracking().FirstOrDefaultAsync(c => c.CountryId.Equals(countryId) && c.IsPublished.Equals(true) && c.LanguageId.Equals(languageId));
            if (country == null)
            {
                return null;
            }

            var countryDTO = new CountryDTO{Uuid = country.CountryId, PNGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(country.PNGImageId)).FilePath, SVGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(country.SVGImageId)).FilePath, DisplayName = country.DisplayName, DisplayNameShort = country.DisplayName, Name = Helper.ReplaceChars(country.DisplayName), Path = Helper.ReplaceChars(country.DisplayName), CompleteResponse = true};
            return countryDTO;
        }

        private async Task<CountryResult> GetAllCountriesDataAsync(int languageId, int pageNo, int pageSize)
        {
            var countryList = new CountryResult();
            var images = await _cacheService.GetAllAsync("imagesCacheKey");
            var countries = await _dbContext.Countries.Where(cc => cc.IsPublished.Equals(true) && cc.LanguageId.Equals(languageId)).Select(c => new
            {
            c.CountryId, c.DisplayName, c.PNGImageId, c.SVGImageId
            }

            ).OrderByDescending(c => c.CountryId).Skip((pageNo - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
            if (countries.Count == 0)
            {
                return null;
            }

            countryList.Countries.AddRange(countries.Select(co => new CountryDTO{Uuid = co.CountryId, PNGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(co.PNGImageId)).FilePath, SVGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(co.SVGImageId)).FilePath, DisplayName = co.DisplayName, DisplayNameShort = co.DisplayName, Name = Helper.ReplaceChars(co.DisplayName), Path = Helper.ReplaceChars(co.DisplayName), CompleteResponse = true}));
            return countryList;
        }
    }
}

Ошибка:

    System.AggregateException
      HResult=0x80131500
      Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Author.Query.New.API.GraphQL.Resolvers.ICountriesResolver Lifetime: Scoped ImplementationType: Author.Query.New.API.GraphQL.Resolvers.CountriesResolver': 
Unable to resolve service for type 'Author.Query.Persistence.Interfaces.ICountryService' while attempting to activate 'Author.Query.New.API.GraphQL.Resolvers.CountriesResolver'.) (Error while validating the service descriptor 'ServiceType: 
Author.Query.New.API.GraphQL.Resolvers.ICountryGroupsResolver Lifetime: Scoped ImplementationType: Author.Query.New.API.GraphQL.Resolvers.CountryGroupsResolver': Unable to resolve service for type 'Author.Query.Persistence.Interfaces.ICountryGroupService' while attempting to activate 'Author.Query.New.API.GraphQL.Resolvers.CountryGroupsResolver'.) (Error while validating the service descriptor 'ServiceType: Author.Query.New.API.GraphQL.Resolvers.IDisclaimerResolver Lifetime: Scoped ImplementationType: Author.Query.New.API.GraphQL.Resolvers.DisclaimerResolver': Unable to resolve service for type 'Author.Query.Persistence.Interfaces.IDisclaimerService' while attempting to activate 'Author.Query.New.API.GraphQL.Resolvers.DisclaimerResolver'.)
      Source=Microsoft.Extensions.DependencyInjection
      StackTrace:
       at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)
       at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
       at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
       at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
       at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
       at Microsoft.Extensions.Hosting.HostBuilder.Build()
       at Author.Query.New.API.Program.Main(String[] args) in /src/QueryStack/Author.Query.New.API/Program.cs:line 15

    Inner Exception 1:
    InvalidOperationException: Error while validating the service descriptor 'ServiceType: Author.Query.New.API.GraphQL.Resolvers.ICountriesResolver Lifetime: Scoped ImplementationType: 
Author.Query.New.API.GraphQL.Resolvers.CountriesResolver': 
Unable to resolve service for type 'Author.Query.Persistence.Interfaces.ICountryService' while attempting to activate 
'Author.Query.New.API.GraphQL.Resolvers.CountriesResolver'.

    Inner Exception 2:
    InvalidOperationException: Unable to resolve service for type 
'Author.Query.Persistence.Interfaces.ICountryService' while attempting to activate 
'Author.Query.New.API.GraphQL.Resolvers.CountriesResolver'.

Может ли кто-нибудь помочь мне узнать, как решить эту проблему?


person santosh kumar patro    schedule 30.01.2020    source источник
comment
Исключение говорит о том, что служба ICountryService не найдена. Startup.cs не показывает ничего, что регистрируется ICountryService   -  person Panagiotis Kanavos    schedule 30.01.2020
comment
Кстати, вы должны размещать только соответствующую информацию. Это изображение совсем не помогает - оно просто выталкивает код на второй экран. GraphQLQuery и AppSchema тоже бесполезны, ошибка даже не упоминает о них. В лучшем случае они должны быть последними фрагментами кода.   -  person Panagiotis Kanavos    schedule 30.01.2020
comment
Спасибо @PanagiotisKanavos за ваш ответ. Единственное намерение состояло в том, чтобы дать четкое представление обо всех компонентах с помощью кода и диаграммы. Здесь ICountryService является частью сборки, которая содержит CommonService, и по этой причине я добавил CommonService в метод: AddProjectRepositories из ProjectServiceCollectionExtensions.cs   -  person santosh kumar patro    schedule 30.01.2020
comment
ICountryService где-нибудь зарегистрирован? Вот на что жалуется ошибка. Вопрос также не содержит какого-либо класса, реализующего этот интерфейс.   -  person Panagiotis Kanavos    schedule 30.01.2020
comment
Неужели AddProjectRepositories его зарегистрировать? Чтобы это работало, интерфейс должен быть реализован классом, заканчивающимся на Persistence, в той же сборке с CommonService.   -  person Panagiotis Kanavos    schedule 30.01.2020
comment
Да, вы правы, утверждая, что метод: AddProjectRepositories содержит логику для регистрации служб, и на это есть ссылка в методе ConfigureServices файла Startup.cs. А также CommonService присутствует в сборке, которая заканчивается на Persistence, которую я пытаюсь зарегистрировать с помощью пакета nuget: NetCore.AutoRegisterDi   -  person santosh kumar patro    schedule 30.01.2020
comment
Затем разместите этот код. Удалите изображение, GraphQLQuery и AppSchema и добавьте эти классы. По какой-то причине этот класс не найден и зарегистрирован. Скорее всего, это проблема с кодом отражения. Это невозможно исправить, не зная, как выглядят эти типы. Кроме того, сколько типов реализуют ICommonService? CountriesResolver просит один экземпляр. Если существует 5 классов, реализующих службу, контейнер DI должен будет выбрать один случайным образом.   -  person Panagiotis Kanavos    schedule 30.01.2020
comment
Большое спасибо @PanagiotisKanavos за ваш ответ. Я обновил код   -  person santosh kumar patro    schedule 30.01.2020


Ответы (3)


Ошибка говорит, что:

Невозможно разрешить службу для типа Author.Query.Persistence.Interfaces.ICountryService при попытке активировать Author.Query.New.API.GraphQL.Resolvers.CountriesResolver

ICountryService никогда не регистрируется. AddGraphQLResolvers регистрирует только ICountriesResolver, ICountryGroupsResolver и IDisclaimerResolver.

Метод AddProjectRepositories регистрирует только классы, имена которых оканчиваются на Persistence, которые находятся в том же пространстве имен, что и CommonService. Он никогда не регистрирует себя CommonService.

Услуга должна быть зарегистрирована, например, с:

services.AddScoped<ICommonService, CommonService>();
person Panagiotis Kanavos    schedule 31.01.2020
comment
Спасибо @PanagiotisKanavos за ваш ответ. Уровень обслуживания в моем проекте имеет множество интерфейсов и конкретных классов его реализации. Я уже выполнил процесс регистрации для всех служб, но с пакетом nuget: NetCore.AutoRegisterDi (thereformedprogrammer.net/) эта настройка будет очень упрощена. Мне не нужно проходить процесс регистрации для классов обслуживания. С помощью методов, доступных как часть пакета nuget, он просканирует сборку и полностью выполнит настройку регистрации. - person santosh kumar patro; 31.01.2020
comment
@santoshkumarpatro ошибка говорит, что вам нужно зарегистрировать службу. Опубликованный вами код не регистрирует эту службу. Это факт, а не предмет обсуждения. Ваш код регистрирует классы, которые заканчиваются на Persistence, а не на CommonService класс - person Panagiotis Kanavos; 31.01.2020
comment
@santoshkumarpatro, почему бы вам просто не добавить services.AddScoped<ICommonService, CommonService>(); и проверить, что происходит? - person Panagiotis Kanavos; 31.01.2020
comment
Метод AddProjectRepositories также регистрирует CommonService, поскольку он также заканчивается на Persistence. Все, что я пытаюсь сделать, это зарегистрировать все классы, у которых пространство имен заканчивается на Persistence. Это будет однократная настройка, и мне не нужно беспокоиться о регистрации дополнительных классов, которые будут добавлены в будущем. - person santosh kumar patro; 19.02.2020

Ваш метод AddProjectRepositories() не регистрирует CountryService, потому что фильтр Where в RegisterAssemblyPublicNonGenericClasses() смотрит на классы (Type), а не на пространства имен.

Так что, возможно, вы можете изменить свой фильтр:

services.RegisterAssemblyPublicNonGenericClasses(Assembly.GetAssembly(typeof(CommonService)))
                .Where(c => c.Name.EndsWith("Service")) // <-- Change "Persistence" to "Service"
                .AsPublicImplementedInterfaces(ServiceLifetime.Scoped);

Это должно зарегистрировать все классы, заканчивающиеся на «Service» в сборке, содержащей CommonService.

person veuncent    schedule 20.02.2020

Для меня это была простая ошибка. У меня был класс с тем же именем в другом пространстве имен, и я ссылался на неправильный класс / забыл удалить повторяющийся класс и добавить правильный в свой запуск.

У меня была служба UserService в myapp.Utils и еще одна в myapp.Services. Я ссылался на myapp.Utils, когда хотел удалить его и использовать только myapp.Services. Я неправильно вводил тот, который был в myapp.Utils, когда мои контроллеры были настроены на использование того, который находится в myapp.Services.

person Michael Buchok    schedule 08.04.2021