Referencing a common instance from a DI constructor - c#

Using Ninject DI, I have implemented two interfaces that I instantiate from my MVC controllers. For example:
public class MyController : Controller
{
private readonly IUnitOfWork _UnitOfWork;
private readonly IAssetService _AssetService;
public MyController(IUnitOfWork unitOfWork, IAssetService assetService)
{
this._UnitOfWork = unitOfWork;
this._AssetService = assetService;
}
// Controller actions etc.
}
In my Ninject module I have created the following bindings:
public class DomainModule : NinjectModule
{
public override void Load()
{
Bind<IUnitOfWork>()
.To<SqlUnitOfWork>()
.InRequestScope()
.WithConstructorArgument("connectionString", "MyDb.Database");
Bind<IAssetService>()
.To<FileSystemAssetService>()
.WithConstructorArgument("rootPath", "C:\\DataStore");
}
}
I now want to inject the IUnitOfWork instance into my IAssetService so I have considered making this a property of IAssetService and modifying my controllers as follows:
public class MyController : Controller
{
private readonly IUnitOfWork _UnitOfWork;
private readonly IAssetService _AssetService;
public MyController(IUnitOfWork unitOfWork, IAssetService assetService)
{
this._UnitOfWork = unitOfWork;
this._AssetService = assetService;
this._AssetService.UnitOfWork = this._UnitOfWork;
}
// Controller actions etc.
}
but I wondered if there was a better/cleaner way of doing this using a different DI technique - ideally I would like to add the IUnitOfWork to the AssetService constructor?

Then why not simply inject the IUnitOfWork into the AssetService?
public class FileSystemAssetService : IAssetService
{
private readonly IUnitOfWork unitOfWork;
private readonly string rootPath;
public FileSystemAssetService(IUnitOfWork unitOfWork, string rootPath)
{
this.unitOfWork = unitOfWork;
this.rootPath = rootPath;
}
}

Related

How do I resolve "Unable to resolve service for type '' while attempting to activate ''"

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.

Unit Of Work / Repository Pattern, Type Not Found In Cache

I am trying to implement a repository pattern for learning purposes in a project. I am using MVVM Light to register interfaces and am trying to inject into a view model.
I have removed all the other repositories for ease of reading and only included the RuleRepository.
View Model Locator
The code breaks when trying to register the IUnitOfWork and gives the error - Type not found in cache: YAI.BomConfigurator.Core.Context.BomConfiguratorContext.
Note: Not sure if I need to register the IRepository interface here?
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
SimpleIoc.Default.Register<IUnitOfWork, UnitOfWork>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
SimpleIoc.Default.Register<IUnitOfWork, UnitOfWork>();
}
SimpleIoc.Default.Register<LoginViewModel>();
}
public LoginViewModel LoginViewModel
{
get
{
return ServiceLocator.Current.GetInstance<LoginViewModel>();
}
}
}
LoginViewModel
Here is where I try and inject an IUnitOfWork into the view model.
public class LoginViewModel : ViewModelBase
{
private readonly IUnitOfWork _UnitOfWork;
public LoginViewModel(IUnitOfWork unitOfWork)
{
_UnitOfWork = unitOfWork;
}
}
IUnitOfWork
public interface IUnitOfWork : IDisposable
{
IRuleRepository Rules { get; }
int Complete();
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly BomConfiguratorContext _context;
public IRuleRepository Rules { get; private set; }
public UnitOfWork(BomConfiguratorContext context)
{
_context = context;
}
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
RuleRepository
public class RuleRepository : Repository<Rule>,IRuleRepository
{
public RuleRepository(BomConfiguratorContext context)
: base(context)
{
}
}
I copied a lot of this from Mosh Hamedani youtube video and tried adding in the bit where we inject into the constructor of the view model. I am a newbie at dependency injection and IOC containers so not to sure what exactly I am doing wrong here.
If someone could give me a good explanation as to what I need to change etc. that would be great. I know many people believe the Unit of Work / Repository Pattern is an anti pattern but I am not concerned about that, this is purely for learning purposes.
Thank you for any suggestions!
You are not only injecting an instance of UnitOfWork into LoginViewModel, you are also injecting an instance of BomConfiguratorContext into UnitOfWork when you instantiate UnitOfWork:
public class UnitOfWork : IUnitOfWork
{
private readonly BomConfiguratorContext _context;
public IRuleRepository Rules { get; private set; }
// here you inject a BomConfiguratorContext, but none is registered in the VM Locator
public UnitOfWork(BomConfiguratorContext context)
{
_context = context;
}
...
}
So you need to change your ViewModelLocator to also register BomConfiguratorContext:
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
SimpleIoc.Default.Register<IUnitOfWork, UnitOfWork>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
SimpleIoc.Default.Register<IUnitOfWork, UnitOfWork>();
}
SimpleIoc.Default.Register<LoginViewModel>();
// Missing something like this (not sure what interface it implements...)
SimpleIoc.Default.Register<IConfiguratorContext, BomConfiguratorContext>();
}

