Search code examples
c#azure-cosmosdbasp.net-core-2.2entity-framework-core-2.2graphql.net

Cannot resolve 'GraphQL.Resolvers.ICountriesResolver' from root provider because it requires scoped service 'Query.Persistence.SampleDbContext'


I am working on API development project using ASP.NET Core 2.2, GraphQL.NET , CosmosDB, EntityFrameworkCore (Microsoft.EntityFrameworkCore.Cosmos(2.2.4).

On running the solution, I see an error :

Cannot resolve 'GraphQL.Resolvers.ICountriesResolver' from root provider because it requires scoped service 'Query.Persistence.SampleDbContext'.

Here goes my code details:

Startup.cs

 public void ConfigureServices(IServiceCollection services)
 {
        string serviceEndPoint = this.Configuration.GetValue<string>("CosmosDBEndpoint");
        string authKeyOrResourceToken = this.Configuration.GetValue<string>("CosmosDBAccessKey");
        string databaseName = this.Configuration.GetValue<string>("CosmosDBName");
        services.AddEntityFrameworkCosmos();
        services.AddDbContext<SampleDbContext>(options => options.UseCosmos(serviceEndPoint, authKeyOrResourceToken, databaseName, contextOptions =>
        {
            contextOptions.ExecutionStrategy(d => new CosmosExecutionStrategy(d));
        }

        ));
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
        services.AddSingleton<IUtilityService, UtilityService>();
        services.AddTransient<ICommonService, CommonService>();
        services.AddTransient<ICountryService, CountryService>();
        services.AddSingleton<CountryResultType>();
        services.AddSingleton<GraphQLQuery>();
        services.AddTransient<ICountriesResolver, CountriesResolver>();
        services.AddSingleton<CountryType>();
        services.AddSingleton<Response>();
        services.AddScoped(typeof(ResponseGraphType<>));
        services.AddScoped(typeof(ResponseListGraphType<>));
        services.AddSingleton<IDataLoaderContextAccessor, DataLoaderContextAccessor>();
        services.AddSingleton<DataLoaderDocumentListener>();
        services.AddTransient<IAddressRepository, AddressRepository>();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest);
        services.AddSingleton<SampleSchema>();
        var sp = services.BuildServiceProvider();
        services.AddSingleton<ISchema>(new SampleSchema(new FuncDependencyResolver(type => sp.GetService(type))));
} 

CountriesResolver.cs

public class CountriesResolver : Resolver, ICountriesResolver
{
    private readonly ICountryService _countryService;
    private readonly IHttpContextAccessor _accessor;
    private readonly IUtilityService _utilityService;
    public CountriesResolver(ICountryService countryService, IHttpContextAccessor accessor, IUtilityService utilityService)
    {
        _countryService = countryService;
        _accessor = accessor;
        _utilityService = utilityService;
    }

