4

I am using ASP.NET MVC4 Web API, and my PUT action, I want to do something like this:

public void Put(string id, [FromBody]Foo value)
{
    var context = new FooBarEntities();
    Foo existingFoo = context.Foos.Where(x => x.Id == id).First();
    existingFoo = value;
    context.SaveChanges();
}

But the changes in the Foo value object are not saved. If I were to do each property, it would work, like this:

public void Put(string id, [FromBody]Foo value)
{
    var context = new FooBarEntities();
    Foo existingFoo = context.Foos.Where(x => x.Id == id).First();
    existingFoo.Prop1 = value.Prop1;
    existingFoo.Prop2 = value.Prop2;
    context.SaveChanges();
}

Is there a way that I can updated every property by just assigning the object?

xdumaine
  • 10,096
  • 6
  • 62
  • 103
  • possible duplicate of [What is the equivalent of ObjectContext.ApplyCurrentValues for DbContext](http://stackoverflow.com/questions/5807622/what-is-the-equivalent-of-objectcontext-applycurrentvalues-for-dbcontext). Marked as duplicate because it contains an answer to your question, but I can imagine you were not aware of that. Don't worry, duplicates are not a crime. – Gert Arnold Apr 19 '13 at 14:41
  • 1
    @GertArnold maybe. I think [this question/answer](http://stackoverflow.com/questions/623672/update-entity-framework-objects/4212581#4212581) that Michael Haren linked below is a closer duplicate, with answer. – xdumaine Apr 19 '13 at 14:48
  • Unrelated to your question: `.Where(x=>...).First()` can usually be replaced with `.First(x=>...)`. If there must be only one, then `.Single(x=>...)` would be even better. If there must be 0 or 1, then I'd use `SingleOrDefault(x=>...)` and check for null. – Michael Haren Apr 19 '13 at 14:52
  • @MichaelHaren Thanks, that's helpful. I wasn't aware I could do `.First` like that, or that `.Single` existed. (I omitted null check here for simplicity) – xdumaine Apr 19 '13 at 14:55

4 Answers4

2

This is definitely not going to work:

Foo existingFoo = context.Foos.Where(x => x.Id == id).First();
existingFoo = value;

You're looking it up, and then immediately overwriting the reference with something entirely different.

I think you want to attach your value to the context and then do a save. Something more like this might do it for you:

context.Foos.AddObject(value);
context.ObjectStateManager.ChangeObjectState(value, System.Data.EntityState.Modified);
context.SaveChanges();

This doesn't require that you load the object to be updated first, which is nice. If that doesn't do it, then knowing that this is called something like disconnected updating might help your future googling.

If you already have the object to be updated in hand, along with the new copy, I'm not aware of anything clean that merges them for you.


Another way I've done it before looks more like this, which might feel less hacky:

var entity = context.Entry(value);
entity.State = EntityState.Modified;
context.SaveChanges();

Or more concisely:

context.Entry(value).State = EntityState.Modified;
context.SaveChanges();

I can't remember if that's a built in thing or not, though.

Community
  • 1
  • 1
Michael Haren
  • 105,752
  • 40
  • 168
  • 205
  • Just tested this out and it seems to do exactly what I was looking for. It does seem a little hacky, like it's *tricking* entity framework. – xdumaine Apr 19 '13 at 14:50
  • It might be weird at first, but I don't think it's a hack. You can use this approach for deletes, too, I think. – Michael Haren Apr 19 '13 at 14:56
1

Short answer: No.

But there a numerious ways to get the same result, I wrote this code once that uses reflection to copy all properties of your objects:

    public void CopyLinqObject(object obj_source, object obj_dest)
    {
        Type t_source = obj_source.GetType();
        PropertyInfo[] p_source = t_source.GetProperties();

        Type t_dest = obj_dest.GetType();
        PropertyInfo[] p_dest = t_dest.GetProperties();

        foreach (PropertyInfo ps in p_source)
        {
            foreach (PropertyInfo pd in p_dest)
            {
                if (ps.Name == pd.Name)
                {
                    if (ps.PropertyType == pd.PropertyType)
                    {
                        if (ps.PropertyType.IsSerializable)
                        {
                            pd.SetValue(obj_dest, ps.GetValue(obj_source, null), null);
                        }
                    }
                    else
                    {
                        if (ps.PropertyType.BaseType == pd.PropertyType.BaseType)
                        {
                            if (ps.PropertyType.IsSerializable)
                            {
                                if (ps.GetValue(obj_source, null) != null)
                                {
                                    try
                                    {
                                        pd.SetValue(obj_dest, ps.GetValue(obj_source, null), null);
                                    }
                                    catch
                                    {
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
Michael Haren
  • 105,752
  • 40
  • 168
  • 205
ikwillem
  • 1,044
  • 1
  • 12
  • 24
0

You can't "assign" properties of one object to another that way. Your code is just changing the value of variable existingFoo.

You can either implement your own property-copying code, or take a look at attaching class to existing data context.

alex
  • 12,464
  • 3
  • 46
  • 67
0

Is there a way that I can updated every property by just assigning the object?

No, EF track changes with the properties, they classes implement inotifypropertychanged, when you just change the variable reference, you didn't change the properties.

gdoron
  • 147,333
  • 58
  • 291
  • 367