C# Automapper Generic Mapping

When playing around with AutoMapper I was wondering whether the following is possible to implement like this (haven't been able to set it up correctly).
Base Service:
public class BaseService<T, IEntityDTO> : IService<T, IEntityDTO> where T : class, IEntity
{
private IUnitOfWork _unitOfWork;
private IRepository<IEntity> _repository;
private IMapper _mapper;
public BaseService(IUnitOfWork unitOfWork, IMapper mapper)
{
_unitOfWork = unitOfWork;
_repository = unitOfWork.Repository<IEntity>();
_mapper = mapper;
}
public IList<IEntityDTO> GetAll()
{
return _mapper.Map<IList<IEntityDTO>>(_repository.GetAll().ToList());
}
}
Concrete Service:
public class HotelService : BaseService<Hotels, HotelsDTO>, IHotelService
{
private IUnitOfWork _unitOfWork;
private IRepository<Hotels> _hotelsRepository;
private IMapper _mapper;
public HotelService(IUnitOfWork unitOfWork, IMapper mapper) : base(unitOfWork, mapper)
{
_unitOfWork = unitOfWork;
_hotelsRepository = unitOfWork.Repository<Hotels>();
_mapper = mapper;
}
}
Current mappings:
public class AutoMapperProfileConfiguration : Profile
{
protected override void Configure()
{
CreateMap<Hotels, HotelsDTO>().ReverseMap();
}
}
I'm kindly clueless on how the mapping should be done. Anyone any advice or is this just not the way to go?
You can specify DTO type in BaseService as generic parameter:
public class BaseService<T, TDTO> : IService<T, TDTO>
where T : class, IEntity
where TDTO : class, IEntityDTO
{
private IRepository<T> _repository;
...
...
public IList<TDTO> GetAll()
{
return _mapper.Map<IList<TDTO>>(_repository.GetAll().ToList());
}
}
Managed to solve my problem with the following line of code which looks up the mapping of the passed entity to the basecontroller.
public List<TDTO> GetAll()
{
var list = _repository.GetAll().ToList();
return (List<TDTO>)_mapper.Map(list, list.GetType(), typeof(IList<TDTO>));
}

How to implement a UserFactory using IHttpContextAccessor

I used to have a UserFactory (before vNext) that used HttpContext.Current but since that is now gone I am having trouble recreating it.
I want it to be a static class that sets and gets the current user to access user data throughout the application.
I know I must use the DI system but not sure how.
Code so far:
public class CurrentContext : IHttpContextAccessor
{
private IHttpContextAccessor ctx;
public HttpContext HttpContext
{
get
{
return ctx.HttpContext;
}
set
{
ctx.HttpContext = value;
}
}
}
services.AddTransient<IHttpContextAccessor, CurrentContext>();
public class UserFactory
{
private IHttpContextAccessor _context;
public UserFactory(IHttpContextAccessor Context)
{
_context = Context;
}
public void Add(string s) => _context.HttpContext.Session.SetString(s, s);
public string Get(string s) => _context.HttpContext.Session.GetString(s);
}
How can I get a UserFactory instance anywhere in my app with the current context?
I suggest you make the UserFactory class non-static and register it as scoped:
services.AddScoped<UserFactory>();
This will create one instance per web request. You can inject this into every other class and let the UserFactory take a dependency on IHttpContextAccessor to get the current HttpContext.
This adheres to the dependency inversion philosophy Microsoft is trying to implement in ASP.NET 5. Static classes do not really fit into this and should be avoided as much as possible.
Example
UserFactory class:
public class UserFactory
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserFactory(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
// other code...
}
ConfigureServices() in Startup class:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddScoped<UserFactory>();
// ...
}
Now you can inject an instance of UserFactory in a controller for example:
public class SomeController : Controller
{
private readonly UserFactory _userFactory;
public SomeController(UserFactory userFactory)
{
_userFactory = userFactory;
}
// ...
}
Now when UserFactory is begin resolved, IHttpContextFactory inside UserFactory will also be resolved.

How to inject the same instance of a class with Ninject?

I am using Ninject for IoC. I have the following classes.
// Repository
public class EFProductRepository : IProductRepository, IUnitOfWorkRepository
{
private IUnitOfWork unitOfWork;
private EFDbContext efDbContext;
public EFProductRepository(IUnitOfWork uow)
{
unitOfWork = uow;
efDbContext = new EFDbContext();
}
//
}
// Controller
public class ProductController : Controller
{
private IUnitOfWork unitOfWork;
private IProductRepository productRepository;
public ProductController(IUnitOfWork uow, IProductRepository repo)
{
unitOfWork = uow;
productRepository = repo;
}
}
Currently my ninject bindings are as follow which assign new instance of the concrete class for the interface.
ninjectKernel.Bind<IUnitOfWork>().To<UnitOfWork>();
ninjectKernel.Bind<IProductRepository>().To<EFProductRepository>();
using my ninject controller factory, I need to inject same instance of the IUnitOfWork class to the ProductController and EFProductRepository. Please guide me.

Categories