Unity, Using Statement and PerRequestLifetimeManager - c#

I'm using Unity in my project. But I'm unsure whether I should be using the using statement or not as I'm using the PerRequestLifetimeManager.
Simple example:
Injection:
container.RegisterType<IDataContext, MyContext>(new PerRequestLifetimeManager());
Usage:
class MyClass
{
private readonly IDataContext _context;
public MyClass(IDataContext context)
{
_context = context;
}
public string MyMethod()
{
// 1. Is this needed?
using (var u = _context)
{
var customers = u.Set<Customer>().OrderBy(o => o.Name);
// ........
}
// 2. OR can I just do this
var customers = _context.Set<Customer>().OrderBy(o => o.Name);
As I'm using PerRequestLifetimeManager() in my injection, does this negate the requirement of the using statement as the context will be disposed after every http request anyway?

If you would use using then you would immediately dispose DbContext and if you try to access it again it will result in exception, so 1 approach is generally bad idea. 2 would work but what if there would occur problem in DbContext, for instance new data that you would like to persist will be not compliant to db design (for instance indexes). In such case you will be unable to recreate DbContext. My advice would be to register and then resolve factory method for DbContexts:
class MyClass
{
private readonly Func<IDataContext> _contextFactory;
public MyClass(Func<IDataContext> contextFactory)
{
_contextFactory = contextFactory;
}
public string MyMethod()
{
// 1. Is this needed?
using (var u = _contextFactory())
{
var customers = u.Set<Customer>().OrderBy(o => o.Name);
// ........
}
}

Best practice to use Unit of Work and Repository patterns while you are working with MVC4. While you are creating every time of our context best to register your context in the UnityConfig.cs file located at App_Start folder. as
container.RegisterType<IDataContext, MyContext>(new PerRequestLifetimeManager());
after this when you start your application the context will be in open till your application closed. so, do not use the using statement at the use of context. It will dispose your context after complete of using and the context will not available further and when use context after closing using then you must got an exception like context is not available. so, just use
var customers = _context.Set<Customer>().OrderBy(o => o.Name);

Related

How to instantiate DbContext from IHostingService

I have a class that derives from BackgroundService (IHostedService) for running background tasks. This will be added to my services using builder.Services.AddHostedService<BackgroundTaskService>()
BackgroundService's task runs for the entire duration of the web application, checking for queued data to process.
My question is, how do I instantiate an instance of DbContext from this code?
I could have the BackgroundTaskService constructor accept a DbContext. But wouldn't that keep the DbContext open forever?
And how else could I instantiate it without duplicating all the code to scan my settings file for the connection string, etc.?
The recemmended approach is to inject IDbContextFactory<TContext> as described in the following article: Using a DbContext factory (e.g. for Blazor)
Some application types (e.g. ASP.NET Core Blazor) use dependency injection but do not create a service scope that aligns with the desired DbContext lifetime. Even where such an alignment does exist, the application may need to perform multiple units-of-work within this scope. For example, multiple units-of-work within a single HTTP request.
In these cases, AddDbContextFactory can be used to register a factory for creation of DbContext instances.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(
options =>
options.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=Test"));
}
Then in your controller:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
You can use scope service factory. Check here for reference.
Here you have an example:
// Injection
public class DataApi : BackgroundService
{
private readonly ILogger<DataApi> logger;
private readonly IServiceScopeFactory scopeFactory;
public DataApi(ILogger<DataApi> _logger, IConfiguration _cfg, IServiceScopeFactory _sSF)
{
logger = _logger;
scopeFactory = _sSF;
// e.g. data from appsettings.json
// var recovery = _cfg["Api:Recovery"];
}
// ...
// Usage
protected async Task DataCollector()
{
logger.LogInformation("Collector");
using (var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
var myList = await db.MyEntity
.AsNoTracking()
.Where(t => t.active)
.ToListAsync();
if (myList.Count == 0)
{
logger.LogInformation("Empty...");
return;
}
// logic...
}
await Task.CompletedTask;
}

Access dependency-injected Entity Framework database from static class method

In my project, I have a static converter method to convert client and database objects into each other. One of those static methods needs to access the database. Before introducing dependency injection into my project, that was quite simple:
internal async static Task<ViewerColumn> FromClientColumn(ViewerColumnSettings col) {
using MpaContext db = new MpaContext();
return new ViewerColumn() {
// ...
SourceColumnID = await db.SourceColumns
.Where(sc => sc.Key == col.DataField)
.Select(sc => sc.ID)
.SingleAsync()
};
}
I want to change this by introducing dependency injection project-wide. My first approach was to simply add the database context as a separate parameter:
internal async static Task<ViewerColumn> FromClientColumn(ViewerColumnSettings col, MpaContext context) {
using MpaContext db = context;
// ...
}
This, however, leads to problems, if the context from the parameter gets disposed somewhere else. So my idea was to dependency-inject the context to the class inself. This, however, doesn't work, because you obviously can't use parameters for static constructors.
Here's how the method is called (currently with the context parameter):
// Controller method with dependency injection
[HttpPut("ViewerRoles/{vrID}")]
public async Task<ActionResult> UpdateViewSettings(int vrID, ViewerRoleSettings updatedData) {
using MpaContext db = _mpaContext;
await storedViewerRole.ApplyViewerRoleSettingsAsync(updatedData, _mpaContext);
}
// ViewerRole.cs
internal async Task ApplyViewerRoleSettingsAsync(ViewerRoleSettings updatedData, MpaContext context) {
// Create new entries
foreach (Client.ViewerColumnSettings col in updatedData.ViewerColumns) {
ViewerColumns.Add(await ViewerColumn.FromClientColumn(col, context));
}
}
This approach fails, because the context gets disposed in UpdateViewSettings and in FromClientColumn.
What's the best-practice approach for such a case? I could dispose the context only, if it wasn't open beforehand, but that sounds stupid to me.
Dependency Inversion / Dependency Injection does not play well with static.
Make an abstraction and derived implementation with injected context
public class ViewerColumnService : IViewerColumnService {
private readonly MpaContext db ;
public ViewerColumnService (MpaContext db) {
this.db = db;
}
public async Task<ViewerColumn> FromClientColumn(ViewerColumnSettings col) {
return new ViewerColumn() {
// ...
SourceColumnID = await db.SourceColumns
.Where(sc => sc.Key == col.DataField)
.Select(sc => sc.ID)
.SingleAsync()
};
}
}
Register this new service and explicitly inject it where it is needed. Stop manually disposing of the context by wrapping it in a using statement. Let the DI container handle the lifetime of the components.

Where to initialize DbContext in MVVM

Let's say I'm making an application. For the user interface I decide to go with an Model-View-ViewModel pattern. The UI will access a service layer which will use Entity Framework Core as a replacement for the more traditional repository (I know people have mixed feelings about this, but this is not the point of this question). Preferably the DbContext from EFCore will be injected into the service. Something like this:
public void SomeUserInterfaceMethod()
{
using (var context = new MyContext())
{
var service = new MyService(context);
service.PerformSomeAction();
}
}
Now this isn't so bad at all, but I do have an issue with it. The using (var context = new MyContext()) will be in a lot of places in the code, even in a small application. This means trouble if I want to change the context as well as for testing purposes.
Now I could replace the new MyContext() with a factory method (MyFactory.GetMyContext()), which would make it easier to replace. But what if I want to change the context to a testing one (using another database)?
Is there some more clever way to initialize MyContext which allows both for easy replacement, but also for easy testing?
Honestly, I don't see any problems in using factory method for your purposes.
Easy replacement example:
public class ClassWithSomeUserInterfaceMethod
{
private readonly IDataContextsFactory dataContextsFactory;
public ClassWithSomeUserInterfaceMethod(IDataContextsFactory dataContextsFactory)
{
this.dataContextsFactory = dataContextsFactory;
}
public void SomeUserInterfaceMethod()
{
using (var context = dataContextsFactory.GetDataContext())
{
var service = new MyService(context);
service.PerformSomeAction();
}
}
}
You can pass any class that implements IDataContextsFactory interface in dataContextsFactory.
Easy testing example:
public AnotherDatabaseDataContextFactory : IDataContextsFactory
{
public IDataContext GetDataContext()
{
return new AnotherDataContext();
}
}
[Test]
public void SomeTest()
{
var factory = new AnotherDatabaseDataContextFactory();
var classWithSomeUserInterfaceMethod = new ClassWithSomeUserInterfaceMethod(factory);
classWithSomeUserInterfaceMethod.SomeUserInterfaceMethod();
// Assert.That ...
}
Hope it helps.

How to properly inject a DbContext for WebAPI using a ContextFactory and a repository?

I'm trying to safely inject a database context per web request for a repository on the back of a Web API. The consuming class calls the repository in order to retrieve the object, and if it comes back null, then it gets the object from a different data store and saves that in the database for faster access later. This means it's trying to Get from the DB then doing stuff then creating a new record.
Currently I have this repository
public class OrganisationRepository : IOrganisationRepository
{
public Func<IOrganisationDomainDbContext> ContextFactory { get; set; }
public Organisation GetDetailByIdentifier(int id)
{
using (var context = ContextFactory.Invoke())
{
var org = context.Organisations.SingleOrDefault(x => x.Id == id);
return org;
}
}
public void Create(Organisation orgToCreate)
{
using (var context = ContextFactory.Invoke())
{
context.Organisations.Add(orgToCreate);
context.SaveChanges();
}
}
}
and the repository is injected into the consuming class with a transient lifestyle. The DbContext is injected per web request.
Previously, the Repository was injected with a singleton lifestyle, which was breaking on the Create action.
My question is, am I doing a cheap hack by making the Repository transient? Will this cause me problems down the line? If so, how should I be doing this differently?
EDIT: For further information, the DI Container in use is Castle Windsor
EDIT: Relevant parts of the DI Installer
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IOrganisationDomainDbContext>().ImplementedBy<OrganisationDomainDbContext>().LifeStyle.PerWebRequest,
Component.For<Func<IOrganisationDomainDbContext>>().Instance(container.Resolve<IOrganisationDomainDbContext>),
Component.For<IOrganisationRepository>().ImplementedBy<OrganisationRepository>().LifeStyle.Transient,
Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
}
UPDATE: Transient repository did not fix the problem, that was a mistake on my part - I just forgot that the record I was looking at had in fact been committed to the database, and therefore the create action was not being called. My mistake, apologies.
As user jbl pointed out, I didn't need to use using blocks around the context usage - the DI Container disposes of the context as it is used per web request.
Here's the code now:
public class OrganisationRepository : IOrganisationRepository
{
public Func<IOrganisationDomainDbContext> ContextFactory { get; set; }
public Organisation GetDetailByIdentifier(int id)
{
var context = ContextFactory.Invoke()
var org = context.Organisations.SingleOrDefault(x => x.Id == id);
return org;
}
public void Create(Organisation orgToCreate)
{
var context = ContextFactory.Invoke();
context.Organisations.Add(orgToCreate);
context.SaveChanges();
}
}
And the DI container installer:
container.Register(
Component.For<IOrganisationDomainDbContext>().ImplementedBy<OrganisationDomainDbContext>().LifeStyle.PerWebRequest,
Component.For<Func<IOrganisationDomainDbContext>>().Instance(container.Resolve<IOrganisationDomainDbContext>),
Component.For<IOrganisationRepository>().ImplementedBy<OrganisationRepository>().LifeStyle.PerWebRequest,
Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
Hopefully this is the correct answer, debugging shows that the contexts are disposed of after each web request and I'm not running into the exceptions. Thanks for all the help jbl and Mark Seeman.

Custom resolve of registered type on runtime in multi-tenant architecture with Autofac

I'm facing a problem where I need to implement some sort of a custom resolver for registered types in Autofac.
My setup looks like this:
I have a multi tenant architecture where I have several databases (one per tenant, all sharing the same schema). One application needs to traverse all databases to collect data.
I've come up with an idea to use autofac to register the DbContext, but when resolving the IEnumerable<DbContext> I need a way to resolve these in runtime by some custom code to figure out the connection strings for each context from another database.
I'll try to make it clearer with some pseudo-code:
private void Configure()
{
_container.RegisterType<DbContext>()
.ResolveBy(() => /*some custom code to resolve all DbContext based on the number of tenants*/);
}
public class MultiContextService
{
private readonly IEnumerable<DbContext> _dbContexts;
public MultiContextService(IEnumerable<DbContext> dbContexts )
{
_dbContexts = dbContexts;
}
public void SomeMethod()
{
foreach (var context in _dbContexts)
{
//do something to each context...
}
}
}
Note that tenants can be added during runtime and should be able to be resolved without the instance needs to be restarted.
The problem becomes much easier if you don't inject the DbContext at all. Instead provide application code with an abstraction that allows you to retrieve the DbContext at runtime. For instance:
public interface IContextProvider {
IEnumerable<DbContext> Contexts { get; }
}
Now you move the problem of collecting the right set of DbContexts to the IContextProvider implementation, but this will be considerably easier.
If possible, try to hide the fact that you have a list of DbContext instances behind an abstraction, in such way that you don't have to litter your application code with foreach (var context in contexts) methods.
If you want runtime behaviour then you can work with the Assembly registrar. Something like below:
using System;
using System.Collections.Generic;
using Autofac;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var containerBuilder = new ContainerBuilder();
// customise your assembly loader for runtime behaviour
containerBuilder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.Where(t => t.BaseType == typeof(DbContext))
.As<DbContext>().OnPreparing(eventArgs =>
{
// depending on which context you are activating you can do use somekind of convention to get the correct connection string
});
containerBuilder.RegisterType<MultiContextService>();
var container = containerBuilder.Build();
var mcs = container.Resolve<MultiContextService>();
}
}
internal class MultiContextService
{
public MultiContextService(IEnumerable<DbContext> allContexts)
{
// all contexts are resolved here
}
}
internal abstract class DbContext
{
}
internal class DbContext1 : DbContext
{
}
internal class DbContext2 : DbContext
{
}
}
Will that work?

Categories

Resources