beaucrawford.net

Give me data or give me death

About the author

Author Name is someone.
E-mail me Send mail

Recent comments

Don't show

Authors

Tags

Don't show

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    © Copyright 2010

    Entity Framework Detached Instances

    When working with the Entity Framework you will often find yourself working with a detached Entity instance (working with Entities over a WCF service or perhaps using them as part of an n-tier architecture).  Before you can perform any data access operations dealing with this Entity you must first attach it to an ObjectContext.  This, as you might expect, is slightly more complicated than simply calling ObjectContext.Attach.

    Thankfully, the ObjectContext class has a method named “ApplyPropertyChanges”.  Calling it is pretty straightforward, as shown below in the AttchUpdated method.   It’s never that simple though is it?  It took me awhile to realize that this method only works with Scalar property types.  As stated on MSDN:

    ApplyPropertyChanges does not affect navigation properties or related objects.

    This ended up being a major show stopper for me until I figured it out.  It turns out that, for properties that are EntityReference instances, i.e. Navigation properties, you must query the Entity’s RelationshipManager (see the ApplyReferencePropertyChanges method).

     

    public static void LoadMetadataFromAssembly(this ObjectContext context)
    {
        context.MetadataWorkspace.LoadFromAssembly(context.GetType().Assembly);
    }
    
    public static void AttachUpdated(this ObjectContext context, EntityObject detachedEntity)
    {
        if (detachedEntity.EntityState == EntityState.Detached)
        {
            object currentEntity = null;
    
            if (context.TryGetObjectByKey(detachedEntity.EntityKey, out currentEntity))
            {
                context.ApplyPropertyChanges(detachedEntity.EntityKey.EntitySetName, detachedEntity);
    
                var newEntity = detachedEntity as IEntityWithRelationships;
                var oldEntity = currentEntity as IEntityWithRelationships;
    
                if (newEntity != null && oldEntity != null)
                {
                    context.ApplyReferencePropertyChanges(newEntity, oldEntity);
                }
            }
            else
            {
                throw new ObjectNotFoundException();
            }
        }
    }
    
    private static void ApplyReferencePropertyChanges(this ObjectContext context, IEntityWithRelationships newEntity, IEntityWithRelationships oldEntity)
    {
        foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())
        {
            var oldReference = relatedEnd as EntityReference;
    
            if (oldReference != null)
            {
                var newReference = newEntity.RelationshipManager.GetRelatedEnd(oldReference.RelationshipName, 
                    oldReference.TargetRoleName) as EntityReference;
    
                if (newReference != null)
                {
                    oldReference.EntityKey = newReference.EntityKey;
                }
            }
        }
    }

    Example:

    Product product = SomeProduct();
    
    using (var context = new BuyMoreStuffEntities())
    {
        context.LoadMetadataFromAssembly();
        context.AttachUpdated(product);
        context.SaveChanges();
    }

    Categories: Entity Framework
    Posted by Beau on Monday, March 09, 2009 8:35 PM
    Permalink | Comments (0) | Post RSSRSS comment feed

    Comments