I've been doing a lot of reading around garbage collecting and and using IDisposable to handle connections via entity framework. I understand that I should be disposing of resources as soon as I'm done with them but I haven't seen a lot of good examples around when exactly that call should be made and where from and want to ensure I'm handling this correctly.
Let's say I'm returning a basic inventory list from a web API call. My assumption is I should call my dispose method immediately after I get the list. Is this correct? For example's sake let's look at the below method that lives in the service layer:
public class ServiceLayer
{
private InventoryContext _db = new InventoryContext();
public List<Inventory> GetFullInventoryList()
{
var inventoryList = _db.Inventory.ToList();
_db.Dispose();
return inventoryList;
}
public Inventory GetInventoryRecordById(int id)
{
var inventoryRecord = _db.Inventory
.Where(y => y.Id == id)
.First();
_db.Dispose();
return inventoryRecord;
}
}
Is this the correct way to dispose of resources? If not, when should it be called? If it is correct, but not the appropriate way, what is the appropriate way?
Thanks!
There are several options available, but all of them follow a common rule: the owner of the context (the one who creates it) should also dispose it.
You can:
Create your context in a service method and dispose it in the same method:
public List<Inventory> GetFullInventoryList()
{
using (var _db = new InventoryContext())
{
return _db.Inventory.ToList();
}
}
Create your context in your service class but then you would need to make the class IDisposable and implement the dispose pattern . This is more complicated, so only use it if you need to call multiple service methods that each work with the database.
Use dependency injection to supply context to the controller. In such a case, the lifetime of the context is configured while registering it in the container, and the container will call dispose on your context (for example Autofac or Windsor do this, assuming you call dispose on the container or its life scope). You can use PerWebRequest lifestyle for this.
Related
I have following registration of the service:
For<Internal.FullPdfExporter>().Use<Internal.FullPdfExporter>();
I have a exporter factory to create different exporters. First of all injected StructureMap container to this factory:
private readonly IContainer _container;
public ExporterFactory(IContainer container)
{
_container = container;
}
Then i use following code to get a instance of FullPdfExporter:
private IExporterBase CreateInstance(Type exporterType)
{
return _container.GetInstance(exporterType) as IExporterBase ;
}
But this code doesn't return control at all. What's the problem is here?
I tried to check all registered instances in debug, and notices that container has registration for "Internal.FullPdfExporter".
Note: Internal is a alias for namespace;
How i get exporter from factory (code of business service which is called from Http Controller):
var task = Task.Run(async () =>
{
foreach (var item in exportData.OrderBy(x => x.ExportUid))
{
var exporter = _exporterFactory.GetBookExporter(param1, param2);
await exporter.StartExport(item.ExportUid);
if (!exporter.IsCompletedSuccessfully)
{
break;
}
}
});
return response;
Note: If to place _exporterFactory.GetBookExporter call before the task.Run, everything works good. Container is able to resolve the dependency.
Code of the GetBookExporter method:
public IExporterBase GetBookExporter(BookSchema bookSchema, PublishProfileType publishProfileType)
{
return CreateInstance(typeof(Internal.FullPdfExporter));
}
Your general logic is flawed. You expect things to run, even after your http call is already over.
Once you return a response, things happen in the ASP.NET framework.
Dependency injection containers finish their scope, objects get disposed.
Keeping those references that are probably scoped or transient in nature until after the call has completed is not going to work. Granted, sometimes it might work, when you have classes that do not implement IDisposable or you just get lucky with a microsocond timing, but generally speaking, this is not anticipated and not meant to work.
If you want to do work after sending out the reply, you have to hand this work over to another party. Maybe you save it into a database and another service picks it up and works on it. Or maybe you have another program or thread running and you try some IPC. But you should not expect threads or tasks referencing things from the scope of your http call to work after it's done.
I recently came across AddPooledDbContextFactory concept as part of my NET 5 self-education piece and am keen to implement it properly. However, I am not sure how to use it with generics that I generally use.
Example of my current setup:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TestDbContext>(
(s, o) => o.UseNpgsql(Configuration.GetConnectionString("DatabaseConnection"))
.UseLoggerFactory(s.GetRequiredService<ILoggerFactory>()));
// other code //
}
my repository generic:
public class Repository<T> : IRepository<T> where T
{
private readonly TestDbContext _dbContext;
public Repository(TestDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task Create(T entity)
{
await _dbContext.Set<T>().AddAsync(entity);
await _dbContext.SaveChangesAsync();
}
// other methods //
}
this is invoked in following manner as example:
public class WeatherForecastController : ControllerBase
{
private readonly IRepository<Test> testRepo;
public WeatherForecastController(IRepository<Test> testRepo)
{
this.testRepo= testRepo;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> GetAsync()
{
await testRepo.Create(new Test { Name = "Superman" });
// other code
}
}
I would like to convert this to use the new AddPooledDbContextFactory concept but cannot find enough documentation to figure out how to do this.
Atm only thing that comes to mind is using statements at each method but that doesn't make sense.
Any advice on this?
Documentation is not yet complete and is in progress, you track this issue
https://github.com/dotnet/EntityFramework.Docs/issues/2523
You can also a look at the tests for AddPooledDbContextFactory to see how to register DbContext with
https://github.com/dotnet/efcore/search?q=AddPooledDbContextFactory
for example to register DbContext:
services.AddPooledDbContextFactory<TContext>(ob =>
ob.UseSqlServer("ConnectionString").EnableServiceProviderCaching(false), poolSize: 32)
Then in your class, inject an IDbContextFactory<TContext> and use it like this:
using(var context = _factory.CreateDbContext())
{
var orders = await context.Orders.Where(o => o.Id > 3).ToListAsync();
}
According to this post:
Note that the DbContext instances created in this way are not managed
by the application's service provider and therefore must be disposed
by the application
You can also check out this post to see how to use IDbContextFactory:
https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-5.0
#Aeseir your code looks good to me. You are following best practices and you don't need to change it.
You are using the Repository Pattern, so your Repository class has all of your query logic which helps create loosely coupled and maintainable code.
In your ConfigureServices, calling: services.AddDbContext<TestDbContext>() registers TestDbContext with Scoped service lifetime. This is the way that DbContext is designed to work, and it will also work well with ASP.NET controllers, since they have a Scoped lifetime as well.
You did not show your code for registering IRepository, but that service lifetime should be Scoped as well. Btw, you can tell BuildServiceProvider() to validate scope registrations:
builder.Services.BuildServiceProvider(validateScopes: true);
Since DbContext is designed to have a Scoped service lifetime, and since your IRepository and Controller services are Scoped as well, every request gets brand new:
Controller
IRepository
DbContext
Those services are used for the request and then Diposed. This is how ASP.NET is intended to work.
Apparently at some point, DbContext pooling has been introduced to improve performance. In this case, EF Core manages a pool of context instances for you and resets them after each request. This can improve performance, although in some situations, the benefit might be small. See MSDN documentation for more details.
I think for use with ASP.NET controllers (i.e. the code you posted above) all you need to do to take advantage of EF Core context pooling is call AddDbContextPool():
builder.Services.AddDbContextPool<ApplicationDbContext>(/* ... */);
However, if you needed to use DbContext in services registered with Singleton lifetime, then the pattern above would not work well. Because when a Scoped service gets used in a Singleton service, the Scoped service is essentially a Singleton. Each request would not get a new DbContext, nor a reset one from the pool. (See QUESTION below.)
In that case, you might want to use the DbContext factory pattern instead:
builder.Services.AddDbContextFactory<ApplicationDbContext>(/* ... */);
Or, if you want to use context pooling with a factory pattern:
builder.Services.AddPooledDbContextFactory<ApplicationDbContext>(/* ... */);
The DbContextFactory can then be used in other services through constructor injection. For example:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
The injected factory can then be used to construct DbContext instances in the controller code. For example:
public void DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
Keep in mind that when you call CreateDbContext(), context instances are not managed by the service provider and therefore must be disposed by the application. Hence you need to Dispose of them yourself, such as in the example above which does so with the using statement.
QUESTION
I am doing my best to understand this stuff and explain it, but I might be wrong, so please call out an inaccuracies in my post.
When using AddDbContextPool(), does the DbContext get registered as a Singleton or Scoped?
I found in MSDN documentation that it's effectively registered as a Singleton:
Context pooling works by reusing the same context instance across requests; this means that it's effectively registered as a Singleton, and the same instance is reused across multiple requests (or DI scopes). This means that special care must be taken when the context involves any state that may change between requests.
However, I have found that if AddDbContextPool() is used along with true for validatedScopes:
builder.Services.BuildServiceProvider(validateScopes: true)
When DbContext is consumed from another service which is registered as a Singleton, the following exception is thrown:
System.InvalidOperationException: 'Cannot consume scoped service 'ApplicationDbContext' from singleton 'IRepository'.'
Hence why I stated above that DbContext still gets Scoped service lifetime.
I need to provide an api to a long running windows service which does a bunch of processing and retains a memory of that history. An api is required to provide status on current activity levels of the system (records processed, records waiting to be processed, etc).
I was wanting to use a self-hosted Owin ApiController to provide a nice interface to the system. However, the ApiController is completely stateless and there is no method (after searching dozens of IoC posts) for injecting an already active instance into the controller.
Is there a way to provide a class instance to an ApiController?
I don't think you can inject an old instance of the controller, because you get a new instance every time you perform a request.
However you can create a singleton object with a collection inside, and you can inject it into the controller constructor and use in every request.
You can also use some sort of persistence such as DB, that you can run on your device.
Here is an example for a singleton class:
using System;
public class Singleton
{
public Dictionary<string,object> State {get; private set;}
private static Singleton instance;
private Singleton() {
State = new Dictionary<string,object>();
}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
Even though you can get Session state in web api controller, it is going to be questionable solution, instead, I would recommend going the following way:
include Hangfire as dependancy and use it for long running tasks
Use signal-r to to push state of background task
return JobID from your API method and use it on the client to subscribe to signal-r hub
I am using ASP.NET MVC WebApi 2 and injecting concrete types into controllers using Unity 3 and this Unity MVC bootstrapper.
The issue here is that one the registered types initialises an Entity Framework 6 DbContext for every resolve:
public sealed class EntityFrameworkUnitOfWork : IUnitOfWork
{
internal DbContext Context { get; private set; }
public EntityFrameworkUnitOfWork()
{
Context = new SomeContext();
}
public void Commit()
{
Context.SaveChanges();
}
public void Dispose(bool disposing)
{
if (disposing)
{
if (Context != null)
{
Context.Dispose();
Context = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
For example, the EntityFrameworkUnitOfWork would be constructor injected into a controller like this:
public class UserController : ApiController
{
public UsersController(IUserRepository userRepository, IUnitOfWork unitOfWork)
{
// unitOfWork is a EntityFrameworkUnitOfWork
}
// ...
}
When the relevant MVC controller disposes, I need the Dispose() method to be called on the above EntityFrameworkUnitOfWork type, which in turn will dispose of the underlying DbContext.
Registering this type with Unity:
Container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>(
new DisposingTransientLifetimeManager()
);
I am using the DisposingTransientLifetimeManager lifetime manager, as suggested in this article, thinking that it would automatically dispose of my IDisposable types. It seems that I still need to call on Container.Teardown(someInstance). This is not possible for me to do as the MVC Bootstrapper is handling all DI resolving.
Where and how can I perform the teardown of these initialised types?
It would be ideal to perform this teardown when the relevant MVC controller destructs, but perhaps this can also occur at the end of the HTTP request?
EDIT:
The IDisposables that are injected aren't necessarily accessible from the controller. For example, I could also inject a IUserService into a controller which itself (the IUserService class) is injected with an IUserRepository and an IUnitOfWork. I could chain Dispose methods from the IUserService to dispose of them, but this would requires changes to hundreds of controllers and services. Ideally I should be able to call container.Dispose() somewhere to have Unity dispose all injected disposables.
EDIT 2:
RaraituL brought something else to mind. IUnitOfwork does not implement IDisposable, only EntityFrameworkUnitOfWork does. This essentially means that the an MVC controller wouldn't be able to call on a dispose method as it only knows of IUnitOfWork. This is another reason why Unity should do this - it created the IDisposables so it should dispose of them too.
Sounds like you want a PerRequestTransientLifetimeManager. That will be something you will have to build. It's not hard and since you are using Unity 3 most of the work is already done for you.
public class PerRequestTransientLifetimeManager : ILifetimePolicy
{
public object GetValue()
{
// will always create a new object (Transient)
return null;
}
public void SetValue(object newValue)
{
// No point in saving to http context if not disposable
if (newValue is IDisposable)
{
var perRequestLifetimeManager = new PerRequestLifetimeManager();
perRequestLifetimeManager.SetValue(newValue);
}
}
public void RemoveValue()
{
// can't remove since transient didn't keep a reference
}
}
You'll need Unity.Mvc nuget package if the PerRequestLifetimeManager class is missing. You'll also need to register the UnityPerRequestHttpModule using Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule
I should point out from MS site
Although the PerRequestLifetimeManager lifetime manager works
correctly and can help in working with stateful or thread-unsafe
dependencies within the scope of an HTTP request, it is generally not
a good idea to use it when it can be avoided, as it can often lead to
bad practices or hard to find bugs in the end-user's application code
when used incorrectly. It is recommended that the dependencies you
register are stateless and if there is a need to share common state
between several objects during the lifetime of an HTTP request, then
you can have a stateless service that explicitly stores and retrieves
this state using the Items collection of the Current object.
You could use the UnityHierarchicalDependencyResolver in the same NuGet package you already reference (Unity.AspNet.WebApi). Then register your services you want disposed with the HierarchicalLifetimeManager. This dependency resolver creates and disposes a new child container on every Web Api request. When a Unity container is disposed, all built up objects in that container are also disposed.
IUnityContainer rootContainer = new UnityContainer();
GlobalConfiguration.Configuration.DependencyResolver =
new UnityHierarchicalDependencyResolver(rootContainer);
rootContainer.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>
(new HierarchicalLifetimeManager());
If I have two controllers:
public class PrimaryController : Controller
{
private IRepository<Primaries> repository;
public PrimaryController(IRepository<Primaries> repository)
{
this.repository = repository;
}
// CRUD operations
}
and
public class AuxiliaryController : Controller
{
private IRepository<Primaries> repository;
public AuxiliaryController(IRepository<Primaries> repository)
{
this.repository = repository;
}
// CRUD operations
public ActionResult CreateSomethingAuxiliary(Guid id, AuxiliaryThing auxiliary)
{
var a = repository.Get(id);
a.Auxiliaries.Add(auxiliary);
repository.Save(a);
return RedirectToAction("Details", "Primary", new { id = id });
}
}
and DI is implemented like (code is from a Ninject module)
this.Bind<ISessionFactory>()
.ToMethod(c => new Configuration().Configure().BuildSessionFactory())
.InSingletonScope();
this.Bind<ISession>()
.ToMethod(ctx => ctx.Kernel.TryGet<ISessionFactory>().OpenSession())
.InRequestScope();
this.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
will this work properly? I mean will controllers use the same repository instance?
Thanks!
Simple answer - yes! Code will use same implementation for all controllers unless you explicitly configure otherwise, using When... methods.
If you want to reuse not implementation, but same instance of object, you could configure that using methods like InScope, InRequestScope, InSingletonScope as you already do for ISession and ISessionFactory.
From documentation:
// Summary:
// Indicates that instances activated via the binding should be re-used within
// the same HTTP request.
IBindingNamedWithOrOnSyntax<T> InRequestScope();
//
// Summary:
// Indicates that only a single instance of the binding should be created, and
// then should be re-used for all subsequent requests.
IBindingNamedWithOrOnSyntax<T> InSingletonScope();
Using Repository in singleton is not a good Idea. I use InRequestScope to make one instance serve just one request. If using entity framework, you could check out this answer for details
It depends on how the default scope in ninject works (I'm not a ninject user).
It will however work if you specify InRequestScope on the repository mapping.
this.Bind(typeof(IRepository<>))
.To(typeof(Repository<>))
.InRequestScope();
Singleton scope will work as long as the connection to the database is not closed. Your application will stop work when it does since all requests would still try to use the same repository object.
That's why Request scope is better. If the repos fail, it will only fail for one request (unless it's a problem with the db).
I've written a set of best practices: http://blog.gauffin.org/2011/09/inversion-of-control-containers-best-practices/