21

I'm building a wpf application which implements the following features:

  1. Take user input and read data from databases
  2. perform some calculations on it
  3. Showcase it to the user in multiple types of views and write changes back to db

Proposed architecture: Database -> Entity Framework -> Repository -> Business Logic -> Data Service -> ViewModel

Reasons to use this architecture: Multiple scenarios present in the application (Multiple views) and multiple databases. Hence, i'm willing to use repository in the middle for abstraction.

One caveat is that the context will be long lived if repository is implemented. To overcome this, is it okay to create a context and dispose them in a using() block in each of the crud methods.?

feel free to suggest alternate approaches.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Skyuppercut
  • 321
  • 1
  • 2
  • 6
  • Have a look at this thread which is similar to your query. https://stackoverflow.com/questions/21875816/is-disposing-of-entity-framework-context-object-required – Gopinath Oct 24 '17 at 17:37
  • The factory pattern is the most straightforward solution to the issue of long-lived injected dependencies that occur in a persistent runtime (e.g. WPF or Windows services). This is not necessary for shortlived runtimes such as a web project as it disposes of everything (or it should) on a per-request basis. – Flater Jun 09 '22 at 19:00

4 Answers4

23

Use one DbContext object per data access or transaction.

DbContext is a lightweight object; it is designed to be used once per business transaction. Making your DbContext a Singleton and reusing it throughout the application can cause other problems, like concurrency and memory leak issues.

DbContext essentially implements a Unit of Work. Treat it accordingly.

Don't dispose DbContext objects.

Although the DbContext implements IDisposable, you shouldn't manually dispose it, nor should you wrap it in a using statement. DbContext manages its own lifetime; when your data access request is completed, DbContext will automatically close the database connection for you.

To understand why this is the case, consider what happens when you run a Linq statement on an entity collection from a DbContext. If you return a lazy-loading IQueryable from your data access method, you stand up a pipeline that isn't actually executed until the client compels some data from it (by calling FirstOrDefault(), ToList() or iterating over it).

