Should I consider Entity infrastructure as an unmanaged resource?

I am working with a class that uses an EF reference in its constructor.

I implemented IDisposable , but I'm not sure if I need a destructor because I'm not sure that I can classify EF as an unmanaged resource.

If EF is a managed resource, then I don't need a destructor, so I think this is the right example:

 public ExampleClass : IDisposable { public ExampleClass(string connectionStringName, ILogger log) { //... Db = new Entities(connectionStringName); } private bool _isDisposed; public void Dispose() { if (_isDisposed) return; Db.Dispose(); _isDisposed= true; } } 

If the EF is unmanageable, then I will go with this:

 public ExampleClass : IDisposable { public ExampleClass(string connectionStringName, ILogger log) { //... Db = new Entities(connectionStringName); } public void Dispose() { Dispose(true); } ~ExampleClass() { Dispose(false); } private bool _isDisposed; protected virtual void Dispose(bool disposing) { if (_isDisposed) return; // Dispose of managed resources if (disposing) { // Dispose of managed resources; assumption here that EF is unmanaged. } // Dispose of unmanaged resources Db.Dispose(); _isDisposed = true; //freed, so no destructor necessary. GC.SuppressFinalize(this); } } 

Which one?

+7
source share
2 answers

You will never want to use a finalizer (destructor) in this case.

Whether DbContext unmanaged resources or not, and even whether it frees these unmanaged resources responsibly or not, it does not matter if you can try to call DbContext.Dispose() from the finalizer.

The thing is, whenever you have a managed object (which is an instance of DbContext ), it is never safe to try to call any method on that instance. The reason is that by the time the finalizer is DbContext , the DbContext object DbContext already be assembled by the GC and no longer exists. If this happens, you will get a NullReferenceException when you try to call Db.Dispose() . Or, if you're lucky and Db is still alive, you can also throw an exception from within the DbContext.Dispose() method if it has dependencies on other objects that have since been completed and collected.

As written in the MSDN article "Destroy Template" :

X DO NOT allow access to any final objects in the finalizer code path, as there is a significant risk that they have already been completed.

For example, a finalizable object A that has a reference to another finalizable object B cannot reliably use B as a finalizer or vice versa. Finalizers are called randomly (with the exception of weak order guarantees for critical finalization).

Also, pay attention to the following from Eric Lippert When everything that you know is wrong, part two :

Myth: finalizers work in a predictable manner.

Suppose we have a tree of objects, all finalizable and all in the finalizer queue. There is no requirement that a tree be completed from root to leaves, from leaves to root, or any other order.

Myth: A terminated object can safely access another object.

This myth follows directly from the previous one. If you have a tree of objects and you are finalizing the root, then the children are still alive - because the root is alive, because it is in the queue for completion, and therefore the children have a living link - but the children may already have been finalized and are not in very good condition to access their methods or data.


Something else to consider: what are you trying to dispose of? Do you care about timely closure of database connections? If so, then you will be interested in what the EF documentation says about this:

By default, context manages database connections. Context opens and closes connections as needed. For example, a context opens a connection to execute a query, and then closes the connection when all result sets have been processed.

This means that by default, connections do not need to call DbContext.Dispose() for a timely close. They open and close (from the connection pool) as requests are executed. Therefore, while it is still very useful to make sure that you always call DbContext.Dispose() explicitly, it is useful to know that if you do not or for some reason forget it, by default it does not cause any connection to leak.


And finally, the last thing you can keep in mind is that with the code you posted that does not have a finalizer, because you are creating an instance of DbContext inside the constructor of another class, it is actually possible that DbContext.Dispose() method is not always called. It’s nice to be aware of this special case so that you don’t get caught in your pants.

For example, suppose I modified your code a bit to eliminate the possibility of throwing it after the line in the constructor that creates the DbContext instance:

 public ExampleClass : IDisposable { public ExampleClass(string connectionStringName, ILogger log) { //... Db = new Entities(connectionStringName); // let pretend I have some code that can throw an exception here. throw new Exception("something went wrong AFTER constructing Db"); } private bool _isDisposed; public void Dispose() { if (_isDisposed) return; Db.Dispose(); _isDisposed= true; } } 

And let's say your class is used as follows:

 using (var example = new ExampleClass("connString", log)) { // ... } 

Although this seems like a completely safe and clean project, because an exception occurs after the ExampleClass constructor, a new instance of DbContext has already been created, ExampleClass.Dispose() never called, and, accordingly, DbContext.Dispose() never called in newly created instance.

You can read more about this sad situation here .

To ensure that the DbContext Dispose() method is always called, regardless of what happens inside the ExampleClass constructor, you will have to change the ExampleClass class to something like this:

 public ExampleClass : IDisposable { public ExampleClass(string connectionStringName, ILogger log) { bool ok = false; try { //... Db = new Entities(connectionStringName); // let pretend I have some code that can throw an exception here. throw new Exception("something went wrong AFTER constructing Db"); ok = true; } finally { if (!ok) { if (Db != null) { Db.Dispose(); } } } } private bool _isDisposed; public void Dispose() { if (_isDisposed) return; Db.Dispose(); _isDisposed= true; } } 

But the above really matters only if the constructor does more than just instantiate the DbContext .

+8
source

C # provides garbage collection and thus does not need an explicit destructor. However, if you control an unmanaged resource, you will need to explicitly release this resource when you are done with it. Implicit control over this resource is provided by the Finalize () method (called the finalizer), which will be called by the garbage collector when your object is destroyed.

https://www.oreilly.com/library/view/programming-c/0596001177/ch04s04.html

0
source

All Articles