.Net MVC Entities and ViewModels...Same or Separate? - c#

I am new to asp.net MVC and i have created a project using Entity Framework code first approach. I have put my POCO objects in to a separate class library called Entities.
Now i would like to get some data from my service class, which returns an Entity and output that to the View. here is some very basic code
// in POCO library
public class MyEntity() {
public int Id { get; set; }
public String Name { get; set; }
}
// in service library
public class EntityService() {
public MyEntity Get(int id) {
return new MyEntity() { Id=1, Name="This is my entity name" };
}
}
// controller in asp.net MVC web application
public MyController() : Controller
{
private EntityService _service;
public MyController(EntityService service) {
_service = service;
}
public ActionResult Index()
{
MyEntity entity = _service.Get(1);
return View(entity);
}
}
Now should i push MyEntity to the View, or should i be creating a separate ViewModel? Part of me thinks that creating a separate ViewModel would be best as to keep the separation between the Entities and my View, and also the "logic" to copy the fields i need would be in the controller. But another part of me thinks that creating a ViewModel is just going to be a near copy of the Entities so seems like a waste of time?
I would like to do it correctly, so thought i would ask here. Thanks in advance

Viewmodel is best solution.
You can put attributes(validations and other)
Your viewmodel can contain data from several data entities
As you say you get separation between the Entities and View
General approach get entities in controller and use some mapper library(I recommend emit mapper)
to map entity to your viewmodel

Related

Correctly Mapping viewmodel to entity