    public void Resolve(GraphQLQuery graphQLQuery)
    {
        graphQLQuery.Field<ResponseGraphType<CountryResultType>>("countriesresponse", resolve: context =>
        {
            var locale = _utilityService.GetLocale(_accessor.HttpContext.Request.Headers);
            var list = _countryService.GetAllCountries(locale);
            return Response(list);
        }

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

CountryService.cs

public class CountryService : ICountryService
{
    private readonly SampleDbContext _dbContext;
    private readonly ICommonService _commonService;
    private readonly IOptions<AppSettings> _appSettings;
    public CountryService(SampleDbContext dbContext, ICommonService commonService, IOptions<AppSettings> appSettings)
    {
        _dbContext = dbContext;
        _commonService = commonService;
        _appSettings = appSettings;
    }

    public async Task<CountryResult> GetAllCountries(string locale)
    {
        var result = new CountryResult();
        var language = _commonService.GetLanguageFromLocale(locale);
        var localeLangId = language.LanguageId;
        var dftLanguageId = int.Parse(_appSettings.Value.DefaultLanguageId);
        var dftDisplayName = await _dbContext.Countries.Where(cc => cc.IsPublished.Equals(true) && cc.LanguageId.Equals(dftLanguageId)).Select(df => df.DisplayNameShort).FirstOrDefaultAsync();
        var countries = await (_dbContext.Countries.Where(cc => cc.IsPublished.Equals(true) && cc.LanguageId.Equals(localeLangId)).Join(_dbContext.Disclaimers.Where(dc => dc.LanguageId.Equals(localeLangId)), c => c.CountryId, d => d.DefaultCountryId, (c, d) => new CountryDTO{Uuid = c.CountryId, DisplayName = c.DisplayName ?? dftDisplayName, DisplayNameShort = c.DisplayName ?? dftDisplayName, ProviderName = d.ProviderName, ProviderTerms = d.ProviderTerms, Name = dftDisplayName, Path = dftDisplayName, CompleteResponse = true}).ToListAsync());
        result.Countries = countries;
        return result;
    }
}

GraphQLQuery.cs

public class GraphQLQuery : ObjectGraphType
{
    public GraphQLQuery(IServiceProvider serviceProvider)
    {
        var type = typeof(IResolver);
        var resolversTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(p => type.IsAssignableFrom(p));
        foreach (var resolverType in resolversTypes)
        {
            var resolverTypeInterface = resolverType.GetInterfaces().Where(x => x != type).FirstOrDefault();
            if (resolverTypeInterface != null)
            {
                var resolver = serviceProvider.GetService(resolverTypeInterface) as IResolver;
                resolver.Resolve(this);
            }
        }
    }
}

Can anyone help me here to fix this issue?


Solution

  • I quickly created a demo .net core 3.0 web application, and tested as you showed above. However, everything was fine at my side.

    The CountriesResolver and CountryService classes seem good. But you did not post your SampleDbContext class. You may have a check of it.

    Here are my code snippets:

    Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
    
        services.AddDbContext<CosmosDBContext>(options => options.UseCosmos(
            "https://jackcosmos.documents.azure.com:443/",
            "Y1tiY********lJurBg==",
            "DB"
        ));
    
        services.AddTransient<IMyService, MyService>();
        services.AddTransient<IMyResolver, MyResolver>();
    }
    

    CosmosDBContext.cs

    For convenience, I just tested with Cosmos DB SQL API. But, it should not affect the DI:

    public class CosmosDBContext : DbContext
    {
        public CosmosDBContext(DbContextOptions<CosmosDBContext> options) : base(options)
        {
    
        }
    
        public DbSet<Item> Items { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasDefaultContainer("Item");
        }
    }
    
    public class Item
    {
        public Item(string key, string content) => (this.key, this.content) = (key, content);
    
        public string id { get; set; } = Guid.NewGuid().ToString();
        public string key { get; set; }
        public string content { get; set; }
    }
    

    MyService.cs

    public class MyService : IMyService
    {
        private CosmosDBContext _cosmosDbContext;
    
        public MyService(CosmosDBContext cosmosDBContext)
        {
            _cosmosDbContext = cosmosDBContext;
        }
    
        public List<Item> GetByKey(string key)
        {
            return _cosmosDbContext.Items.Where(i => i.key.Equals(key)).ToList();
        }
    }
    

    MyResolver.cs

    public class MyResolver : IMyResolver
    {
        private IMyService _myService;
    
        public MyResolver(IMyService myService)
        {
            _myService = myService;
        }
    
        public List<Item> Resolve(string key)
        {
            return _myService.GetByKey(key);
        }
    }
    

    And In my HomeController, I just:

    private IMyResolver _myResolver;
    
    public HomeController(ILogger<HomeController> logger, CosmosDBContext cosmosDBContext, IMyResolver myResolver)
    {
        _myResolver = myResolver;
    
        // Add one
        cosmosDBContext.Database.EnsureCreated();
        cosmosDBContext.Items.Add(new Item("aaa", "abcdefg"));
        cosmosDBContext.SaveChanges(true);
    
        _logger = logger;
    }
    
    public IActionResult Index()
    {
        var result = _myResolver.Resolve("aaa");
        return Ok(JsonConvert.SerializeObject(result));
    }
    

    The output:

    enter image description here