0

I'm having a small problem with ASP.NET MVC and Entity Framework 4. I have an entity called "UF" and another one called "Pais", and they have this relation:

UF [* ... 0..1] Pais

I can access the Pais object directly from UF using a navigation property:

UF.Pais.Whatever = "foobar";

Currently I have a View which inserts a new item into the database, and it has an editor for "Pais.Codigo" ("Codigo" is the primary key for Pais). So when I insert a new UF, the framework creates an instance of class UF with a reference to an instance of class Pais. Then this is done:

if (ModelState.IsValid)
{
    db.UFs.AddObject(uf);
    db.SaveChanges();

    return View();
}

The problem is that the EF is inserting a new Pais into the database, so it basically ignores the existing one.

For example, if let's say my object UF has a Pais with an ID of 1. The current value of uf.Pais.Codigo is 1. Other attributes, like the description, is currently null. When I execute the SaveChanges, both "uf" and "uf.Pais" are with the state of Added. The correct state for "uf.Pais" should be Unchanged, since it already exists on the database.

My question is: there's some way of changing the default relationship EntityState for Unchanged? The following code solves the problem, but adding it to each function with adds a new entry to the database (and for each FK) is overkill!

db.ObjectStateManager.ChangeObjectState(uf.Pais, EntityState.Unchanged);

That's it. I'm not sure if I was clear enough. Feel free to ask more information if needed. And sorry for any english mistakes!

Thanks,

Ricardo

PS: "Pais" stands for Country and "UF" for State.

Ricardo
  • 172
  • 8

1 Answers1

0

My question is: there's some way of changing the default relationship EntityState for Unchanged?

Yes by calling Attach instead of Unchanged.

The following code solves the problem, but adding it to each function with adds a new entry to the database (and for each FK) is overkill!

No it is not overkill, it is a solution because either Attach or AddObject will always make the operation for all entities and associations in entity graph. That means that calling AddObject will make everything what context is not aware of yet as Added and Attach will make everything what context is not aware of as Unchanged (so you will in turn have to set each modified or inserted entity to its correct state). That is how EF works if you are using detached entities.

Another solution for the problem is making the connection after the UF is added:

// Here UF.Pais is null
db.UFs.AddObject(uf);
// Create dummy Pais
var pais = new Pais { Id = "Codigo" };
// Make context aware of Pais
db.Pais.Attach(pais);
// Now make the relation
uf.Pais = pais;
db.SaveChanges();

If you are working with detached entities you are always responsible for setting the correct state for each entity (and independent association). So you will either use attached entities to let EF make the magic for you (as shown in the example) or you will use the approach you dislike. In more complex scenarios you can find out that best approach is to load entity graph again and merge incoming changes into the attached graph.

Community
  • 1
  • 1
Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • I can't use the second option. The View is sending me a UF object already filled with a Pais instance. After executing the `db.UFs.AddObject` it changes the state of both `uf` and `uf.Pais` to Added. When trying to execute `db.Paises.Attach(uf.Pais)` an exception is throwed: _The object cannot be attached because it is already in the object context. An object can only be reattached when it is in an unchanged state._ – Ricardo Aug 05 '11 at 12:36
  • Also, I cannot use `Attach` with a new object. At least it didn't work here when i tried. So I guess my option is really change the state manually? – Ricardo Aug 05 '11 at 12:38
  • I didn't say that first option will work for you. I say that it is another solution of the problem and you can indeed modify your code to be able to use it. Using `Attach` alone will not help you as well. After calling attach you must change the state of `UF` to `Added` manually. – Ladislav Mrnka Aug 05 '11 at 12:44
  • Ah ok. Now I understand what is happening. It's all about context, right? I was wondering if it helps adding the FK field to the entity class, so the input in the View will use something like `model.PaisCodigo` (where `PaisCodigo` is the key for the correct Pais). – Ricardo Aug 05 '11 at 12:47
  • It depends if your model binder fills `Pais` property as well or not. – Ladislav Mrnka Aug 05 '11 at 12:51
  • This is all new to me. So if I understood correctly, if I receive the ID from a Pais entry directly into an attribute that is mapped to the FK in the database I still need to load the Pais instance to make sure the context is correct? – Ricardo Aug 05 '11 at 12:58
  • If you have private key exposed you don't need to set the navigation property. It should simply save the entity with foreign key but if you set the navigation property you will have the same problem. – Ladislav Mrnka Aug 05 '11 at 13:29
  • As far as I know I can't have both together (if I remember correctly the EDMX won't compile or something like that). Navigation properties are one of the many great features of EF in my opinion, so I guess I'm gonna stick with your `Attach()` suggestion, which worked flawlessly here. Also, using `AddObject()` someone can forget to change the relation to `Unchanged` and that might throw some exceptions or even insert wrong data on the database. Thanks a lot! – Ricardo Aug 05 '11 at 13:57
  • After studying the links you sent me I understand a little more of my problem. Indeed, detached objects are a problem. I tested your solution using a dummy object for loading the correct `Pais` into the context, but there's a problem: that runs another query in the server (a select). This can reduce the overall performance if this operation is done too many times. Now exposing the FK attribute doesn't look so bad. Do you think there's another solution that keep the transaction with 1 query only (the insert) and doesn't kill the navigation property (like exposing the FK)? – Ricardo Aug 08 '11 at 20:00