I have my entity as:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I have my UserViewModel as
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I am using these as below in my controller:
//This is called from my view via ajax
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
MyRepository.UpdateUser(uv);
}
My UpdateUser in repository class is as below:
public void UpdateUser(User u)
{
var user = GetUserDetails(u.Id);
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
How can I correctly map UserViewModel in my controller to my entity
By using AutoMapper you can do something like:
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<UserViewModel , User>();
});
User u = config.CreateMapper().Map<User>(uv);
MyRepository.UpdateUser(u);
}
Or manually :
public void Save(UserViewModel uv)
{
User u = new User()
{
Id = uv.Id
Name = uv.Name;
Address = uv.Address;
};
MyRepository.UpdateUser(u);
}
Which is not good to do it manually if you change your view-model and your model then you should change your code also, but with Automapper you don't need to change the code.
EDIT1:
This is not good idea to use model-view in repository (DataAccess Core) so it would be better to keep your public void UpdateUser(User u) and don't change it, in outside it is better to pass user to UpdateUser not UserViewModel like what you have done before.
EDIT2:
In my opinion non of answered posts doesn't related to SOC (Separation on concerns) even mine...
1- When I passed UserViewModel I've violated the SOC ....
2- In the other side if I got User in Peresentation layer directly I also violated the SOC.
I think the best way is a middle layer as proxy....
Presentation <----> Proxy <----> Repository.
Your repository deals with objects of type User, so you need to map the values back to an instance of that type and then make the call.
Assuming you have a method to get the user called GetUser:
public void Save(UserViewModel uv)
{
var user = MyRepository.GetUser(uv.Id);
user.Name = uv.Name;
user.Address = uv.Address;
MyRepository.UpdateUser(user);
}
You can then save the changes in your repository class. You can attach the object to make sure there are no issues if the object was created in a different context:
public void UpdateUser(User u)
{
_context.Users.Attach(u);
_context.Entry(u).State = EntityState.Modified;
_context.SaveChanges();
}
You are doing the mapping of property values(view model->enity model) inside your repositories UpdateUser method. So use the view model class (UserViewModel) as the parameter type of that.
public void UpdateUser(UserViewModel u)
{
// Get the entity first
var user = GetUserDetails(u.Id);
// Read the property values of view model object and assign to entity object
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
Now from your Save method ,you can pass the view model object to this method.
This will fix your compile time error (which is your current problem in the question), but be careful about what classes you are using in what layers. If you are too much worried about using a view model class in your data access layer, you can do that in a middle service layer. But then you are getting the entity model in that layer and doing the update there.
Remember, there is no definite answer for that question. Use the approach you think is readable and consistent with the project/ team. Often times, i tend to use the term "Common DTO classes" than "View models" so i can peacefully pass those around to another layer. I keep them in a separate project (called Common DTO) and this will be cross cutting across other projects. That means i will add a reference to this Common DTO project in my Web/UI layer and my data access/service layer and use those as needed.

Populate nested ICollection within viewmodel

Im trying to figure out a nice way to populate a nested collection model. I have the following in my viewmodel.
public class ListViewModel
{
public ICollection<Wish> Wishes { get; set; }
}
The Wish model looks like this:
public class Wish
{
public ICollection<Image> Images { get; set; }
}
Now in my controller I want to populate the ListViewModel with wishes also populate each wish with their corresponding images. What I have so far:
public IActionResult Index()
{
ICollection wishes = _repoWish.GetAllAsync().Result;
ICollection images = _repoImage.GetAllAsync().Result;
var model = new ListViewModel
{
Wishes = wishes
};
return View(model);
}
I know I can make a lot of foreach statements but I want to make use of LINQ to populate each wish with their corresponding images.
**I do have a generic repository class which makes it possible for me to retrieve all images in the same manner as the wishes.
*** Think about the repositories as contexts.
So instead of _repoWish and _repoImage its wishContext and imageContext
I am using ASP.NET Core 2.0 with Entity Framework Core
To load the related entities, you need to explicitly use the Include method call when you query the Wishes collection to eager load the Images property.
Also make sure you await your async calls.
var wishesWithImages = await yourDbContext.Wishes
.Include(g => g.Images)
.ToListAsync();
The variable wishesWithImages will be a collection of Wish objects with Images property loaded. You can now use that to populate your view model.
var vm = new ListViewModel { Wishes = wishesWithImages };
Assuming your Wish entity has a collection property of type Images
public class Wish
{
public int Id { set;get;}
public ICollection<Image> Images { set;get;}
}
public class Image
{
public int Id { set;get;}
public int WishId { set;get;}
public virtual Image Image{ set;get;}
}
As of today, Entity framework core is a light weight version of EF6 and doesn't automatically inherit all the features from EF 6. Lazy loading is not implemented yet in EF core.

Entity framework update with business model

I'm trying to implement a business layer into my application. The reason for this is that my (legacy) database is very complex for the use cases we have. So what I'm trying to do is the following
Retrieve datamodel from the DbContext
Transform the datamodel to a business model
Pass it on to my controller to be used.
This works perfectly for retrieving objects, but updating them keeps giving me problems. Let me first give you (some of) my code (somewhat simplified):
using System;
/* The datamodel*/
public class DataModel
{
[Key]
public int Id { get; set; }
public double InterestRate { get; set; }
}
/*The business model */
public class BusinessModel
{
public int Id { get; set; }
public double InterestRate { get; set; }
public bool IsHighInterest()
{
return InterestRate > 10;
}
}
public class MyDbContext : DbContext
{
public MyDbContext() : base("connectionstring")
{
}
public DbSet<DataModel> DataModels { get; set; }
}
/* In reality I've got a repository here with a unit-of-work object instead of accessing the DbContext directly. */
public class BusinessLayer
{
public BusinessModel Get(int id)
{
using (var context = new MyDbContext())
{
var dataModel = context.DataModels.FirstOrDefault(x => x.Id == id);
BusinessModel = Transform(dataModel); //Do a transformation here
}
}
public void Update(BusinessModel model)
{
using (var context = new MyDbContext())
{
var dataModel = TransformBack(dataModel);
context.Entry<dataModel>.State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
}
}
}
Obviously this isn't going to work, because entity framework cannot track the changes of the datamodel anymore. I'm looking for a design pattern where I can do these sort of things. Hope anyone of you can help me with this. In reality the datamodel is way more complex and the BusinessModel simplyfies it a lot, so just using the DataModel isn't really an option either.
That's essentially the ViewModel pattern. While you can certainly add a repository keep in mind entity framework already implements Unit of Work, but I digress. Many of us do something very similar to your code using POCO entity models to interact with the database and then transforming those to ViewModels, DTOs, or as you call them Business Models. Automapper is great for this.
So in my update code I do something like this (MVC):
if (ModelState.IsValid)
{
var entity = context.Entities.First(e => e.Id == viewmodel.Id); // fetch the entity
Mapper.Map(viewmodel, entity); // Use automapper to replace changed data
context.SaveChanges();
}
If you have access to Pluralsight here is a good video on the topic: https://wildermuth.com/2015/07/22/Mapping_Between_Entities_and_View_Models

MVC Models, Entity Framework and binding dropdowns best practice

Extremely basic question about best practice in MVC when binding drop down lists.
This inst a real world example but a basic example that explains my question:
Take the following model
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public int ID {get;set;}
public string Make {get; set;{}
public string Model {get; set;}
}
Then assume that these get flattened into a view model:
public class IndexViewModel
{
public int PersonID;
public string Name;
public int SelectedCarID;
public SelectList<Cars> Cars;
}
In my constructor I have an index method:
[HttpGet]
public ActionResult Index()
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == 1);
var vm = new IndexViewModel(){
Name = person.Name,
SelectedCarID = person.Car.ID,
};
return View(vm);
}
Now, Assume that the person that is returned from the context has NO car record when the page first loads.
The view has a line :
#Html.DropDownListFor(m=>m.SelectedCarID, Model.Cars)
When the form is submitted it is picked up by the action :
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == model.PersonID);
var car = _ctx.Cars.FirstOrDefault(x=>x.ID == model.SelectedCarID);
person.Name = model.name;
person.Car = car;
_ctx.SaveChanges();
}
Now that is the way I have done it for ages, I started using EF back when LINQ to SQL was taking off and I have always created my models like that as I was under the imperssion that it was the recommended way.
After a discussion with another developer today I am not sure if this is stil the best way? It has always irked me that I need to do a lookup against the database to get the Car record out just so that I can update the record.
My questions are:
What is the best way to achive what I have described above?
Is the above correct?
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys in the model)?
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net and furious googling seems to indicate that you cant)?
This really ins't the place for Best Practices sort of questions (that would probably be Code Review).
However some notes initially.
Keep your domain objects in the domain
The first thing that stood out to me was the SelectList<Car> property. Where it appears as your Car entity is actually a domain entity. A domain entity should not be exposed to the UI for multiple reasons.
Entity framework proxy classes monitor changes to properties that can be inadvertently saved.
Re-factoring of domain entities requires re-factoring of UI Code.
Domain entities typically contact properties you would not like exposed or otherwise.
Serialization of the Domain Entities will also serialize navigation properties and (mostly likely) cause circular reference errors.
Your question
Given the above you know have your answer, you will have to do a lookup for an entity based on your criteria from your View Model. Your view model should not have any understanding of the data context. It is in fact a View Model not a Domain Entity. By telling your View Model to interact with your data contexts you have no separation between your Data Access layers and your Presentation layers.
Don't make your controller manage data access as well
Your controller has a lot of work to-do, managing data access shouldn't be one of them. Doing so you have infarct coupled your Presentation Layer with your Data Access layer. Now as this is an example its easy to forgive however re factoring your data access layer will have direct consequences to your Presentation layer. I would suggest places a Services layer in between your data access layer and the presentation layer.
Ok All this in practice how does it look.
This is my personal approach here but will look at decoupling the data layer from the Presentation layer, no domain objects passed to the Presentation layer and using services to broker the transactions to the data layer.
Sample Service
This service is responsible for handling the interaction between the data layer and presentation (note mock repositories).
public class SampleService
{
public SampleService()
{
_dbContext = new SampleContext();
}
readonly SampleContext _dbContext;
public virtual Person GetPersonById(int id)
{
return _dbContext.Persons.FirstOrDefault(x => x.ID == id);
}
public virtual Car GetCarById(int id)
{
return _dbContext.Cars.FirstOrDefault(x => x.ID == id);
}
public virtual IList<Car> GetAllCars()
{
return _dbContext.Cars.ToList();
}
public virtual void UpdatePerson(Person person)
{
if (person == null)
throw new ArgumentNullException(nameof(person));
_dbContext.SaveChanges();
}
public virtual void UpdateCar(Car car)
{
if (car == null)
throw new ArgumentNullException(nameof(car));
_dbContext.SaveChanges();
}
}
Does this appear to be more work, absolutely does but better to implement your service now than have to do it later. What we also achieve is one location to update if we wish to change any queries or interaction methods.
IndexViewModel
As we have agreed we are no longer passing the car object to the SelectList. Infact we only need to construct a basic IList<SelectListItem> and populate this from our controller.
public class IndexViewModel
{
public IndexViewModel()
{
AvailableCars = new List<SelectListItem>();
}
public int PersonID { get; set; }
public string Name { get; set; }
public int SelectedCarId { get; set; }
public IList<SelectListItem> AvailableCars { get; set; }
}
Controller
Now our controller is pretty simple to wire up.
[HttpGet]
public ActionResult Index()
{
var person = sampleService.GetPersonById(1);
var model = new IndexViewModel
{
Name = person.Name,
PersonID = person.ID,
SelectedCarId = person.Car.ID
};
model.AvailableCars = sampleService.GetAllCars()
.Select(car => new SelectListItem
{
Text = $"{car.Make} - {car.Model}",
Value = car.ID.ToString()
})
.OrderBy(sli => sli.Text)
.ToList();
return View(model);
}
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = sampleService.GetPersonById(model.PersonID);
if(person != null)
{
person.Name = model.Name;
//only update the person car if required.
if(person.Car == null || person.Car.ID != model.SelectedCarId)
{
var car = sampleService.GetCarById(model.SelectedCarId);
if (car != null)
person.Car = car;
}
sampleService.UpdatePerson(person);
}
return View();
}
View Drop Down list
#Html.DropDownListFor(m => m.SelectedCarId, Model.AvailableCars)
If you compare your code to my code I have actually added more code to the solution, however removes a lot of coupling and dependencies that could become hard to manage in larger applications.
Now back to your original questions.
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys
in the model)?
No, you should be doing a lookup for that entity (car) outside of the Model. The model should not be aware of the data context.
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
NO, your model should not be aware of the data context, therefore you do not need to define foreign keys (in a data context sense) leave that to your controller and services.
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net
and furious googling seems to indicate that you cant)?
You could, but you don't want to. Our Car entity is a domain entity and we dont want to expose the entity to the UI (Presentation). Instead we will use other classes to expose what properties are bound. In this example a simple IList<SelectListItem> was more than sufficient.

