Reputation: 8326
I'm trying Entity Framework 4.0 and here is the most simplified version of the case -
I have the following two related tables -
Address
Id
City
Client
Id
AddressId
Name
I've loaded the addresses in the ComboBox. All I want is to enter the Client name in the TextBox, select an Address from the ComboBox as the Client's Address and hit the Save Button. I want my Client to be saved in the Client table. Here's what i tried -
/*loads Addresses to the ComboBox*/
private void LoadData()
{
using (CAEntities ctx = ModelAccess.GetContext())
this.AddressList = ctx.Addresses.ToList();
}
/*(tries) to insert the Client to the database*/
private void Save()
{
/*here this.Client is the Client object that is instantiated & initialized
by previous code and this.Address is the SelectedItem of the ComboBox*/
this.Client.Address = this.Address;
using (CAEntities ctx = ModelAccess.GetContext())
{
ctx.Clients.AddObject(this.Client);
ctx.SaveChanges();
}
}
Julie Lerman in Programming Entity Framework says that ...Because the
entities are joined, you can add either entity, and it will bring along the rest of the graph...
But what I'm having is an InvalidOperationException that says "The EntityKey property can only be set when the current value of the property is null."
If I use -
this.Client.AddressId = this.Address.Id;
instead of -
this.Client.Address = this.Address;
the Client is inserted to database perfectly. But I think I'm also supposed to be able to relate Entities directly to each other, right?
I assumed that the problem is related to the separate context i'm creating. So i tried this -
private void Save()
{
this.Client.Address = this.Address;
using (CAEntities ctx = ModelAccess.GetContext())
{
ctx.Addresses.Attach(this.Address);
ctx.SaveChanges();
}
}
but this time i'm getting an InvalidOperationException that says "An object with a temporary EntityKey value cannot be attached to an object context." So what is it that I'm doing wrong here? Thanks in advance.
Upvotes: 1
Views: 195
Reputation: 17367
This should fix the problem:
using (CAEntities ctx = ModelAccess.GetContext())
{
ctx.Addresses.Attach(this.Address);
this.Client.Address = this.Address;
ctx.Clients.AddObject(this.Client);
ctx.SaveChanges();
}
Why does this work?
The DbContext keeps track of objects that it's pulled down or that have been explicitly attached to it. In your case, you were setting this.Client.Address
to an object that the DbContext was not aware of. In some cases, this will cause entity framework to insert both a new client row and a new address row, but in your case, due to some semantic detail I'm not privy to, it threw an obscure exception.
By attaching this.Address
to the DbContext, entity framework understands that the Address already exists in the database, and that you are creating an association with an existing object when assigning it to this.Client.Address
.
IMO, Entity Framework's semantics need to be better documented. There are a lot of cases like these where a misleading or obscure exception is thrown. For example when attaching objects, it's important to keep in mind that they are attached recursively. If you were to in a new data context attach this.Client
it would recursively attach this.Client.Address
as well, and any other entities this.Client
might reference. For this reason, if you were to explicitly attach this.Client
and then this.Address
you would get an exception because the Address had been implicitly attached when you attachedthis.Client
.
Upvotes: 2