Should DBContext be globally defined or explicitly created every time? - entity-framework

I'm a SQL guy who's tinkering with Web API and Entity Framework 6 and I keep receiving the error "The operation cannot be completed because the DbContext has been disposed" when I my code is:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
}
But if change my code around a little and try this it works:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
readonly coreEntities coreDB = new coreEntities();
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
My question is why does the second one work but not the first? Is it better practice to have a global connection string or call DBContext explicitly each time?

Your are getting error because you are returning the IQueryable for which Entity framework has yet not executed the query and DbContext has been disposed when that query needs to be executed.
Remember Entity framework will not execute query until collection is initialized or any method that does not support deferred execution. Visit this link for list of Linq deferred execution supported method.
why does the second one work but not the first?
In first code snippet you are returning an instance of IQuerable which has not executed DbQuery and then after it just fires dispose on your context (coreDB). So then after whenever your code iterate over the collection it tries to fire DbQuery but finds that context has already been destroyed so you are getting an error.
In second case when ever you are iterating over the collection coreDB context must be alive so you are not getting an error.
Is it better practice to have a global connection string or call DBContext explicitly each time?
Answer to this question is based on developers taste or his own comforts. You can use your context wrapped within using statements as below:
public IList GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType.ToList();
}
}
As shown in above code snippet if you would use ToList before returning it would execute query before your coreDB got destroyed. In this case you will have to make sure that you returned materialized response (i.e. returned response after executing the DbQuery).
Note: I have noticed most of the people choose the second way. Which targets context as an instance field or property.

Related

Can I keep Entity Framework context as class variable?

I'm used to working the database connections where you connect/open/close as fast as possible in each method. I'm now working with the Entity Framework and so my methods all do this type of thing:
using (var context = new FooEntities()) {
// linq to sql query here
}
I've been told that with Entity Framework I can actually have that context variable be a class level variable and not have to instantiate it in each method. Is that really the case, or should I continue this pattern in each method?
I'm using version 5.0.0 of the framework if that makes a difference.
It depends on how you are expecting it to act. The only reason you'd want it to stick around is if you wanted to use the caching feature of DbContext across multiple method calls. But since its pulling connections from the Pool anyway, disposing of a DbContext shouldn't really impact performance when creating a new one.
For me personally, I create the context as close as possible and kill it as soon as possible. Thus, the Get calls should use AsNoTracking() to speed up the calls a lot if you don't care about trying to update them later. You could also create a DbContextFactory so each class could control that interaction as it sees fit. (i.e. Method A always creates a new one, but Methods B and C could share if either one called first). Though, that could cause its own issues down the road, but then you can opt into those conditions.
You can have Context as a property of a class, but you have to consider how to control the disposing of the Context. For example:
public class UnitOfWork:IDisposable
{
public DbContext Context { get; set; }
public UnitOfWork()
{
Context = null; //initialize context here
}
public void DoWorkWithContext1()
{
//anything you need
}
public void DoWorkWithContext2()
{
//anything you need
}
public void Dispose()
{
if (Context != null)
Context.Dispose();
}
}
Then you'll use the class in this way:
using (var unit= new UnitOfWork())
{
unit.DoWorkWithContext1();
unit.DoWorkWithContext2();
}

Delete loaded and unloaded objects by ID in EntityFrameworkCore

