Is there a way to factory inject a custom DbContext? - c#

...Maybe using TFactory in AddDbContextFactory<TContext, TFactory> in EF Core extensions?
I've only seen AddDbContextFactory examples being used with just the TContext generic. They're always very explicit to say you have to use a using statement.
In similar situations (when I useClass in Angular or AddScoped in .NET Core), I make the variable I want to see in a constructor the first generic argument and the second generic argument what actually gets injected. You know, like:
services.AddScoped<IService, RealService>();
Obviously, this isn't the case with
services.AddDbContextFactory<ADbContextIHaveBeenInjecting, AFactoryThatWillReturnADbContextIHaveBeenInjecting>();
I was hoping this would eliminate the need to do the whole using thing.
Is there another way I can do this without having to re-write every injected DbContext to conform with their prescribed:
public void DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
As I said, my hope was to use something like this for the factory:
public class MyDbContextFactory : IDbContextFactory<MyDbContext>
{
public MyDbContextFactory(DbContextOptions options)
{
}
public MyDbContext CreateDbContext()
{
var ProviderName = GetProviderName();
switch (ProviderName)
{
case "System.Data.SqlClient":
return new SqlServerDbContext(new DbContextOptionsBuilder<SqlServerDbContext>().UseSqlServer(ConnectionString).Options);
case "Npgsql":
return new PostgreSqlDbContext(new DbContextOptionsBuilder<PostgreSqlDbContext>().UseNpgsql(ConnectionString).Options);
default:
throw new NullReferenceException("Missing provider name for DbContext. Should be Npgsql or System.Data.SqlClient");
}
}
}
Then, set it up in Startup.cs ConfigureServices like:
services.AddDbContextFactory<MyDbContext, MyDbContextFactory>();
So I could inject in a class like this:
public class MyController : BaseApiController
{
private readonly MyDbContext _myDbContext;
public MyController(MyDbContext myDbContext)
{
_myDbContext = myDbContext;
}
[HttpGet("GetACount")]
public IActionResult GetACount()
{
var count = _myDbContext.MyRecord.Count();
return Ok(count);
}
...
Is there a way to do this using the AddDbContextFactory? What is TFactory actually for? Is there another way to do this?

DbContextFactory is specifically intended to require you to manage the lifecycle of your DbContext, because Blazor server apps don't use a Scope-per-Http request like ASP.NET Core does, so a Scoped DbContext won't work.
If you want a Scoped DbContext just use .AddDbContext intead of .AddDbContextFactory.
If you have registered a DbContextFactory but still want to inject a scoped DbContext direcly in your services, then register it like this:
services.AddScoped<MyDbContext>(sp =>
{
var cf = sp.GetRequiredService<IDbContextFactory<MyDbContext>>();
var db = cf.CreateDbContext();
return db;
});

Dependency inject the concrete class. create a factory to select the subclass by type. Create a parent class with a private DbContext _dbContext. Inherit the subclass from the parent class and call the parent class constructor with :base(dbContext) of the subclass. The parent class can now access in its methods the subclass context. The subclass can share the methods of the parent class for (add, select, update, and deleting by set the data context of the subclass). the subclass will dependency inject the specific dbcontext in its constructor and set the parent class dbcontext variable in its constructor. the subclass repository class can then access the base class methods within its body.
in startup define the subclass repository pattern
in public void ConfigureServices(IServiceCollection services)
var connectionString = Configuration.GetConnectionString("ABCContext_DbCoreConnectionString");
services.AddDbContext<ABCContext>(options1 => options1.UseSqlServer(connectionString));
services.AddTransient<IRepositoryMySubclass, RepositoryMySubclass>();
sub class
public RepositorySubclass(ABCContext dbContext) : base(dbContext)
{
_dbContext = dbContext;
}

Related

How to refactor from static methods to Dependency Injection using MS.DI and .NET Core?

I am in the process of migrating a project from .Net Framework to .Net Core. In the existing project we have a utility class with a few functions like below:
public static class BudgetUtilities
{
public static decimal CalculateBudgetRemaining(string fiscalYear = null)
{
if (string.IsNullOrWhiteSpace(fiscalYear))
fiscalYear = DateTime.Now.GetFiscalYear().ToString();
using (AppContext _context = new AppContext())
{
FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);
return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
}
}
// other functions removed for brevity
}
I can then reference it anywhere else using BudgetUtilities.CalculateBudgetRemaining(). Very simple and straightforward.
When migrating this function to .Net Core I need to use Dependency Injection so I have amended the class by removing the static modifier (since static constructors cannot have parameters) and injecting the AppContext into the constructor:
public class BudgetUtilities
{
private readonly AppContext _context;
public BudgetUtilities(AppContext context)
{
_context = context;
}
public decimal CalculateBudgetRemaining(string financialYear = null)
{
if (string.IsNullOrWhiteSpace(fiscalYear))
fiscalYear = DateTime.Now.GetFiscalYear().ToString();
FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);
return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
}
}
I then tried to call my code by doing the following:
BudgetUtilities utils = new BudgetUtilities();
decimal remaining = utils.CalculateBudgetRemaining();
But I cannot make a new instance of BudgetUtilities without providing an AppContext in the constructor which makes sense. Every method in this application is at some point initiated by a controller action, and I know that DbContexts are supposed to be short lived, so I assume passing the context the whole way down to this BudgetUtilities class from the initial controller is a bad idea.
The only other option I can see is to keep going back up the call stack from where CalculateBudgetRemaining() is referenced and keep adding in constructor injections until I get to a controller but this is not the only class I will have to inject like this so my constructors further up the chain are going to be really bloated and this will make my ConfigureServices() method bloated too.
I'm sure there's a simple way to do this but I just can't see it.
Don't manually create a new BudgetUtilities instance, that type should also be registered with the DI Framework, preferably interfaced:
public interface IBudgetUtilities
{
decimal CalculateBudgetRemaining(string financialYear);
}
public class BudgetUtilities : IBudgetUtilities
Then in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddScoped<IBudgetUtilities, BudgetUtilities>();
}
Then it can be injected into any class that needs it, such as a controller:
public class YourController : Controller
{
private readonly IBudgetUtilities _utils;
public YourController(IBudgetUtilities utils)
{
_utils = utils;
}
public ActionResult YourMethod()
{
//...
decimal remaining = _utils.CalculateBudgetRemaining();
}
}
By default, registered DbContexts have a scoped lifetime, which means a single instance is used for the entirety of a HTTP request.

