45

I have an entity by getting it from DbEntityEntry.Entity. This returns the Entity Framework proxy for the entity.

How do I access the underlying object as its original type instead of the proxy?

Alternatively I need to dynamically try to cast the proxy to the entity type. Here's a start.

var theEntityType = entityEntry.Entity;

if (
    theEntityType.BaseType != null 
    && entityType.Namespace == "System.Data.Entity.DynamicProxies"
)
   theEntityType = entityType.BaseType;

// Now I need to cast to the correct type
// THIS WON'T WORK BECAUSE `theEntityType` is dynamic.
var entityObject = (theEntityType)entityEntry.Entity; 
// My entites also don't implement IConvertible
ruffin
  • 16,507
  • 9
  • 88
  • 138
Rich
  • 561
  • 1
  • 4
  • 11
  • 4
    Why do you want to do this, what are you trying to achieve? EF proxies inherit from the actual entity so what would casting back to the original type give you? – Ben Robinson Sep 10 '14 at 16:21
  • 1
    @Ben for example to overload methods to process a few entity types. With proxies it doesn't work with something like this `checkRequired(Customer c)`, `checkRequired(Order o)` – Ivan Ferrer Villa Feb 17 '16 at 17:41
  • I correct my comment. If we have `checkRequired(Object o)` (my case) it will be called instead of other overloads because of the proxies. But if that overload with `Object o` doesn't exist, it will call pertinent method (the one accepting Customer, Order, etc). Not perfect but useful at least. – Ivan Ferrer Villa Feb 18 '16 at 11:01
  • Another answer here https://stackoverflow.com/a/8973887/80434 – Korayem Oct 16 '17 at 17:47

6 Answers6

52

While working with EF 6 i used the following code to get the underlying POCO entity type from proxy type,

var entityType = ObjectContext.GetObjectType(dbEntitymodifiedEntry.Entity.GetType());

ObjectContext.GetObjectType : Return the POCO from proxy object

reference : https://learn.microsoft.com/en-us/ef/ef6/fundamentals/proxies

John Cummings
  • 1,949
  • 3
  • 22
  • 38
Ajay Bhingare
  • 527
  • 4
  • 2
  • 2
    A note for those with bad eyes like me.. that's a capital O ObjectContext meaning it's a static method =) http://stackoverflow.com/a/16005340/3507333 – madannes Jan 13 '17 at 17:21
  • 1
    This is definitely the best solution. If it's not an entity type, this function just returns the passed type. Very clean. I started off with Anthony Nichols's answer, but checking namespace is not future proof, and could become an undetected bug later. – Derrick Jan 22 '18 at 22:38
44

First I should say there is no underlying object. A proxy doesn't wrap an entity object (decorator pattern), it derives from it (inheritance). So we can't unwrap the entity, we can only convert a proxy to a base object. Conversion (contrary to casting) always creates a new object.

For this conversion, we can exploit the fact that most of the time, by the way proxies are returned by EF, the compile time type of a proxy is the base type. That is, if a proxy is entered as an argument to a generic method, the generic parameter will be inferred as the base type. This feature allows us to create a method that does what you want:

T UnProxy<T>(DbContext context, T proxyObject) where T : class
{
    var proxyCreationEnabled = context.Configuration.ProxyCreationEnabled;
    try
    {
        context.Configuration.ProxyCreationEnabled = false;
        T poco = context.Entry(proxyObject).CurrentValues.ToObject() as T;
        return poco;
    }
    finally
    {
        context.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
    }
}

Explanation

The proxy object enters the method. Its type is inferred as the base POCO type. Now we can temporarily turn off ProxyCreationEnabled on the context and copy the proxy object to an object of its base POCO type. This copy action gratefully uses a few EF features.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • 6
    If the context is used in other threads, this can trip you up... just a warning. – IDisposable Nov 11 '14 at 23:05
  • Also, you must be dealing with a tracked entity, so can't be the result of a .AsNoTracking() query. – IDisposable Nov 11 '14 at 23:18
  • 3
    @IDisposable using a context in multiple threads is unhealthy anyway. Yes, it must be a tracked entity. – Gert Arnold Nov 12 '14 at 07:25
  • What about a situation where there is a linked entity that is also of proxy type? i.e. proxy entity company with a relation list of proxy entities of type staff. Your function just turns any relation to null. – DFTR May 18 '16 at 04:20
  • @DFTR It's what was asked for. To populate navigation properties a recursive loop should be build in. Feel free :) – Gert Arnold May 18 '16 at 05:58
  • True enough. Can't blame a guy for hoping other more keen people would write it for him. I've done it and I'm only populating navigation properties that were loaded with Include. Thank you for the base code. – DFTR May 19 '16 at 23:38
  • I used the above code in a loop and found that the call to `dbContext.Entry()` was slow as my context had a lot of tracked entities. I ended up using the same pattern as used for `Configuration.ProxyCreationEnabled` to temporarily set `Configuration.AutoDetectChangesEnabled` to false and the performance improved dramatically. – TimS Jul 22 '16 at 02:38
  • I don't have access to the DbContext at the point that I need to unproxy it. Should I just use a reflection-based deep cloning method? – xr280xr Apr 05 '18 at 18:50