I have a method that receives an IEnumerable<Guid> of IDs to objects I want to delete. One suggested method is as follows
foreach(Guid id in ids)
{
var tempInstance = new MyEntity { Id = id };
DataContext.Attach(tempInstance); // Exception here
DataContext.Remove(tempInstance);
}
This works fine if the objects aren't already loaded into memory. But my problem is that when they are already loaded then the Attach method throws an InvalidOperationException - The instance of entity type 'MyEntity' cannot be tracked because another instance with the key value 'Id:...' is already being tracked. The same happens if I use DataContext.Remove without calling Attach.
foreach(Guid id in ids)
{
var tempInstance = new MyEntity { Id = id };
DataContext.Remove(tempInstance); // Exception here
}
I don't want to use DataContext.Find to grab the instance of an already loaded object because that will load the object into memory if it isn't already loaded.
I cannot use DataContext.ChangeTracker to find already loaded objects because only objects with modified state appear in there and my objects might be loaded and unmodified.
The following approach throws the same InvalidOperationException when setting EntityEntry.State, even when I override GetHashCode and Equals on MyEntity to ensure dictionary lookups see them as the same object.
foreach(Guid id in ids)
{
var tempInstance = new MyEntity { Id = id };
EntityEntry entry = DataContext.Entry(tempInstance);
entry.State == EntityState.Deleted; // Exception here
}
The only way so far I have found that I can achieve deleting objects by ID without knowing if the object is the following:
foreach(Guid id in ids)
{
var tempInstance = new MyEntity { Id = id };
try
{
DataContext.Attach(tempInstance); // Exception here
}
catch (InvalidOperationException)
{
}
DataContext.Remove(tempInstance);
}
It's odd that I am able to call DataContext.Remove(tempInstance) without error after experiencing an exception trying to Attach it, but at this point it does work without an exception and also deletes the correct rows from the database when DataContext.SaveChanges is executed.
I don't like catching the exception. Is there a "good" way of achieving what I want?
Note: If the class has a self-reference then you need to load the objects into memory so EntityFrameworkCore can determine in which order to delete the objects.
Strangely, although this is a quite common exception in EF6 and EF Core, neither of them expose publicly a method for programmatically detecting the already tracked entity instance with the same key. Note that overriding GetHashCode and Equals doesn't help since EF is using reference equality for tracking entity instances.
Of course it can be obtained from the DbSet<T>.Local property, but it would not be as efficient as the internal EF mechanism used by Find and the methods throwing the aforementioned exception. All we need is the first part of the Find method and returning null when not found instead of loading from the database.
Luckily, for EF Core the method that we need can be implemented relatively easily by using some of the EF Core internals (under the standard This API supports the Entity Framework Core infrastructure and is not intended to be used directly from your code. This API may change or be removed in future releases. policy). Here is the sample implementation, tested on EF Core 2.0.1:
using Microsoft.EntityFrameworkCore.Internal;
namespace Microsoft.EntityFrameworkCore
{
public static partial class CustomExtensions
{
public static TEntity FindTracked<TEntity>(this DbContext context, params object[] keyValues)
where TEntity : class
{
var entityType = context.Model.FindEntityType(typeof(TEntity));
var key = entityType.FindPrimaryKey();
var stateManager = context.GetDependencies().StateManager;
var entry = stateManager.TryGetEntry(key, keyValues);
return entry?.Entity as TEntity;
}
}
}
Now you can use simply:
foreach (var id in ids)
DataContext.Remove(DataContext.FindTracked<MyEntity>(id) ?? new MyEntity { Id = id }));
or
DataContext.RemoveRange(ids.Select(id =>
DataContext.FindTracked<MyEntity>(id) ?? new MyEntity { Id = id }));

How to Mock an Entity Framework 6 Async Projecting Query

By leveraging the Testing with async queries section of the Testing with a Mocking Framework article on MSDN, I've been able to create many successfully passing tests.
Here's my test code, which uses NSubstitute for mocks:
var dummyQueryable = locations.AsQueryable();
var mock = Substitute.For<DbSet<Location>, IDbAsyncEnumerable<Location>, IQueryable<Location>>();
((IDbAsyncEnumerable<Location>)mock).GetAsyncEnumerator().Returns(new TestDbAsyncEnumerator<Location>(dummyQueryable.GetEnumerator()));
((IQueryable<Location>)mock).Provider.Returns(new TestDbAsyncQueryProvider<Location>(dummyQueryable.Provider));
((IQueryable<Location>)mock).Expression.Returns(dummyQueryable.Expression);
((IQueryable<Location>)mock).ElementType.Returns(dummyQueryable.ElementType);
((IQueryable<Location>)mock).GetEnumerator().Returns(dummyQueryable.GetEnumerator());
sut.DataContext.Locations = mock;
var result = await sut.Index();
result.Should().BeView();
sut.Index() doesn't do much, but it makes the following query:
await DataContext.Locations
.GroupBy(l => l.Area)
.ToListAsync());
This works fine until I add a projection into the query:
await DataContext.Locations
.GroupBy(l => l.Area)
.Select(l => new LocationsIndexVM{ Area = l.Key }) // added projection
.ToListAsync());
which results in this exception:
System.InvalidOperationException
The source IQueryable doesn't implement IDbAsyncEnumerable<LocationsIndexVM>. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
at System.Data.Entity.QueryableExtensions.AsDbAsyncEnumerable(IQueryable`1 source)
at System.Data.Entity.QueryableExtensions.ToListAsync(IQueryable`1 source)
at Example.Web.Controllers.HomeController.<Index>d__0.MoveNext() in HomeController.cs: line 25
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Example.Test.Web.Controllers.HomeControllerShould.<TempTest>d__4.MoveNext() in HomeControllerShould.cs: line 71
UPDATE: I've uploaded a small, simple solution that reproduces this problem.
Can anyone provide an example of what is required to unit test a query that is both async and contains a .Select() projection?
So I did a bit of digging, and the issue is to do with the way the TestDbAsyncEnumerable<T> exposes the IQueryProvider. My best guess as to the reasoning is below, and the solution below that.
TestDbAsyncEnumerable<T> inherits from EnumerableQuery<T>, which in turn inherits from IQueryable<T>, and explicitly implements the Provider property of this interface:
IQueryProvider IQueryable.Provider { get ... }
Given that it's implemented explicitly, I am assuming that the LINQ internals explicitly cast a type before trying to get the Provider:
((IQueryable<T>)source).Provider.CreateQuery(...);
I don't have a source on hand (and can't be bothered looking for one), but I believe the type binding rules are different for explicit implementations; essentially, the Provider property on your TestDbAsyncEnumerable<T> is not considered to be an implementation of IQueryable<T>.Provider as an explicit one exists further up the chain, so your TestDbAsyncQueryProvider<T> is never returned.
The fix for this is to make TestDbAsyncEnumerable<T> also inherit IQueryable<T> and explicitly implement the Provider property, as below (adjusted from the MSDN article you linked):
public class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression) : base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}

