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>();
}
Related
I am attempting to implement a Repository pattern and make use of Dependency Injection. In the code below I have a Genmeric Repository interface and class from which other repository interfaces and classes are derived from, the example below InvestmentTransactionRepository.
In my GenericRepository class I am attempting to DI the application dbContext.
Code:
Interfaces/IGenericRepository.cs
namespace WebApi.Shared.Interfaces
{
public interface IGenericRepository<T> where T : class
{
}
}
Interfaces/IInvestmentTransactionRepository.cs
namespace WebApi.Shared.Interfaces
{
public interface IInvestmentTransactionRepository : IGenericRepository<InvestmentTransactionEntity>
{
}
}
/Repositories/GenericRepository.cs
using WebApi.Shared.Interfaces;
namespace WebApi.Shared.Repositories
{
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected readonly AccountingContext _context;
public GenericRepository(AccountingContext context)
{
_context = context;
}
}
}
/Repositories/InvestmentTransactionRepository.cs
namespace WebApi.Shared.Repositories
{
public class InvestmentTransactionRepository : GenericRepository<InvestmentTransactionEntity>, IInvestmentTransactionRepository
{
public InvestmentTransactionRepository(AccountingContext dbContext) : base(dbContext)
{
}
}
}
/Controllers/InvestmentController.cs
namespace WebApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class InvestmentsController : ControllerBase
{
private Services.IUserService _userService;
private Shared.Repositories.InvestmentTransactionRepository _investmentTransactionRepository;
public InvestmentsController(Services.IUserService userService,
WebApi.Shared.Repositories.InvestmentTransactionRepository investmentTransactionRepository)
{
_userService = userService;
_investmentTransactionRepository = investmentTransactionRepository;
}
[Authorize]
[HttpPost("list")]
public IActionResult List(RequestContext.Investment.ListDto request)
{
}
}
}
/AccountingContext.cs
namespace WebApi.Shared
{
public class AccountingContext : DbContext
{
public AccountingContext()
{
}
public AccountingContext(DbContextOptions<AccountingContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// hidden
}
}
}
/Program.cs
services.AddScoped<AccountingContext>();
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped<IInvestmentEntityRepository, InvestmentEntityRepository>();
services.AddScoped<IInvestmentTransactionRepository, InvestmentTransactionRepository>();
When I run the above, the build is succesfull but the following error is produced at run time:
Unable to resolve service for type "WebApi.Shared.Repositories.InvestmentTransactionRepository" while attempting to activate "WebApi.Controllers.InvestmentsController"
Can anyone see what I am doing wrong?
You're adding IInvestmentTransactionRepository,the interface, to the container but attempting to inject and resolve WebApi.Shared.Repositories.InvestmentTransactionRepository, the class. You should either add the class to the container or (better) inject the interface.
I am building a web application where I will have a lot of controllers with their corresponding action methods in them.
I want to save every exception in database and for this reason I have created
ExceptionService (DbContext is injected in it).
let's say that this is the general form of my controllers:
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly UserManager userManager;
private readonly IExceptionService exceptionService;
public UserController(UserManager userManager, IExceptionService exceptionService)
{
this.userManager = userManager;
this.exceptionService = exceptionService;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] User user)
{
try
{
//some code
}
catch (Exception e)
{
exceptionService.Save(e);
//some code
}
}
}
In order to avoid so many try-catch blocks I decided to create a filter which looks like this:
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IExceptionService exceptionService;
public ApiExceptionFilterAttribute(IExceptionService exceptionService)
{
this.exceptionService = exceptionService;
}
public override void OnException(ExceptionContext context)
{
Exception e = context.Exception;
exceptionService.Save(e);
//some code
}
}
Code in ConfigureServices method in StartUp.cs looks like this (some code removed for simplicity):
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
services
.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default")));
services.AddScoped<UserManager>();
services.AddScoped<SignInManager>();
services.AddScoped<IExceptionService, ExceptionService>();
services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();
ConfgureMvcOptions class looks like this:
public class ConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
private readonly IExceptionService exceptionService;
public ConfigureMvcOptions(IExceptionService exceptionService)
{
this.exceptionService = exceptionService;
}
public void Configure(MvcOptions options)
{
options.Filters.Add(new ApiExceptionFilterAttribute(exceptionService));
}
}
When I run this application, I get the following error:
System.InvalidOperationException: 'Cannot consume scoped service 'SmartWay.Services.IExceptionService' from singleton 'Microsoft.Extensions.Options.IConfigureOptions`1[Microsoft.AspNetCore.Mvc.MvcOptions]'.'
If I change IExceptionServcise's lifetime to transient than I have to do so for
Dbcontext, then for DbContextOptions... It seems that it isn't right way..
So, How can I solve this problem?
For resolving scoped service from singleton service, try _serviceProvider.CreateScope.
Follow steps below:
ExceptionService
public interface IExceptionService
{
void Save(Exception ex);
}
public class ExceptionService : IExceptionService
{
private readonly IServiceProvider _serviceProvider;
public ExceptionService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Save(Exception ex)
{
using (var scope = _serviceProvider.CreateScope())
{
var _context = scope.ServiceProvider.GetRequiredService<MVCProContext>();
_context.Add(new Book() { Title = ex.Message });
_context.SaveChanges();
}
}
}
Startup.cs
services.AddSingleton<IExceptionService, ExceptionService>();
services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();
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'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 am new to Repository concept and get some questions. I have created simple repository class in my MVC app.
public interface IRepository<TEntity> where TEntity : class
{
List<TEntity> FetchAll();
IQueryable<TEntity> Query { get; }
void Add(TEntity entity);
void Delete(TEntity entity);
void Save();
}
public class SqlRepository<T> : IRepository<T> where T : class
{
readonly DataContext _db;
public SqlRepository(DataContext db)
{
_db = db;
}
#region IRepository<T> Members
public IQueryable<T> Query
{
get { return _db.GetTable<T>(); }
}
public List<T> FetchAll()
{
return Query.ToList();
}
public void Add(T entity)
{
_db.GetTable<T>().InsertOnSubmit(entity);
}
public void Delete(T entity)
{
_db.GetTable<T>().DeleteOnSubmit(entity);
}
public void Save()
{
_db.SubmitChanges();
}
#endregion
}
In my Controller I initialize repository classes for specified table class like this
public class AdminController : Controller
{
private readonly SqlRepository<User> _userRepository = new SqlRepository<User>(new DataContext(ConfigurationManager.ConnectionStrings["ConnectionString"].ToString()));
private readonly SqlRepository<Order> _orderRepository = new SqlRepository<Order>(new DataContext(ConfigurationManager.ConnectionStrings["ConnectionString"].ToString()));
//Skip code
}
But I duplicate this code again and again in many places in my app. What is the best place to instanceate these repository classes?
I think you should refer to repository via it's interface:
public class AdminController : Controller
{
private readonly IRepository<User> _userRepository;
private readonly IRepository<Order> _orderRepository;
public AdminController(IRepository<User> userRepository,
IRepository<Order> orderRepository)
{
_userRepository = userRepository;
_orderRepository = orderRepository;
}
//Skip code
}
And inject implementations via some dependency injection framework.
UPDATE
You can use Ninject for dependency injection to your controllers. Here is Example how to add DependencyResolver to your application. In your case you can configure kernel this way:
IKernel kernel = new StandardKernel();
var connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
kernel.Bind(typeof(DataContext)).ToMethod(context => new DataContext(connectionString));
kernel.Bind(typeof(IRepository<>)).To(typeof(SqlRepository<>));
And that's it. No duplication. Dependencies are resolved. Your class does not depend on concrete repositories. You can easy mock dependencies for testing.