LinqToDB DataConnection not disposed upon exiting using block - c#

This issue has been perplexing me for quite a while. I'm trying to dispose of a connection to a database, but it keeps leaking no matter how I try to dispose of it. Worse yet, it's supposed to dispose automatically.
Long story short, I have a HostedService that starts with the following code:
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var scope = _serviceProvider.CreateScope())
{
_logger.LogDebug($"Инициализация операторов");
using (var operatorRepo = scope.ServiceProvider.GetService<IOperatorRepository>()) {
var availableOperators = operatorRepo.GetAllOperatorsWithSkills();
_logger.LogDebug($"Инициализовано {availableOperators.Count()} операторов");
foreach (OperatorRow opRow in availableOperators)
{
OperatorMeta opMeta = new(opRow.Idr, opRow.Name, opRow.Number, opRow.OperatorSkills.Select(skill => (skill.SkillId, skill.Experience)));
if (!TryAddOperator(opMeta))
throw new ArgumentException($"Оператор с id {opRow.Idr} уже был добавлен!");
}
if (Convert.ToBoolean(_configuration.GetSection("isOperatorTesting")?.Value))
{
_globalOperatorPool.TryAdd(
-1,
new CampaignOperator(
new OperatorMeta(-1, "Test operator", _configuration.GetSection("testOperatorNumber").Value, Enumerable.Empty<(int, int)>()),
OperatorState.Unassigned)
);
}
}
}
}
I have tried to wrap the insides of the using in a try finally blocks just in case but I honestly don't see how that would be an issue because the using isn't inside of one. Still, connection doesn't close.
The underlying class of IOperatorRepository extends from a chain of two classes which extend from LinqToDB.DataConnection. It's the only IDisposable in the chain.
Honestly, I'm stumped. Any help would be appreciated.
Edit: I feel like I should specify that the connection class does indeed return ObjectDisposedException, but the connection stays open in the database, and more are made constantly as if they're not being reused.

Related

EFCore, keeping entity after disposing of scope/context

Im having troubles figuring out how to keep an entity after disposing of a scope:
public async Task<Camera> GetCamera(string cameraGuid)
{
DirectCamera directCamera;
using (var scope = _scopeFactory.CreateScope())
{
var _accessor = scope.ServiceProvider.GetService<CamerasAccessor>();
//CameraAccessor is just a DbContext, registered with `services.AddDbContext<CamerasAccessor>();`
directCamera = _accessor.Cameras.Single(can => can.Id == cameraGuid);
return directCamera;
}
}
This ofc won't work cause the accessor/dbcontext will be disposed, with all proxies included, before the value will be handled.
My question is: "How can I keep/get entity without it being wrapped in a proxy, that wont be disposed as soon as the scope will be disposed?"
My first thought was to use AsNoTracking() but that doesnt give you a non proxy object.
Any help is appreciated :)

Understanding how using block dispose of objects

I have the following code, where I want to immediately dispose of ApplicationDbContext instance:
using (var context = ApplicationDbContext.Create())
{
MyRepository myRespository = new MyRepository(context);
myRepository.ReadData();
}
If C# garbage collector wants to immediately dispose of ApplicationDbContext, it would have to immediately dispose of MyRepository as well, because it has a reference to ApplicationDbContext? Is this actually happening?
Or do I need to write my code like below to ensure ApplicationDbContext is Disposed, immediately?
using (var context = ApplicationDbContext.Create())
using (MyRepository myRespository = new MyRepository(context))
{
myRepository.ReadData();
}
Your second block of code is right.
One using clause is applied to one declaration.
Wat you wrote here:
using (var context = ApplicationDbContext.Create())
using (MyRepository myRespository = new MyRepository(context))
{
myRepository.ReadData();
}
Is the same as:
var context = ApplicationDbContext.Create();
try
{
var myRespository = new MyRepository(context);
try
{
myRepository.ReadData();
}
finally
{
myRepository.Dispose();
}
}
finally
{
context.Dispose();
}
That's what the compiler generate for you, therefore you do not need to worry about writing such a heavy and repetitive thing which is a possible source of error and bugs.
In the first code, myRespository is not disposed.

Object lifetime when created and used only in the scope of a using statement