unable to query EntityFramework shared dbcontext reliably

I'm trying to share a simple DbContext with 4 DbSets among multiple repositories, each of my repositories inherit from this base class
public class CodeFirstRepository : IDisposable
{
private static MyContext _ctx = new MyContext();
protected MyContext Context
{
get { return _ctx; }
}
public void Dispose()
{
if (Context != null)
{
Context.Dispose();
}
}
}
Question: is this an appropriate way to share a connection between repositories?
I'm getting intermittent failures in my unit tests when accessing the various repositories. An exception is thrown from the repository method GetEntityByName
public IOfferResult GetEntityByName(string name)
{
return Context.Entities.Where(o => o.Name == name).FirstOrDefault()
}
Test method
Tests.Service.TestDelete
threw exception: System.ObjectDisposedException: The ObjectContext
instance has been disposed and can no longer be used for operations
that require a connection.
if the database already exists, the code executes as expected. it also works when i change the implementation of GetEntityByName(string name) to the following non-performant code
public IOfferResult GetEntityByName(string name)
{
foreach (OfferResult offer in Context.Offers)
{
if (offerName.ToLower() == offer.Name.ToLower())
{
return offer;
}
}
}
Question: what is going on here?
bear in mind that if the database exists when i run the tests i don't get the error at all.
tia,
jt
This problem is arising because you are treating the DbContext like a singleton by declaring it as a static field, but then you are treating it like it like a transient instance by disposing it as soon as any instance of CodeFirstRepository gets disposed. For example:
using (var r = new PersonRepository())
{
// do something
} // When you hit the end of this block, your static DbContext is disposed.
using (var r = new IOfferRepository())
{
r.GetEntityByName("test"); // this will fail because the context is still disposed.
}
You should not share contexts this way. If you really want all of your repositories to use a single instance of the DbContext, remove the call to Context.Dispose(). This would fix the problem you're getting right now, but it will likely introduce other problems in the future.
But I would strongly caution against using a single DbContext in a scenario where multiple threads could be trying to access it simultaneously. According to the DbContext specs:
Any instance members are not guaranteed to be thread safe.
You'd be better off just removing the static keyword from your field.

Entity Framework and Entity Tracker Problems

If I run the following code it throws the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
public void Save(Category category)
{
using(var db = new NorthwindContext())
{
if(category.CategoryID == 0)
{
db.AddToCategorySet(category);
}
else
{
//category.RemoveTracker();
db.Attach(category);
}
db.SaveChanges();
}
}
The reason is of course that the category is sent from interface which we got from GetById method which already attached the EntityChangeTracker to the category object. I also tried to set the entity tracker to null but it did not update the category object.
protected void Btn_Update_Category_Click(object sender, EventArgs e)
{
_categoryRepository = new CategoryRepository();
int categoryId = Int32.Parse(txtCategoryId.Text);
var category = _categoryRepository.GetById(categoryId);
category.CategoryName = txtUpdateCategoryName.Text;
_categoryRepository.Save(category);
}
I'm still learning Entity Framework myself, but maybe I can help a little. When working with the Entity Framework, you need to be aware of how you're handling different contexts. It looks like you're trying to localize your context as much as possible by saying:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... within your data access method. Did you do the same thing in your GetById method? If so, did you remember to detach the object you got back so that it could be attached later in a different context?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
That way when you call Attach it isn't trying to step on an already-attached context. Does that help?
As you pointed out in your comment, this poses a problem when you're trying to modify an item and then tell your database layer to save it, because once an item is detached from its context, it no longer keeps track of the changes that were made to it. There are a few ways I can think of to get around this problem, none of them perfect.
If your architecture supports it, you could expand the scope of your context enough that your Save method could use the same context that your GetById method uses. This helps to avoid the whole attach/detach problem entirely, but it might push your data layer a little closer to your business logic than you would like.
You can load a new instance of the item out of the new context based on its ID, set all of its properties based on the category that is passed in, and then save it. This costs two database round-trips for what should really only need one, and it isn't very maintainable.
You can dig into the context itself to mark the Category's properties as changed.
For example:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
This looks a little uglier, but should be more performant and maintainable overall. Plus, if you want, you could make it generic enough to throw into an extension method somewhere so you don't have to see or repeat the ugly code, but you still get the functionality out of it.