Autofac - memory leak - c#

I've created simple factory to creating Entity Framework's DbContext. It's implemented like this (simplified code):
public class ContextFactory : IContextFactory
{
private Func<IDbContext> _dbContext;
public ContextFactory(Func<IDbContext> dbContext)
{
_dbContext = dbContext;
}
public IDbContext CreateContext()
{
var context = _dbContext();
context.Configuration.ProxyCreationEnabled = false;
return context;
}
}
As you see DbContext is injected to my factory as Func and created every time when CreateContext() is called.
Registration in bootstrapper is:
builder
.RegisterType<ContextFactory>()
.As<IContextFactory>()
.SingleInstance();
So factory is singleton and context registration:
builder
.RegisterType<OdynDbContext>()
.AsImplementedInterfaces()
.InstancePerDependency();
is instance per dependency.
I use created context in using() block so it should be disposed every time:
public TestClass
{
private readonly IContextFactory _contextFactory;
public TestClass(IContextFactory contextFactory)
{
_contextFactory = contextFactory;
}
public void TestMethod()
{
using(var context = _contextFactory.CreateContext())
{
... operations on context
}
}
And unfortunately context isn't disposed correctly. It stays somewhere in memory and causes leak. I don't know why. I overrided Dispose() method in DbContext and it is called. Anyone has met with such a problem?

The answer is to use .ExternallyOwned() extension method while registering DbContext:
builder
.RegisterType<DbContext>()
.AsImplementedInterfaces()
.ExternallyOwned()
Then wrapping it in using block causes proper disposal of object.

Related

EF Core: Create an object by passing an injected DbContext object as parameter

I have created a .Net Core MVC project and understand that how the dependency injection works for our MVC controller as shown below, but same like I wanted to create an object for my own class by calling the same injected interface/class as a parameter.
public class ShiftsController : BaseController
{
ShardingDbContext _dbContext;
public ShiftsController(ShardingDbContext ShardingDbContext) : base(ShardingDbContext)
{
_dbContext = ShardingDbContext;
ViewBag.Menu = BuildMenu();
}
I have injected the DbContext into my Startup.cs file as below,
//Entity Framework Core
services.AddDbContext<ShardingDbContext>(options => options.UseSqlServer(ConnectionString),
ServiceLifetime.Transient);
The ShiftsController is a C#-MVC controller and the DbContext is working perfectly when I run my app and go to Shift's page in my application, but when I try like below-given code, it's not working and gives an error. So I don't know how to pass the registered class's object while creating an object by using "new" keyword.
public class JobScheduler
{
ShardingDbContext _dbContext;
public JobScheduler(ShardingDbContext ShardingDbContext)
{
_dbContext = ShardingDbContext;
}...
This is my own class and tried to create an object for the class JobScheduler as shown below.
JobScheduler jobs = new JobScheduler();
So now I don't know how to pass the EF core's DbContext's object to the constructor JobScheduler, the DI works fine for the controller but not for a normal class. Can anyone help with this and I am eagerly waiting to understand this logic as well?.
Register your JobScheculer like this:
services.AddSingleton<JobScheduler>();
then use your dbContext like this:
public class JobScheduler
{
private readonly IServiceProvider provider;
public JobScheduler(IServiceProvider provider)
{
}...
public (or private etc) DoYourJob()
{
using (var scope = provider.CreateScope())
{
var dbContext = scope.GetService<ShardingDbContext>();
//use it here
}
}
At the end of the ConfigureServices method of the Startup.cs class, and I did not change anything in the JobSchedulerclass and passing the DbContext object from the service provider as shown below, thanks to everyone who tried to help with this question.
public void ConfigureServices(IServiceCollection services)
{
...
...
JobScheduler job = new
JobScheduler(services.BuildServiceProvider().CreateScope().ServiceProvider
.GetService<ShardingDbContext>());
job.Start();
}
You are right: Your DI works fine but your ShardingDbContext is not passed into your JobScheduler because you are not using DI to instanciate JobScheduler. Whenever you are explicitly creating an object instance using the new keyowrd you are not using DI.
You have two options:
Wherever you are calling new JobScheduler() let DI inject you a ShardingDbContext through the constructor and pass it to JobScheduler like so new JobScheduler(shardingDbContext)
Register JobScheduler to the dependency injection as well and let DI build up the whole chain so you don't need to call new JobScheduler() but rather get a JobScheduler injected directly wherever you need it
Edit
As requested here is the example for a timed job using a short lived DB context:
public class TimedBackgroundService : IHostedService, IDisposable
{
private readonly Timer timer;
private readonly IServiceProvider serviceProvider;
public TimedBackgroundService(IServiceProvider serviceProvider)
{
timer = new Timer(async state => await ExecuteAsync());
this.serviceProvider = serviceProvider;
}
public Task StartAsync(CancellationToken stoppingToken)
{
timer.Change(0, TimeSpan.FromMinutes(30));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken stoppingToken)
{
timer.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose() => timer.Dispose();
private async Task ExecuteAsync()
{
try
{
using var scope = serviceProvider.CreateScope();
var job = scope.ServiceProvider.GetRequiredService<MyJob>();
await job.Execute();
}
catch (Exception exception)
{
// log error here
return;
}
}
}
The MyJob class wil look something like this:
public class MyJob
{
private readonly ShardingDbContext dbContext;
public MyJob(ShardingDbContext dbContext)
{
this.dbContext = dbContext;
}
public Task Execute()
{
// Your logic goes here
}
}
Then you register your classes in the startup like so:
services
.AddHostedService<TimedBackgroundService>()
.AddScoped<MyJob>();
Now you have a job which runs every 30 minutes and uses a short lived db context.

Autofac - DataContext not being disposed at end of lifetime scope

I have a command-line program which is importing data into my system. Because it's inserting data into many tables, I require change tracking. To try and prevent the job from slowing down over time, I have used Autofac (my dependency injection framework) to create an inner lifetime scope from which I resolve dependencies. At the end of each batch, I recreate the lifetime scope and get new instances of my dependencies. The problem is that when I do this, my DataContext which the UnitOfWork depends on is not being refreshed each time, leading to a situation where the job slows down and eventually terminates before completing.
I can see this when debugging by setting the 'Make Object ID' on my DbContext, e.g.
After each batch, the object ID remains $2, demonstrating that the DataContext instance is not getting a new instance. Why is it not getting a new instance?
My code looks something like this:
foreach (var batch in data.Batch(10))
{
using (var scope = LifetimeScope.BeginLifetimeScope(b =>
{
b.RegisterType<UnitOfWork>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
b.RegisterType<MyService1>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
b.RegisterType<MyService2>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
b.RegisterGeneric(typeof(EntityBaseRepository<>)).As(typeof(IEntityBaseRepository<>)).InstancePerLifetimeScope();
}))
{
UnitOfWork = scope.Resolve<IUnitOfWork>();
MyService1 = scope.Resolve<IMyService1>();
MyService2 = scope.Resolve<IMyService2>();
Thing1Repository = scope.Resolve<IEntityBaseRepository<Thing1Repository>>();
Thing2Repository = scope.Resolve<IEntityBaseRepository<Thing2Repository>>();
foreach (var row in batch)
{
try
{
ParseRow(row);
}
catch (Exception e)
{
JobLogger.Error(e, "Failed to parse row. Exception: " + e.Message);
throw;
}
}
}
}
It was my understanding that when I get a new instance of my dependencies, the child dependencies will get new instances as well? Why is the original DataContext still hanging about?
My UnitOfWork looks like this:
public class UnitOfWork : Disposable, IUnitOfWork
{
private readonly IDbFactory _dbFactory;
private DataContext _dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
_dbFactory = dbFactory;
}
public DataContext DbContext => _dbContext ?? (_dbContext = _dbFactory.Initialise());
public void Commit()
{
DbContext.Commit();
}
}
My DbFactory is responsible for creating a new instance of my DataContext:
public class DbFactory : Disposable, IDbFactory
{
DataContext _dbContext;
public DbFactory()
{
_dbContext = new DataContext();
}
public DataContext Initialise()
{
return _dbContext ?? (_dbContext = new DataContext());
}
protected override void DisposeCore()
{
_dbContext?.Dispose();
}
}
My services are registered by scanning the assembly when the program first starts by calling this method:
AutofacConfig.InitialiseJobRunner();
Inside this method, I register my types like this:
builder.RegisterType<DataContext>().AsSelf().InstancePerMatchingLifetimeScope(lifetimeScope);
builder.RegisterGenericInstance(typeof(EntityBaseRepository<>), typeof(IEntityBaseRepository<>), lifetimeScope);
builder.RegisterAssemblyInterfaces(Assembly.Load(Data), lifetimeScope);
RegisterAssemblyInterfaces is implemented as:
public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
RegisterAssemblyInterfaces(this ContainerBuilder builder, Assembly assembly, object lifetimeScope)
{
return builder.RegisterAssemblyTypes(assembly)
.AsImplementedInterfaces()
.InstancePerMatchingLifetimeScope(lifetimeScope);
}
As you register assembly interfaces like below
public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
RegisterAssemblyInterfaces(this ContainerBuilder builder, Assembly assembly, object lifetimeScope)
{
return builder.RegisterAssemblyTypes(assembly)
.AsImplementedInterfaces()
.InstancePerMatchingLifetimeScope(lifetimeScope);
}
my guess is that your DbFactory is also registered this way. In such case (according to: https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope ) you will get the same instance of your factory as your child scope is not named. Try to add
b.RegisterType<DbFactory>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
in your loop or change DbFactory to always return new context in Initialise method instead returning the same if it's already instantiated.

InstancePerRequest DbContext ASP.NET MVC

Registering DbContext in ASP.NET MVC Application as InstancePerRequest. (IoC Autofac)
builder.RegisterType<ADbContext>().As<IADbContext>().InstancePerRequest();
Using inside BService
public class BService : IBService
{
readonly IADbContext _dbContext;
public BService(IADbContext dbContext)
{
_dbContext = dbContext;
}
}
Trying to register IBService as Singleton.
builder.RegisterType<BService>().As<IBService>().SingleInstance();
Obviously, this gives me an error
No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
Simplest solution is to register IBService as InstancePerRequest, but there is no reason having PerRequest IBService rather than error message mentioned above.
How can i use PerRequest DbContext inside Singleton service ?
First attempt, you can inject IContainer into BService. But this will look like Service locator pattern, which is not good. Otherwise, you can define factory interface
public interface IFactory<T>
{
T GetInstance();
}
Then implement it and register
public class SimpleFactory<T> : IFactory<T>
{
private IContainer _container;
public SimpleFactory(IContainer container)
{
_container = container;
}
public T GetInstance()
{
return _container.Resolve<T>();
}
}
public class DbContextFactory : SimpleFactory<IADbContext>
{
public DbContextFactory(IContainer container):base(container)
{
}
}
Finally, use this factory in your singletone
public class BService : IBService
{
IADbContext _dbContext => _dbContextFactory.GetInstance();
IFactory<IADbContext> _dbContextFactory
public BService(IFactory<IADbContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
}
Each time, when you want to acess to context inside singletone, it will pass this request to IoC container, which able to return context per request.

DbContext and scoped dependency

I have a simple DbContext looking like:
public class MyDbContext : DbContext
{
private readonly IUserContext _userContext;
public MyDbContext(IUserContext userContext) : base("DefaultConnectionString")
{
_userContext = userContext;
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Configuration>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ... Here I need to creates some filters using the IUserContext dependency
base.OnModelCreating(modelBuilder);
}
}
This DbContext is wired using Func<T> factory, using the guidelines in the Simple Injector documentation: container.RegisterFuncFactory<DbContext, MyDbContext>(Lifestyle.Scoped);
public static void RegisterFuncFactory<TService, TImpl>(
this Container container, Lifestyle lifestyle = null)
where TService : class
where TImpl : class, TService
{
lifestyle = lifestyle ?? Lifestyle.Transient;
var producer = lifestyle.CreateProducer<TService, TImpl>(container);
container.RegisterSingleton<Func<TService>>(producer.GetInstance);
}
But apperently, such simple case is not possible with the DbContext because of this message:
The target context 'MyDbContext' is not constructible. Add a default
constructor or provide an implementation of IDbContextFactory.
I dont really like the idea of the IDbContextFactory, so the only solution I can come up with is to remove the dependency on the MyDbContext, set it as a property, modify the RegisterFuncFactory method and manually initialize the context:
internal static void RegisterFuncFactory<TService, TImpl>(this Container container, Func<TImpl> instanceProducer, Lifestyle lifestyle = null) where TService : class where TImpl : class, TService
{
lifestyle = lifestyle ?? Lifestyle.Transient;
var producer = lifestyle.CreateProducer<TService>(instanceProducer, container);
container.Register<Func<TService>>(() => producer.GetInstance, Lifestyle.Singleton);
}
container.RegisterFuncFactory<DbContext, MyDbContext>(() => new MyDbContext
{
UserContext = container.GetInstance<IUserContext>()
}, Lifestyle.Scoped);
While not elegant it works, but is there another and "better" way of doing what I need? I like the explicitly of the dependency on the context, but seem not possible.
UPDATE
The error is coming from:
'System.Data.Entity.Migrations.Infrastructure.MigrationsException'
occurred in EntityFramework.dll but was not handled in user code
On this code the return statement of the Query method here:
internal sealed class EntityFrameworkRepository<TEntity> : IEntityWriter<TEntity>, IEntityReader<TEntity> where TEntity : Entity
{
private readonly Func<DbContext> _contextProvider;
public EntityFrameworkRepository(Func<DbContext> contextProvider)
{
_contextProvider = contextProvider;
}
public IQueryable<TEntity> Query()
{
var context = _contextProvider();
return context.Set<TEntity>().AsNoTracking();
}
// Methods removed for brevity
}
Add a second (default) constructor. This way EF migrations can use this constructor when run from the command line, while you can let your application use the second constructor.
You loose Simple Injector's auto-wiring capabilities on your DbContext when you add this second constructor, but this shouldn't be a problem; you can simply wire your context as follows:
IUserContext userContext = new AspNetUserContext();
container.RegisterSingleton<IUserContext>(userContext);
var contextProducer = Lifestyle.Scoped.CreateProducer<DbContext>(
() => new MyDbContext(userContext),
container);
container.RegisterSingleton<Func<DbContext>>(contextProducer.GetInstance);
This answer is only to display for further users what I ended up with. #Steven answer is the right answer.
In order to be able to inject dependencies into the the DbContext while supporting migrations, we have to use two constructors. One for the migrations and one for the application.
public class MyDbContext : DbContext
{
private readonly IUserContext _userContext;
// For migrations
public MyDbContext() : base("DefaultConnectionString")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Configuration>());
}
// For applications
public MyDbContext(IUserContext userContext) : base("DefaultConnectionString")
{
_userContext = userContext;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ... Code removed for brevity
base.OnModelCreating(modelBuilder);
}
}
This is then wired in the composition root like:
public static void RegisterEntityFramework<TContext>(this Container container, Func<TContext> context) where TContext : DbContext
{
if (container == null) throw new ArgumentNullException(nameof(container));
var contextProducer = Lifestyle.Scoped.CreateProducer<DbContext>(context, container);
container.RegisterSingleton<Func<DbContext>>(() => contextProducer.GetInstance);
}
var userContext = new AspNetHttpUserContext();
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
container.RegisterSingleton<IUserContext>(userContext);
container.RegisterEntityFramework(() => new WayFinderDbContext(userContext));
container.Verify();

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

Categories

Resources