Reputation: 4035
I am struggling with this coming back from a long layoff.
I asked a question regarding the configuring of a DBContext in my generic base repository. Only after a user has logged in can I then construct a connection string so I cannot register a service in startup.cs - I have to use a constructor argument to instantiate my DBContext.
I got this answer which I thought would address the problem however I am getting an error in the following factory class:
public class ContextFactory<T> : IContextFactory<T> : where T : DbContext
{
public T CreateDbContext(string connectionString)
{
var optionsBuilder = new DbContextOptionsBuilder<T>();
optionsBuilder.UseSqlServer(connectionString);
return new T(optionsBuilder.Options);
}
}
The error is on the line return new T(optionsBuilder.Options);
and is:
Cannot create an instance of the variable type 'T' because it does not have the new() constraint
Upvotes: 2
Views: 2193
Reputation: 246998
Even if you add new()
constraint, you will end up with the following error
'T': cannot provide arguments when creating an instance of a variable type.
You were given invalid code.
The new constraint specifies that any type argument in a generic class declaration must have a public parameterless constructor. To use the new constraint, the type cannot be abstract.
Reference new constraint (C# Reference)
Another option to consider could be to use Activator.CreateInstance (Type, Object[]).
Given
public interface IContextFactory<TContext> where TContext : DbContext {
TContext Create(string connectionString);
}
You would implement it as follows
public class ContextFactory<TContext> : IContextFactory<TContext>
where TContext : DbContext {
public TContext Create(string connectionString) {
var optionsBuilder = new DbContextOptionsBuilder<TContext>();
optionsBuilder.UseSqlServer(connectionString);
return (TContext)Activator.CreateInstance(typeof(TContext), optionsBuilder.Options);
}
}
This could be refactored further to separate concerns
public class ContextFactory<TContext> : IContextFactory<TContext>
where TContext : DbContext {
public TContext Create(DbContextOptions<TContext> options) {
return (TContext)Activator.CreateInstance(typeof(TContext), options);
}
}
so that the builder will become the responsibility of where the factory is being used.
var connection = @"....";
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);
//Assuming factory is `IContextFactory<BloggingContext>`
using (var context = factory.Create(optionsBuilder.Options))
{
// do stuff
}
EDIT
The factory can be registered as open generics in ConfigureServices
method
services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
Upvotes: 7