When an object is created and used only within the scope of a using statement, is it disposed of during the disposal of its enclosing IDisposable instance? If not, why wouldn't it be?
For instance, when using Dapper I've often created an anonymous object within the scope of the SqlConnection with the impression that it will be disposed of sooner - however I recently read something to the contrary.
using (var connection = new SqlConnection("Connection String"))
{
var parameters = new
{
Message = "Hello world!"
};
connection.Execute("INSERT INTO...", parameters);
}
Your using statement is essentially equivalent to:
var connection = new SqlConnection("Connection String");
try
{
var parameters = new
{
Message = "Hello world!"
};
}
finally
{
if (connection != null)
{
((IDisposable)connection).Dispose();
}
}
As you can see, there is no reason why any other IDisposable instance created inside the try block be disposed automatically for you. If you do need to ensure other objects are disposed then simply nest using statements:
using (var connection = new SqlConnection("Connection String"))
{
using (var otherDisposableObject = new ....)
{
} //otherDisposableObject disposed
} //connection disposed
If excessive nesting gets in the way of readability, then you can simply write the using statements as follows:
using (var connection = new SqlConnection("Connection String"))
using (var otherDisposableObject = new ....)
using (var anotherDisposableObject = new ....)
using (var andAnotherDisposableObject = new ....)
{
} //andAnotherDisposableObject, anotherDisposableObject, otherDisposableObject and connection all disposed
Do bear in mind though that you seem to be mixing garbage collection with disposing an object. In your example, parameters is elegible for garbage collection once execution has exited the using statement as there is no live refence to it.
When it is garbage collected is up to the GC, it might very well never be, depending on your application's memory pressure. If it is garbage collected, then it will be marked as valid for finalization and the finalizer of the object will be scheduled to run. Again, if and when that happens is entirely up to the GC.
Lastly, if the IDisposable pattern of the object is implemented correctly and the finalizer is run, Dispose() will be called (to be precise, Dispose(false) will be called) and the object will dispose any unmanaged resources it might be holding (managed resources can not be disposed safely in this context but it really doesn't matter, the GC will eventually take care of them too).
See IDisposable Pattern for more details on how it works or even better, check out this excellent SO answer.

Why -- HOW -- are transactions processed after disposal?

I'm trying to work with some ambient transaction scopes (thanks, entity-framework), which I haven't really done before, and I'm seeing some ... odd behavior that I'm trying to understand.
I'm trying to enlist in the current transaction scope and do some work after it completes successfully. My enlistment participant implements IDisposable due to some resources it holds. I've got a simple example that exhibits the strange behavior.
For this class,
class WtfTransactionScope : IDisposable, IEnlistmentNotification
{
public WtfTransactionScope()
{
if(Transaction.Current == null)
return;
Transaction.Current.EnlistVolatile(this, EnlistmentOptions.None);
}
void IEnlistmentNotification.Commit(Enlistment enlistment)
{
enlistment.Done();
Console.WriteLine("Committed");
}
void IEnlistmentNotification.InDoubt(Enlistment enlistment)
{
enlistment.Done();
Console.WriteLine("InDoubt");
}
void IEnlistmentNotification.Prepare(
PreparingEnlistment preparingEnlistment)
{
Console.WriteLine("Prepare called");
preparingEnlistment.Prepared();
Console.WriteLine("Prepare completed");
}
void IEnlistmentNotification.Rollback(Enlistment enlistment)
{
enlistment.Done();
Console.WriteLine("Rolled back");
}
public void Dispose()
{
Console.WriteLine("Disposed");
}
}
when used as illustrated here
using(var scope = new TransactionScope())
using(new WtfTransactionScope())
{
scope.Complete();
}
the console output demonstrates the wtf-ness:
Disposed
Prepare called
Committed
Prepare completed
wut.
I'm getting disposed before the transaction completes. This... kind of negates the benefits of hanging with the transaction scope. I was hoping that I'd be informed once the transaction completes successfully (or not) so I could do some work. I was unfortunately assuming that this would happen after scope.Complete() and before I get disposed as we move out of the using scope. This apparently is not the case.
Of course, I could hack it. But I've got other issues which essentially prevent me from doing this. I'll have to scrap and do something else in this eventuality.
Am I doing something wrong here? Or is this expected behavior? Can something be done differently to prevent this from happening??
This is self-inflicted pain. You violate a very basic rule for IDisposable, an object should only ever be disposed when it is no longer in use anywhere else. It is in use when your using statement calls Dispose(), you handed a reference to your object in the WtfTransactionScope constructor. You cannot dispose it until it is done with it, that necessarily means you have to dispose it after the using statement for the TransactionScope completes and the transaction got committed/rolled-back.
I'll let you fret about making it pretty, but an obvious way is:
using(var wtf = new WtfTransactionScope())
using(var scope = new TransactionScope())
{
wtf.Initialize();
scope.Complete();
}
The Prepare completed is merely an unhelpful debug statement. Just delete it.

Should I keep an instance of DbContext in a separate thread that performs periodic job

I have a class Worker which sends emails periodically,I start in Global.asax.cs on App_start()
public static class Worker
{
public static void Start()
{
ThreadPool.QueueUserWorkItem(o => Work());
}
public static void Work()
{
var r = new DbContext();
var m = new MailSender(new SmtpServerConfig());
while (true)
{
Thread.Sleep(600000);
try
{
var d = DateTime.Now.AddMinutes(-10);
var ns = r.Set<Notification>().Where(o => o.SendEmail && !o.IsRead && o.Date < d);
foreach (var n in ns)
{
m.SendEmailAsync("noreply#example.com", n.Email, NotifyMailTitle(n) + " - forums", NotifyMailBody(n));
n.SendEmail = false;
}
r.SaveChanges();
}
catch (Exception ex)
{
ex.Raize();
}
}
}
}
So I keep this dbcontext alive for the entire lifetime of the application is this a good practice ?
DbContext is a very light-weight object.
It doesn't matter whether your DbContext stays alive or you instantiate it just before making the call because the actual DB Connection only opens when you SubmitChanges or Enumerate the query (in that case it is closed on end of enumeration).
In your specific case. It doesn't matter at all.
Read Linq DataContext and Dispose for details on this.
I would wrap it in a using statement inside of Work and let the database connection pool do it's thing:
using (DbContext r = new DbContext())
{
//working
}
NOTE: I am not 100% sure how DbContext handles the db connections, I am assuming it opens one.
It is not good practice to keep a database connection 'alive' for the lifetime of an application. You should use a connection when needed and close it via the API(using statement will take care of that for you). The database connection pool will actually open and close connections based on connection demands.
I agree with #rick schott that you should instantiate the DbContext when you need to use it rather than keep it around for the lifetime of the application. For more information, see Working with Objects (Entity Framework 4.1), especially the section on Lifetime:
When working with long-running context consider the following:
As you load more objects and their references into memory, the
memory consumption of the context may increase rapidly. This may cause
performance issues.
If an exception causes the context to be in an unrecoverable state,
the whole application may terminate.

Categories

Resources