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.
Related
i created my own dbcontextfactory and now i don't know how to correctly register it in di. Can you somebody help me please? IApplicationDbContext is just interfaces with db sets.
I have register ma DbContext as pooled db context factory
builder.Services.AddPooledDbContextFactory<MyContext>(options =>
{
....
});
Interface of my db factory
interface IApplicationDbContextFactory
{
IApplicationDbContext CreateDbContext();
}
Implementation db factory
public class MyContextFactory<TContext> : IApplicationDbContextFactory where TContext : DbContext, IApplicationDbContext
{
private readonly IDbContextFactory<TContext> _dbContextFactory;
public MyContextFactory(IDbContextFactory<TContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public IApplicationDbContext CreateDbContext()
{
return _dbContextFactory.CreateDbContext();
}
}
How can i correctly register my factory to di?
Thank you
Lifetime for the factory registered by AddPooledDbContextFactory is Singleton. Just register it with builder.Services.AddSingleton<IApplicationDbContextFactory, MyContextFactory<MyContext>>(); (though Scoped and Transient should also work just as fine):
var serviceCollection = new ServiceCollection();
serviceCollection.AddPooledDbContextFactory<SomeContext>(builder => builder.UseSqlite($"Filename={nameof(SomeContext)}.db"));
serviceCollection.AddSingleton<IApplicationDbContextFactory, MyContextFactory<SomeContext>>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var dbContextFactory = serviceProvider.GetRequiredService<IDbContextFactory<SomeContext>>();
using (var scope = serviceProvider.CreateScope())
{
var applicationDbContextFactory = serviceProvider.GetRequiredService<IApplicationDbContextFactory>();
var applicationDbContext = applicationDbContextFactory.CreateDbContext();
}
public class SomeContext : DbContext, IApplicationDbContext
{
public SomeContext(DbContextOptions<SomeContext> options) : base(options)
{
}
public DbSet<MyEntity> Entities { get; set; }
}
interface IApplicationDbContextFactory
{
IApplicationDbContext CreateDbContext();
}
public interface IApplicationDbContext
{
}
public class MyContextFactory<TContext> : IApplicationDbContextFactory where TContext : DbContext, IApplicationDbContext
{
private readonly IDbContextFactory<TContext> _dbContextFactory;
public MyContextFactory(IDbContextFactory<TContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public IApplicationDbContext CreateDbContext()
{
return _dbContextFactory.CreateDbContext();
}
}
I've created an injectable dbcontext
Startup.cs:
public void ConfigureServices(IServiceCollection services) {
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddDbContext<DBContext>(options => options.UseSqlServer("Server=localhost;Database=mydb;Trusted_Connection=True;"));
}
UnitOfWork:
public class UnitOfWork : IUnitOfWork {
private readonly DBContext _context;
public UnitOfWork(DBContext context) {
_context = context;
}
The injection working fine in the controller:
public class UserController : ControllerBase {
private readonly IUnitOfWork unitOfWork;
public UserController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
}
How can I create custom class that takes IUnitOfWork in the constructor and call it from main program?
CustomClass :
public class CustomClass {
private readonly IUnitOfWork unitOfWork;
public CustomClass(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
}
Main program:
public class Program {
public static void Main(string[] args) {
var unitOfWork=new UnitOfWork() // Here I don't want to pass new DBContext I want to reach the same injected DBContext
var customClass =new CustomClass (unitOfWork);
}
}
Firstly, move out your code from ConfigureServices to some shared library that can be used both by Web and Console project. Create extension method to configure all your services.
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp13
{
public static class ConfigureServicesExtensions
{
public static void ConfigureMyServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddDbContext<ApplicationDbContext>();
serviceCollection.AddScoped<IUnitOfWork, UnitOfWork>();
serviceCollection.AddScoped<CustomClass>();
}
}
}
This is how your Console app will look like
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp13
{
class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.ConfigureMyServices();
using var serviceProvider = serviceCollection.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var myService = scope.ServiceProvider.GetService<CustomClass>();
}
}
}
And your web project
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureMyServices();
}
What you can do is just add your class to the dependecy injection container and inject it into your constructor, you wouldn't need to inject the IUnitOfWork in you controller.
services.AddScoped<CustomClass>();
and then in your constroller constructor
public class UserController : ControllerBase {
private readonly CustomClass _CustomClass;
public UserController(CustomClass customClass) {
_CustomClass = customClass;
}
}
after that you are able to use this class in your class methods
I would try something like this:
public class Program {
public static void Main(string[] args) {
var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
optionsBuilder.UseSqlServer(connectionstring);
using(DbContext dbContext = new DbContext(optionsBuilder.Options))
{
var unitOfWork=new UnitOfWork(dbContext)
var customClass =new CustomClass (unitOfWork);
.....
}
}
}
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 trying to get Castle Windsor to inject my DB Context to my controllers I have been following the tutorials on the Castle Windsor website
my code is as follows
Bootstrapper
internal class IOCContainerBootstrap
{
private static IWindsorContainer container;
public static void Configure()
{
container = new WindsorContainer()
.Install(FromAssembly.This());
var controllerFactory = new GravityClimbingControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
#region IDisposable Members
public static void Dispose()
{
container.Dispose();
}
#endregion
}
Installers
public class ControllersInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IController>()
.LifestyleTransient());
container.Register(Component.For<DbContext>().ImplementedBy<GravityClimbingEntities>());
}
}
public class APIInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes
.FromThisAssembly()
.BasedOn<IHttpController>()
.ConfigureFor<ApiController>(c => { c.PropertiesIgnore(pi => false); })
.LifestyleTransient());
}
}
And finally
My API Controller
public class ArticalsController : ApiController
{
private readonly DbContext _context;
private readonly Db.Repositories.ArticalRepository repository;
public ArticalsController(DbContext context)
{
_context = context;
repository = new Db.Repositories.ArticalRepository(context);
}
[HttpGet]
public string HelloWorld()
{
return "Hello.world";
}
}
When I Debug I get no errors and it says it can resolve the dependency
But when i try to call the API controller I get the following Error Message
{
"Message" : "An error has occurred.",
"ExceptionMessage" : "Type 'ArticalsController' does not have a default constructor",
"ExceptionType" : "System.ArgumentException"
}
Is there something silly I am doing wrong, that I cannot see?
For people who are facing the same issue I found the issue.
Castle Windsor didn't know about the IHTTPControler (Base for the APIController) so i needed to create an IHttpControllerActivator and attach it to the GlobalConfiguration.Configuration.Services
I found this link to enable this here