I am working on a webapi application on .net Core and I have a base controller from which all other controller derives from.
Here is the class:
public class ReadOnlyBaseController<TEntity, TEntityResource> : Controller
{
private readonly IMapper mapper;
private readonly IBaseUnitOfWork unitOfWork;
private readonly IBaseRepository<TEntity> repository;
public ReadOnlyBaseController(IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork,
IMapper mapper)
{
this.repository = repository;
this.unitOfWork = unitOfWork;
this.mapper = mapper;
}
[HttpGet]
[Authorize]
virtual public async Task<IActionResult> Get()
{
List<TEntity> TEntitys = await this.repository.GetTodos();
return Ok(TEntitys);
}
[HttpGet("Id")]
[Authorize]
virtual public IActionResult GeSingle(int Id)
{
TEntity tEntity = this.repository.GetSingle(Id);
TEntityResource tEntityResource = this.mapper.Map<TEntity, TEntityResource>(tEntity);
return Ok(tEntityResource);
}
}
However, some of my API endpoints do not require the Authorize attribute. So I created another base controller:
public class ReadOnlyNoOAuthBaseController<TEntity, TEntityResource> : Controller
{
private readonly IMapper mapper;
private readonly IBaseUnitOfWork unitOfWork;
private readonly IBaseRepository<TEntity> repository;
public ReadOnlyNoOAuthBaseController(IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork,
IMapper mapper)
{
this.repository = repository;
this.unitOfWork = unitOfWork;
this.mapper = mapper;
}
[HttpGet]
virtual public async Task<IActionResult> Get()
{
List<TEntity> TEntitys = await this.repository.GetTodos();
return Ok(TEntitys);
}
[HttpGet("Id")]
virtual public IActionResult GeSingle(int Id)
{
TEntity tEntity = this.repository.GetSingle(Id);
TEntityResource tEntityResource = this.mapper.Map<TEntity, TEntityResource>(tEntity);
return Ok(tEntityResource);
}
}
As you probably noticed, other than the [Authorize] attribute, the controllers are identical. Is there any way to make this work without the need to create a new controller?
Cheers!
Create the base controller with no authorizations attributes.
public class ReadOnlyBaseController<TEntity, TEntityResource> : Controller {
protected readonly IMapper mapper;
protected readonly IBaseUnitOfWork unitOfWork;
protected readonly IBaseRepository<TEntity> repository;
public ReadOnlyBaseController(
IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork, IMapper mapper) {
//...
}
[HttpGet]
public virtual async Task<IActionResult> Get() {
//..
}
[HttpGet("Id")]
public virtual IActionResult GeSingle(int Id) {
//...
}
}
Then in derived controllers that need auth you can add it on the controller itself
[Authorize]
public class ReadOnlyOAuthController<TEntity, TEntityResource> : ReadOnlyBaseController<TEntity, TEntityResource> {
public ReadOnlyOAuthController(
IBaseRepository<TEntity> repository, IBaseUnitOfWork unitOfWork, IMapper mapper)
: base(repository, unitOfWork, mapper) {
}
[AllowAnonymous]
[HttpGet("someaction")]
public IAction SomeOtherAction() {
//...
}
}
the [Authorize] attribute will apply to all actions on the derived controller and if you want to allow an action you can use the [AllowAnonymous] attribute.
Related
Update:
The Mediatr in the project is used without any customized logic for dispatching the messages. Can I say it's used as an event aggregator?
In the source code of https://github.com/JasonGT/NorthwindTraders, the Controller gets the Mediator from ControllerBase.
[ApiController]
[Route("api/[controller]/[action]")]
public abstract class BaseController : ControllerBase
{
private IMediator _mediator;
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>();
}
In the controller, it calls Mediator.Send(...) to send the message to the mediator.
public class EmployeesController : BaseController
{
// ....
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<EmployeeDetailVm>> Get(int id)
{
return Ok(await Mediator.Send(new GetEmployeeDetailQuery { Id = id }));
}
And the method Handle() in the inner class GetEmployeeDetailQuery.GetEmployeeDetailQueryHandler will be called for query message GetEmployeeDetailQuery. How is this wired?
public class GetEmployeeDetailQuery : IRequest<EmployeeDetailVm>
{
public int Id { get; set; }
public class GetEmployeeDetailQueryHandler : IRequestHandler<GetEmployeeDetailQuery, EmployeeDetailVm>
{
private readonly INorthwindDbContext _context;
private readonly IMapper _mapper;
public GetEmployeeDetailQueryHandler(INorthwindDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<EmployeeDetailVm> Handle(GetEmployeeDetailQuery request, CancellationToken cancellationToken)
{
var vm = await _context.Employees
.Where(e => e.EmployeeId == request.Id)
.ProjectTo<EmployeeDetailVm>(_mapper.ConfigurationProvider)
.SingleOrDefaultAsync(cancellationToken);
return vm;
}
}
}
In the startup.cs of that project, there's a call to AddApplication, which is an extension method from the NorthwindTraders.Application project, and is defined in DependencyInjection.cs. This calls services.AddMediatR(Assembly.GetExecutingAssembly());, which scans the assembly for handlers and registers them.
In general, you can register MediatR for your own projects by calling services.AddMediatr(Assembly.GetExecutingAssembly()) in your web application's Startup.ConfigureServices method.
My ApplictionDbContextClass looks like this :-
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
//private static ApplicationDbContext _context;
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public DbSet<Trip> Trips { get; set; }
public DbSet<Place> Places { get; set; }
public DbSet<UserTripDetail> UserTripDetails { get; set; }
public DbSet<TripPicture> TripPictures { get; set; }
}
My TripPictureController looks like this:-
//private readonly ApplicationDbContext _db = new ApplicationDbContext();
private readonly IUnitOfWork _unitOfWork;
private readonly ITripPictureRepository _tripPictureRepository;
public TripPicturesController(IUnitOfWork unitOfWork, ITripPictureRepository tripPictureRepository)
{
_unitOfWork = unitOfWork;
_tripPictureRepository = tripPictureRepository;
}
It also contains a post Action:-
[HttpPost]
public ActionResult Create(TripPicture model, HttpPostedFileBase ImageData)
{
if (ImageData != null)
{
model.TripId = 1;
model.Image = this.ConvertToBytes(ImageData);
}
_tripPictureRepository.Add(model);
_unitOfWork.Commit();
//_db.TripPictures.Add(model);
//_db.SaveChanges();
return View(model);
}
When ever i hit the post request, the model is not pushed into database. I am using dependency injection here. My guess is somewhere there is creation of different context object. i saw the following code in startup class :-
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(**ApplicationDbContext.Create**);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);}
My unitOfWork class is:-
public class UnitOfWork : IUnitOfWork
{
private DbContext _context;
public UnitOfWork(DbContext dbContext)
{
_context = dbContext;
}
public void Commit()
{
_context.SaveChanges();
}
}
and my repository class is :-
public class Repository<T> : IRepository<T> where T : class
{
protected DbSet<T> _dbSet;
public Repository(DbContext context)
{
_dbSet = context.Set<T>();
}
public void Add(T entity)
{
_dbSet.Add(entity);
}
}
The object is saved when i don't use unitOfWork. What is the problem!?
You are implementing Unit Of Work pattern incorrectly.
You are adding an item to _tripPictureRepository instance DbContext and calling _unitOfWork.Commit() on _unitOfWork instance which has another DbContext instance that has no idea about the added item (Item isn't tracked by the _unitOfWork's DbContext) which means it saves nothing.
The correct implementation of Unit of work is that your repositories should be exposed as properties, a DbContext injected to your Unit of work class and DbSet<T> of the repository will be populated from the DbContext like this:
public class UnitOfWork : IUnitOfWork
{
private DbContext _context;
public ITripPictureRepository TripsRepository{ get; }
public UnitOfWork(DbContext dbContext)
{
_context = dbContext;
Trips = new Repository<Trip>(_context.Trips)
}
public void Commit()
{
_context.SaveChanges();
}
}
Then in your controller inject an IUnitOfWork instance:
private readonly IUnitOfWork _unitOfWork;
public TripPicturesController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_tripPictureRepository = tripPictureRepository;
}
And now use the _unitOfWork instance to do your CRUD operations:
[HttpPost]
public ActionResult Create(TripPicture model, HttpPostedFileBase ImageData)
{
if (ImageData != null)
{
model.TripId = 1;
model.Image = this.ConvertToBytes(ImageData);
}
_unitOfWork.TripsRepository.Add(model);
_unitOfWork.Commit();
return View(model);
}
You can read more about Repository and Unit Of Work patter together from this Microsoft Docs page.
I found the issue. My DbContextObjects were different whenever generated by ninject. In my ninject file i was registering services in kernel as following :-
kernel.Bind<DbContext>().To<ApplicationDbContext>();
This by default uses Transient scope, i.e. new object is created every time required.
hence i needed to change that to following :-
kernel.Bind<DbContext>().To<ApplicationDbContext>().InRequestScope();
The requested scope means - Only a single instance of the type will be created, and the same instance will be returned for each subsequent request.
More can be found here :- https://github.com/ninject/ninject/wiki/Object-Scopes
I am trying to implement the repository pattern in asp core. Everything seems to work fine with a few adjustments,except adding it to the controller:
public class HomeController : Controller
{
private IDocumentRepository _context;
public HomeController()
{
_context = new DocumentRepository(new myContext());
}
}
DocumentRepository.cs
public class DocumentRepository : IDocumentRepository, IDisposable
{
private myContext context;
public DocumentRepository(myContext context) : base()
{
this.context = context;
}
public IEnumerable<Document> GetDocuments()
{
return context.Document.ToList();
}
public Document GetDocumentByID(int id)
{
return context.Document.FirstOrDefault(x => x.Id == id);
}
IDocumentRepository.cs
public interface IDocumentRepository : IDisposable
{
IEnumerable<Document> GetDocuments();
Document GetDocumentByID(int documentId);
void InsertDocument(Document student);
void DeleteDocument(int documentID);
void UpdateDocument(Document document);
void Save();
}
The error
There is no argument given that corresponds to the required formal
parameter 'options' of
'myContext.myContext(DbContextOptions)
dotnetcore..NETCoreApp,Version=v1.0
Simply resolve IDocumentRepository from the DI container using constructor injection instead of manually instantiating it and it should work:
public class HomeController : Controller {
private IDocumentRepository _repository;
public HomeController(IDocumentRepository repository) {
_repository = repository;
}
}
For that, you'll need to ensure IDocumentRepository is correctly registered in ConfigureServices:
public void ConfigureServices(IServiceCollection services) {
services.AddScoped<IDocumentRepository, DocumentRepository>();
}
I'm learning on doing repository with unit of work. I also know how to do DI/IOC. But my problem is I can't figure out where to apply Unit of Work in my code. Below is a Generic Repository.
public abstract class Repository<T, C> : //IDisposable,
IRepository<T> where T : class
where C : DbContext, new()
{
private C entities = new C();
public C Context
{
get
{
return this.entities;
}
set
{
this.entities = value;
}
}
public virtual void Insert(T entity)
{
this.entities.Set<T>().Add(entity);
}
// remove some code for brevity
}
What I had tried so far:
Make a Unit of Work class
public class UnitOfWork : IUnitOfWork
{
private readonly FooContext _dbContext;
public UnitOfWork()
{
_dbContext = new DataContext;
}
public void Save()
{
_dbContext.SaveChanges();
}
// Dispose method
}
In my service:
public class ProductService : Repository<Product, FooContext>, IProductService
{
private readonly IProductRepository _prodRepo;
private readonly IUnitOfWork _uow;
public ProductService(IUnitOfWork uow, IProductRepository prodRepo)
{
_uow = uow;
_prodRepo = prodRepo;
}
public override void Insert(Item entity)
{
base.Insert(entity);
Save();
}
public void Save()
{
uow.Save();
}
// remove some code for brevity
}
There's no error when I build it. And when I try it apply in my Controller it doesn't give me some error. But when I try to run and debug it, in the Intellitrace, It doesn't give me an Insert statement and again, it does not give me an error.. Where have I gone wrong?
Any help would be much appreciated. Thanks!
We saw together you should separate your service from your repository.
Here, your problem seems to come from the fact you use two different dbcontext instances.
one in your repository and one in your UnitOfWork.
Instead, you should inject the same Dbcontext instance in your repository and your UnitOfWork.
EDIT:
You should write your different layers like this:
public class ProductService
{
private readonly IProductRepository productRepository;
private readonly IUnitOfWork unitOfWork;
public ProductService(IProductRepository productRepository, IUnitOfWork unitOfWork)
{
this.productRepository = productRepository;
this.unitOfWork = unitOfWork;
}
public IEnumerable<Product> GetCurrentProductsOnOrderForCustomer(int customerId)
{
// etc.
}
}
The controller layer should do this:
public class ProductController : Controller
{
private readonly IProductService prodService;
public ProductController(IProductService prodService)
{
this.prodService = prodService;
}
}
Here is your corrected UnitOfWork:
public class UnitOfWork : IUnitOfWork
{
private readonly IDbContext _dbContext;
public UnitOfWork(IDbContext dbContext)
{
_dbContext = dbContext;
}
public void Save()
{
_dbContext.SaveChanges();
}
// Dispose method
}
Here is an example of Repository
public class ProductRepository
{
private readonly IDbContext _context;
public ProductRepository(IDbContext dbContext)
{
this._context = context;
}
public virtual void Insert(T entity)
{
this._context.Products.Add(entity);
}
// remove some code for brevity
}
And you create a context class that inherits from DbContext, and that you inject in UoW and Repos.
public class MyApplicationContext : DbContext
{
public MyApplicationContext(string connectionString)
{
// Configure context as you want here
}
public DbSet<Product> Products { get; set; }
}
I have been to told here that I should separate my Service class and Repository class, so I did. And below is an example.
public class ProductService
{
private readonly IProductRepository productRepository;
public ProductService(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
public IEnumerable<Product> GetCurrentProductsOnOrderForCustomer(int customerId)
{
// etc.
}
}
But how do I use this in my Controller? How I use it is like this:
public class ProductController : Controller
{
ProductService prodService = new ProductService();
}
But I've read that should implement abstraction. Should I make another class called IProductService and use it like this?
public class ProductController : Controller
{
private readonly IProductService _productService;
private readonly IUnitOfWork _uow;
public ProductController(IProductService productService, IUnitOfWork uow)
{
_uow = uow;
_productService = productService;
}
}
An example of IProductService would be great. Any help would be much appreciated. Thanks.
You should write your different layers like this:
public class ProductService : IProductService
{
private readonly IProductRepository productRepository;
private readonly IUnitOfWork unitOfWork;
public ProductService(IProductRepository productRepository, IUnitOfWork unitOfWork)
{
this.productRepository = productRepository;
this.unitOfWork = unitOfWork;
}
public IEnumerable<Product> GetCurrentProductsOnOrderForCustomer(int customerId)
{
// etc.
}
}
So the controller layer should do this:
public class ProductController : Controller
{
private readonly IProductService prodService;
public ProductController(IProductService prodService)
{
this.prodService = prodService;
}
}
And your webapp layer, should use Dependency Injection to fill the different constructors. Or you can do it manually in case of a small business domain.