Getting DbContext via Configuration with DbContextOptions

I am trying to get the DbContext I registered with options via services.AddDbContext(...) on the service provider of the project, but when calling configuration.Get<ModelContext> it can not be constructed as the options apparently weren't provided and therefore also no database provider is given.
I am using ASP.NET Core 2.2 with Entity Framework Core 2.2.3 and my DbContext is defined in a separate project.
My DbContext:
public class ModelContext : DbContext
{
public ModelContext(DbContextOptions<ModelContext> options) : base(options) { }
public ModelContext() { }
}
I did not override OnConfiguring(DbContextOptionsBuilder) in ModelContext.
public class StartUp
{
public void ConfigureServices(IServiceCollection services)
{
public services.AddEntityFrameworkSqlServer();
services.AddDbContext<ModelContext>(options => options.UseSqlServer(modelConnectionString));
}
}
In the controller (or anywhere really) I call public HomeController(IConfiguration configuration) => _modelContext = configuration.Get<ModelContext>(); which throws the unexpected exception.
What I specifically get is an InvalidOperationException with the message:
No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
According to the documentation I read and examples I looked at, the ModelContext should be created with the options I defined when calling AddDbContext<ModelContext>. Is the Get method the wrong one to use?
After configuring the db context service in "ConfigureServices" method of the Startup.cs file with something like this :
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<BottinContext>(options => options.UseSqlServer(connectionString)) ;
Simply add a :
ModelContext db
parameter to the constructor of your controller and let DI magic happen.
If you've got many controllers and wish to simplify things, you can use a base contructor that holds the db context
public BaseController(ModelContext context /* as well as other injections */)
{
_db = context;
}
internal ModelContext _db;
you are trying to get dbContxt instance in a wrong way. Get method is not used to get instance of dbContext object that you registered with dependency injection container.
if you want to get instance of your dbContext class that you registered you can inject it through construction injection for example
public class RepositoryWrapper : IRepositoryWrapper
{
private readonly ModelContext _modelContext;
public RepositoryWrapper(ModelContext modelContext)
{
_modelContext= modelContext;
}
}
is something i am doing in my project.

