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>));
}
Related
I'm hitting the error in the title when running this setup code:
Program.cs:
builder.Services.AddDbContext<TDBContext>(opt => opt.UseInMemoryDatabase("My"));
// Can't work out how to wire up the Repository?
//builder.Services.AddScoped<IRepository>(p => new TDBContext());
//builder.Services.AddScoped<IRepository, Repository>();
builder.Services.AddScoped(typeof(IRepository), typeof(Repository<>));
//builder.Services.AddScoped(typeof(IRepository), typeof(Repository<TDBContext>));
builder.Services.AddScoped<IMyService, MyService>();
var app = builder.Build(); //ERROR HERE!
Service and Repository:
public class MyService : IMyService
{
private readonly IRepository _repository;
public MyService(IRepository repository)
{
_repository = repository;
}
}
public class Repository<TDBContext> : IRepository where TDBContext : DbContext
{
protected DbContext dbContext;
public Repository(DbContext context)
{
dbContext = context;
}
public async Task<int> CreateAsync<T>(T entity) where T : class
{
this.dbContext.Set<T>().Add(entity);
return await this.dbContext.SaveChangesAsync();
}
//.....
}
public class TDBContext : DbContext
{
public TDBContext(DbContextOptions<TDBContext> options)
: base(options)
{
}
public virtual DbSet<MyTransaction> Transactions { get; set; } = null!;
public TDBContext()
{
}
}
I've tried a few suggestions found on here shown as code comments but no luck. Can someone please clarify how I wire up the Repository and get the DI to load in the DbContext?
Check the repository constructor. The container does not know how to handle DbContext as dependency when resolving the repository.
Did you mean to use the generic argument type instead?
Also the naming of the generic parameter might cause confusion.
public class Repository<TContext> : IRepository where TContext : DbContext {
protected DbContext dbContext;
public Repository(TContext context) {
dbContext = context;
}
public async Task<int> CreateAsync<T>(T entity) where T : class {
this.dbContext.Set<T>().Add(entity);
return await this.dbContext.SaveChangesAsync();
}
//.....
}
And the registration will need to use the closed type
//...
builder.Services.AddScoped<IRepository, Repository<TDBContext>>();
//...
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.
I am trying to create a .NET core application that connects to multiple databases, but using one generic repository that contains the logic for all CRUD operations. What is the best way to achieve this?
public Repository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
_set = _dbContext.Set<T>();
}
Above is the constructor of my repository. Here, I inject the ApplicationDbContext. I am looking for a way to make this ApplicationDbContext generic, so I only have to need only one repository, in which I can inject different contexts for accessing multiple databases. Essentially I am looking for something like this:
public class Repository_1<T> where T:EntityBase
{
public Repository_1(IDbContext dbContext)
{
}
}
Where I can swap out the dbContext and replace it with another context that connects to another Database.
Create base context and including all settings into this, DBSET:
public abstract class BaseContext : DbContext
{
public BaseContext(DbContext options)
: base(options)
{ }
public DbSet<object> FirstDbSet { get; set; }
...
}
inherit from BaseContext for both DBs(Databases):
public class NavaContext : BaseContext
{
public NavaContext (DbContext<NavaContext> options) : base(options)
{
}
}
public class StackContext : BaseContext
{
public StackContext(DbContext<StackContext> options) : base(options)
{
}
}
and register both in Startup.cs:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<NavaContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LATAMConnectionString")));
services.AddDbContext<StackContext>(options => options.UseSqlServer(Configuration.GetConnectionString("EUConnectionString")));
// Autofac
var builder = new ContainerBuilder();
// needed only if you plan to inject ICollection<BaseContext>
builder.RegisterType<NavaContext>().As<BaseContext>();
builder.RegisterType<StackContext>().As<BaseContext>();
builder.Populate(services);
return new AutofacServiceProvider(builder.Build());
}
add connection strings in appsettings.json:
"ConnectionStrings": {
"NavaConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true",
"StackConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
and now you can inject both contexts:
public class ReportRepository : IReportRepository
{
private readonly NavaContext latamDbContext;
private readonly StackContext euDbContext;
public ReportRepository(NavaContext latamDbContext, StackContext euDbContext)
{
this.latamDbContext = latamDbContext;
this.euDbContext = euDbContext;
}
}
or if you plan to inject collection of contexts:
public class ReportRepository : IReportRepository
{
private readonly ICollection<BaseContext> dbContexts;
public ReportRepository(ICollection<BaseContext> dbContexts)
{
this.dbContexts = dbContexts;
}
}
to access specific context:
var _stackContext= dbContexts.FirstOrDefault(x => x is StackContext) as StackContext;
var _navaContext= dbContexts.FirstOrDefault(x => x is NavaContext) as NavaContext;
You could set two parameters for your repository and add constrains on them, asusume that your dbContext is inherited from DbContext
public class TestRepository<TContext, T> : ITestRepository<TContext,T>
where TContext : DbContext
where T : BaseEntity
{
private readonly TContext _context;
private DbSet<T> entities;
public TestRepository(TContext context)
{
_context = context;
entities = context.Set<T>();
}
public List<T> GetAll()
{
return entities.AsNoTracking().ToList();
}
}
ITestReposiroty:
public interface ITestRepository<TContext,T>
where TContext : DbContext
where T : BaseEntity
{
List<T> GetAll();
}
Startup.cs
services.AddScoped(typeof(ITestRepository<,>), typeof(TestRepository<,>));
Controller:
public class ProductsController : ControllerBase
{
private readonly ITestRepository<ApplicationDbContext, Product> _repository;
public TestRepoController(ITestRepository<ApplicationDbContext, Product> repository)
{
_repository = repository;
}
// GET api/products
[HttpGet]
public List<Product> Get()
{
return _repository.GetAll();
}
}
In your RepositoryBase class:
public abstract class RepositoryBase<TContext, TEntity>
where TContext : DbContext
where Entity: class
public TContext Context { get; private set; }
public RepositoryBase(TContext context)
{
this.Context = context;
}
public IQueryable<TEntity> GetAll()
{
return this.Context.Set<TEntity>().AsNoTracking();
}
Usage from a specific repository:
public class ComputerGroupRepo : RepositoryBase<RequestContext, ComputerGroup>
{
public ComputerGroupRepo()
: base(new RequestContext())
{
}
public IQueryable<ComputerGroup> GetComputerGroups()
{
IQueryable<ComputerGroup> result = this.GetAll()
.Include(v => v.ComputerGroupIps);
return result;
}
}
BaseRepository class can use like this and it is use inheritance on data access layer classes.
public class GenericRepository<TEntity, TContext> : IGenericRepository<TEntity>
where TEntity : class
where TContext : DbContext, new()
{
private readonly DbContext _context;
private readonly DbSet<TEntity> _dbSet;
public GenericRepository()
{
_context = new TContext();
_dbSet = _context.Set<TEntity>();
}
}
Data access class;
public class ProductDal:GenericRepository<Product,AppDbContext>,IProductDal
{
}
If use dependency injection data access classes must be registered. Generic Repository can not register, because it instance inside class. _context = new TContext();
I've been searching for the Repository and Unit of Work Pattern in C#, and so far this is what I found:
public class Repository<T> : IRepository<T> where T:class
{
private DbContext context;
private DbSet<T> _dbSet;
public Repository(DbContext context)
{
this.context = context;
_dbSet = context.Set<T>();
}
}
However, in a project that was given to me for study, I saw this one:
public class Repository<T> : IRepository<T> where T:class
{
private readonly IUnitOfWork _unitOfWork
private readonly DbSet<T> _dbSet;
public Repository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork
_dbSet = ((DbContext)_unitOfWork).Set<T>();
}
}
Can someone please clarify the difference for me? Any explanation will be highly appreciated. Thanks!
No much diference technically, but the use of the interface is much better, because you can abstract the DbContext, look if you need to change the EF to another ORM, you will just need to implement an concrete UnitOfWork, so the change will be less traumatic.
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;
}
}