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.
Related
Lets say I have 2 models, PeopleClass, CarsClass, also I am using Lazy Loading. Many 'People' can own the same 'Car'. How do I update the relationship to handle that? Ideally as an Interface, but if I have to do it in a Controller that is fine too.
I am aware there is an .add() and .attach() method, but how do you implement that in a Web API 2 Controller w/ EF using async actions and lazy loading (need to know how to create, add, update/modify, and delete)?
I found this as well and curious as to what ppl think as a solution: http://blog.brentmckendrick.com/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/
Code Example:
People Class/Model:
public class People
{
public People()
{
this.Cars = new HashSet<Car>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public string PeopleName { get; set; }
public virtual ICollection<Car> Cars { get; set; }
}
Car Class/Model:
public class Car
{
public Car()
{
this.Peoples = new HashSet<People>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public string CarName { get; set; }
public virtual ICollection<People> Peoples { get; set; }
}
DbContext file:
public DbSet<People> Peoples { get; set; }
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<People>()
.HasMany(c => c.Cars)
.WithMany(t => t.Peoples)
.Map(m => {
m.ToTable(“PeopleCars”);
m.MapLeftKey(“PeopleId”);
m.MapRightKey(“CarId”);
});
}
What do I have to do in my async controller using EF w/ actions? I know there are methods called: clear, add, attach find etc. But I am unclear how to implement in GET, POST, PUT, DELETE controller actions.
UPDATE SINCE INITIAL POST:
I got PUT to work in my Web API 2 Controller using EF + Async Actions. I just need to know how to do a DELETE now.
// PUT: api/People/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> People(int id, People peoples)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != peoples.Id)
{
return BadRequest();
}
var env = db.People
.Include(p => p.Cars)
.Single(q => q.Id == peoples.Id);
db.Entry(env).CurrentValues.SetValues(peoples);
foreach(var automobile in peoples.Cars)
{
db.Cars.Attach(automobile);
env.Cars.Add(automobile);
}
//db.Entry(peoples).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PeopleExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
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 have an editing ViewModel that does some DB work.
Does it make any difference which of these approaches I take?
Pass in the controller's DBContext instance to the ViewModel (example 1 below)
Create a new DBContext instance within the ViewModel itself (example 2 below)
If I pass in the controller's DBContext instance, that means .Dispose() will be called on it at some point (assuming there is the .Dispose() method in the controller). By creating the DBContext instance within the ViewModel method itself, this never happens. Does that matter?
Example 1:
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(ApplicationDbContext db, int id)
{
ID = id;
PropA = db.ModelOneDbSet
.Where(i => i.ID == id)
.Select(i => i.PropA)
.FirstOrDefault();
}
public void SaveChanges(ApplicationDbContext db)
{
var modelOne = db.ModelOneDbSet.FirstOrDefault(i => i.ID == ID);
modelOne.PropA = PropA;
db.SaveChanges();
}
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 viewModel = new EditModelOnePropAViewModel(DB, id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(EditModelOnePropAViewModel postedModel)
{
if (ModelState.IsValid)
{
postedModel.SaveChanges(DB);
return RedirectToAction("index");
}
return View(postedModel);
}
}
Example 2:
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(int id)
{
using (var db = new ApplicationDbContext())
{
ID = id;
PropA = db.ModelTwoDbSet
.Where(i => i.ID == id)
.Select(i => i.PropA)
.FirstOrDefault();
}
}
public void SaveChanges()
{
using (var db = new ApplicationDbContext())
{
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
{
[HttpGet]
public ActionResult Edit(int id)
{
var viewModel = new EditModelTwoPropAViewModel(id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(EditModelTwoPropAViewModel postedModel)
{
if (ModelState.IsValid)
{
postedModel.SaveChanges();
return RedirectToAction("index");
}
return View(postedModel);
}
}
View models should be simple POCO's. Classes with properties which are needed for your view. Nothing else.
public class CustomerViewModel
{
public int Id {set;get;}
public string FirstName {set;get;}
public string LastName {set;get;}
}
And in your controller,
public ActionResult Edit(int id)
{
using(var db=new YourDbContext())
{
var c= db.Customers.FirstOrDefault(s=>s.Id==id);
if(c!=null)
{
var vm= new CustomerViewModel { Id=id,
FirstName=c.FirstName,
LastName=c.LastName
};
return View(vm);
}
}
return View("NotFound");
}
Even better approach is creating an abstraction on your data access layer so that your controller code will not have any idea what data access technology you are using. This will help you to unit test your controller actions.
So basically you will create an abstraction like this
public interface ICustomerRepository
{
CustomerDto GetCustomer(ind id);
}
public class EFCustomerRepository : ICustomerRepository
{
public CustomerDto GetCustomer(int id)
{
using(var db=new YourDbContext())
{
var c= db.Customers.FirstOrDefault(s=>s.Id==id);
if(c!=null)
{
return new CustomerDto { Id=id, FirstName = c.FirstName };
}
}
return null;
}
}
Assuming you have a class called CustomerDto which is a data structure to represent the customer entity and is accessible to both the Web code and the data access code( You can keep in a Common Project and add reference to that in both the projects)
And in your controller, you will be using an implementation of the ICustomerRepository
public CustomerController : Controller
{
ICustomerRepository repo;
public CustomerController(ICustomerRepository repo)
{
this.repo =repo;
}
// You will use this.repo in your action methods now.
}
This will help you to use a fake implementation of ICustomerRepository in your unit tests. You can use mocking libraries like Moq/FakeItEasy to do that.
You can use a dependency injection framework like Unity,StructureMap or Ninject to inject the concrete implementation of the interface in your app.
A ViewModel is MVVM, not MVC. The Model in MVC is the same thing as the Model in MVVM. That pattern is called Model-View-Viewmodel
In MVC the controller is responsible for the page flow. Nothing else. A DbContext has nothing to do with page flow.
The Model is responsible for the business logic. A DbContext has a lot to do with the business logic. If you want to use the database this close to the presentation layer, the Model should create the DbContext.
Your controller example 2 is better than the one proposed by Shyju. Now you are able to test the page flow without being required to use a DbContext. And since the DbContext has nothing to do with page flow, it makes much more sense.
Some nitpicking: A POCO is not an object with public properties and no logic. It is a class that is not dependent on anything framework specific (e.g. a specific interface to implement).
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);
}
}