Updating a dependency injected singleton

I generate a singleton at runtime
public void ConfigureServices(IServiceCollection services)
{
var applications = Utils.generateApplications()
services.AddSingleton<ApplicationModel[]>(applications);
services.AddMvc();
}
How can I later update this dependency injected ApplicationModel[] with a completely new ApplicationModel[]. I have a feature in my application that the user can use to trigger a data refresh on the applications, but how can I update the underlying injected Singleton so all future injections will use the updated applications? I have a function Utils.generateApplications() that gets an up to date list of applications and returns a ApplicationModel[]. How do I overwrite the old injected Singleton with a new object to be injected into future calls if that makes sense?
I have some code :
public void UpdateData()
{
var applications = Utils.generateApplications()
//How do I set applications to replace the injected singleton for all future injections?
}
You should use an additional layer of indirection. I think the simplest way is to use an abstract factory. Define an interface something like this:
interface IApplicationModelFactory
{
public ApplicationModel[] GetModel();
}
Define a second interface with the method (or methods) needed to update the model:
interface IApplicationModelUpdate
{
void UpdateModel();
}
You can then change your ApplicationModel[] registration from single instance to scoped and delegate to the factory:
var modelFactory = new ApplicationModelFactory();
services.AddSingleton<IApplicationModelFactory>(modelFactory);
services.AddSingleton<IApplicationModelUpdate>(modelFactory);
services.AddScoped<ApplicationModel[]>(provider =>
provider.GetRequiredService<IApplicationModelFactory>().GetModel());
Inject IApplicationModelUpdate into the types that update the model and ApplicationModel[] into the types that use it. This has the advantage that all types resolved for the same request will get a consistent view of the model, even if it changes in the middle of processing that request.
You could also inject IApplicationModelFactory into the consumer code, but I think injecting the model directly is better. Using the factory can lead to different bits of code seeing different models during the same request. The mutability of the model is also an implementation detail that consumer code shouldn't have to worry about.
I wouldn't monkey with dependency injection that way. Instead, inject a factory, and write whatever logic you need to return the proper instance.
Simple factory:
interface IApplicationModelFactory
{
ApplicationModel[] Model { get; }
}
class ApplicationModelFactory : IApplicationModelFactory
{
public ApplicationModel[] Model { get; set; }
}
Registration:
services.AddSingleton<IApplicationModelFactory>
(
new ApplicationModelFactory[] { Model = util.generateApplications() }
)
class receiving the injection:
class Foo
{
protected readonly IApplicationModelFactory _factory;
public Foo(IApplicationModelFactory injected)
{
_factory = injected;
}
protected ApplicationModel[] => _factory.Model;
public void Bar()
{
DoSomethingWithModel(this.ApplicationModel);
}
}

Unable to register DbConnection with Unity and Entity Framework

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"));

Register IDbContext in Unity