22

If you end up needing to do this from a project that does not have access to EF or the DBContext, and you don't know if the type you are referencing is a proxy you can do something like this:

    public Type GetType
    {
        get
        {
            var thisType = _baseObject.GetType();

            if (thisType.Namespace == "System.Data.Entity.DynamicProxies")
                return thisType.BaseType;

            return thisType;
        }
    }
Anthony Nichols
  • 1,586
  • 2
  • 25
  • 50
  • 1
    The solution of Ajay is still the only good one: using ObjectContext.GetObjectType(). This is a static method, so you don't actually need a reference to the DbContext. – codetuner Jul 19 '16 at 14:11
  • 3
    @codetuner If you don't have a reference to Entity Framework in your project then you won't have access to the DbContext at all which was my situation. – Anthony Nichols Sep 20 '16 at 18:11
  • 3
    @codetuner Also, my API requires an variable of type `object` be passed. So I cant get to the `.Entity()` method without knowing the type in advance, which defeats the purpose of needing the solution in my case. – Jeff Aug 28 '17 at 18:42
  • 1
    This may be the cleanest solution compared to others. No reference to the DbEntity is needed and I can implement this in a static method – BGilman Oct 10 '17 at 22:01
  • Another case: if (thisType.Namespace == "Castle.Proxies") – heringer Sep 18 '18 at 16:39
  • Beautiful solution – L4marr Apr 25 '22 at 19:57
4

Proposed answer has number of problems - for example it doesn't preserve properties defined in partial classes for generated POCO classes and it reloads entity from DB (which also hits the performance).

You can try to turn proxies off before you ask for changes, but it may not help if entities have already been loaded before - they'll be proxy types already (probably it depends on EF version, but it worked out once and didn't work another time in my experience). You also need to materialize it before you turn proxies back - it isn't obvious but it's just deferred query which has to be materialized:

context.Configuration.ProxyCreationEnabled = false;
var changes = context.ChangeTracker.Entries().ToArray();
amarax
  • 508
  • 5
  • 14
  • I happen to see this now, but I don't see how this answers the question. `context.ChangeTracker.Entries()` doesn't return base types but proxies. And what is this stuff about deferred execution about? As for the criticism, my method doesn't reload anything. The only point you have is that is doesn't preserve unmapped properties. – Gert Arnold Apr 09 '15 at 06:19
  • Changed my answer to make it clear about deferred execution. Well, I may be wrong about reloading data from DB.Obviously I mixed it up with OriginalValues (which I happened to need in my app). – amarax Sep 11 '15 at 22:51
3

To get a JSON friendly object in EF Core I used this method:

T UnProxy<T>(T efObject) where T : new()
{
    var type = efObject.GetType();

    if (type.Namespace == "Castle.Proxies")
    {
        var baseType = type.BaseType;
        var returnObject = new T();
        foreach (var property in baseType.GetProperties())
        {
            var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
            if (propertyType.Namespace == "System")
            {
                var value = property.GetValue(efObject);
                property.SetValue(returnObject, value);
            }
        }
        return returnObject;
    }

    return efObject;
}
DavidC
  • 654
  • 1
  • 14
  • 20
  • In my case, the ChangeTracker.Entries are sometimes proxies, and sometimes not. I got my code to work by comparing the Entry.Entity to hardcoded namespace "Castle.Proxies". But is it safe to assume that this namespace will most likely not change ever/for a long time? – mathkid91 Dec 07 '21 at 14:28
  • Use ProxyUtil.IsProxy(efObject); – Craig Jul 22 '22 at 17:57
2

Use AutoMapper 4.2.1 It is having DynamicMap which can remove the Proxy from the object.

     var parents = parentsRepo.GetAll().ToList();
     Mapper.CreateMap<Parent,ParentDto>();
     var parentsDto = Mapper.DynamicMap<List<ParentDto>>(parents);