Use AutoMapper to map two VM to one Entity object

I'm using AutoMapper to map a lot of Entity models to View Model that I use in my controllers and views (.Net MVC)
There is a lot of relations in the DB and so our VM have a lot of childs (who have childs, and so and so)
public class InvoiceVMFull : VMBase
{
public int Id { get; set; }
public InvoiceType InvoiceType { get; set; }
public string Reference { get; set; }
//.... shortened code for readability
// list all entity fields
public List<string> InvoiceMainAddress { get; set; }
public List<string> InvoiceDlvAddress { get; set; }
}
It works just fine, but is very slow and always load from the DB all relations whereas I usually need only a few datas...
So I created some light VM that I want to use for the majority of our pages.
public class InvoiceVMLite : VMBase
{
public int Id { get; set; }
public string Reference { get; set; }
//.... shortened code for readability
// list only some of the entity fields (most used)
public StoredFileVM InvoiceFile { get; set; }
}
The problem is I can't find how :
to map one Entity object to the two VMs and how to choose the right one (to load from DB) using the context (the page or event called)
to map two VMs to one entity and save (on the DB) only the fields that are present in the VM used and don't erase the absent ones
I tried to create the mapping both VM :
Mapper.CreateMap<Invoice, InvoiceVMLite>();
Mapper.CreateMap<Invoice, InvoiceVMFull>();
But when I try to call the mapping for Lite, it doesn't exist (have been overridden by Full) :
Mapper.Map(invoice, InvoiceEntity, InvoiceVMLite)
Correct Use of Map function
It looks like you are calling map incorrectly. Try these instead
var vmLite = Mapper.Map<Invoice, InvoiceVMLite>(invoice);
var vmFull = Mapper.Map<Invoice, InvoiceVMFull>(invoice);
var vmLite = Mapper.Map(invoice); // would work if it were not ambiguous what the destination was based on the input.
Entity to two view models
You would usually create two mappings, one for each view model from the one entity. I'd suggest the cleanest is to have two separate views (separate Actions in a controller) for each view model. This may involve a quick redirect after you've decided on context which one to use.
View models to entity
Automapper is not meant for mapping from view models to Entities for many reasons, including the challenge you'd face. Instead you would pass specific parameters. The author of Automapper, Jimmy Bogard, wrote a good article on why this is the case.
I couldnt manage to do that with AutoMapper, and so I created my own convert methods (Entity <=> VM) with a lot of reflexivity, and with specific cases handled in each of the VM classes.
Now I can easily get a full or lite VM from an Entity, and also specify the depth in relation I want to go. So it's A LOT faster and more adaptable than AutoMapper
And I can save a VM to an entity (only saving modified fields if I want) that I create or that i got from base. So it's A LOT faster and adaptable than AutoMapper
In conclusion : Don't use autoMapper, it seem easy but create so many performance issues that it isn't worth it

Categories