please note that this is my first project with DI and IoC thank you for all your help.
I am creating a multitenant app, were the application db context change from one tenant to another by providing a dynamic connection string.
while initialize the controllers and providing the connection string to the DAL class i am getting "String connectionString = Required primitive dependency is not explicitly defined"
DbContext:
public partial class DataContext : DbContext
{
public DataContext(string connectionString)
: base(connectionString)
{
}
// rest of the code ....
}
DAL:
public class ManagerDAL : IEntityRepository<TestManager>
{
private readonly DataContext _context;
public ManagerDAL(string connectionString)
{
_context = new DataContext(connectionString);
}
// rest of the code
}
Controller:
public class TestController
{
private ManagerDAL _managerDAL;
// .... different DAL
public TestController()
{
_managerDAL = new ManagerDAL("ConnectionString");
}
// reset of the code
}
Please any help would be much appreciated thank you.
Related
I have an .net core web api application where I'm using entity framework core with service layer, unit of work and repository layer pattern. For DI I'm using Autofac.
The application has multiple clients and each client has its own database and the schema for all these databases is same. With each API call I'll get the client specific connection string, using which I have to create a DbContext and use it for all its operations.
On Startup class I have registered my dbcontext ClientDbContext and all other classes. When the unit-of-work class is called I am creating my new DbContext based on the connection string. I want the repository to use this instance, but the repository is still using the initial ClientDbContext instance which was created at startup.
How can I make the repository use the new DbContext instance?
Unit of Work:
public class UnitOfWork : IUnitOfWork
{
public ClientDbContext ClientDbContext { get; private set; }
public UnitOfWork ()
{
}
public void SetDbContext(string connectionString)
{
if(ClientDbContext == null)
{
//creating new db context instance here
ClientDbContext = MembershipRepository.CreateDbContext(connectionString);
}
}
//property injection
public IGenericRepository<SomeEntity, ClientDbContext> SomeEntityGenericRepository { get; }
}
Generic Repository:
public class GenericRepository<TEntity, TDbContext> : IGenericRepository<TEntity, TDbContext> where TEntity : class
where TDbContext : DbContext
{
private readonly TDbContext _context;
private readonly DbSet<TEntity> _dbset;
public GenericRepository(TDbContext context)
{
// need to get updated context here, but getting the initial one
_context = context;
_dbset = _context.Set<TEntity>();
}
}
Autofac module called in Startup.cs:
builder.Register(a => new ClientDbContext()).InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(GenericRepository<,>)).As(typeof(IGenericRepository<,>)).InstancePerLifetimeScope();
//Register Unit of Work here
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope().PropertiesAutowired();
//Register Services here
builder.RegisterType<SomeService>().As<ISomeService>().InstancePerLifetimeScope();
Can anyone please help me out on how to achieve the above requirement?
Is there any way I can make Autofac use my new created dbcontext object?
Instead of
builder.Register(a => new ClientDbContext()).InstancePerLifetimeScope();
you could use
builder.Register(c => c.Resolve<IUnitOfWork>().ClientDbContext)
.InstancePerLifetimeScope();
By the way I'm not sure what is the responsibility of your IUnitOfWork. Another way of doing this would be to have a class that would provide information about the current user :
public interface IClientContext
{
public String ClientIdentifier { get; }
}
Then a DbContextFactory that would create the DbContext based on the IClientContext
public interface IDbContextFactory
{
IDbContext CreateDbContext();
}
public class DbContextFactory
{
public DbContextFactory(IClientContext clientContext)
{
this._clientContext = clientContext;
}
private readonly IClientContext _clientContext;
public IDbContext CreateDbContext()
{
// get the connectionstring from IClientContext and return the IDbContext
}
}
The concrete implementation of IClientContext depends on the way you can get this information, it could be from current HttpContext or any other way it's up to you.
It seems that at some point you call SetDbContext you can keep this way by creating a XXXClientContextProvider where XXX is relative to the way you get this information.
public class XXXClientContextProvider
{
private IClientContext _clientContext;
public IClientContext GetClientContext()
{
if(this._clientContext == null)
{
throw new Exception("client context is null. You should do X or Y");
}
return this._clientContext;
}
public void SetClientContext(String clientId)
{
if(this._clientContext != null)
{
throw new Exception("client context has already been set");
}
this._clientContext = new StaticClientContext(clientId);
}
}
and then register everything like this :
builder.Register(c => c.Resolve<IClientContextProvider>().GetClientContext())
.As<IClientContext>()
.InstancePerLifetime();
builder.Register(c => c.Resolve<IDbContextFactory>().CreateDbContext())
.As<IDbContext>()
.InstancePerLifetime();
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 have a ASP.NET Core 2.1 project that references a "Data Access Layer" project of typ .NET Core Class Library.
The Data Access Layger needs connection string from the appsettings.json in the ASP.NET Core project.
I have created a simple container like this :
public class DatabaseConnectionString : IDatabaseConnectionString
{
private readonly string _connectionString;
public DatabaseConnectionString(string connectionString)
{
_connectionString = connectionString;
}
public string ConnectionString {
get { return _connectionString; }
set { }
}
}
In the ASP.NET Core Startup.cs > ConfigureService I have this :
services.AddScoped<IDatabaseConnectionString>(p => new DatabaseConnectionString(Configuration.GetConnectionString("DefaultConnection")));
I know that I can add the IDatabaseConnectionString to a constructor of a controller in ASP.NET to get the container. But How do I get it while in the class library? I dont want to pass it all the way down from the controller and just adding the IDatabaseConnectionString to the constructor of a class in the class library do not work.
I probably need a service where I can ask to create a object of a class and let the service fill in the constructor interfaces with the correct objects?
For example filling in the IDatabasConnectionString in this class :
public class UserFactory : FactoryBase
{
private readonly IDatabaseConnectionString _iDatabaseConnectionString;
public UserFactory(IDatabaseConnectionString connectionString)
{
_iDatabaseConnectionString = connectionString;
}
}
I know that I can add the IDatabaseConnectionString to a constructor of a controller in ASP.NET to get the container.
No, that's not needed and it would be wrong.
just adding the IDatabaseConnectionString to the constructor of a class in the class library do not work.
It doesn't work because you need to create the service that will use the connection string and add it to the services container.
For example:
public class Repository: IRepository
{
public Repository(IDatabaseConnectionString databaseConnectionString)
{
_databaseConnectionString = databaseConnectionString;
}
}
public class ServiceThatRequiresDatabase : IServiceThatRequiresDatabase
{
public ServiceThatRequiresDatabase(IRepository repository)
{
_repository = repository;
}
}
// ...
services.AddScoped<IRepository, Repository>();
services.AddScoped<IServiceThatRequiresDatabase, ServiceThatRequiresDatabase>();
public class HomeController : Controller
{
public HomeController(IServiceThatRequiresDatabase service)
{
_service = service;
}
}
By the way, as #YeldarKurmangaliyev said, your DatabaseConnectionString should be like this if you want to make it read-only:
public class DatabaseConnectionString : IDatabaseConnectionString
{
public string ConnectionString { get; }
public DatabaseConnectionString(string connectionString)
{
ConnectionString = connectionString;
}
}
There is no difference between controller and class from a class library. You need to
Define a class in a class library and inject IDatabaseConnectionString into it. Your UserFactory is the right way.
register the UserFactory for DI
serviceCollection.AddScoped<IUserFactory, UserFactory>();
Resolve the UserFactory by the DI. For example, use the UserFactory as the constructor parameter in some controller. Everything is connected by DI automatically.
public MyController(IUserFactory userFactory)
{
_userFactory = myUserFactory;
}
Here is the good explanation for understanding Composition root.
I have setup .net core project and db context also. But i cant start using dbContext yet due this error-
"there is no argument given that corresponds to the required formal
parameter 'options'"
Controller:
public IActionResult Index()
{
using (var db = new BlexzWebDb())
{
}
return View();
}
Dbcontext Code:
public class BlexzWebDb : DbContext
{
public BlexzWebDb(DbContextOptions<BlexzWebDb> options)
: base(options)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<AssignedRole> AssignedRoles { get; set; }
}
error picture attached. How can this issue be fixed?
Instantiate new object of DbContext from ConnectionString
var connectionstring = "Connection string";
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(connectionstring);
ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options);
// Or you can also instantiate inside using
using(ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options))
{
//...do stuff
}
Note
At the time of writing the use of EF Core with the Dependency injection framework wasn't as known as it is now. This answers gives answer to the question from a DI perspective, which at the time, helped out OP.
The other answer provides you a conventional way to instantiate the DbContext using the new operator.
TL;DR, 3 options:
Option 1
Register the DbContext during application configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
}
and use the DI framework to retrieve it:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
}
Option 2
If you are looking for a design-time IdentityDbContext using IOptions<OperationalStoreOptions>, see: Add migration for ApiAuthorizationDbContext from another project - EF Core
Option 3
Or use the new operator and provide the details, see #Qamar Zaman's answer for details.
The long answer, and why DI is a treat
In EF Core it's common to pass some DbContextOptions to the constructor.
So in general, a constructor looks like this:
public BlexzWebDb(DbContextOptions<BlexzWebDb> options) : base(options)
As you can see there, there is no valid overload in the form of a parameter-less constructor:
Thus, this does not work:
using (var db = new BlexzWebDb())
Obviously, you can pass in an Option object in the constructor but there is an alternative. So,
Instead
.Net Core has IoC implemented in it's roots. Okay, this means; you don't create a context, you ask the framework to give you one, based on some rules you defined before.
Example: somewhere you will register your dbcontext, (Startup.cs):
//typical configuration part of .net core
public void ConfigureServices(IServiceCollection services)
{
//some mvc
services.AddMvc();
//hey, options!
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
//...etc
Now the registering part is done, you can retrieve your context from the framework. E.g.: inversion of control through a constructor in your controller:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
//etc.
why?
So, why not just provide the arguments and new it?
There is nothing wrong with the use of new - there are a lot of scenario's in which it works best.
But, Inversion Of Control is considered to be a good practice. When doing asp dotnet core you're likely to use it quite often because most libraries provide extension methods to use it. If you are not familiar with it, and your research allow it; you should definitely give it a try.
Therefore, instead of providing "just a way to instantiate" the object, I'll try to get you onto this track - inline with the framework. It will save you some hassle afterwards. Besides, otherwise "use an activator's CreateInstance" would just be as valid as an answer ;-)
Some links:
MSDN Fundamentals
MSDN Dependency Injection
Wikipedia Inversion Of Control
As addition of #Stefan's answer there is another way to achieve this. You can set db connection string in OnConfiguring method of DbContext class without adding DbContext service in startup.cs.
Setting.cs
public static class Setting
{
public static string ConnectionString { get; set; }
}
Startup.cs
Setting.ConnectionString = Configuration.GetSection("ConnectionStrings:BlexzDbConnection").Value;
BlexzWebDb.cs
public class BlexzWebDb : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(Setting.ConnectionString);
}
}
}
HomeController.cs
public class HomeController : Controller
{
private readonly BlexzWebDb db;
public HomeController()
{
this.db = new BlexzWebDb();
}
//etc.
Code sample for EF Core 3.1:
public class Test
{
private readonly IServiceProvider _serviceProvider;
public Test(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<RequestResult> Handle(...)
{
await using var context = CreateContext();
...
}
private DocumentContext CreateContext()
{
var options = _serviceProvider.GetService<IOptions<DocumentContextOptions>>();
return new DocumentContext(options);
}
}
I was following this tutorial:
http://blog.johanneshoppe.de/2010/10/walkthrough-ado-net-unit-testable-repository-generator/
And I had this issue:
MVC3 & EF. Interface for TDD
However, now I have my interfaces setup (I am not using ninject due to project restrictions)
I am getting a null error here;
`Public partial class MyEntitiesRepository : MyEntitiesRepository
{
public IEnumerable<userdetails> getAlluserDetails()
{
return this.Context.userDetails.ToList();
}`
Context is null. I am using the exact same structure as the tutorial.
The header in my MVC controller that calls this is:
`
[HandleError]
public class HomeController : Controller
{
private MyEntitiesRepository _repository;
...
...
public HomeController() : this(new externalEntities(), new MyEntitiesRepository ()){}
public HomeController(externalEntities external, MyEntitiesRepository repository)
{
_repository = repository;
_ContextExt = external;
}
`
EDIT:
context is from:
[System.CodeDom.Compiler.GeneratedCode("ADO.NET Unit Testable Repository Generator", "0.5")]
public partial class MyEntitiesRepository
{
/// <summary>
/// Gets or sets the specialised object context
/// </summary>
/// <value>object context</value>
#if !DO_NOT_USE_UNITY
[Dependency]
#endif
public IMyEntities Context { get; set; }
}
}
I am guessing that in the example they pass the Context in the constructor. They can do this because they are using dependency injection and it will create that instance for you. Since you are not using Ninject, you will more than likely need to construct this Context yourself.
If you are unable to use Ninject or any other IoC container then you need to do a better job convincing your bosses to let you. If they still don't let you then you can do poor man's dependency injection I suppose:
public class MyEntitiesRepository
{
private MyDbContext context;
public MyEntitiesRepository() : this(new MyDbContext())
{ }
public MyEntitiesRepository(MyDbContext context)
{
this.context = context;
}
}
It's better than nothing I suppose?
Seeing the edit (the Dependency attribute) I guess the project restrictions you are referring to are that instead of Ninject you are to use Microsoft's Unity.
Now you can solve your problem using or not using Unity. To start with the latter: Adjust your HomeController and MyEntitiesRepository classes a little:
public HomeController() :
this(new externalEntities(),
new MyEntitiesRepository (new MyEntities()))
{
}
public HomeController(externalEntities external, MyEntitiesRepository repository)
{
_repository = repository;
_ContextExt = external;
}
public partial class MyEntitiesRepository
{
public MyEntitiesRepository(IMyEntities context)
{
this.Context = context;
}
public IMyEntities Context { get; private set; }
}
Here I made the assumption that you have a class MyEntities implementing the interface IMyEntities.
You could also use Unity. To get to know that framework a little better you could start at MSDN. I don't have any experience with Unity, but some things I noticied are that you need to create MyEntityRepository using a UnityContainer object:
IUnityContainer container = new UnityContainer();
...
MyEntityRepository repository = container.Resolve<MyEntityRepository>();
Before that works you need to register a mapping of MyEntities to IMyEntities:
container.RegisterType<IMyEntities, MyEntities>();
If you choose to try Unity I suggest you give it a try and ask a new question if you get stuck.