EF4.1-Code-First-Gurus!
I wonder if there is a more elegant way to handle the following ASP.NET MVC 3 EF 4.1 Code First scenario: Lets say we have the following POCOs:
public class Entity
{
[Key]
public int Id { get; set; }
[ScaffoldColumn(false)]
public DateTime CreatedOn { get; set; }
[ScaffoldColumn(false)]
public DateTime ModifiedOn { get; set; }
}
and
public class Person : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
Lets assume we have created some standard editing views, which are not including CreatedOn/ModifiedOn fields, because, they will be set in the repository and not by the user.
In my repository I have the following Update method. The methods excepts a list of fields, which should be updated (leaving CreatedOn/ModifiedOn fields out):
public void Update(Person person, List<string> properties)
{
Person tmpPerson = context.People.Single(x => x.Id == person.Id);
context.People.Attach(tmpPerson);
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(person))
{
if (properties.Contains(descriptor.Name))
descriptor.SetValue(tmpPerson, descriptor.GetValue(person));
}
tmpPerson.ModifiedOn = DateTime.Now;
}
Now the controller is calling this method like this:
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid) {
personRepository.Update(person, new List<string> { "FirstName", "LastName", "Birthday"});
personRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
This all works like a charm. However, I really dislike, that I have to specify the fields manually. How would you handle this requirement? Of course I could add CreatedOn/ModifiedOn fields as hidden fields to the view, but I dont want to bload the form to much (There are much more fields).
Maybe this is a similar question:
How To Update EF 4 Entity In ASP.NET MVC 3?
I highly appreciate your help!
Joris
Yes there is more elegant version:
public void Update(Person person, params Expression<Func<Person,object>>[] properties)
{
context.People.Attach(person);
DbEntityEntry<Person> entry = context.Entry(person);
foreach (var property in properties)
{
entry.Property(property).IsModified = true;
}
person.ModifiedOn = DateTime.Now;
}
You will call the method this way:
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid)
{
personRepository.Update(person, p => p.FirstName,
p => p.LastName, p => p.Birthday);
personRepository.Save();
return RedirectToAction("Index");
}
else
{
return View(person);
}
}
Related
There is an article about how to update related data with the Entity Framework in an ASP.NET MVC application. It implements a simple University in which you can choose your courses as an instructor.
Here is simplified version for its courses controllers:
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
if (selectedCourses == null)
{
instructorToUpdate.Courses = new List<Course>();
return;
}
var selectedCoursesHS = new HashSet<string>(selectedCourses);
var instructorCourses = new HashSet<int>
(instructorToUpdate.Courses.Select(c => c.CourseID));
foreach (var course in db.Courses)
{
if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
if (!instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Add(course);
}
}
else
{
if (instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Remove(course);
}
}
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int? id, string[] selectedCourses)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var instructorToUpdate = db.Instructors
.Include(i => i.Courses)
.Where(i => i.ID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception e)
{
//Log the error
}
}
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
}
As you can see UpdateInstructorCourses fills instructorToUpdate.Courses with objects retrieved from db.Courses based on selectedCourses which is a string array.
So, is it the only way to create many-to-many relationships? Do we need to get objects from db and then Add them to our List member? Isn't it better to only pass related object's Id and update related data?
So, for many-to-many mappings, there are two ways to do it in EF:
1) Two ICollection properties (which you are already using):
public class Instructor
{
public Int32 Id { get; set; }
public ICollection<Course> Courses { get; set; }
}
public class Course
{
public Int32 Id { get; set; }
public ICollection<Instructor> Instructors { get; set; }
}
In this case, EF will generate/use a mapping table, such as:
CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[Instructor_Courses_Mapping]([Id],[InstructorId],[CoursesId])
Now, as you pointed out, there's no way to get a list from that mapping table, making you load the collection into memory using a navigation property. So:
2) However, you can override EF's mapping-table generation with your own custom mapping entity:
public class Instructor
{
public Int32 Id { get; set; }
public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class Course
{
public Int32 Id { get; set; }
public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class InstructorCourse
{
public Int32 Id { get; set; }
public Int32 InstructorId { get; set; }
public Instructor Instructor { get; set; }
public Int32 CourseId { get; set; }
public Course Course { get; set; }
}
Now, EF will generate these tables:
CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[InstructorCourses]([Id],[InstructorId],[CourseId])
This will allow you to then query for InstructorCourses using a dbContext:
var instructorCourses = dbContext.Set<InstructorCourse>().Where( c => c.InstructorId == instructorId ).ToList()
That will return you a list of InstructorCourse objects, with all InstructorId values matching the one your looking for. Then, if you wanted to add/remove mappings:
//add item
dbContext.Set<InstructorCourse>().Add(new InstructorCourse()
{
InstructorId = instructorId,
CourseId = courseId
});
dbContext.SaveChanges();
//remove item
var itemToRemove = dbContext.Set<InstructorCourse>().FirstOrDefault( c => c.InstructorId == instructorId && c.CourseId == courseId);
dbContext.Set<InstructorCourse>().Remove(itemToRemove);
dbContext.SaveChanges();
I found this method to be much more cleaner, represents your database structure more clearer, but it does make nested Linq statements more complicated, and nulls (without proper foreign key restrictions) potentially more common.
I am working on a project that uses an MVC API with Entity Framework 6.1.3. We need to implement Get, GET/ID, Insert, Update and Delete API's.
for Insert and Update we have a parameter class that is not the same one as the DB, i am not sure what will be the best solution for that.
For an Update (the one that i have more questions) i can find a record and then update all properties manually, this is something i want to avoid. If i use the currentvalues from the entity then i will have to set the ExtraValues properties in all the apis that i am going to write, that kind of looks weird.
Note: i want to have a child class since most of the entities uses the same fields (Created/Updated) so instead of having those in all the classes i rather have them as inheritance.
There has to be a better solution for this problem, could someone help with ideas or best ways to do this.
public class DBClassA : ExtraValues
{
public int ID { get; set; }
public string Name { get; set; }
}
public class DBClassB : ExtraValues
{
public int ID { get; set; }
public string Name { get; set; }
}
//This will be use in all the classes
public class ExtraValues
{
public string SameValueInOtherClasses { get; set; }
public string SameValueInOtherClasses2 { get; set; }
public string SameValueInOtherClasses3 { get; set; }
}
[HttpGet]
public List<DBClassA> Get()
{
return new List<DBClassA>();
}
[HttpGet]
public DBClassA Get(int ID)
{
return new DBClassA();
}
[HttpPost]
public HttpResponseMessage Insert(DBClassA obj)
{
using (var context = new DBEntities())
{
context.Entity.Attach(DBClassA);
context.SaveChanges();
}
return Request.CreateResponse(HttpStatusCode.OK);
}
[HttpPut]
public HttpResponseMessage Update(int ID, DBClassA obj)
{
using (var context = new DBEntities())
{
var entity = context.Entity.Find(ID);
//I will have to put the ExtraValues here
obj.ExtraValues = "";
_context.Entry(entity).CurrentValues.SetValues(obj);
context.SaveChanges();
}
return Request.CreateResponse(HttpStatusCode.OK);
}
using (var db = new DBEntities())
{
db.Entry(obj).State = EntityState.Modified;
db.SaveChanges();
}
more info: http://www.entityframeworktutorial.net/EntityFramework4.3/update-entity-using-dbcontext.aspx
Right now, in smaller projects, I flounder between 3 approaches. I would like to choose one of them to use consistently.
I have put example code, with pros/cons below. But essentially, the 3 approaches boil down to:
All DB work in controller
DB instance passed from controller into ViewModel, and work done there
DB created in ViewModel with (using), and work done there
Why would an experienced developer choose one approach over another?
Are there any benefits / disadvantages to these approaches that - as a beginner - I might not be aware of?
Update
I appreciate that these patterns may not be used in larger projects which tend to me more structurally complex.
But really I am interested in the difference in them when they ARE used.
Approach One
All DB work and vaildation flow in controller
Makes it easy to grasp what's going on for simple applications...
...but 'fat' controllers may obscure understanding for complex applications.
Default used by MS in MVC template
public class ModelOne
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
public string PropC { get; set; }
// etc...
}
public class EditModelOnePropAViewModel
{
public EditModelOnePropAViewModel(ModelOne model)
{
ID = model.ID;
PropA = model.PropA;
}
public string PropA { get; set; }
public int ID { get; set; }
}
public class ControllerOne : Controller
{
private ApplicationDbContext DB = new ApplicationDbContext() { };
[HttpGet]
public ActionResult Edit(int id)
{
var model = DB.ModelOneDbSet.FirstOrDefault(i => i.ID);
var viewModel = new EditModelOnePropAViewModel(model);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(EditModelOnePropAViewModel postedModel)
{
if (ModelState.IsValid)
{
var model = DB.ModelOneDbSet.FirstOrDefault(i = i.ID == postedModel.ID);
model.PropA = postedModel.PropA;
DB.SaveChanges();
return RedirectToAction("index");
}
return View(postedModel);
}
}
Approach Two
DB passed into ViewModel from Controller
Controller is 'thin' and 'dumb'.
Because we are using the controller's DB instance, it's Dispose() method will be called on the DB object.
public class ModelTwo
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
public string PropC { get; set; }
// etc...
}
public class EditModelTwoPropAViewModel
{
public EditModelTwoPropAViewModel(ApplicationDbContext db, int id)
{
ID = id;
PropA = db.ModelTwoDbSet
.Where(i => i.ID == id)
.Select(i => i.PropA)
.FirstOrDefault();
}
public void SaveChanges(ApplicationDbContext db)
{
var modelTwo = db.ModelTwoDbSet.FirstOrDefault(i => i.ID == ID);
modelTwo.PropA = PropA;
db.SaveChanges();
}
public string PropA { get; set; }
public int ID { get; set; }
}
public class ControllerTwo : Controller
{
private ApplicationDbContext DB = new ApplicationDbContext() { };
[HttpGet]
public ActionResult Edit(int id)
{
var viewModel = new EditModelTwoPropAViewModel(DB, id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(EditModelTwoPropAViewModel postedModel)
{
if (ModelState.IsValid)
{
postedModel.SaveChanges(DB);
return RedirectToAction("index");
}
return View(postedModel);
}
}
Approach Three
Same as Two, but DB created with (using) in ViewModel.
public class ModelThree
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
public string PropC { get; set; }
// etc...
}
public class EditModelThreePropAViewModel
{
public EditModelThreePropAViewModel(int id)
{
using (var db = new ApplicationDbContext())
{
ID = id;
PropA = db.ModelThreeDbSet
.Where(i => i.ID == id)
.Select(i => i.PropA)
.FirstOrDefault();
}
}
public void SaveChanges()
{
using (var db = new ApplicationDbContext())
{
var modelThree = db.ModelThreeDbSet.FirstOrDefault(i => i.ID == ID);
modelThree.PropA = PropA;
db.SaveChanges();
}
}
public string PropA { get; set; }
public int ID { get; set; }
}
public class ControllerThree : Controller
{
[HttpGet]
public ActionResult Edit(int id)
{
var viewModel = new EditModelThreePropAViewModel(id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(EditModelThreePropAViewModel postedModel)
{
if (ModelState.IsValid)
{
postedModel.SaveChanges();
return RedirectToAction("index");
}
return View(postedModel);
}
}
Your question is too vast and includes lots of different aspects. So I try to give you a very general view. There is nothing wrong or right in software design. There are "Project needs and specifications". But generally it has been accepted using "Design Patterns and Principles" makes project more robust,reliable and easy to maintain and make your code "loosely coupled and highly cohesive".
Having these in your mind, I give my opinion for your selections:
All DB work in controller: This is good for small project that possibility for its expansion in future is low. It is easy to setup and easy to understand for other developers who join later. It is not good for project that need to change database later, which in that case you need to rewrite all your methods and probably your views. Another weakness of this approach is repetitive code for every object you need to add.
DB instance passed from controller into ViewModel, and work done there: For my understanding from you code, you tried add a layer for your project to have all database actions there. It is good if you need to change your data,database or preventing from repetitive code. If you want that, it is better to use another project in you solution,known as "Multi tier architect ", to have more flexibility and "loosely coupled" sections. In that case you could easily replace your database project with other one later without worrying about your views and controllers, in multi tier architect knows as presentation layer.
DB created in ViewModel with (using), and work done there: This isn't new approach. Having "Using block" in your code is about "thread starvation" (more information here).
At the end, consider your project needs and specifications, design your architecture and follow your architecture based on "Design Patterns and Principles". For more information about Design Patterns, I recommend the book "Professional ASP.NET Design Patterns" from "Scott Millett".
I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.
I have this 4 models - 2 Domain Models and 2 DTOs
public class Project
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int ID { get; set; }
public virtual int ProjectID { get; set; }
public string Name { get; set; }
public virtual Project Project { get; set; }
}
public class ProjectDTO
{
[Required]
public string Name { get; set; }
public List<TaskDTO> Tasks { get; set; }
}
public class TaskDTO
{
[Required]
public string Name { get; set; }
public int ID { get; set; }
public bool MarkRemove { get; set; }
}
Heres my automapper configuration
Mapper.CreateMap<Project, ProjectDTO>();
Mapper.CreateMap<ProjectDTO, Project>().ForMember(p =>p.ID, opt=>opt.Ignore()).ForMember(p=>p.Tasks, opt=>opt.Ignore());
Mapper.CreateMap<Task, TaskDTO>();
Mapper.CreateMap<TaskDTO, Task>().ForMember(task=>task.ProjectID, opt=>opt.Ignore()).ForMember(task=>task.Project, opt=>opt.Ignore());
Heres my HttpPost Edit action
[HttpPost]
public ActionResult Edit(int id, ProjectDTO p)
{
if (ModelState.IsValid)
{
var dbProject = db.Projects.Where(pr => pr.ID == id).Single();
Mapper.Map(p, dbProject);
foreach (var task in p.Tasks)
{
Task dbTask;
try
{
dbTask = dbProject.Tasks.Where(t => t.ID == task.ID).Single();
}
catch
{
dbTask = new Task();
Mapper.Map(task, dbTask);
dbProject.Tasks.Add(dbTask);
}
if (task.MarkRemove)
{
db.Tasks.Remove(dbTask);
}
else {
Mapper.Map(task, dbTask);
}
}
db.Entry(dbProject).State = EntityState.Modified;
db.SaveChanges();
TempData["Success"] = "Modelo Valido";
return RedirectToAction("Index");
}
return View(p);
}
Im not completely happy with this but I dont think there is a much cleaner approach to handling this somewhat complex scenario....
now that it is working I would like to at least refactor this to use repository pattern or something in a way that the controller action is not that convoluted.. this will eventually be production code :s
can anyone give me some advice on how to refactor this?
please help.
I would use a service layer, like this:
public interface IProjectsService
{
void RemoveTasks(int projectId, IEnumerable<int> taskIdsToRemove);
}
and then the controller would depend on this service layer:
public class ProjectsController : Controller
{
private readonly IProjectsService _service;
public ProjectsController(IProjectsService service)
{
_service = service;
}
public ActionResult Edit(int id)
{
// TODO: Add methods to your service layer
// allowing to retrieve projects, then map
// the resulting project into a view model
throw new NotImplementedException();
}
[HttpPost]
public ActionResult Edit(int id, ProjectDTO p)
{
if (!ModelState.IsValid)
{
return View(p);
}
var taskIdsToRemove = p.Tasks.Where(x => x.MarkRemove).Select(x => x.ID);
_service.RemoveTasks(id, taskIdsToRemove);
TempData["Success"] = "Modelo Valido";
return RedirectToAction("Index");
}
}
This way the controller logic is more weakly coupled to the way we do data access. That's an implementation detail that a controller should never have to worry about.
As a further improvement to the RemoveTasks method you could make it return a boolean indicate the success or failure of the operation along with an error message so that the Edit action could redisplay the view and show the error in case something goes wrong.
Now as far as this service layer is concerned, the RemoveTasks method is a business operation that could be built upon multiple CRUD operations with some repository. So this service layer would itself depend on a repository. It is only this repository that will have to know about EF or whatever you are using to do your data access.
So basically everytime I see someone asking a question about ASP.NET MVC and EF at the same time, for me, those are two completely different questions. ASP.NET MVC should not know anything about EF. EF should be buried away behind an abstraction of a repository.
I realize this question has been inactive for a while, but I had the same problem, and though I would post my solution for anyone else struggling with the nested model not being saved during edit.
[HttpPost]
public ActionResult Edit(ProjectDTO p)
{
if (ModelState.IsValid)
{
// *****MODIFIED CODE HERE********
for (int i = 0; i < p.Tasks.Count; i++)
{
db.Entry(p.Tasks[i]).State = EntityState.Modified;
}
// *************************************
db.Entry(dbProject).State = EntityState.Modified;
db.SaveChanges();
TempData["Success"] = "Modelo Valido";
return RedirectToAction("Index");
}
return View(p);
}
Basically, you want to set State of each nested model to Modified as well as the root model.