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).
Related
I am building a simple search, sort, page feature. I have attached the code below.
Below are the usecases:
My goal is to pass the "current filters" via each request to persist them particularly while sorting and paging.
Instead of polluting my action method with many (if not too many) parameters, I am thinking to use a generic type parameter that holds the current filters.
I need a custom model binder that can be able to achieve this.
Could someone please post an example implementation?
PS: I am also exploring alternatives as opposed to passing back and forth the complex objects. But i would need to take this route as a last resort and i could not find a good example of custom model binding generic type parameters. Any pointers to such examples can also help. Thanks!.
public async Task<IActionResult> Index(SearchSortPage<ProductSearchParamsVm> currentFilters, string sortField, int? page)
{
var currentSort = currentFilters.Sort;
// pass the current sort and sortField to determine the new sort & direction
currentFilters.Sort = SortUtility.DetermineSortAndDirection(sortField, currentSort);
currentFilters.Page = page ?? 1;
ViewData["CurrentFilters"] = currentFilters;
var bm = await ProductsProcessor.GetPaginatedAsync(currentFilters);
var vm = AutoMapper.Map<PaginatedResult<ProductBm>, PaginatedResult<ProductVm>>(bm);
return View(vm);
}
public class SearchSortPage<T> where T : class
{
public T Search { get; set; }
public Sort Sort { get; set; }
public Nullable<int> Page { get; set; }
}
public class Sort
{
public string Field { get; set; }
public string Direction { get; set; }
}
public class ProductSearchParamsVm
{
public string ProductTitle { get; set; }
public string ProductCategory { get; set; }
public Nullable<DateTime> DateSent { get; set; }
}
First create the Model Binder which should be implementing the interface IModelBinder
SearchSortPageModelBinder.cs
public class SearchSortPageModelBinder<T> : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
SearchSortPage<T> ssp = new SearchSortPage<T>();
//TODO: Setup the SearchSortPage<T> model
bindingContext.Result = ModelBindingResult.Success(ssp);
return TaskCache.CompletedTask;
}
}
And then create the Model Binder Provider which should be implementing the interface IModelBinderProvider
SearchSortPageModelBinderProvider.cs
public class SearchSortPageModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType.GetTypeInfo().IsGenericType &&
context.Metadata.ModelType.GetGenericTypeDefinition() == typeof(SearchSortPage<>))
{
Type[] types = context.Metadata.ModelType.GetGenericArguments();
Type o = typeof(SearchSortPageModelBinder<>).MakeGenericType(types);
return (IModelBinder)Activator.CreateInstance(o);
}
return null;
}
}
And the last thing is register the Model Binder Provider, it should be done in your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
.
.
services.AddMvc(options=>
{
options.ModelBinderProviders.Insert(0, new SearchSortPageModelBinderProvider());
});
.
.
}
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'm working on a basic MVC5/EF6 application and am running into the following error:
No parameterless constructor defined for this object.
This happens when I use the default Create Action and View that are scaffolded by Visual Studio 2013 when you create a new Controller. I have not adjusted anything within those generated files (TestItemController, Views/TestItem/Create.cshtml). My entities on which the controller is scaffolded look like this:
public class TestItem
{
private Category _category;
// Primary key
public int TestItemId { get; set; }
public int CategoryId { get; set; }
public string TestColumn { get; set; }
public virtual Category Category {
get { return _category; }
set { _category = value; }
}
protected TestItem()
{
}
public TestItem(Category category)
{
_category = category;
}
}
public class Category
{
private ICollection<TestItem> _testItems;
// Primary key
public int CategoryId { get; set; }
public string Description { get; set; }
public virtual ICollection<TestItem> TestItems
{
get { return _faqs; }
set { _faqs = value; }
}
public Category()
{
_testItems = new List<TestItem>();
}
}
I'm guessing this is due to the TestItem class having the constructor taking in a Category object, which is there to keep the domain model anemic. A TestItem cannot be created without a Category. But as far as I know the protected parameterless constructor should be used by EF in this exact case when lazy loading etc.
What's going on here, or what am I doing wrong?
UPDATE:
The controller looks like this (trimmed):
public class TestItemsController : Controller
{
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "TestItemId,OtherColumns")] TestItem testItem)
{
if (ModelState.IsValid)
{
db.TestItems.Add(testItem);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(testItem);
}
}
Sure, EF can use protected constructors, but scaffolding creates action methods for creating a new item. These action methods require a parameterless public constructor.
You can find some details of these create methods here.
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.