35

I'm deep somewhere in the Business layer in a debugging session in Visual Studio trying to figure out why an Entity is behaving strangely when trying to persist the changes.

It would really be helpful to get a reference to the DbContext this Entity belongs to, at this point in the call stack.

I.e. to see what the state is of this Entity is (Unchanged, Modified, etc).

So I'm looking for a helper method like this:

var db_context = DbContextHelpers.GetDbContext(entity);

// after that I could do something like this
var state = db_context.Entry(entity);

I can use this stuff in the Immediate window during debugging.

Anyone any suggestions?

Extra notes

The Entity must be aware of the DbContext somewhere, because it is using it for lazy loading navigation properties?

Dirk Boer
  • 8,522
  • 13
  • 63
  • 111
  • 1
    I have some doubts of the correctness of your approach... If you need to debug something, why don't you DEBUG something? There are nice tools for that for .NET (debugger in VS is a nice example). Also you might want to do some research on unit testing, that might help you as well.. – walther Aug 10 '14 at 15:29
  • 1
    I am debugging @walther. I'm very deep in a debugging session, and I just need to get the `EntityState` of the model that I'm looking at - say 12 layers deep within the Business layer. It has no access to the DbContext that has been created at a completely other part of the code. – Dirk Boer Aug 10 '14 at 15:46
  • How about putting a hack in place that saves the `DbContext` so it is available where you need it to be available? If you're developing an ASP.NET application you could put it in `HttpContext.Current.Items` or keep it around as a thread-local, you could event simply assign the `DbContext` to some static field in some static class. In some cases, you could do that in the immediate window of the VS debugger, especially putting something into the `HttpContext` is easily done there. Be sure to remove the hack once you're done debugging though :-). – PermaFrost Aug 10 '14 at 15:55
  • Hi @PermaFrost, that is good secondary option. But the problem is that you have to clutter your code, **before** starting your debug session. When you have bugs that are hard to reproduce you ideally want to take have something in place that doesn't require some hacks in advance. – Dirk Boer Aug 10 '14 at 15:58
  • That's why your code should be nicely layered and you should follow basic guidelines of software architecture. Your business logic shouldn't know a thing about the dbcontext, nor should you need to know that information. It's baffling to me how many people neglect the software architecture and proper design, and then go around and try to implement hacks to be even able to debug their own app... – walther Aug 10 '14 at 16:05
  • 1
    @Dirk Boer You are absolutely right, but sometimes it's better to just go with something pragmatic. If you are doing this kind of debugging more often you could even write yourself some helper code that automatically keeps a reference to the `DbContext` around and enable it just when you're debugging - via DI or a config flag for example. I'm unsure whether retrieving `DbContext` for an entity object is possible, you could do a deep dive into the object using the VS debugger's watch functionality, maybe you can find some reference that you could extract via reflection or so. – PermaFrost Aug 10 '14 at 16:07
  • 3
    Whow @walther , pretty suprising how you know so much about my architecture from just this question? Ever heard of leaky abstractions? Entity Framework is most definitely one, and if you did some real life high traffic application you would have figured out that the theory of not caring about the database is absolutely not working that perfectly - sometimes you need to get your hands dirty in debug session to see what's going on. – Dirk Boer Aug 10 '14 at 16:24
  • Hi @PermaFrost, thanks. I'll give it a look. Just was wondering if maybe other people had already invented this wheel. If there is indeed no other way I'll go your route, but for now I rather would try to find a solution that is as simple as possible - and hopefully I can just make one stateless helper function :) – Dirk Boer Aug 10 '14 at 16:27

3 Answers3

28
public static DbContext GetDbContextFromEntity(object entity)
{
    var object_context = GetObjectContextFromEntity( entity );

    if ( object_context == null )
        return null;

    return new DbContext( object_context, dbContextOwnsObjectContext: false );
}

private static ObjectContext GetObjectContextFromEntity(object entity)
{
    var field = entity.GetType().GetField("_entityWrapper");

    if ( field == null )
        return null;

    var wrapper  = field.GetValue(entity);
    var property = wrapper.GetType().GetProperty("Context");
    var context  = (ObjectContext)property.GetValue(wrapper, null);

    return context;
}
Community
  • 1
  • 1
Dirk Boer
  • 8,522
  • 13
  • 63
  • 111
  • 2
    I used this and it works in most of the case. On one project entity.GetType().GetField("_entityWrapper") returns null, any idea about the possible reasons ? – gilles emmanuel Jun 09 '15 at 13:24
  • Hmm didn't happen to myself (yet). Maybe the Entity is not wrapped or detached from the DbContext? Like when you use AsNoTracking() ? – Dirk Boer Jun 09 '15 at 17:16
  • 2
    @gillesemmanuel this would happen if no proxy class would be created, e.g. if you had disabled proxy creation http://stackoverflow.com/questions/24674310/disable-dynamic-proxy-in-entity-framework-globally or if your entity class fails the requirements for proxy classes https://msdn.microsoft.com/en-us/library/dd468057(v=vs.100).aspx , etc. – fuchs777 Nov 23 '15 at 15:37
  • This is gold! You could put it in a super class or create a set of extension methods for the entity interface with "Save", "Find", "Destroy", etc, and use it as a private method (with lazy loading) to create an "ActiveRecord" of some sorts. Think of the possibilities. – SparK May 10 '16 at 14:59
  • 1
    Note that the returned context can be disposed. Therefore I changed the null-test in GetDbContextFromEntity: `var disposedField = typeof(ObjectContext).GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance); if (object_context == null || (bool)disposedField.GetValue(object_context)) { return null; }`. (EF6) – Olivier Jacot-Descombes May 03 '17 at 21:34
  • LIFESAVER! Brilliant. – str8ball Jun 24 '22 at 15:43
22

For EF6, I modified Dirk's answer slightly:

    public static DbContext GetDbContextFromEntity(object entity)
    {
        var object_context = GetObjectContextFromEntity(entity);

        if (object_context == null || object_context.TransactionHandler == null)
            return null;

        return  object_context.TransactionHandler.DbContext;
    }

    private static ObjectContext GetObjectContextFromEntity(object entity)
    {
        var field = entity.GetType().GetField("_entityWrapper");

        if (field == null)
            return null;

        var wrapper = field.GetValue(entity);
        var property = wrapper.GetType().GetProperty("Context");
        var context = (ObjectContext)property.GetValue(wrapper, null);

        return context;
    }

No new DbContext() and it's castable into your main Entities class.

Note: To the above question of null return value, this will happen if the entity has not been saved/committed. New entities that can only be found in .Local do not seem to have the "_entityWrapper" field.

kernelk
  • 368
  • 2
  • 8
5

I found another way to get ObjectContext. Don't know if it's any better though.

public static ObjectContext GetContext(this IEntityWithRelationships entity)
{
  if (entity == null)
    throw new ArgumentNullException("entity");

  var relationshipManager = entity.RelationshipManager;
  var relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault();

  if (relatedEnd == null)
    throw new Exception("No relationships found");

  var query = relatedEnd.CreateSourceQuery() as ObjectQuery;
  if (query == null)
    throw new Exception("The Entity is Detached");

  return query.Context;
}

And you can use it like:

MyContext context = myEntity.GetContext() as MyContext;
Aximili
  • 28,626
  • 56
  • 157
  • 216