I am working on an application that uses EF 4.2 and database-first development, using the standard T4 template to generate a DbContext and POCOs. The T4 templates generate entities something like this:
public class Address
{
public int AddressId { get;set; }
public string Address1 { get;set; }
public string City { get;set; }
}
public class Account
{
public int AccountId { get;set; }
public string Name { get;set; }
public int AddressId { get;set; }
public Address BillingAddress { get;set; }
}
When I create a billing address for an existing account, my code is something like this:
public void Save(Account updated)
{
var existing = DbContext.Find(updated.AccountId);
MyContext.Entry(existing).CurrentValues.SetEntry(updated);
existing.Address = updated.Address;
MyContext.SaveChanges();
}
Watching SQL Server Profiler, I can see the Address entry being inserted into the database, but unfortunately, it is occurring after the Account entry is updated, so the address is detached from its parent account, and when I next load the account, the billing address is empty again.
A workaround is to add the following code after the call to SaveChanges():
if (existing.AddressId == null && existing.Address != null)
{
existing.AddressId = existing.Address.AddressId;
MyContext.SaveChanges();
}
which, while it may work, requires a second SQL UPDATE to the database, and as the entity grows and adds more associations, requires more and more hacks. Is there something obvious that I’m missing?
** UPDATE **
Following Ladislav’s answer below, I added a call to the following method in the WriteNavigationProperty to my T4 template:
void WriteKeyAttribute(CodeGenerationTools code, NavigationProperty navigationProperty, MetadataTools ef)
{
var dependentProperties = navigationProperty.GetDependentProperties();
if (dependentProperties.Any())
{
var keys = new List<string>();
foreach (var key in dependentProperties)
{
keys.Add(String.Format("\"{0}\"", key.Name));
}
#>
[ForeignKey(<#= String.Join(", ", keys) #>)]
<#+
}
}
Hope that helps!
It sounds like your BillingAddress is incorrectly mapped because AddressId is not handled as the FK of the relation.
Try to add this attribute to your navigation property:
If you are using EDMX with database first make sure that the there is correctly configured relation between those classes. EF uses this information in its store mapping and store mapping defines sequence of the operations. If you don’t have correctly configured relation entities are processed in alphabetical order of their type names =>
Accountis processed prior toAddress.Btw. are you sure that your
Accountis not duplicated during yourSaveChangescall?