Database connection errors on EF - c#

I am very new to entity framework and I am having a problem with a web api based site (connected to mssql) that I am writing. I keep getting seemingly random errors (mostly seeming to be database related). These errors happen most often when the site is first published but they do sometimes happen when it has been hours since the last publish. A selection of the errors:
Invalid operation. The connection is closed.
There is already an open DataReader associated with this Command which must be closed first.
The connection was not closed. The connection's current state is connecting.
The context cannot be viewed while the model is being created
Underlying provider failed to open
My context looks like this:
public class Context : DbContext
{
public Context() : base("name=DefaultConnection")
{
}
public override int SaveChanges()
{
DateTime now = DateTime.Now;
foreach (ObjectStateEntry entry in (this as IObjectContextAdapter).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship)
{
IHasUpdated updated = entry.Entity as IHasUpdated;
if (updated != null)
updated.updated = now;
}
}
return base.SaveChanges();
}
public DbSet<Branch> Branches { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UsefulLink> UsefulLinks { get; set; }
}
There are many more DbSets than this. Should I be creating a separate context for each?
One of my basic controllers:
public class UsefulLinksController : ApiController
{
private Context db = new Context();
[ResponseType(typeof(UsefulLinksWrapper))]
public IHttpActionResult GetUsefulLinks([FromUri]UsefulLinkParams prams)
{
UsefulLinksWrapper wrapper = new UsefulLinksWrapper();
Meta meta = new Meta();
IQueryable<UsefulLink> query = db.UsefulLinks;
if (prams.sortBy == null)
{
prams.sortBy = "ID";
}
// Paging
query = query.OrderBy(prams.sortBy + " " + prams.sortDirection).Skip(prams.offset - 1).Take(prams.limit);
List<UsefulLink> data = query.ToList();
meta.totalCount = query.Count();
meta.offset = 1;
meta.limit = prams.limit;
wrapper.meta = meta;
wrapper.data = data;
return Ok(wrapper);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool UsefulLinkExists(int id)
{
return db.UsefulLinks.Count(e => e.ID == id) > 0;
}
}
I don't seem to see these errors when I run the site locally though there are two of us hitting it when it is published so perhaps the issue stems from multiple users?

Chris, I notice in your controller you are sharing your db context with all of the methods in your controller class.
This is generally not a best practice in Entity Framework (see: EntityFramework 4 ObjectContext Lifetime). You should keep your context alive as briefly as possible. Leaving the context alive to share across multiple methods could result in many of the errors that you list above.
I would recommend trying to instantiate a new instance of the context, instead, wherever it is used and quickly disposing of it.
This should generally result in more stable behavior.
So the below:
class SomeClass
{
private context = new Context(); //sharing your context with all methods
public someMethod()
{
context.doSomething;
}
public someMethod2()
{
context.doSomething;
}
}
should become:
class SomeClass
{
public someMethod()
{
Context context = new Context(); //now your context is declared and disposed of within each method
context.doSomething;
}
public someMethod2()
{
Context context = new Context(); //now your context is declared and disposed of within each method
context.doSomething;
}
}
Or even better, you can use a using construct to ensure that your context is properly disposed of:
class SomeClass
{
public someMethod3()
{
using(Context context = new Context()) //now wrapping the context in a using to ensure it is disposed
{
context.doSomething;
}
}
}
I would recommend trying the above changes and seeing if your behavior becomes more stable.

Since I do not know how your page uses the methods UsefulLinksController and in which order, I would say UsefulLinkExists is perhaps the culprit due to lazy loading
Lazy loading means delaying the loading of related data until you
specifically request it
Which would explain why your "reader" remains "open".
Try:
return db.UsefulLinks.ToList().Count(e => e.ID == id) > 0;
In any case, you can disable lazy loading by default in the context constructor as such noted here:
public MyEntitiesContext() : base("name=MyEntitiesContext", "MyEntitiesContext")
{
this.ContextOptions.LazyLoadingEnabled = false;
OnContextCreated();
}
As far as I know, it applies to EF4 and up.

Related

How can I lazy load relation in Entity Framework 6?

I have a project where I am using Entity Framework 6 and ASP.NET MVC 5.
I have a need to use lazy loading instead of eager. In other words, I want to be able to access a relation/child after the parent model have been retrieved without using .Include()
Here is my simple use case
public ActionResult Test(int id)
{
using(var conn = new MyAppContent())
{
var user = conn.User.Where(u => u.Id == id).First();
var capsule = new SomeWrapper(user)
return View(capsule);
}
}
This is my SomeWrapper class in a nutshell. (I removed most of the code for the sake of simplicity.)
public SomeWrapper
{
public User MyUser { get; set; }
public SomeWrapper(User user)
{
MyUser = user;
}
// Of course this class does more than this.
}
Here is my User Model
public User
{
public int Id { get; set; }
public string Username { get; set; }
[ForeignKey("TimeZone")]
public int? TimeZoneId { get; set; }
}
Now I want to know the user's time zone from the view. So I did something like this
#model App.SomeWrapper
<p>Model.MyUser.Username<p>
<p>Model.MyUser.TimeZone.Name<p>
However, when trying to access the TimeZone.Name I get the following error
{"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."}
I am not sure how it is disposed? I return View() within my using(){} block. so it should be disposed after I access it.
I can switch to eager loading "code below" to solve the problem, but I want to know how to do the same thing using lazy loading.
public ActionResult Test(int id)
{
using(var conn = new MyAppContent())
{
var user = conn.User.Where(u => u.Id == id).Include(x => x.TimeZone).First();
var capsule = new SomeWrapper(user)
return View(capsule);
}
}
How can I lazy load the TimeZone relation correctly?
You should not access MyUser.TimeZone inside SomeWrapper.
What you did back there is shallow copy User to MyUser, and mixing Domain Model (some call Entity Model) and View Model. It is not a good practice as Domain Model should never be exposed to public.
You should not shallow copy from Domain Model to View Model. Instead, you want deep copy using object-object mapper such as AutoMapper.
Lazy Loading
Entity Framework already provides lazy loading, but you cannot dispose DbContext right after retrieving data.
Instead, you'll need to inject MyAppContent using IoC container such as Autofac, Ninject.
For example,
public class MyController : Controller
{
private readonly MyAppContent _myAppContent;
public MyController(MyAppContent myAppContent)
{
_myAppContent = myAppContent;
}
public ActionResult Test(int id)
{
var user = _myAppContent.User.Where(u => u.Id == id).First();
...
}
}
The problem is that your context should be request-scoped. By using using in your action, it's scoped much less than that, and in particular is unavailable during the rendering of the view. Note that the fact that the return is inside is inconsequential; the view rendering can happen outside the action-scope.
Long and short, you just need to ensure you have a context that will survive the length of the request. The easiest way to do that is to inject it into your controller using a dependency injection container, and setting it to be request-scoped. If you don't want to go that far, simply make it an ivar on your controller (which by nature is request-scoped). You should also remember to override Dispose on your controller, in that case, to dispose of the context properly:
private readonly MyAppContent conn = new MyAppContent();
// other controller code
protected override void Dispose(bool disposing)
{
if (disposing)
{
conn.Dispose();
}
base.Dispose(disposing);
}

Entity Framework ICollection Navigation Properties null only when testing

I'm using Entity Framework 6.1.3 in an MVC 5 project, and I'm having issues with ICollection navigation properties being null when unit testing. I am hitting an actual test database in SQL Express, so this may be more like an integration test for you purists. Regardless of what you call it, it is a problem that I would like to solve.
I have read many answers to similar sounding questions, but none of them seem to hit the same problem that I am having here. I understand EF for the most part, I have lazy loading enabled, my classes are public, and I'm using virtual on my navigation properties.
Here is a simplified example of what I am trying to do:
Models:
public class Session
{
public int Id { get; set; }
public string Name { get; set; }
// Navigation Property
public virtual ICollection<File> Files { get; set; }
}
public class File
{
public int Id { get; set; }
public string Name { get; set; }
public int SessionId { get; set; }
public virtual Session Session { get; set; }
}
Test methods:
[TestMethod]
public void Test_TotalFileCount1()
{
ApplicationDbContext context = new ApplicationDbContext();
// Create session with no files
var session = new Session() { Name = "Session1" };
context.Sessions.Add(session);
context.SaveChanges();
// This line blows up because session.Files == null
Assert.AreEqual(0, session.Files.Count);
}
[TestMethod]
public void Test_TotalFileCount2()
{
ApplicationDbContext context = new ApplicationDbContext();
// Create session
var session = new Session() { Name = "Session2" };
context.Sessions.Add(session);
context.SaveChanges();
// Create file for session
var file = new File() { Name = "File1", Session = session };
context.Files.Add(file)
context.SaveChanges();
// This test passes because session.Files is a
// collection of one file
Assert.AreEqual(1, session.Files.Count);
}
The first test above fails because session.Files throws an ArgumentNullException. However, when I call this same code in the full MVC application, session.Files is not null and instead is an empty collection with Count = 0. The second test passes because session.Files is a collection of one File as I would expect. The navigation properties are clearly doing what they're supposed to in the second case, but not in the first case.
Why is EF behaving like this?
I was able to get around this problem by initializing Files as an empty list in the constructor. I know I could do this conditionally in the getter instead, but I don't think I should have to do either of these things because it just works when it's running normally.
public Session()
{
this.Files = new List<File>();
}
Does anyone have any insight into what is going on here?
If you are working with lazy loading enabled and if you want that the navigation property gets populated after adding the object with the foreign key property to the context you must use the Create method of DbSet (instead of instantiating the object with new):
var session = context.Sessions.Create();
With active lazy loading this will create a proxy object which ensures that the navigation property gets loaded.
[TestMethod]
public void Test_TotalFileCount1()
{
ApplicationDbContext context = new ApplicationDbContext();
// Create session with no files
var session = context.Sessions.Create();
session.Name = "Session1";
context.Sessions.Add(session);
context.SaveChanges();
// This line blows up because session.Files == null
Assert.AreEqual(0, session.Files.Count);
}
[TestMethod]
public void Test_TotalFileCount2()
{
ApplicationDbContext context = new ApplicationDbContext();
// Create session
var session = context.Sessions.Create();
session.Name = "Session2";
session.Files = new List<File>()
{
new File() { Name = "File1" }
};
context.Sessions.Add(session);
context.SaveChanges();
// This test passes because session.Files is a
// collection of one file
Assert.AreEqual(1, session.Files.Count);
}
See more about Proxies

Caching and lazy loading with entity framework

let's say I have an application, for example a web site, where my objectcontext leaves during the time of a request. Some datas I load with EF should be cached to avoid to read in DB and improve performance.
Ok, I read my datas with EF, I put my object in cache (says AppFabric, not in memory cache), but related datas that can be lazy loaded are now null (and access to this property results in a nullreferenceexception). I don't want to load everything in one request, because it's going to be too long, so I want to keep the loading on demand and as soon as it's read, I would like to complete the cache with the new fetched datas.
Note :
only read operations, no create/update/delete.
Don't want to use second level cache like "EF Provider Wrappers" made by Jarek Kowalski
How can I do that ?
EDIT : I've built this samples with northwind database, it's working :
class Program
{
static void Main(string[] args)
{
// normal use
List<Products> allProductCached = null;
using (NORTHWNDEntities1 db = new NORTHWNDEntities1())
{
allProductCached = db.Products.ToList().Clone<DbSet<Products>>();
foreach (var product in db.Products.Where(e => e.UnitPrice > 100))
{
Console.WriteLine(product.ProductName + " => " + product.Suppliers.CompanyName);
}
}
// try to use cache, but missing Suppliers
using (NORTHWNDEntities1 db = new NORTHWNDEntities1())
{
foreach (var product in allProductCached.Where(e => e.UnitPrice > 100))
{
if (product.Suppliers == null)
product.Suppliers = db.Suppliers.FirstOrDefault(s => s.SupplierID == product.SupplierID).Clone<Suppliers>();
Console.WriteLine(product.ProductName + " => " + product.Suppliers.CompanyName);
}
}
// try to use full cache
using (NORTHWNDEntities1 db = new NORTHWNDEntities1())
{
foreach (var product in allProductCached.Where(e => e.UnitPrice > 100))
{
Console.WriteLine(product.ProductName + " => " + product.Suppliers.CompanyName);
}
}
}
}
public static class Ext
{
public static List<Products> Clone<T>(this List<Products> list)
{
return list.Select(obj =>
new Products
{
ProductName = obj.ProductName,
SupplierID = obj.SupplierID,
UnitPrice = obj.UnitPrice
}).ToList();
}
public static Suppliers Clone<T>(this Suppliers obj)
{
if (obj == null)
return null;
return new Suppliers
{
SupplierID = obj.SupplierID,
CompanyName = obj.CompanyName
};
}
}
The problem is that I have to copy everything (without missing a property) and test everywhere if the property is null and load the needed property. My code is of course more and more complex, so that will be a problem if I miss something. No other solution ?
You cannot access the database in EF without an ObjectContext or a DbContext.
You can still use caching effectively, even if you don't have the original context any more.
Maybe your scenario is something like this... Imagine that you have some reference data that you use frequently. You do not want to hit the database each time you need it, so you store it in a cache. You also have per-user data that you don't want to cache. You have navigation properties from your user data to your reference data. You want to load your user data from the database, and have EF automatically "fix up" the navigation properties to point to the reference data.
For a request:
Create a new DbContext.
Retrieve reference data from the cache.
Make a deep copy of the reference objects. (You probably don't want to have the same entities attached to multiple contexts simultaneously.)
Attach each of the reference objects to the context. (e.g. with DbSet.Attach())
Execute whatever queries are required to load the per-user data. EF will automatically "fix up" the references to the reference data.
Identify newly loaded entities that could be cached. Ensure that they contain no references to entities that should not be cached, then save them to the cache.
Dispose of the context.
Cloned Objects and Lazy Loading
Lazy loading in EF is usually accomplished using dynamic proxies. The idea is that you make all properties that could potentially be loaded dynamically virtual. Whenever EF creates an instance of your entity type, it actually substitutes a derived type instead, and that derived type has the lazy loading logic in its overridden version of your properties.
This is all well and good, but in this scenario you are attaching entity objects to the context that were not created by EF. You created them, using a method called Clone. You instantiated the real POCO entity, not some mysterious EF dynamic proxy type. That means you won't get lazy loading on these entities.
The solution is simple. The Clone method must take an additional argument: the DbContext. Don't use the entity's constructor to create a new instance. Instead, use DbSet.Create(). This will return a dynamic proxy. Then initialize its properties to create a clone of the reference entity. Then attach it to the context.
Here is the code you might use to clone a single Products entity:
public static Products Clone(this Products product, DbContext context)
{
var set = context.Set<Products>();
var clone = set.Create();
clone.ProductName = product.ProductName;
clone.SupplierID = product.SupplierID;
clone.UnitProce = product.UnitPrice;
// Initialize collection so you don't have to do the null check, but
// if the property is virtual and proxy creation is enabled, it should get lazy loaded.
clone.Suppliers = new List<Suppliers>();
return clone;
}
Code Sample
namespace EFCacheLazyLoadDemo
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
class Program
{
static void Main(string[] args)
{
// Add some demo data.
using (MyContext c = new MyContext())
{
var sampleData = new Master
{
Details =
{
new Detail { SomeDetail = "Cod" },
new Detail { SomeDetail = "Haddock" },
new Detail { SomeDetail = "Perch" }
}
};
c.Masters.Add(sampleData);
c.SaveChanges();
}
Master cachedMaster;
using (MyContext c = new MyContext())
{
c.Configuration.LazyLoadingEnabled = false;
c.Configuration.ProxyCreationEnabled = false;
// We don't load the details here. And we don't even need a proxy either.
cachedMaster = c.Masters.First();
}
Console.WriteLine("Reference entity details count: {0}.", cachedMaster.Details.Count);
using (MyContext c = new MyContext())
{
var liveMaster = cachedMaster.DeepCopy(c);
c.Masters.Attach(liveMaster);
Console.WriteLine("Re-attached entity details count: {0}.", liveMaster.Details.Count);
}
Console.ReadKey();
}
}
public static class MasterExtensions
{
public static Master DeepCopy(this Master source, MyContext context)
{
var copy = context.Masters.Create();
copy.MasterId = source.MasterId;
foreach (var d in source.Details)
{
var copyDetail = context.Details.Create();
copyDetail.DetailId = d.DetailId;
copyDetail.MasterId = d.MasterId;
copyDetail.Master = copy;
copyDetail.SomeDetail = d.SomeDetail;
}
return copy;
}
}
public class MyContext : DbContext
{
static MyContext()
{
// Just for demo purposes, re-create db each time this runs.
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
}
public DbSet<Master> Masters { get { return this.Set<Master>(); } }
public DbSet<Detail> Details { get { return this.Set<Detail>(); } }
}
public class Master
{
public Master()
{
this.Details = new List<Detail>();
}
public int MasterId { get; set; }
public virtual List<Detail> Details { get; private set; }
}
public class Detail
{
public int DetailId { get; set; }
public string SomeDetail { get; set; }
public int MasterId { get; set; }
[ForeignKey("MasterId")]
public Master Master { get; set; }
}
}
Here is a sample model, different from yours, that shows how to get this working in principle.

Entity Framework, posting an entity containing a collection of already existing entites

A bit of background, I have 2 tables in a database that share a many-to-many relationship through a junction table. Almost identical to the scenario outlined in this blog post.
I will use a very simplified example of what I'm trying to do. When a user on my web app wants to create an Order, they have access to a list of Products to add to the order before posting. This list of products is fetched trough a REST api call and is made available to the client app as JSON data.
My problem arises when I post an Order, Entity frame work tries to re-save the Products in my collection rather that just create the association.
Consider the following extremely simplified code sample of my controller and my UnitOfWork class.
public class OrdersController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
private readonly IOrderRepository _orderRepository;
public OrdersController(IUnitOfWork unitOfWork, IOrderRepository orderRepository)
{
_unitOfWork = unitOfWork;
_orderRepository = orderRepository;
}
// POST api/Orders
public Dto.Get.Order Post(Dto.Post.Order postOrder)
{
Models.Order modelOrder = new Model.Order();
modelOrder.Name = postOrder.Name;
modelOrder.Description = postOrder.Description;
foreach (var getProduct in postOrder.Products)
{
Models.Product modelProduct = new Models.Product();
modelProduct.Id = getProduct.Id;
getProduct.Name = getProduct.Name;
getProduct.Price = getProduct.Price;
modelOrder.Products.Add(modelProduct);
}
modelOrder.AccountId = 999;
_orderRepository.Insert(modelOrder);
_orderRepository.Save();
_unitOfWork.SaveChanges();
//Return new Order as it exists in DB
return Get(modelOrder.Id);
}
}
public class UnitOfWork : IUnitOfWork
{
public MyApplicationContainer Context { get; set; }
public UnitOfWork(MyApplicationContainer context)
{
Context = context;
this.Context.Configuration.ProxyCreationEnabled = false;
this.Context.Configuration.LazyLoadingEnabled = false;
}
public void SaveChanges()
{
Context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
if (disposing)
Context.Dispose();
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Note: For simplicity, I didn't bother to post my Post and Get DTOs, or my Models for the Order and Product entities.
One interesting observation, if I fetch the model version of the Product based on the incoming ID and then assign it to the collection to be saved, it works fine. But that seems very "chatty" to me, considering an Order could potentially have hundreds of Products. Example:
foreach (var getProduct in postOrder.Products)
{
var modelProduct = _productRepository.Get(getProduct.Id);
modelOrder.Products.Add(modelProduct);
}
You are explicitly creating a new product with the new operator. Obviously EF tries to create new products in the database. You need to query the database for the products but you should do it with one query. Something along the lines of
var productsIDs = postOrder.Products.Select(p => p.ID).ToArray();
var actualProducts = from p in product
where productsIDs.Contains(p.ID)
select p;

How do I share dbContext without CodeFirst in EntityFramework 4.1?

Long time lurker, first time poster.
I've found tons of stuff on here about how to share dbContext across repositories using CodeFirst, but I can't seem to relate that to the project I'm working on, which doesn't use code first or dependency injection.
First, a little background on the project to make sure that I'm approaching this the right way. I came into this project and they were using EF4 and with a DB first. I'm far from an expert on EF, but I've fumbled around with several different projects now.
I've had to implement several different requirements that have forced me to intervene between their "service" level and the database. In other words, their objects were making calls directly to the EF db objects like
using (var db = new MYDB()){
var bar = db.Foo
.Include("Transactions")
.Include("blah")
.Where(...);
//do stuff
db.SaveChanges();
}
One thing I had to do was track all fields that changed, so I abstracted back a level and now we have
FooObject bar = GetFooObject(...);
bar.Title = "asdfasdf";
//do stuff to bar
bar.Save();
which wraps up all the fields into properties so I can log out any changes. In bar.save I open a db context, get the existing Foo or create a new one, assign all the values and then call db.SaveChanges.
As it turns out they also do lots of sub-queries based on Transactions and blah. So when they do something like
var bar = GetFooObject(...);
var t = new Transaction();
//do stuff to t
...
bar.Transactions.Add(t);
bar.Save();
I get hit with all kinds of context errors saying that the dbcontext is no longer available etc. Which I totally understand. What I don't know is how to fix it. I've seen lots of stuff about creating a dbContext before it's used and then passing it in, but I can't seem to figure out the proper way to do it so it will work with my code.
My most recent attempt based on several examples about how to convert DBContext to ObjectContext (which was in turn based on the fact that all of the examples I found about sharing a connection referenced ObjectContext and not DBContext) looks like this:
using (var db = ((IObjectContextAdapter)(new FooDB())).ObjectContext)
{
using (var context = new DbContext(db, false))
{
var bar = FooObject.GetFooObject(fooId);
Result r = bar.ProcTrans(amount,
transDate,
db.TransactionTypes
.Include(tt => tt.Description)
.SingleOrDefault(tt => tt.TypeID == transactionTypeId),
employeeId,
comment);
But with this code I get an error that I have no definition for TransactionTypes. It doesn't recognize any of my db Objects.
How can I create a DBContext and pass it to my FooObject so that I can keep it open for the related updates? I don't even know if I'm asking the question exactly right. How do I bridge this gap without recoding the whole thing?
EDIT
Here are some things I've found since opening this question. Maybe one of the two will do the trick.
Well, this finding certainly is more along the lines of recoding the whole thing but I did find this when looking for links regarding the "do change tracking" with triggers response.
poco in the entity framework part-3: change tracking with poco
And I just found this how do I share a data context across various model repositories in asp.net which might be a simple way to approach it.
I would leave behind the object context stuff.
The way I achieve a shared DBContext in my MVC app is like this:
public class BaseRepository
{
public static MyAppContext GetDataContext()
{
string ocKey = "ocm_" + HttpContext.Current.GetHashCode().ToString("x");
if (!HttpContext.Current.Items.Contains(ocKey))
HttpContext.Current.Items.Add(ocKey, new MyAppContext());
return HttpContext.Current.Items[ocKey] as MyAppContext;
}
}
Then whenever I need to do a database operation I can call:
BaseRepository.GetDataContext().YourObjects.Where(x => ...);
....
BaseRepository.GetDataContext().SaveChanges();
As long as you are still in the same HTTP context you will be sharing the same DB Context. Not entirely sure this will eliminate the errors that you are getting, but it's at least a way to share your context.
The answer, for me, was related to one of the links I posted.
how do I share a data context across various model repositories in asp.net
What threw me off when I saw these types of injection answers was that syntactically they didn't work for me. I don't have DataContext nor do I have any Repository models, but I decided to give it a try conceptually and pass the Context around everywhere.
Basically, I passed in the connection to the Object constructor or to any factory methods where a new object is created and store that in a local variable, sorta like this.
public class Foo{
private MyDB _db;
private Foo _foo;
public FooObject(MyDB dbContext)
{
_db = dbContext;
}
public static FooObject GetFooObject(int FooID, MyDB db){
bool closeFlag = false;
//if null was passed in, then we will create our own connection and manage it
if (db == null)
{
_db = new MyDB();
closeFlag = true;
} else {
//otherwise, we set our local variable
_db = db;
}
//from now on, all queries are done using the local variable
var _f = _db.Foos
.Include("x")
.Include("y")
.Include("z")
.SingleOrDefault(f => f.FooID == FooID);
var fo = FooObjectFromFoo(_f, db);
if (closeFlag)
db.Dispose();
return fo;
}
// This copies all of the values from Foo and puts the into a FooObject
public static FooObject FooObjectFromFoo(Foo f, MyDB dbContext){
if (l == null)
return null;
// note that we pass the dbContext to the constuctor
FooObject _f = new FooObject(dbContext){
_foo = f,
...
//note x, y, and z are the other EF "table references". I'm not sure what you technically call them.
x = f.x,
y = f.y,
z = f.z
};
return _f;
}
//we call this to save the changes when we're done
public bool Save(){
bool close = false;
bool retval = true;
MyDB db = _db;
//remember we set _db in the constructor
if (db == null) {
db = new MyDB();
close = true;
}
try
{
// a reference to this row should have been saved in _foo if we loaded it from the db.
// take a look at FooObjectFromFoo
if (_foo == null)
{
_foo = db.Foos.SingleOrDefault(x => x.FooID == _FooID);
}
if (_foo == null)
{
_foo = new Foo();
}
//copy all my object values back to the EF Object
_foo.blah = blah;
_foo.x = x;
_foo.y = y;
try
{
//save the new one.
db.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
TransactionResult.AddErrors(dbEx);
retval = false;
}
}
catch { throw new Exception("Something went wrong here.");}
finally { if (close) db.Dispose(); } //if we created this connection then let's close it up.
}
}
And now in my methods, I always use the local _db connection. Outside of my FooObject we have a FooService which is what is called from all of the controllers. So when FooService is instantiated I create a db connection using my class below.
If I understand it properly, this should give me a context that exists for the duration of my service request which, in my case, fairly reliably mimics the request.
namespace My.Domain
{
public class MyDataContext : IDisposable {
private MyDB _context;
private bool _ownContext;
public MyDataContext(){
_context = new MyDB();
_ownContext = true;
}
public MyDataContext(MyDB db)
{
_context = db;
_ownContext = false;
}
public MyDB Context
{
get { if (_context == null) { _context = new MyDB(); _ownContext = true; } return _context; }
set { _context = value; }
}
public bool OwnContext
{
get { return _ownContext; }
set { _ownContext = value; }
}
public void Dispose()
{
if (_context != null && _ownContext)
_context.Dispose();
}
}
}
In the FooService I do stuff like this.
private MyDb db;
public FooService (){
var _db = new MyDataContext();
db = _db.Context;
}
public Result ProcessTransaction(int FooId, string comment)
{
var foo = FooObject.GetFooObject(FooId,db);
Result r = foo.ProcessTransaction(comment);
if (r.Success)
foo.Save();
return r;
}
I think to do it "right" I should only save the changes when I close out the context... but I already had a Save method on my FooObject, so I just call db.SaveChanges in there.
I know there are lots of ways to improve this and I'm sure I'll implement some of them over time, but for now, this did the trick. This was how I got around all of the "Context is no longer available" and this object was from a different context errors.
The thing that tripped me up when looking at other peoples examples is they were all using CodeFirst and dependency injection of some sort. They commonly used Repository patterns and we don't have any of that. But it turns out that I just had to implment my own hacked-up localized version of connection injection! :)

Categories