I'm working on web application (web form). I want to be able to change EntityFrameworkRepositoryFactory to NHibernateRepositoryFactory in the future.
IRepositoryFactory
public interface IRepositoryFactory
{
IProductRepository GetProductRepository();
}
ProductRepository
public class ProductRepository : IProductRepository
{
ExDbContext _db;
public ProductRepository(ExDbContext dbContext)
{
_db = dbContext;
}
public IList<Product> ListProductsByCategoryId(int categoryId)
{
List<Product> productsByCategoryId = _db.Products.Where(x => x.ProductCategoryId == categoryId).ToList();
return productsByCategoryId;
}
}
And there is EntityFrameworkRepositoryFactory.
class EntityFrameworkRepositoryFactory:IRepositoryFactory
{
ExDbContext _db;
public EntityFrameworkRepositoryFactory(ExDbContext dbContext)
{
_db = dbContext;
//
// TODO: Add constructor logic here
//
}
public IProductRepository GetProductRepository()
{
return new ProductRepository(_db);
}
}
How can i make easy for changing this in future ? I want use ninject for access EntityFrameworkRepositoryFactory but I'm stuck. Is there any example for this ?
Thanks.
We will add Ninject to your web application, fix your repository classes and add some Ninject modules to configure dependency injection:
Install Ninject. You can do this easily using the Package Manager Console: Install-Package Ninject.Web -dependencyVersion Highest
Remove your RepositoryFactory. Delete IRepositoryFactory and EntityFrameworkRepositoryFactory. You don't need them. Ninject will create a Repository and provide the dependencies as soon as your application asks for them. You need factories only to have better control of an object's lifetime.
Fix the repository. Let's make things more conventional and use an IEnumerable<Product> to return a read-only collection of products as result of our query. We also use Get as a prefix, as most repository patterns do:
public interface IProductRepository
{
IEnumerable<Product> GetProductsByCategoryId(int categoryId);
}
class EfProductRepository : IProductRepository
{
private readonly ExDbContext db;
public EfProductRepository(ExDbContext dbContext)
{
this.db = dbContext;
}
public IEnumerable<Product> GetProductsByCategoryId(int categoryId)
{
var productsByCategoryId = this.db
.Products
.Where(x => x.ProductCategoryId == categoryId)
.ToArray();
return productsByCategoryId;
}
}
Create a Ninject module. We need to bind our repository implementation to its interface. The Entity Framework DbContext uses the "Unit of Work" pattern, so we also need to make sure that our entity context instances are going to be disposed as soon as a request ends. We could do this using a context factory and the using directive, but we can also use the "Request Scope" of Ninject as it's easier:
public class EfRepositoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IProductRepository>().To<EfProductRepository>();
this.Bind<ExDbContext>().ToSelf().InRequestScope();
}
}
At first, we bind IProductRepository to our concrete implementation. Thereby, whenever a component needs a product repository, Ninject will create an instance of EfProductRepository and use that.
Then we tell Ninject to bind ExDbContext to itself and use the request scope. All dependencies on ExDbContext will be served by one single instance of this class during a request, and this instance is going to be disposed when the request ends.
Load the module. In App_Start/NinjectWebCommon.cs update the following method:
private static void RegisterServices(IKernel kernel)
{
kernel.Load<EfRepositoryModule>();
}
Add dependencies to your pages. In every page where you need to show products, add the following property:
[Inject]
public IProductRepository ProductRepository { get; set; }
We need to use property injection or method injection here, because Web Pages doesn't support constructor injection (which should usually be favored). The Inject attribute tells Ninject that we have a dependency here that we want to be injected.
Add a module for NHibernate later on.
public class NHibernateRepositoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IProductRepository>().To<NHibernateProductRepository>();
// Bind whatever else you need when working with NHibernate
}
}
// NinjectWebCommon
private static void RegisterServices(IKernel kernel)
{
kernel.Load<EfRepositoryModule>();
}
Related
I have project A which is a class library and project B that uses A. Project A is meant to be a generic helper library that can be used across projects (such as B).
The EF Core datacontext and the data entities need to be defined in project B (as they can vary by project) but I need to inject the datacontext in to constructors of service classes in Project A (which handle everything in a generic way).
In Project B I have the datacontext
public class MyDataContext : DbContext
{
public MyDataContext(DbContextOptions<MyDataContext> options): base(options)
{
}
public DbSet<Test> Tests { get; set; }
}
In project A I have class UnitOfWork that implements IUnitOfWork. In it's constructor I need to inject the datacontext. However since project A cannot reference project B (project A is meant to be generic), I cannot use the actual name of the datacontext in the parameter list. Since the datacontext inherits from DbContext, I tried
public UnitOfWork(DbContext dc){...}
In the startup of Project B, I have
services.AddDbContext<MyDataContext>(options =>
{
options.UseSqlServer("...<the connection string> ...");
});
services.AddScoped<IUnitOfWork, UnitOfWork>();
Everything compiles but in runtime when the UnitOfWork needs to be created, I get the error
System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: IUnitOfWork Lifetime: Scoped ImplementationType: UnitOfWork': Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'UnitOfWork'.)
The Inner Exception is
InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'UnitOfWork'.
Any help is deeply appreciated.
EDIT
I was asked for the UnitOfWork class details in the comments. So here it is
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext dc;
private readonly IServiceProvider serviceProvider;
public UnitOfWork(DbContext dc, IServiceProvider serviceProvider)
{
this.dc = dc;
this.serviceProvider = serviceProvider;
}
public void BeginTransaction()
{
dc.Database.BeginTransaction();
}
public void BeginTransaction(IsolationLevel isolationLevel)
{
dc.Database.BeginTransaction(isolationLevel);
}
public void CommitTransaction()
{
dc.Database.CommitTransaction();
}
public void RollbackTransaction()
{
dc.Database.RollbackTransaction();
}
public bool IsTransactionActive()
{
return dc.Database.CurrentTransaction != null;
}
public async Task<bool> SaveAsync()
{
return await dc.SaveChangesAsync() > 0;
}
public bool Save()
{
return dc.SaveChanges() > 0;
}
}
Your UnitOfWork service depends on a DbContext type, not the derived MyDataContext type which is registered into DI.
So you have two options:
You can modify the UnitOfWork registration like this (tell the IoC container to instantiate UnitOfWork with MyDataContext):
services.AddScoped<IUnitOfWork>(srp => new UnitOfWork(srp.GetRequiredService<MyDataContext>(), srp));
Or you can register DbContext into DI as well, so the DI container knows that when someone asks for a DbContext it should return MyDbContext:
services.AddScoped<DbContext, MyDataContext>();
Note that the ServiceProvider field seems to be unused in your UnitOfWork class.
The solution was to make two changes. First was to explicitly register the service as suggested by #fbede
services.AddScoped<DbContext, MyDataContext>();
Now when we do this, we lose the convenience of setting the DbContextOptionsBuilder options via the AddDbContext Extension method.
So we need to override the OnConfiguring method in the datacontext to set the configurations options we need. For example:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(configuration.GetConnectionString(<key>));
}
Of course, IConfiguration is injected in the MyDataContext constructor
I have a situation where I need to instantiate my DBContext after my solution has started up. I asked this question which indicated that I could do this with a constructor argument.
It was suggested that I implement as an example this:
var connection = #"Server=(localdb)\mssqllocaldb;Database=JobsLedgerDB;Trusted_Connection=True;ConnectRetryCount=0";
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);
using (var context = new BloggingContext(optionsBuilder.Options))
{
// do stuff
}
However I have implemented the repository pattern (for better or worst) and given my changed circumstances - not having a connection string until after the solution has run startup - I need to implement this into the base repository class and I am at a bit of a loss..
Currently I have this:
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntityBase, new()
{
public JobsLedgerAPIContext _context;
#region Properties
public EntityBaseRepository(JobsLedgerAPIContext context)
{
_context = context;
}
#endregion
public virtual IQueryable<T> GetAll()
{
return _context.Set<T>().AsQueryable();
}
public virtual int Count()
{
return _context.Set<T>().Count();
}
......
How do I implement this change both instantiating the DBContext in the constructor (there by bypassing the need to add the context as a service in startup) and then with the wrapping each of the virtual methods with "using" etc
EDIT.. Camilo indicated I had not identified when I have the database name.
The basic situation is that the system starts up (This is an Aurelia SPA project which is irrelevant to this issue) sends the package to the browser which shows a login screen. User logs in.. User is verified via a JWT controller.. Once verified in the controller (using a catalog database that has one table with 3 fields - username, password, database name) I use the database name to create a connection string and then instantiate my DBContext at that point.. so via a constructor.
The answers below need to be modified as the one with the factory answer (promising) has errors as discovered by this question.. Nkosi responded with an great answer to the error.
EDIT 2..
This is a response to the edited question below:
Here is my original Client Repository with :base(context) on the constructor.
using JobsLedger.DATA.Abstract;
using JobsLedger.MODEL.Entities;
namespace JobsLedger.DATA.Repositories
{
public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
{
private new JobsLedgerAPIContext _context;
public ClientRepository(JobsLedgerAPIContext context) : base(context)
{
_context = context;
}
public void RelatedSuburbEntities(Suburb _suburb)
{
_context.Entry(_suburb).Reference<State>(a => a.State).Load();
}
}
}
It has a reference to the base class "context". I am not sure how to modify this given that I believe I still need that ":base(context)" at the end. As well, I have a method in this that accesses _context as well which is part of the constructor...
Further I assume that I can no longer inject the service into the controller but instead new it up once I have secured the connection string and then pass that connection string to service.
Also, Given I have now added a singleton on the startup do I need to remove the original entry? :
services.AddDbContext<JobsLedgerAPIContext>(options => options.
UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("JobsLedger.API")));
effectively replacing it with my singleton reference as per below:
services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
Edited
The answer has been edited to rectify the mistake spotted and
fixed by Nkosi. Thanks, #Nkosi.
Implement a factory pattern. You can create a factory, call it ContextFactory as below:
First, define the interface. Further modified, removed the connectionString parameter
public interface IContextFactory<T> where T : DbContext
{
T CreateDbContext();
}
Create a factory class that implements this interface (edited as per Nkosi answer). Further modified to inject IHttpContextAccessor
public class ContextFactory<T> : IContextFactory<T> where T : DbContext
{
private readonly HttpContext _httpContext;
public ContextFactory(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
public T CreateDbContext()
{
// retreive the connectionString from the _httpContext.Items
// this is saved in the controller action method
var connectionString = (string)_httpContext.Items["connection-string"];
var optionsBuilder = new DbContextOptionsBuilder<T>();
optionsBuilder.UseSqlServer(connectionString);
return (T)Activator.CreateInstance(typeof(T), optionsBuilder.Options);
}
}
Then modify your base repository and make the JobsLedgerAPIContext protected. This context is going to be set by the derived class. Further modified to remove the constructor. It will use the parameterless constructor.
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntityBase, new()
{
protected JobsLedgerApiContext Context { get; set; }
public virtual IQueryable<T> GetAll()
{
return Context.Set<T>().AsQueryable();
}
public virtual int Count()
{
return Context.Set<T>().Count();
}
}
Change your derived class to use IContextFactory. Further modified to use the _contextFactory.CreateDbContext() parameter less method
The IClientRepository should have SetContext method defined.
public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
{
private readonly IContextFactory<JobsLedgerApiContext> _contextFactory;
public ClientRepository(IContextFactory<JobsLedgerApiContext> factory)
{
_contextFactory = factory;
}
// this method will set the protected Context property using the context
// created by the factory
public void SetContext()
{
Context = _contextFactory.CreateDbContext();
}
public void RelatedSuburbEntities(Suburb suburb)
{
Context.Entry(suburb).Reference<State>(a => a.State).Load();
}
}
In the controller, that receives IClientRepository instance, you can set the connection in the HttpContext.Items, which will be valid for the request. This value will then be retrieved by the ContextFactory using IHttpContextAccessor. Then you simply call the _repository.SetContext(); method on the repository.
public class HomeController : Controller
{
private readonly IClientRepository _repository;
public HomeController(IClientRepository repository)
{
_repository = repository;
}
public IActionResult Index()
{
// save the connectionString in the HttpContext.Items
HttpContext.Items["connection-string"] = "test-connection";
// set the context
_repository.SetContext();
return View();
}
}
Make sure you register the IContextFactory in ConfigureServices as open generics and Singleton as below, also register the HttpContextAccessor and IClientRepository
services.AddHttpContextAccessor();
services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
services.AddTransient<IClientRepository, ClientRepository>();
You may define your JobsLedgerAPIContext like this:
public class JobsLedgerAPIContext : DbContext
{
// public DbSet<Job> Jobs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=dotnetcore;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// may need to reflect entity classes and register them here.
base.OnModelCreating(modelBuilder);
}
}
I see a lot of questions and answers on this topic, however the vast majority are dealing with ASP.Net or other web based applications and something called .InRequestScope. I have yet to find this method in Ninject with a Windows Application.
I have the usual Unit of Work (UoW) and Repository (Repo) classes and Interfaces, but I am wanting to inject the same DbContext into both, each time a UoW is run from the DIContainer. My code looks like this;
public class UnitOfWork : IUnitOfWork, IDisposable
{
private readonly FinancialContext _context;
private IAccountRepository _accountRepository;
public IAccountRepository Accounts
{
get { return _accountRepository; }
}
UnitOfWork(IMyContext context, IAccountRepository accountRepository)
{
_context = context;
_accountRepository = accountRepository;
}
public void SaveChanges()
{
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
public class AccountRepository : Repository<Account>, IAccountRepository
{
public AccountRepository(IMyContext context) : base(context) { }
}
The DIContainer holds the following associations;
Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
Bind<IUnitOfWorkFactory>().ToFactory();
Bind<IMyContext>().To<MyContext>().InSingletonScope();
Bind<IAccountTypeRepository>().To<AccountTypeRepository>().InTransientScope();
I'll come back to the .InSingletonScope();
The way I have seen people do this normally has been in the UoW Properties for each Repo to have code to this effect;
private IAccountRepository _accountRepository;
public IAccountRepository Accounts
{
get
{
if(_accountRepository = null)
{
_accountRepository = new AccountRepository(_context);
}
return _accountRepository;
}
}
And remove the injected repositories from the Constructor, there by ensuring that each instance of a repository using the same _context.
However in my mind this breaks the Dependency Injection for this class. Is there a way to do this where each creation of a UoW like so;
public TestUnitOfWork(IUnitOfWorkFactory unitOfWork)
{
using (var UoW = unitOfWork.Create())
{
Work done on UoW...
}
}
Currently the .InSingletonScope allows this, but is this keeping an instance of the context always open? Introducing the errors associated with not disposing a context properly?
Or is it better to create a Factory for the Repositories and give them a context parameter, then in the properties initialise it like so;
private IAccountRepository _accountRepository;
public IAccountRepository Accounts
{
get
{
if(_accountRepository = null)
{
_accountRepository = RepositoryFactory.CreateAccountRepository(_context);
}
return _accountRepository;
}
}
Thanks in advance for any help!
The solution is the use Ninject's Extensions.Factory class and pass in an IAccountFactory.Create() to initialise a new object. This then uses the DI Container to resolve its dependencies and doesn't break the DI approach.
I have a custom SiteRole class that inherits RoleProvider. I inject the IUserService in the constructor in order to do a query to get all Roles.
It kept on throwing an error that the SiteRole class needed a parameterless constructor. So I ended up injecting the IUserService like this:
public SiteRole()
{
_userService = DependencyResolver.Current.GetService<IUserService>();
}
Inside the class I override the GetRolesForUser function
public override string[] GetRolesForUser(string nickname)
{
return new string[] { _userService.GetRoleForUser(nickname) };
}
The UserService calls this LINQ query in the UserRepository (This line throws System.InvalidOperationException: 'The operation cannot be completed because the DbContext has been disposed.')
public string GetRoleForUser(string nickname)
{
return DbContext.Users.Where(u => u.Nickname == nickname).FirstOrDefault().Role.Name;
}
Any hints on how I can resolve this? You cannot inject dependencies to RoleProvider via constructor so I have to use DependencyResolver.Current.GetService().
Looks like your SiteRole class has a longer lifetime than the injected IUserService. You should check if it's really the source of issue and tune lifetimes of registered services with use of your DI container API. Here are some links for different containers: Autofac, NInject, Unity, Simple injector.
And, probably, change your SiteRole type a bit — use a property to get alive UserService instead of field, with which the only instance is created at time of SiteRole instantiation.
public SiteRole()
{
// this field should be removed
// _userService = DependencyResolver.Current.GetService<IUserService>();
}
// this property should be used instead of field
private IUserService UserService
{
get { DependencyResolver.Current.GetService<IUserService>(); }
}
Or in modern syntax
private IUserService UserService => DependencyResolver.Current.GetService<IUserService>();
But be careful and check that you won't get uncontrolled count of UserService instances created, if it's for example configured as instance per call in your DI container.
I solved the problem by doing this:
virtual Owned<IUserService> ResolveUserService()
=> DependencyResolver.Current.GetService<Owned<IUserService>>();
And call the service in the GetRolesForUser Method with a Using statement:
public override string[] GetRolesForUser(string nickname)
{
using(var userService = ResolveUserService())
{
return new string[] {userService.Value.GetRoleForUser(nickname) };
}
}
On a sidenote, DbContext is constructed in the RepositoryBase. I'm using the Generic Repository Pattern for my project.
public abstract class RepositoryBase<T> where T : class
{
#region properties
private StoreEntities dataContext;
private readonly IDbSet<T> dbSet;
protected IDbFactory DbFactory
{
get;
private set;
}
protected StoreEntities DbContext
{
get { return dataContext ?? (dataContext = DbFactory.Init()); }
}
#endregion
protected RepositoryBase(IDbFactory dbFactory)
{
DbFactory = dbFactory;
dbSet = DbContext.Set<T>();
}
#region Implementation of defaults
public virtual void Add(T entity)...........
Thanks for the help!
I am not at all sure what the underlying problem is that is causing this exception.
I am using ASP.NET MVC, with Unity.Mvc, and Entity Framework 6. I have the following code to register my repositories:
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterType<IGenericRepository<Customer>, GenericRepository<Customer>>();
container.RegisterType<IGenericRepository<Product>, GenericRepository<Product>>();
container.RegisterType<IGenericRepository<Order>, GenericRepository<Order>>();
container.RegisterType<IGenericRepository<OrderItem>, GenericRepository<OrderItem>>();
container.RegisterType<IGenericRepository<Supplier>, GenericRepository<Supplier>>();
}
And then in a controller I have:
public class IndexController : Controller
{
public IndexController(IGenericRepository<Customer> testGenericRepository)
{
var result = testGenericRepository.SelectAll();
}
public ActionResult Index()
{
return View();
}
}
And the repository has the following code:
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
private readonly DbContext _dbContext;
private readonly IDbSet<T> _dbSet;
public GenericRepository(DbContext dbContext)
{
if (dbContext == null)
{
throw new ArgumentNullException(nameof(dbContext));
}
_dbContext = dbContext;
_dbSet = _dbContext.Set<T>();
}
public IEnumerable<T> SelectAll()
{
return _dbSet.AsEnumerable<T>();
}
}
The problem that I'm having is that if I have a breakpoint in the "RegisterTypes" method, I can see that the container is definitely getting all the repositories registered, but a breakpoint in the constructor of the repositories never gets hit.
So I think that the fact that the breakpoint does not get hit, and I have not registered a "System.Data.Common.DbConnection" means that the DbContext that the repository uses never gets set.
I can't find any useful information about how to use "System.Data.Common.DbConnection" with Unity and the DbContext from Entity Framework.
How do I resolve this?
You should add to your RegisterTypes how to build your DbContext, and probably with which lifetime.
If you have your own class (say CustomContext) inheriting from DbContext, register it. Supposing your default lifetime is adequate:
container.RegisterType<DBContext, CustomContext>();
If you use directly DbContext, instruct Unity which constructor it should use. By example, supposing your connection string is named appConnectionString:
container.RegisterType<DBContext>(
new InjectionConstructor("name=appConnectionString"));