0

I feel like this should be a pretty common thing to do. I have a model with a related object on it. Let's say it's a User and a user has one Role.

public class User { public int Id { get; set; } public virtual Role Role { get; set; } /* other stuff that saves fine */ } public class Role { public int Id {get;set;} public string Name { get;set;} } 

So if I save a new user, or if I edit a user (but don't change his Role), I have no issues. If I have a user without a role, and add a role to him, again no problem (though I manually lookup the role and assign it). If I try and change a role, I get a modelstate error on the Role property that the ID is part of the object's key and can't be changed. So how do folks go about making updates like this? Whitelist the simple values and then manually update the Role?

My controller code in question is here:

[HttpPost] public ActionResult Save(int id, FormCollection form) { var user = data.Users.FirstOrDefault(d=> d.Id == id); if (user != null) { TryUpdateModel(user, form.ToValueProvider()); if (!ModelState.IsValid) { var messages = ModelState.Values.Where(m => m.Errors.Count() > 0).SelectMany(m=>m.Errors).Select(e => e.ErrorMessage); if (Request.IsAjaxRequest()) return Json(new { message = "Error!", errors = messages }); return RedirectToAction("index"); // TODO: more robust Flash messaging } updateDependencies(user); /* negotiate response */ } } 

I'll probably just do it manually for now, but it seems like a scenario that I would have expected to work out of the box, at least to some degree.

1 Answer 1

2

Your User model should have a foreign key:

public int? RoleId { get; set; } public virtual Role Role { get; set; } 

You can assign a Role.Id to this value, or make it null when the user does not have a role.

I'm also not sure if your Save function is correct. I'm always using this pattern (not sure if it is correct either...), but of course it depends on the data you post to the server:

[HttpPost] public ActionResult Save(User model) { if (ModelState.IsValid) { // Save logic here, for updating an existing entry it is something like: context.Entry(model).State = EntityState.Modified; context.SaveChanges(); return View("Success"); } return View("Edit", model); } 
Sign up to request clarification or add additional context in comments.

3 Comments

I see. I thought the idea of the POCO classes in EF 5 was that you didn't have to litter your domain with database-specific properties like the foreign key field of RoleId. Disappointment. Anyway, that works so thanks. The save pattern you're using works only if you have a hard reference to the DbContext, which I don't since I'm injecting mine as an interface that I don't want to couple tightly to EF. It does pick up that the model was changed if you fetch it from the DB first, though I guess your method might save a round-trip?
I had a similar issue; my solution was to have a generic Update(T item) method in that interface where T is a base class that your models inherit from. Then, in the implementation to that interface, you can change the EntityState as needed.
My safe pattern is a bit simplified in my answer. In fact I'm using the Unit of Work pattern with a generic IRepository<T> class (where T is a datamodel). As @anaximander mentions you can use functions like Update(T item) and Insert(T item). More info here: asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.