66

I'm wondering why there is no Detach method on the DbContext object like there is for ObjectContext.  I can only assume this omission was intentional, but I have a hard time figuring out why.  I need to be able to detach and re-attach entities (for putting in the cache in an ASP.NET project, for example).  However, since I can't detach an entity, when I try to attach an entity that was associated with a previous context, I get the "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" exception.

What's the guidance here?  Am I missing something?

Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83
Brian Sullivan
  • 27,513
  • 23
  • 77
  • 91
  • Has anyone anything to say about: "What's the guidance here? Am I missing something?" Personally, I am only interested in using Detach while looking for a solution to read an entity from the context, just before saving the instance that came from back from the UI. This gives me "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key." – R. Schreurs Mar 24 '13 at 15:06

4 Answers4

87

For people that might stumble upon this question, as of CTP5 you now need to write

((IObjectContextAdapter)context).ObjectContext

in order to get to ObjectContext.

Joakim
  • 2,217
  • 15
  • 20
  • +1 Clean, elegant, one liner. I love it. Also wanted to throw in that the interface is found here specifically: System.Data.Entity.Infrastructure.IObjectContextAdapter and yes like Pandincus pointed out you have to make a reference to System.Data.Entity too. – dyslexicanaboko Feb 25 '13 at 22:56
  • I get "IObjectContextAdapter cannot be found" and it suggests installing entity framework. Which is definitely installed. – micahhoover Apr 25 '22 at 15:39
38

DbContext uses an ObjectContext internally and EF team make this available as a protected property just in case you ever need to drop down to the lower level API and sounds like this is the case here, so you can use or expose the required functionality from a derived DbContext:

public class YourContext : DbContext 
{
    public void Detach(object entity) 
    {
        ObjectContext.Detach(entity);            
    }
}

Then you can call this method from your controller to detach an entity.

Alternatively, you can change it to even have a richer API:

public class YourContext : DbContext
{
    public void ChangeObjectState(object entity, EntityState entityState)
    {
        ObjectContext.ObjectStateManager.ChangeObjectState(entity, entityState);
    }
}

Here is how DbContext looks like from metadata:

public class DbContext : IDisposable 
{      
    protected System.Data.Objects.ObjectContext ObjectContext { get; }
    ...
}
Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83
  • @Stacker this is not the answer because it requires the cast outlined in the selected answer above, i.e. the `DbContext` implements `IObjectContextAdapter` giving it the `ObjectContext` property which is not accessible otherwise. Combining the 2 answers gives @splite's answer below – eudaimos Nov 19 '12 at 19:07
  • 1
    ChangeObjectState is pretty lazy, its using "linear algorithm with cast" which is... Sort of... You know... Reeeeally bad. – Jan 'splite' K. Apr 02 '13 at 07:43
16

EF:CF 4.1 RC1 and EF:CF 4.1 RTW have the same explicitly implemented IObjectContextAdapter:

public static class DbContextExtensions
{
    public static void Detach(this System.Data.Entity.DbContext context, object entity)
    {
         ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext.Detach(entity);
    }
}

Microsoft decided "Detach is too advanced technology and should be hidden". IMHO the man who invented this should be shot - because if you add brand new entity, it is otherwise difficult to just remove it without commiting changes to db (you can manipulate with DbEntityEntry but that's another story).

Edit 4 years later:

With EF6 (i somehow skipped EF5 :) ) you dont need detach() anymore, becouse removing freshly added entry does not generate delete from [table] where [Id] = 0 as in EF4 - you can just call mySet.Remove(myFreshlyCreatedAndAddedEntity) and everything will be allright.

Jan 'splite' K.
  • 1,667
  • 2
  • 27
  • 34
  • shot? who needs that feature anyways? :P – quetzalcoatl Feb 12 '13 at 21:32
  • 1
    @quetzalcoatl : `var context = ...; var poco = new Poco(); context.Add(poco); context.Remove(poco);` <--- throws exception becouse you have to DETACH it, not remove - hey, detach is hidden... – Jan 'splite' K. Mar 29 '13 at 10:12
  • 1
    (5 min for edit gone, sry :) ) At that time I was so angry... When you are making windows app, you have to have some way to "cancel creating new, probably incomplete entry but keep UoW context"... We get rid of whole EF btw, and write our own ORM: There is no way to utilize lazy loading (i have bunch of Invoices and i cant load every InvoiceItem in single "where InvoiceId in (xxyy)" query - Include doesnt work in way we expected), bigger repositories are sooooo lazy (linear (!!!) search before every execution, wtf...), creating proxies for 100+ poco types take too much time and so on... – Jan 'splite' K. Mar 29 '13 at 10:26
  • And last btw, i am talking about massive (= 5 programmers full time job for 2y now) app for production (= folks in media will be making money with it), where **fast** (and colorful :)) ) app is good app. We have to "bend" some things to make it super fast... ComboBox with 1k+ entries with freaking (under specific, not generic, circumstances, you can or cant CRUD entry) RoleManagement? Have to be under 0.2s... If you write ASP or small app, it does not hurt so much ;) – Jan 'splite' K. Mar 29 '13 at 10:53
  • 1
    Heyheyhey.. I was joking, note the ':P' at the end:) Seems I accidentially managed to troll on you, sorry :) I totally agree with you. No real attach/detach support makes the framework very limited for any nontrivial usages, and that's why they were introduced in newer versions of EF. From the numbers you give, it sounds like a 'medium' project and database, not truly massive, but that's very subjective, I don't know your project. Anyways, from my experience, most of ORM/DB problems does not came from ORM, but from ill-defined business logic, or from DB not designed for use through ORM. – quetzalcoatl Mar 29 '13 at 14:08
  • ORM never frees you from havin to design tables, indexes, (..) properly **for usage** (not just 'by the book'!) and from structuring your queries properly. Don't get me wrong now :) I'm not advocating after EF. I personally don't like EF too much, because of its cumbersome triple-mapping (EDMX files, now simplified with code-first tools). I rather prefer NH, but EF has some pros too. But even with 'the bestest' ORM, when working with huge amounts of data we will still have to polish indexes by hand and sometimes even have to glue up some handcrafted weel-tailored sql query:) – quetzalcoatl Mar 29 '13 at 14:18
  • Yeah, 'massive' was not realy wise chosen word, certainly we arent creating new google db or something :D – Jan 'splite' K. Apr 02 '13 at 07:34
  • I need this feature. I will attach many objects on the same `DbContext`, and some objects need to be detached, and free the memories occupied by those objects. – AechoLiu Jan 14 '14 at 09:21
6

I usually extend the base class(inherits from the DbContext) with the property:

public class MyDbContext : DbContext
{
    public ObjectContext ThisObjectContext
    {
        get
        {
            return ((IObjectContextAdapter)this).ObjectContext;
        }
    }
}

later you can use this property for variety of useful stuff ... like Detach :)

Stefan Michev
  • 4,795
  • 3
  • 35
  • 30