EF5 How to get list of navigation properties for a domain object

Adolfo Perez picture Adolfo Perez · Jul 26, 2013 · Viewed 8.7k times · Source

I'm working on an EF 5 Code-First solution and I am trying to update an existing entity with a modified one using the Repository pattern:

    public void UpdateValues(T originalEntity, T modifiedEntity)
    {
        _uow.Context.Entry(originalEntity).CurrentValues.SetValues(modifiedEntity);

My simplified domain object looks like this:

public class PolicyInformation : DomainObject
{
     //Non-navigation properties
     public string Description {get;set;}
     public bool Reinsurance {get;set;}
     ...
     //Navigation properties
     LookupListItemFormType FormType {get;set;}
     ...
}

The problem I have is that the CurrentValues.SetValues(modifiedEntity); method seems to be updating only scalar and complex type properties but not Navigation properties. I've seen this happening to a lot of people and still don't know why that is. However, I figured out that if I set those navigation properties manually after executing my UpdateValues everything works fine:

            _policyInfoRepo.UpdateValues(existingPolicyInfo, info);
            existingPolicyInfo.FormType = info.FormType;

The question is, since I use a generic repository: How can I get a list of Navigation properties in my domain object? Perhaps through linq, reflection, or any other way so my update method in my repository can loop through them and set them automatically?

Something like this:

    public void UpdateValues(T originalEntity, T modifiedEntity)
    {
        //Set non-nav props
        _uow.Context.Entry(originalEntity).CurrentValues.SetValues(modifiedEntity);
        //Set nav props
        var navProps = GetNavigationProperties(originalEntity);
        foreach(var navProp in navProps)
        {
           //Set originalEntity prop value to modifiedEntity value
        }

Thanks!

Answer

Brandon Barkley picture Brandon Barkley · Jan 7, 2014

I wrote the following using EF6, but I believe it is all compatible with EF5. The general idea behind the code is to use the excellent classes in a System.Data.Metadata.Edm to get the navigation properties and use reflection on those property names to get the true properties of the object for updating.

I wanted to make my example as generic yet complete as possible. In the asker's case, he'd just obviously replace "context" with "_uow.Context".

public class MyClass<T> where T : class //T really needs to always be an entity, 
                                        //but I don't know a general parent type
                                        //for that. You could leverage partial classes
                                        //to define your own type.
{
    public MyEntities context { get; set; }

    public void UpdateValues(T originalEntity, T modifiedEntity)
    {
        //Set non-nav props
        context.Entry(originalEntity).CurrentValues.SetValues(modifiedEntity);
        //Set nav props
        var navProps = GetNavigationProperties(originalEntity);
        foreach (var navProp in navProps)
        {
            //Set originalEntity prop value to modifiedEntity value
            navProp.SetValue(originalEntity, navProp.GetValue(modifiedEntity));                
        }
    }

    public List<System.Reflection.PropertyInfo> GetNavigationProperties(T entity)
    {
        List<System.Reflection.PropertyInfo> properties = new List<System.Reflection.PropertyInfo>();
        //Get the entity type
        Type entityType = entity.GetType();
        //Get the System.Data.Entity.Core.Metadata.Edm.EntityType
        //associated with the entity.
        var entitySetElementType = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext.CreateObjectSet<T>().EntitySet.ElementType;
        //Iterate each 
        //System.Data.Entity.Core.Metadata.Edm.NavigationProperty
        //in EntityType.NavigationProperties, get the actual property 
        //using the entityType name, and add it to the return set.
        foreach (var navigationProperty in entitySetElementType.NavigationProperties)
        {
            properties.Add(entityType.GetProperty(navigationProperty.Name));
        }
        return properties;
    }
}