I'm trying to register IDbContext in Unity but I'm getting this
error
"The type IDbContext does not have an accessible constructor."
UnityConfig.cs
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IProductServices, ProductServices>();
container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
//---all ERROR below---
//container.RegisterType(typeof(IDbContext), typeof(DbContext));
//container.RegisterType<IDbContext>();
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ApplicationContext"].ConnectionString;
container.RegisterType(typeof(IDbContext), typeof(DbContext), new ContainerControlledLifetimeManager(), new InjectionConstructor(connectionString));
}
IDbContext.cs
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;
int SaveChanges();
}
ApplicationContext.cs
public partial class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext()
: this("name=ApplicationContext")
{
}
public ApplicationContext(string name)
: base(name)
{
}
public DbSet<Product> Products { get; set; }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
You have 2 constructor in your DbContext therefore Unity can not initialize it simply remove ApplicationContext(string name) constructor an let default exist.
public partial class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext()
: this("name=ApplicationContext")
{
}
public DbSet<Product> Products { get; set; }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
An in unity side write:
container.RegisterType<IDbContext, ApplicationContext>();
But if you want keep your constructors try this:
container.RegisterType<IDbContext, ApplicationContext>(
new InjectionConstructor("YourConnectionStringYouProvidedSomewhere"));
So you could do something like this:
public class MyContext : DbContext
{
public MyContext() : base("connectionstringproperty") { }
}
public interface IMyContextFactory : IDbContextFactory<MyContext>
{
}
public class MyContextFactory : IMyContextFactory
{
public MyContext Create()
{
return new MyContext();
}
}
Register Like this:
container.RegisterType<IMyContextFactory, MyContextFactory>(new PerResolveLifetime());
And use in class like so:
public class UserService
{
IMyContextFactory contextFactory
public UserService(IMyContextFactory contextFactory)
{
contextFactory = contextFactory;
}
public List<User> GetUsers()
{
using(var context = this.contextFactory.Create())
{
return context.Users.ToList();
}
}
}
I don't use repositories until I really have to (which is nearly never :) ). But if UserService was a repository class instead (it kinda is at the moment) the implementation would be the same using constructor injection.
DbContext
If you want testability, EF 6 did a good job in making all methods and properties virtual so you could override them rather than creating an interface to back it.
For dependency injection, EF created IDbContextFactory<T> where T : DbContext
You should be inheriting from DbContext so that you can create some tables in the database. When you inherit you can create different constructors depending on your needs, the easiest I find is to create a parameterless one and call the base constructor overload which takes the web.config connection string property.
DbContext does not have a public, default constructor. The most minimial public constructor is one which takes a string value containing the name of the connection string to use (defined in your app.config or web.config file) or the actual connection string to the database itself.
If you have such a connection string defined in your app.config or web.config (or you have a suitable default connection string to be used throughout your application), try the following Unity registration:
// In the code below, "connection_string" is either the full database
// connection string or the name of the connection string as defined
// in the app.config or web.config files
//
// NOTE: This method requires a LifetimeManager.
// I used the default "singleton container" provided by unity.
// This may not be appropriate for your application--please use an
// appropriate container lifetime manager.
container.RegisterType(typeof(IDbContext), typeof(DbContext),
new ContainerControlledLifetimeManager(),
new InjectionConstructor("connection_string"));
If you have multiple databases you will need to connect to, then you may want to subclass DbContext (e.g. MyDB1DbContext, MyDB2DbContext) or use named registrations. If you subclass DbContext, then you could have the subclasses implement a public default constructor, passing a suitable connection string to the base DbContext class--at which point, your context registrations become simpler.
For one application-wide DbContext though, I'd probably just use the code snippet above.
Update after OP's Update
Unless you'll need to use multiple connection strings with your application-defined DbContext, I would simply define as follows:
public class ApplicationDbContext() : System.Data.Entity.DbContext
{
public ApplicationDbContext() : base("my_connection_string_name")
{ }
... // Rest of context class definition
}
Then, to register:
// NOTE: Can also use the generic version--may need to also change the
// lifetime management of the context depending on your application.
container.Register(typeof(IDbContext), typeof(ApplicationDbContext),
new ContainerControlledLifetimeManager());

Categories

Resources