Further Reading
Do I always have to call Dispose() on my DbContext objects?
Why you shouldn't use Singleton DataContexts in Entity Framework
Returning IEnumerable<T> vs. IQueryable<T>
Should Repositories return IQueryable?

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • 6
    While I'm sure someone will come up with some sort of exceptional case for this, I honestly can't come up with a good use case for returning an un-materialized IQueryable from your data access classes. That just gives calling code the capability to reach into your data access (something it probably has no business doing) and muck with things. That being said, is it really that big of a concern to worry about using `using` blocks? Or am I just not thinking of some case where using an IQueryable as you suggest would be worth the trouble? – Becuzz Oct 24 '17 at 18:38
  • @Becuzz: The `DbContext` is responsible for managing its own lifetime. My suggestion is to let it do that; it will work whether you're using `IQueryable` or `IEnumerable`. The most obvious use case that I can think of for lazy loading is where you return some ViewModel object with a related collection in it, but the collection is never used (or only partially used). `IQueryable` allows you to avoid the cost of retrieving unused records. – Robert Harvey Oct 24 '17 at 18:42
  • 3
    I understand all of that, it's just that I've been burned by having people return IQueryables all over the place. And then something farther up the chain added some includes or other things that made for a horrifically bad query (from a DB performance perspective). And that was a fun bug to track down. As such, I've had opportunity to think about if returning an IQueryable is ever a good idea. And I could never think of a time where it was worth the maintenance trouble. (cont.) – Becuzz Oct 24 '17 at 18:50
  • (cont) And if it was never a good idea, then the idea of avoiding a `using` block becomes a moot point. I was more curious if you had seen somewhere where returning an IQueryable was actually useful. I can see the potential benefits, but have never seen them become practical benefits. I'm always interested when people can find good uses for things I've (mostly) written off as poor practice. – Becuzz Oct 24 '17 at 18:50
  • @Becuzz: Unless you can guarantee that nobody in your shop will ever return an `IQueryable`, allowing an object that is designed to manage its own lifetime to manage its own lifetime still seems like a sensible choice to me. And `IQueryable` might not be the only reason you'd want to make that choice. – Robert Harvey Oct 24 '17 at 18:58
  • 4
    @RobertHarvey what exactly is the point of a repository layer if it is returning an IQueryable to the client? You are basically giving the client the option to write queries--sure they will not be sql queries but nonetheless they are queries (just written using C#) and it will be all over the place. If a repository is returning IQueryable then you may as well throw away the repository. – CodingYoshi Oct 25 '17 at 01:34
  • 13
    @RobertHarvey I disagree with not disposing DbContext and disagree with the referred article. The whole idea is to code against the interface and the interface tells me it is IDisposable. I am not going to write my code based on the inner workings of how the EF team has implemented DbContext or chase the devs on that team--they can change it anytime they want. Neither am I going to ask myself or other developers to start digging into the inner workings of each class to see if IDisposable is really useful. I have worked too hard to get the devs on my team to dispose only to ask them not always. – CodingYoshi Oct 25 '17 at 01:46
  • @CodingYoshi: Do what you wish; nobody's stopping you. It's not like this is the only [leaky abstraction](https://stackoverflow.com/questions/15021304) that Microsoft has ever invented. – Robert Harvey Oct 25 '17 at 02:50
  • 2
    I am seeing the Microsoft themselves recommend a `using` statement with DbContext, as seen [here](https://docs.microsoft.com/en-us/ef/ef6/fundamentals/working-with-dbcontext). Particularly: "If the context is created in application code, remember to dispose of the context when it is no longer required." I am wondering then what the justification is for not using `Dispose` or a `using` statement? – pay Oct 28 '19 at 15:12
  • @pay: There isn't one. `using` calls `Dispose` when the `using` block goes out of scope. – Robert Harvey Oct 28 '19 at 15:14
  • 1
    "I am wondering then what the justification is for not using Dispose **or** a using statement?" – pay Oct 28 '19 at 15:17
  • @pay: "DbContext manages its own lifetime; when your data access request is completed, DbContext will automatically close the database connection for you." – Robert Harvey Oct 28 '19 at 15:19
  • @pay: The problem is that some queries in EF are deferred. If you dispose the DBContext before the data in a query is compelled (with ToList() or something similar) data loss can occur. – Robert Harvey Oct 28 '19 at 15:23
  • So there is a chance that a `using` statement may cause that side effect as well? – pay Oct 28 '19 at 15:32
  • So don't dispose and don't use singleton. What is the recommendation here - create context as a method level local variable or class level private variable; and wait for GC to dispose when the variable is out of scope. Could we run into scenario where multiple contexts are tracking same entity, in between gc disposals? Or does DbContext takes care of that internally? – theSushil May 07 '20 at 17:13
  • 1
    One of the special cases where disposing manually is needed is when you open the connection manually. In my case I use that for executing a stored procedure that sets session context before executing more queries. This is a pattern when using Row Level Security in SQL Server for multi tenancy for example. – Gabriel Smoljar Jul 19 '21 at 12:51
  • @RobertHarvey Previous best practice was to dispose everything before they out of scope. May not necessary all the time, but shouldn't hurt. Looks like MS is going to make a mess by introducing exceptions around here. If end users shouldn't call Dispose, why didn't they make it internal or private? I guess that because there are certain cases where users will benefit calling Dispose (Instances may stay less time in memory). Having said that, I think what you said makes sense. Looks like CA2000 warning is disabled now for core platforms. – CharithJ Dec 06 '22 at 00:33
  • @GabrielSmoljár If I opened the connection manually, then I would probably just close the connection manually as well. That's just me though; nothing wrong with the way you said either but just pointing out that you wouldn't necessarily need to dispose of the context. – BVernon Jan 27 '23 at 13:33
3

You probably don't need to, but that doesnt mean you should never.

There is a lot of advice, around the internet, and in the top rated post on this page, telling you that you "Must Not" dispose the DB Context.

The logic here is fundamentally flawed. This is a bold statement, so let me explain.

There is a difference between "You don't need to do X" and "You must never do X".

If you create the context, then let the garbage collector take care of it at some undefined time in the future, you aren't going to get memory leaks. This means, essentially, that you "Don't need" to dispose of the context explicitly.

However, this does not logically translate into "Must Never".

If you read the actual MSDN documentation for DbContext you will see that there is a benefit to explicitly disposing the context; it has many resources that it holds, including caches.

Use using if you want all the resources that the context controls to be disposed at the end of the block. When you use using, the compiler automatically creates a try/finally block and calls dispose in the finally block.

If the context is created in application code, remember to dispose of the context when it is no longer required.

https://docs.microsoft.com/en-us/ef/ef6/fundamentals/working-with-dbcontext#lifetime

Leaving the task of disposing that stuff to the garbage collector will slightly increase memory usage - of course, you "Might Not" ever encounter that problem, and will be able to ignore it.

The automatic internal disposal seems to be a safety measure only

Applications doing huge volumes of DB access using a DbContext will have interesting memory usage patterns. Having an understanding of the various modes of garbage collection is important here.

In particular, you need to understand what happens when you hit what Microsoft refer to as "High Memory Usage Percent".

when the physical memory load reaches 90%, garbage collection becomes more aggressive about doing full, compacting garbage collections to avoid paging

This is when the large amount of cached data that you find in as yet undisposed DB context begins to be an issue.

https://docs.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#high-memory-percent

Many of Microsoft's own examples of EF usage literally tell you to dispose explicitly

For example, here is a microsoft tutorial (for EF5) of the implementation of a repository.

https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application#creating-the-unit-of-work-class

The code sample here explicitly disposes the context when the repo is disposed.

Make a decision based on the facts

The DbContext is intended to act as a single "unit of work". Its unfortunate that Microsoft chose to hide the fact that they are implementing the unit of work pattern - I believe that's what has led to this confusion.

If your services are doing things like, processing huge volumes of data, you will find that the undisposed resources maintained internally by your DbContext will push you into "high memory percentage" territory. At that point, its worth seeing if explicitly disposing of your DbContext makes a difference. You as the developer know that those resources are no longer needed, whereas the garbage collector does not. That isn't its fault - its a general purpose tool.

-3

Ideally the context should be initialized and terminated for a single transaction. In your case the context should be instantiated in Business Logic and passed on to Repository for data read/write.

Gopinath
  • 1
  • 2
-3

If you call DbContext on each method in your application you will experience memory leak. Use a single instance of the DbContext. See the comment in the bellow example:

public bool IsInStock(int _ProductId)
{
  var result = false;

  try
  {
    using (var dataService = new StoreDbDataService()) // NB: This line on each method will eventually cause memory leak.
    {
      result = dataService.IsInStock(_ProductId);
    }
  }
  catch (Exception ex)
  {
    Log.LogException(ex);
  }

  return result;
}
  • 1
    Can you explain why calling DbContext would cause a memory leak? The comment in the source does not help me understand it. I would assume that the using block would cause Dispose to be called on the StoreDBDataService which eventually clean all resources allocated, wouldn't it? – Kasper van den Berg Oct 01 '18 at 14:31
  • This is basically the Ron Swanson "permit" clip but in code. – Dagrooms Aug 12 '19 at 20:06