CTP5 EF Code First Question
You can find the source code demonstrating this issue @ http://code.google.com/p/contactsctp5/
I have three model objects. Contact,ContactInfo,ContactInfoType. Where a contact has many contactinfo's and each contactinfo is a contactinfotype. Fairly simple I guess. The problem I'm running into is when I go to edit the contact object. I pulled it from my contact repository. Then I run "UpdateModel(contact);" and it updates the object with all the values from my form. (monitoring with debug) When I save the changes though, I get the following error:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
It seems like after I call update model it nulls out my references and this seems to break everything? Any thoughts on how to remedy would be greatly appreciated. Thanks.
Here are my models:
public partial class Contact {
public Contact() {
this.ContactInformation = new HashSet<ContactInformation>();
}
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<ContactInformation> ContactInformation { get; set; }
}
public partial class ContactInformation {
public int ContactInformationId { get; set; }
public int ContactId { get; set; }
public int ContactInfoTypeId { get; set; }
public string Information { get; set; }
public virtual Contact Contact { get; set; }
public virtual ContactInfoType ContactInfoType { get; set; }
}
public partial class ContactInfoType {
public ContactInfoType() {
this.ContactInformation = new HashSet<ContactInformation>();
}
public int ContactInfoTypeId { get; set; }
public string Type { get; set; }
public virtual ICollection<ContactInformation> ContactInformation { get; set; }
}
My Controller Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Contact person) {
if (this.ModelState.IsValid) {
var contact = this.contactRepository.GetById(person.ContactId);
UpdateModel(cont开发者_如何学编程act);
this.contactRepository.Save();
TempData["message"] = "Contact Saved.";
return PartialView("Details", contact);
} else {
return PartialView(person);
}
}
Context Code:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder) {
modelBuilder.Entity<Contact>()
.HasMany(c => c.ContactInformation)
.WithRequired()
.HasForeignKey(c => c.ContactId);
modelBuilder.Entity<ContactInfoType>()
.HasMany(c => c.ContactInformation)
.WithRequired()
.HasForeignKey(c => c.ContactInfoTypeId);
}
There's a few things going on here.
1 If you are set up for lazy loading child objects are only loaded if you tell them to load. This can be done with the following in your query.
..
context.Contacts.Include(c => c.ContactInfos).Include(c => c.ContactInfos.ContactInfoType)
see this article for full details on making sure objects are loaded as you want.
2 If you don't want to save contactinfo and contactinfotype (because they are not loaded or you just don't want to), you will need to tell the context not to save child objects that shouldn't be updated. This can be done using:
..
context.StateManager.ChangeObjectState(entity.ContactInfos.ContactInfoType, EntityState.Unchanged);
I find I need to do that when changing/using a country object to user data. I definitely never want that to be updated by a user.
In the middle of writing a bit of a guide to all this, but could be weeks until it's done on my blog
3 MVC won't store/send back anything you don't put into the form. If you send an object heirarchy to the form and the values aren't represented in hidden inputs, they will come back empty on your model. For this reason, I generally make viewmodels that are editable only versions of the entities with a ToEntity and a ToModel method on them. This also covers me for security as I don't want all sorts of user ids in hidden inputs, just so my entities map straight into MVC (see this article on overposting).
I WOULD have thought that the fact you have your contactinfo properties set to virtual, the UpdateModel wouldn't mind if they didn't exist on the return, but I could well be wrong as I haven't tried it.
I figured this question out thanks to Morteza Manavi on the entity framework website. My issue was caused by my ContactInformation model properties, 'contactid' & 'contacttypeid' not being nullable. Once I fixed this everything with UpdateModel() worked correctly. Thank you very much!
精彩评论