Reputation: 21
I'm trying to implement email background service based on IHostedService
.
But I get this error:
System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Business.Abstract.ICustomerService Lifetime: Scoped ImplementationType: Business.Concrete.CustomerManager': Unable to resolve service for type 'DataAccess.Concrete.EntityFramework.SqlContext' while attempting to activate 'DataAccess.Concrete.EntityFramework.EfCustomerDal'.) (Error while validating the service descriptor 'ServiceType: DataAccess.Abstract.ICustomerDal Lifetime: Scoped ImplementationType: DataAccess.Concrete.EntityFramework.EfCustomerDal': Unable to resolve service for type 'DataAccess.Concrete.EntityFramework.SqlContext' while attempting to activate 'DataAccess.Concrete.EntityFramework.EfCustomerDal'.) (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: KodiksProject.API.Services.EmailBackgroundService': Unable to resolve service for type 'Business.Concrete.CustomerManager' while attempting to activate 'KodiksProject.API.Services.EmailBackgroundService'.)'
How can I fix this issue ?
ICustomerService
:
public interface ICustomerService
{
IResult Add(Customer customer);
IResult Update(Customer customer);
IResult Delete(int id);
IDataResult<Customer> Get(int id);
IDataResult<List<Customer>> GetAll();
Task SendEmailsToCustomersAsync();
}
CustomerManager
:
public class CustomerManager : ICustomerService
{
ICustomerDal _customerDal;
private readonly IEmailService _emailService;
public CustomerManager(ICustomerDal customerDal, IEmailService emailService)
{
_customerDal = customerDal;
_emailService = emailService;
}
public IResult Add(Customer customer)
{
_customerDal.Add(customer);
return new SuccessResult();
}
public IResult Delete(int id)
{
_customerDal.Delete(id);
return new SuccessResult();
}
public IDataResult<Customer> Get(int id)
{
return new SuccessDataResult<Customer>(_customerDal.Get(c => c.CustomerId == id));
}
public IDataResult<List<Customer>> GetAll()
{
return new SuccessDataResult<List<Customer>>(_customerDal.GetAll());
}
public IResult Update(Customer customer)
{
_customerDal.Update(customer);
return new SuccessResult();
}
public async Task SendEmailsToCustomersAsync()
{
var customerEmails = await _customerDal.GetAllCustomerEmailsAsync();
var tasks = new List<Task>();
foreach (var email in customerEmails)
{
var task = _emailService.SendEmailAsync(email, "asdasas", "asasadasdasd");
tasks.Add(task);
}
await Task.WhenAll(tasks);
}
}
SqlContext
:
public class SqlContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"my connection string");
}
public DbSet<Customer> Customers { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Product> Products { get; set; }
}
EfCustomerDal
:
public class EfCustomerDal : EfEntityRepositoryBase<Customer, SqlContext>, ICustomerDal
{
private readonly SqlContext _context;
public EfCustomerDal(SqlContext context)
{
_context = context;
}
public async Task<List<string>> GetAllCustomerEmailsAsync()
{
var emails = await _context.Customers
.Select(c => c.Email)
.ToListAsync();
return emails;
}
}
ICustomerDal
:
public interface ICustomerDal : IEntityRepository<Customer>
{
Task<List<string>> GetAllCustomerEmailsAsync();
}
EmailService
:
public class EmailService : IEmailService
{
private readonly SmtpClient _smtpClient;
public EmailService()
{
_smtpClient = new SmtpClient("server")
{
Port = 465,
Credentials = new NetworkCredential("username", "password"),
EnableSsl = true,
};
}
public async Task SendEmailAsync(string to, string subject, string body)
{
var mailMessage = new MailMessage("username", to, subject, body);
await _smtpClient.SendMailAsync(mailMessage);
}
}
I tried everything but can't solve this problem. This worked before I try to send emails with background worker. I can't see any problem with my dependency injection or service lives.
I'm stuck here and don't know what to do.
Can you help me with this situation?
Upvotes: 0
Views: 126
Reputation: 142273
This is covered in the Consuming a scoped service in a background task section of the docs. Hosted services are registered as singletons and they should not (can not if scope validation is enabled) consume the scoped dependencies. And by default EF Core context is added as a scoped one (i.e. by AddDbContext...
call). The solution is to inject IServiceProvider
and use it to resolve the dependency. For example:
public class ConsumeScopedServiceHostedService : BackgroundService
{
private readonly ILogger<ConsumeScopedServiceHostedService> _logger;
private readonly IServiceProvider Services;
public ConsumeScopedServiceHostedService(IServiceProvider services, ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Consume Scoped Service Hosted Service running.");
using var scope = Services.CreateScope();
var scopedProcessingService = scope.ServiceProvider
.GetRequiredService<IScopedProcessingService>();
await scopedProcessingService.DoWork(stoppingToken);
}
}
Read also:
Upvotes: 3