I have a project where I use mediatr, CQRS and onion architecture.
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, CreatedOrderDto>
{
private readonly IOrderRepository _orderRepository;
private readonly IProductRepository _productRepository;
private readonly IMapper _mapper;
public CreateOrderCommandHandler(IOrderRepository orderRepository,IProductRepository
productRepository, IMapper mapper)
{
_orderRepository = orderRepository;
_productRepository = productRepository;
_mapper = mapper;
}
public async Task<CreatedOrderDto> Handle(CreateOrderCommand request, CancellationToken
cancellationToken)
{
// _orderRepository.Add(order);
// _ productRepository.Update(product);
}
}
Should CreateOrder Command depend on Product Service instead of ProductRepository?
Should the repository be used in the Handler?
As per your code in the question, it should depend on ProductRepository not on the service, and service is a collection of classes while Reposiriry represents a single class so you cannot inject a service but you can inject a repository.
and by the way, I cannot see any Product Service in your code.
Related
When I attempt requests to a .net core 3.1 WebAPI from Postman I am getting error
System.InvalidOperationException: Unable to resolve service for type 'PaymentsAPI.Repository.PaymentService' while attempting to activate 'PaymentsAPI.Controllers.PaymentController'
'
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddCors(c =>
{
c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
});
services.AddDbContext<ApplicationDbContext>(o => o.UseSqlServer(Configuration.GetConnectionString("SqlSvrConn")));
services.AddTransient<IAsyncPaymentsService<PaymentDetail>, PaymentService>();
}
IAsyncPaymentsService.cs
public interface IAsyncPaymentsService<TEntity>
{
Task<IEnumerable<TEntity>> GetAllAsync();
}
PaymentService.cs
public class PaymentService : IAsyncPaymentsService<PaymentDetail>
{
private readonly ApplicationDbContext _dbContext;
public async Task<IEnumerable<PaymentDetail>> GetAllAsync()
{
return await _dbContext.PaymentDetails.ToListAsync();
}
}
PaymentController.cs
[ApiController]
[Route("[controller]")]
public class PaymentController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly PaymentService _service;
public PaymentController(ApplicationDbContext context, PaymentService service)
{
_context = context;
_service = service;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<PaymentDetail>>> GetAsync()
{
var items = (await _service.GetAllAsync());
return Ok(items);
}
}
I have tried rearranging the order of services in the container but the error still persists. What am I missing ?
The quick fix would be to change the controller constructor to depend on the abstraction instead of the implementation since the abstraction is what was registered with the container.
//...
private readonly ApplicationDbContext _context;
private readonly IAsyncPaymentsService<PaymentDetail> _service;
public PaymentController(ApplicationDbContext context, IAsyncPaymentsService<PaymentDetail> service)
{
_context = context;
_service = service;
}
//...
However, the generic abstraction could derived to a closed type if so desired
public interface IPaymentService : IAsyncPaymentsService<PaymentDetail> {
}
applied to the implementation
public class PaymentService : IPaymentService {
//...omitted for brevity
}
registered with the container
services.AddTransient<IPaymentService, PaymentService>();
and refactored in the controller
//...
private readonly ApplicationDbContext _context;
private readonly IPaymentService _service;
public PaymentController(ApplicationDbContext context, IPaymentService service)
{
_context = context;
_service = service;
}
//...
The only thing you should have to change to make this work is to accept the interface into your controller instead of the concrete service.
public PaymentController(ApplicationDbContext context, IAsyncPaymentsService<PaymentDetail> service)
{...}
This is recommended over taking the concrete type for various reasons such as testing. If you truly need the concrete type, you'd have to instead change your registration to
services.AddTransient<PaymentService>();
and leave your controller's constructor as is.
I am trying to create a CQRS-patterned application. I have handler classes to manage my business logic. But the handler constructors have many dependency and this leads to a lot of boilerplate. Is there any solution that allows me to inject all these items in a base handler class and make my handler more pure?
public class Handler : IRequestHandler<Command>
{
private readonly DataContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly string _value;
private readonly IMapper _mapper;
private readonly IEventBus _bus;
public Handler(
DataContext context,
IHttpContextAccessor httpContextAccessor,
IMapper mapper,
IEventBus bus)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
_mapper = mapper;
_bus = bus;
if (httpContextAccessor.HttpContext != null)
_value = _httpContextAccessor.HttpContext.Items["Value"].ToString();
}
public async Task<Unit> Handle(Command request, CancellationToken cancellationToken)
{
if (true) return Unit.Value;
throw new Exception("Error Message");
}
}
It is a common problem.
It is addressed very well with the Facade Pattern or Facade services.
What you do is you create a new service that works as a wrapper for the rest. In this way you inject only one service.
Single responsibility principle alert
You inject too many services in your class and this is typical code smell for single responsibility principle abuse. You should try to split your functionality in different classes. That would help you write cleaner code.
I have created an extension to map my types that implemented IHaveStandardMapping interface.
public static class AutomapperExtensions
{
public static TDest MapTo<TDest>(this object src, IMapper mapper)
where TDest: IHaveStandardMapping
{
return (TDest)mapper.Map(src, src.GetType(), typeof(TDest));
}
}
And I am using it in my service.
public class ComponentService : IComponentService
{
private readonly PostgresqlDataContext _context;
private readonly IMapper _mapper;
public ComponentService(PostgresqlDataContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<ComponentViewModel> GetComponent(string id)
{
var existing = await _context.Components.FirstOrDefaultAsync(s => s.Id == id);
if (existing == null)
throw new EntityNotFoundException("Component");
var result = existing.MapTo<ComponentViewModel>(_mapper);
return result;
}
}
But I need to get IMapper interface for all services.
I should use it in method
existing.MapTo<T>(_mapper);
The old versions were did not need the IMapper interface.
Is there a short way without using IMapper?
The static API is now deprecated. IMapper is used so you can inject the mapper in your controllers/services. I you want to you can create your own service wrapping the IMapper interface, so you can limit your dependency on the IMapper interface to one service only.
Please bear with me if I have misunderstood something fundamental, but...
Assuming a Controller need several dependency injected services, like a DBContext, an AutoMapper, maybe some other registered service (of course properly registered in the Startup class), is that possible?
Pseudo-code for a single injected service:
class MyController
{
private DBContext _context;
MyController(DBContext context)
{
_context = context;
}
}
But if I need several services, like (again just pseudo code):
class My2ndController
{
private DBContext _context;
private IMapper _mapper;
private SomeConfig _config;
My2ndController(DBContext context, IMapper mapper, SomeConfig config)
{
_context = context;
_mapper = mapper;
_config = config;
}
}
Is that possible?
Yes that's possible, you'll just need to make sure you register your service in your Startup.cs.
In your ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
// . . . code above
services.AddTransient<IMapper, Mapper>();
/// . . . code below
}
Now any controller that requires an IMapper interface will be passed the Mapper class upon creation.
Also, just know there are other lifetimes besides Transient. Such as Singleton, where there can only be 1 instance of that class.
Microsoft Unity DI offers a feature where you can register multiple objects for the same interface and give them a name. Then in your constructor you can use an attribute with the argument to specify by name which object you want to inject.
I am wondering how to achieve this same functionality using StructureMap.
Example:
container
.RegisterType<IMappingEngine, MappingEngine>("MappingEngineOne", new HierarchicalLifetimeManager(), new InjectionConstructor(typeof(MappingEngineOneConfiguration)))
.RegisterType<IMappingEngine, MappingEngine>("MappingEngineTwo", new HierarchicalLifetimeManager(), new InjectionConstructor(typeof(MappingEngineTwoConfiguration)))
....
public class MyServiceAgent {
private readonly IMappingEngine _mapper;
public MyServiceAgent([Dependency("MappingEngineOne")] IMappingEngine mapper) {
_mapper = mapper;
}
}
public class MyOtherServiceAgent {
private readonly IMappingEngine _mapper;
public MyOtherServiceAgent ([Dependency("MappingEngineTwo")] IMappingEngine mapper) {
_mapper = mapper;
}
}