Gavin Draper's Blog

Father, Developer, Tech Geek, Extreme Sports Fanatic

@gavdraper | +gavdraper | github | StackOverflow Careers
 

Entity Framework Navigation Property Not Saving For Re-Attached Entity

I thought I had the whole disconnected Entity Framework over WCF working well but
this week I came accross a problem to do with Navigation Properties not saving when
you reattach Entities and set them as modified.

Take the following example of a console app that uses EF4 with POCO objects

static void Main(string[] args)  
{
    Service svc = GetService();
    Console.WriteLine("Service : " + svc.Name + " , Status : " + svc.Status.Name);
    //Change and save Status
    svc.Status = GetStatus("Stopped");
    using (var ctx = new TestEFContext())
    {
        //Status is changed
        Console.WriteLine("Service : " + svc.Name + " , Status : " + svc.Status.Name);
        ctx.AttachModify("Services", svc);
        ctx.AttachTo("Statuses", svc.Status);
        ctx.SaveChanges();
    }
    //Re-fetch service from db and check status
    svc = GetService();
    //Status is set back to its old value!!!!!!!!
    Console.WriteLine("Service : " + svc.Name + " , Status : " + svc.Status.Name);
    Console.ReadLine();
}

private static Service GetService()  
{
    using (var ctx = new TestEFContext())
    {
        return ctx.Services.Include("Status").FirstOrDefault();
    }
}

private static Status GetStatus(string name)  
{
    using (var ctx = new TestEFContext())
    {
        return ctx.Statuses.Where(n=>n.Name  name).FirstOrDefault();
    }
}

public class Service  
{
    [DataMember] public int ServiceID { get; set; }
    [DataMember] public string Name { get; set; }
    [DataMember] public Status Status { get; set; }
}

public class Status  
{
    [DataMember] public int StatusID { get; set; }
    [DataMember] public string Name { get; set; }
}

The Table Schema looks like this

Service
ServiceID INT
Name NVARCHAR(40)
StatusID INT

Status
StatusID INT
Name NVARCHAR(20)

The POCO objects look like this

Service
ServiceID int
Status Status
Name string

Status
StatusID int
Name string

As in previous posts the AttachModify is a method I created in my context to attach
an entity and mark it as modified, it looks like this

public void SetModified(object entity)  
{
    ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
}

public void AttachModify(string entitySetName, object entity)  
{
    AttachTo(entitySetName, entity);
    SetModified(entity);
}

When I save the service it updates the name but leaves the StatusID as its previous
value. This seems to be because I'm working disconnected EF doesnt know the navigation
property has been changed and therefore doesn't update the StatusID even though I
explicitly set the Service object to modified. I tried also seting the Status object
to modified but the only effect this has was that it then also updates the Status
table but still no update for the StatusID on the service table.

The solution for this seems to be either keep the foreign keys in your model rather
than just the Navigation Properties or you can do the following

        static void Main(string[] args)  
        {
            Service svc = GetService();
            svc.Status = GetStatus("Stopped");
            using (var ctx = new TestEFContext())
            {
                var svc2 = ctx.Services.Where(s=>s.ServiceID  svc.ServiceID).FirstOrDefault();
                svc2.Status = ctx.Statuses.Where(n => n.StatusID == svc.Status.StatusID).FirstOrDefault();
                ctx.ApplyCurrentValues("Services", svc);                                
                ctx.SaveChanges();
            }
        }

On save this then refetches the entities and any child entities from the database
and uses ApplyCurrentValues on them to apply the updated POCO object values. I realize
that this is no where near as neat as just re-attaching the entities and setting them
to modified but it was the only way I could get this to work. In future if I'm working
in a disconnected way like this I will probably keep the Foreign Keys in my model
then I can avoid having to re-fetch entities from the database every time I want to
save a disconnected POCO object that uses Navigation Properties.


 
comments powered by Disqus