View using DTO - ASP.NET MVC - c#

I'm fairly new to MVC and I've been trying to create a view using a DTO as the Model Class, but it seems to be using the Data Context class I use for my Models, even though I am clearing the selection when I am creating the view.
This issue seems to be causing a NullReferenceException which is caused by the following exception being thrown and the view not having any returned to it.
ITSSkillsDatabase.Models.PersonSkillSetsDTO: : EntityType 'PersonSkillSetsDTO' has no key defined. Define the key for this EntityType.
PersonSkillSets: EntityType: EntitySet 'PersonSkillSets' is based on type 'PersonSkillSetsDTO' that has no keys defined.
My DTO:
namespace ITSSkillsDatabase.Models
{
public class PersonSkillSetsDTO
{
public int IDSkillset { get; set; }
public int IDCategory { get; set; }
public string Product { get; set; }
public string P_Version { get; set; }
public string Notes { get; set; }
public int PersonSkillsID { get; set; }
public int IDPerson { get; set; }
public int Score { get; set; }
public DateTime ScoreDate { get; set; }
public int TargetScore { get; set; }
public DateTime TargetDate { get; set; }
public DateTime RefresherDate { get; set; }
}
}
Controller method:
public ActionResult SkillSets(int? id)
{
try
{
if (id == null)
{
return HttpNotFound();
}
var viewModel = (from a in db.SkillSets
join c in db.PersonSkills on a.IDSkillset equals c.IDSkillSet
where c.IDPerson == id
select new Models.PersonSkillSetsDTO
{
IDSkillset = a.IDSkillset,
IDCategory = a.IDCategory,
Product = a.Product,
P_Version = a.P_Version,
Notes = a.Notes,
PersonSkillsID = c.PersonSkillsID,
IDPerson = c.IDPerson,
Score = c.Score,
ScoreDate = c.ScoreDate,
TargetScore = c.TargetScore,
TargetDate = c.TargetDate,
RefresherDate = c.RefresherDate
}).ToList();
return View(viewModel);
}
catch
{
return View(); //this is where the NullReferenceException is thrown
}
}
These are the settings when I'm creating the view:
I realise I can get rid of the NullReferenceException by checking for null values, but I don't have any idea how to fix the issue with my DTO.

I am going to try to explain using a ViewModel/DTO to create a Form and POST back.
ViewModels are outside of the Database Context, So if you are using a ViewModel you have to Map your data from ViewModel to Model and Model to ViewModel.
So if you are reading from Database
Create DBContext
read data you want to read
Map to a ViewModel
Pass ViewModel to the View or API
If you are writing to the database
POST ViewMdoel from View to Controller (You can use Ajax)
Create DBContext
Map from ViewModel to Model
Save Model to Database
let's say you have a DTO,
public class CountryDTO
{
public int CountryId { get; set; }
[Display(Name = "Country Name")]
[Required(ErrorMessage = "This field is required")]
public string CountryName { get; set; }
[Display(Name = "Latitude")]
[Required(ErrorMessage = "This field is required")]
public double CentralLat { get; set; }
[Display(Name = "Longitude")]
[Required(ErrorMessage = "This field is required")]
public double CentralLang { get; set; }
[Display(Name = "GMT Offset")]
[Required(ErrorMessage = "This field is required")]
public double GMTOffSet { get; set; }
[Display(Name = "Currency")]
[Required(ErrorMessage = "This field is required")]
public string Currency { get; set; }
}
Create a controller i.e. CountryController and you have a Views Folder Country, Right Click Country Folder Add --> View, Name it CreateCountry and Select Model to be CountryDTO
You can't select DataContext here , because DTO is not part of the Context
This will create your view with Fields from the DTO.
Now in your Controller you need 2 Actions
GET method to return the View
POST method to POST back the form
public ActionResult CreateCountry()
{
return PartialView(new CountryDTO());
}
Now in the POST method you will Pass the DTO, Let's assume you have a Country table in your Database, you will have to create a New Country type Object and add to the Context
[HttpPost]
public ActionResult CreateCountry(CountryDTO model)
{
if (ModelState.IsValid)
{
// Model State is Valid
// here you will create Context
using (var dbContext = new DATBASE_CONTEXT())
{
var newCountry = new Country() // Country is a Model from Database
{
CountryName = model.CountryName,
CentralLat = model.CentralLat,
// Map All Properties from View Model to Model
};
// Add the New Country to the Countries
dbContext.Countries.Add(newCountry);
// Save Database Changes
dbContext.SaveChanges();
}
}
return PartialView(model);
}
IF you want to display this Country:
public ActionResult CountryDetails(int id)
{
var model = new CountryDTO();
using (var dbContext = new DATABASE_CONTEXT())
{
var country = dbContext.Country.First(s => s.CountryId == id);
model.CountryName = country.CountryName;
// Same for other Properties
// You can use AutoMapper Library to Map from Model to DTO/ViewModel
}
return View(model);
}

try
{
// <...>
return View(viewModel);
}
catch
{
return View(); //this is where the NullReferenceException is thrown
}
You get NullReferenceException because your view expects the model and doesn't perform null checks in Razor.
To verify this, you can create a dummy model and pass it into your return View(/* model */); call.

As I understood the exception trouble is not with DbContext but with lame model: "No key defined". I think checking data-annotations might help.

I created a new View Model which contains everything I need from the two models.
I then set the values for the View Model within the controller.
Which gave me the result I wanted.

Related

Don't save null image data if image was not re-uploaded Asp.net mvc

I have been following the SportsStore example project in Apress Pro ASP.NET MVC 3 Framework book and trying to apply the concepts to my application. One area that is bugging me is that in the sample, I can add an image to a product and it gets saved to the database, but if I edit any given product, without uploading a new image for it, the image data is cleared out. I want to be able to edit a product, but if the image data returned from the HTTP post is null, that I want Entity Framework to keep the exisiting image data (and content type). How can I command EF to not update this image field with null if a new image isn't uploaded?
[HttpPost]
public ActionResult Edit(int id, HttpPostedFileBase image1, FormCollection collection)
{
using (ISession session = Database.OpenSession())
{
try
{
DoctorsModel db = new DoctorsModel();
db.Id_d = id;
db.D_city = collection["D_city"].ToString();
db.D_egn = collection["D_egn"].ToString();
db.D_email = collection["D_email"].ToString();
db.D_family_name = collection["D_family_name"].ToString();
db.D_first_name = collection["D_first_name"].ToString();
db.D_gender = collection["D_gender"].ToString();
db.D_mid_name = collection["D_mid_name"].ToString();
db.D_phone = collection["D_phone"].ToString();
db.D_specialty = collection["D_specialty"].ToString();
db.D_room = collection["D_room"].ToString();
db.D_floor = collection["D_floor"].ToString();
if (image1 != null)
{
db.D_picture = new byte[image1.ContentLength];
image1.InputStream.Read(db.D_picture, 0, image1.ContentLength);
}
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(db);
transaction.Commit();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
Model
public class DoctorsModel
{
public virtual int Id_d { get; set; }
[Display (Name ="Име: ")]
public virtual string D_first_name { get; set; }
[Display(Name = "Презиме: ")]
public virtual string D_mid_name { get; set; }
[Display(Name = "Фамилия: ")]
public virtual string D_family_name { get; set; }
[Display(Name = "Специалност: ")]
public virtual string D_specialty { get; set; }
[Display(Name = "Пол: ")]
public virtual string D_gender { get; set; }
[Display(Name = "Тел.номер: ")]
public virtual string D_phone { get; set; }
[Display(Name = "Email: ")]
public virtual string D_email { get; set; }
[Display(Name = "ЕГН: ")]
public virtual string D_egn { get; set; }
[Display(Name = "Град: ")]
public virtual string D_city { get; set; }
[Display(Name = "Снимка: ")]
public virtual byte[] D_picture { get; set; }
[StringLength(5)]
public virtual string D_rating { get; set; }
public virtual string D_content { get; set; }
[Display(Name = "Стая:" )]
public virtual string D_room { get; set; }
[Display(Name = "Етаж: ")]
public virtual string D_floor { get; set; }
}
The issue is going to be trying to use something like "SaveOrUpdate" if that is resolving to EF's AddOrUpdate which is intended for migrations & initial data population scenarios, or boiling down to a DbContext.Update(doctor) then your data gets overwritten with #null. You're giving EF an entity to insert or update without any consideration of data that is already there. EF will merely do an Exists check then issue an INSERT or effectively an UPDATE * based on ALL fields populated as they are in the provided entity.
Strictly for a RESTful API, you should be distinguishing between "Add" and "Update" by issuing a POST or PUT respectively. On POST you take the data, create a new model, add it to the DB Context and SaveChanges. For PUT you fetch the current model, update the appropriate values, and SaveChanges. A PUT operation should never insert the row if not found.
Your method looks like it's meant to only update an existing record:
[HttpPut]
public ActionResult Edit(int id, HttpPostedFileBase image1, FormCollection collection)
{
using (var context = new AppDbContext())
{
try
{
var doctor = context.Doctors.Single(x => x.Id == id); // Will throw if Dr not found.
doctor.D_city = collection["D_city"].ToString();
doctor.D_egn = collection["D_egn"].ToString();
doctor.D_email = collection["D_email"].ToString();
doctor.D_family_name = collection["D_family_name"].ToString();
doctor.D_first_name = collection["D_first_name"].ToString();
doctor.D_gender = collection["D_gender"].ToString();
doctor.D_mid_name = collection["D_mid_name"].ToString();
doctor.D_phone = collection["D_phone"].ToString();
doctor.D_specialty = collection["D_specialty"].ToString();
doctor.D_room = collection["D_room"].ToString();
doctor.D_floor = collection["D_floor"].ToString();
if (image1 != null)
{
doctor.D_picture = new byte[image1.ContentLength];
image1.InputStream.Read(doctor.D_picture, 0, image1.ContentLength);
}
context.SaveChanges();
return RedirectToAction("Index");
}
catch
{ // do more than this. :)
return View();
}
}
}
Ideally the DbContext or a UnitOfWork is injected. It's hard to know what you Session implementation does or interacts with the DbContext. I generally don't recommend trying to abstract away the EF DbContext as it really cripples the capability it offers for working with data entities efficiently. For update operations, fetch the existing entity and then copy the values across. The DbContext by default uses tracked entities so as you actually change values, those will be included into an UPDATE statement. Anything that isn't explicitly changed will be left as-is and doesn't get appended into the UPDATE query.
For potentially large data that is used infrequently like Images, I would recommend considering isolating those to a related table/entity. I.e.
public class DoctorImage
{
[Key]
public int Id { get; set; } // == Doctor.Id
public byte[] Image { get; set; }
}
Where the relationship of Doctor to DoctorImage is set up as HasOptional.WithRequired / HasOne.WithOne. The benefit is that loading the image when dealing with Doctors is entirely optional. Unless you use projection explicitly everywhere, loading an existing Doctor entity would cause EF to fetch the Image every time when you probably don't need it. By separating it, you only fetch it when explicitly eager/lazy loaded. Listing Doctors for instance can easily be done without the overhead of loading all of their image data.

Passing value from one model to another in a ViewModel

I am building my first MVC website and I am struggling to pass a value from one model to another in a ViewModel. The two classes are Music and MusicGenre. In Music, there is a field called 'GenreId' which is an int and is related to the 'Id' field in MusicGenres. I want to return the genre name relating to the Id that's passed over.
Here's what I've got so far:
MusicViewModel vmMusic = new MusicViewModel
{
Music = _context.Music.SingleOrDefault(c => c.Id == id),
MusicGenres = _context.MusicGenres.Where(gi => gi.Id == xxx).ToList()
};
return View(vmMusic);
This all renders nicely minus displaying the right genre. (I replaced xxx with Id but that just uses the id field from Music, not the GenreId field)
So xxx is where I want to pass 'GenreId' but I don't know how. Any help?
Thanks
To expand on my initial comment to the question, I'd just do something like:
var music = _context.Music.SingleOrDefault(c => c.Id == id);
var musicGenres = _context.MusicGenres.Where(gi => gi.Id == music.GenreId).ToList(); // whatever the genre id property is called.
MusicViewModel vmMusic = new MusicViewModel
{
Music = music,
MusicGenres = musicGenre
};
return View(vmMusic);
You should separate your view models from your persistence models. View model is designed to display what you want to display to the users. Persistence model might reflect your data storage structures.
Also from your example it looks like you might already have been using ORM like EntityFramework. EntityFramework provides features like navigation properties and lazy loading.
Either way, I strongly disagree accepted answer's approach, which sends the music and its genre persistence models to the view, when you only need to get the genre name.
Rather, I will approach it this way:
Persistence Models
You might have one-to-may relationship between a genre and an album in your persistence storage:
public class Album
{
public int AlbumId { get; set; }
public string Title { get; set; }
public double Price { get; set; }
public int GenreId { get; set; }
public Genre Genre { get; set; }
}
public class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Album> Albums { get; set; }
}
But obviously you don't want/need to reveal everything you have in your persistence storage to the user. Hence you create view models and only contain what you want to display.
View Models
Let's say you have a view to display information about the selected album. You might create a view model like this:
public class AlbumViewModel
{
public int AlbumId { get; set; }
public string AlbumName { get; set; }
public string Genre { get; set; }
}
Then in the album controller, you can build the view model by using any ORM, lazy loading, etc:
Controller
If you happen to use EntityFramework and have lazy loading enable, you can fetch genre name via its navigation property:
public ActionResult Details(int id)
{
var album = _dbContext.Albums.SingleOrDefault(x => x.AlbumId == id);
if (album == null)
{
return HttpNotFound();
}
var vm = new AlbumViewModel
{
AlbumId = album.AlbumId,
AlbumName = album.Title,
// You can get the genre name via Navigation property, and/or lazy
// loading
Genre = album.Genre.Name
};
return View(vm);
}
Now in a more advanced architecture, the read and write is separated, which is
referred as CQRS. For all the reads (i.e., displaying information to the user), you can build your view model with data directly from executing plain SQL statement.
CQRS with Dapper
using Dapper;
using System.Data;
...
public class AlbumController : Controller
{
private readonly IDbConnection _dbConnection;
public AlbumController(IDbConnection dbConnection)
{
_dbConnection = dbConnection;
}
public ActionResult Details(int id)
{
const string sql = #"
SELECT a.AlbumId, a.Title AS [AlbumName], g.Name AS [Genre]
FROM [Album] a
JOIN [Genre] g ON a.GenreId = g.GenreId
WHERE a.AlbumId = #albumId;
";
var vm = _dbConnection.QuerySingleOrDefault<AlbumViewModel>(sql, new {
albumId = id
});
if (vm == null)
{
return HttpNotFound();
}
return View(vm);
}
}

How to get primary key in Entity Framework for a new entry and set FK Relationship

In my ASP.NET MVC 5 application, I have situation where I need to get the primary key that got generated for a particular entry in database. Also, I need to get that id and set a foreign key relationship in database. I have tried below attempt and it seems to be working but I had to call _context.SaveChanges() TWICE.
UI Behaviour: the user has a dropdown with a list of companies in referral form. When a company is not found, user will select "Others" in the dropdown. And it will show a textbox, where user will enter the company name. So I need to add this company to the database and link it to the referral row.
Relationship between tables Referral and Company:
dBModelBuilder.Entity<Company>()
.HasMany(u => u.Referrals)
.WithRequired(u => u.Company)
.HasForeignKey(u => u.CompanyId);
Model classes:
public class Company
{
[Key]
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public ICollection<Referral> Referrals { get; set; }
public ICollection<CoverLetter> CoverLetters { get; set; }
}
public class Referral
{
[Key]
public int ReferralId { get; set; }
[Display(Name = "REFERRALS")]
public string ReferralName { get; set; }
public int? CoverLetterId { get; set; }
public virtual CoverLetter CoverLetter { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
ViewModel:
public class Referral
{
[Key]
public int ReferralId { get; set; }
[Display(Name = "REFERRALS")]
public string ReferralName { get; set; }
public int? CoverLetterId { get; set; }
public virtual CoverLetter CoverLetter { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
My attempt inside the controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ReferralViewModel viewModel)
{
var candidateId = User.Identity.GetUserId();
var referral = new Referral
{
ReferralName = viewModel.ReferralName,
};
if (viewModel.CompanyId.HasValue)
// if it is option 4 then we need to add it to the company table
{
if (!string.IsNullOrEmpty(viewModel.TempCompany))
{
var f = new Company
{
CompanyName = viewModel.TempCompany
};
_context.Companies.Add(f);
// ********FIRST CALL
_context.SaveChanges();
_context.Referrals.Add(referral);
referral.CompanyId = f.CompanyId;
// **********SECOND CALL
_context.SaveChanges(); // SECOND CALL ------
}
else
{
referral.CompanyId = viewModel.CompanyId.Value;
_context.Referrals.Add(referral);
_context.SaveChanges();
}
}
return RedirectToAction("ReferralCenter");
}
Question: can I do these steps in one call to _context.SaveChanges(); ?
EDIT
With this code, I get a NullReferenceException:
if (!string.IsNullOrEmpty(viewModel.TempCompany))
{
var f = new Company
{
CompanyName = viewModel.TempCompany
};
f.Referrals.Add(referral); // Referrals is NULL(EXCEPTION)
_context.Companies.Add(f);
// _context.SaveChanges();
//_context.Referrals.Add(referral);
// referral.CompanyId = f.CompanyId;
_context.SaveChanges();
}
It is my understanding that FK constraints are managed within EF, so long as they are configured in the DB.
If in your EF model the Referral is a child of Company then there should be a referrals collection accessible from the Company object instance.
UPDATE
I just built a simple sample MVC Proj, 2x SQL tables (Referrals and Company FK on CompanyId) 4 x Companies, 4th being OTHER, built an EF model with Reverse POCO for speed, added a Controller with scaffolding and views and modified my controller to the below. (I just insert the date time rather than tempCompanyName. The syntax generated here is more like that in the answer by #ashin, but it works for me.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ReferralId,CompanyId")] Referral referral)
{
if (ModelState.IsValid)
{
if (referral.CompanyId == 4)
{
var f = new Company() { Name = DateTime.Now.ToString() };
referral.Company = f;
}
db.Referrals.Add(referral);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CompanyId = new SelectList(db.Companies, "CompanyId", "Name", referral.CompanyId);
return View(referral);
}
My Company model does instanciate a new Referrals Collection
public Company()
{
Referrals = new System.Collections.Generic.List<Referral>();
}
If you have the navigation property defined, you could just save both the Company and Referral in one shot as follows:
if (!string.IsNullOrEmpty(viewModel.TempCompany))
{
var f = new Company
{
CompanyName = viewModel.TempCompany
};
referral.Company = f;
_context.SaveChanges();
}
EF will take care of setting the FK.
EDIT
Have updated the code to include _context.Referrals.Add(referral) as mentioned in the comments:
if (!string.IsNullOrEmpty(viewModel.TempCompany))
{
var f = new Company
{
CompanyName = viewModel.TempCompany
};
referral.Company = f;
_context.Referrals.Add(referral);
_context.SaveChanges();
}

Mapping of ViewModel with SelectList in MVC3

Hi I'm struggling to find the correct approach on SO for what I am currently doing, so I thought I would ask.
Here is my simplified code:
The entities are nested types based on using them with EF CodeFirst and the ViewModel is being mapped with AutoMapper.
When posting the form the ModelState is not valid due to the dropdownlist being mapped to model.CourseId and displaying my Course data.. i.e. CourseId = 2, CourseList = Null, but also having the [Required] attribute, really only CourseId is required but I also needed a relevant error message.
I then thought that in my Create GET & POST actions the view should probably just have the CourseId but I still need to display it as a dropdown and populate it and I was unsure as how to do that correctly.
I may also not be understanding how this should be used correctly and if I even need CourseName, i.e. since the Course already exists in the database I just want a foreign key to it, which will still let me show the selected course.
I'm also planning to break out all this mapping and data setting in my controller actions into a separate service layer but at the moment its a small prototype.
// Entities
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public Course Course { get; set; }
}
public class Course {
public int Id { get; set; }
public string Name { get; set; }
}
// View Model
public class RecipeCreateViewModel {
// Recipe properties
public int Id { get; set; }
public string Name { get; set; }
// Course properties, as primitives via AutoMapper
public int CourseId { get; set; }
public string CourseName { get; set; }
// For a drop down list of courses
[Required(ErrorMessage = "Please select a Course.")]
public SelectList CourseList { get; set; }
}
// Part of my View
#model EatRateShare.WebUI.ViewModels.RecipeCreateViewModel
...
<div class="editor-label">
Course
</div>
<div class="editor-field">
#* The first param for DropDownListFor will make sure the relevant property is selected *#
#Html.DropDownListFor(model => model.CourseId, Model.CourseList, "Choose...")
#Html.ValidationMessageFor(model => model.CourseId)
</div>
...
// Controller actions
public ActionResult Create() {
// map the Recipe to its View Model
var recipeCreateViewModel = Mapper.Map<Recipe, RecipeCreateViewModel>(new Recipe());
recipeCreateViewModel.CourseList = new SelectList(courseRepository.All, "Id", "Name");
return View(recipeCreateViewModel);
}
[HttpPost]
public ActionResult Create(RecipeCreateViewModel recipe) {
if (ModelState.IsValid) {
var recipeEntity = Mapper.Map<RecipeCreateViewModel, Recipe>(recipe);
recipeRepository.InsertOrUpdate(recipeEntity);
recipeRepository.Save();
return RedirectToAction("Index");
} else {
recipe.CourseList = new SelectList(courseRepository.All, "Id", "Name");
return View(recipe);
}
}
I fixed my particular problem just by doing the below.
[Required(ErrorMessage = "Please select a Course.")]
public int CourseId { get; set; }
// public string CourseName { get; set; }
public SelectList CourseList { get; set; }
The view will use the DropDownListFor helper to map the drop down to my CourseId and that's all I really needed.
On to another problem now with AutoMapper and why it is not mapping back to the Recipe entity in the POST Create action.
I probably first need to find a way to store the relevant Course name in the "CourseName" property.

How to do Lists in a view model? + dataannotations

I am using asp.net mvc 3, data annotations and auto mapper.
I want to have all my annotations on properties in my view model once the properties pass validation I use auto mapper to map it back to my domain object.
I have a viewmodel that has properties that I want to have a collection of since I want to generate a table from them. I also want to use them later to use as a form to add rows to this table.
So what do I do? How do I take these properties and make a collection of them?
public class UserViewModel()
{
[Required()]
public string UserName = {get; set;}
[Required()]
public string FirstName = {get; set;}
[Required()]
public string LastName = {get; set;}
}
I want to use these properties to generate my table and be used for a form.
The only thing I can think of is doing this
public class AddUserViewModel()
{
[Required()]
public string UserName = {get; set;}
[Required()]
public string FirstName = {get; set;}
[Required()]
public string LastName = {get; set;}
}
public class UserViewModel()
{
public List<User> Users {get; set;}
public AddUserViewModel {get; set;}
public UserViewModel()
{
Users = new List<Users>();
}
}
So basically I have it as a separate view model that is enclosed into another viewmodel that contains a list of Users(my domain model)
That way I use my domain model to generate the table and my AddUserViewModel for my adding of users.
Seems kinda redundant so I am not sure if there is a better way.
Edit
I have something like this
var viewModel = new UserViewModel();
List<Users> users= UserService.GetAllUsers();
viewModel = Mapper.Map<Users, UserViewModel>(users);
return View("Index", viewModel);
I also tried
var viewModel = new UserViewModel();
List<Users> users= UserService.GetAllUsers();
viewModel.AddUserViewModel = Mapper.Map<Users, AddUserViewModel>(users);
return View("Index", viewModel);
Edit 2
I have this and it compiles but I get this error
SomeViewModel viewModel = new SomeViewModel ();
List<User> users= userService.GetAllUsers();
viewModel.UserViewModel = Mapper.Map<List<User>, List<UserViewModel>>(users);
return View("Index", viewModel);
Trying to map Domain.User to ViewModels.UserViewModel.
Missing type map configuration or unsupported mapping.
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
Why would you want to return a list of domain objects in your view model? That's not what view models are supposed to be. View models should reference only other view models. So you have a nice UserViewModel which represents a user. Now you need to work with multiple users in your view, so either you pass an IEnumerable<UserViewModel> or if you need some other properties you design a view model for this:
public class UserViewModel
{
[Required]
public string UserName = { get; set; }
[Required]
public string FirstName = { get; set; }
[Required]
public string LastName = { get; set; }
}
public class SomeViewModel
{
public List<UserViewModel> Users { get; set; }
public string SomeOtherProperty { get; set; }
}
and now your controller action might look like this:
public ActionResult Foo()
{
SomeModel model = _repository.GetModel();
SomeViewModel viewModel = Mapper.Map<SomeModel, SomeViewModel>(model);
return View(viewModel);
}
Now inside your view you could simply use a display template for this Users property (Html.DisplayFor(x => x.Users)) to show a list of them.
UPDATE:
After seeing your update here's how to proceed in terms of good practices:
public ActionResult Foo()
{
IEnumerable<Users> users = _repository.GetUsers();
IEnumerable<UserViewModel> usersViewModel = Mapper
.Map<IEnumerable<Users>, IEnumerable<UserViewModel>>(users);
return View(usersViewModel);
}
I've also used an AutoMap attribute in a sample project which could simplify your code to this:
[AutoMap(typeof(IEnumerable<Users>), typeof(IEnumerable<UserViewModel>))]
public ActionResult Foo()
{
IEnumerable<Users> users = _repository.GetUsers();
return View(users);
}
This attribute will automatically run after the controller action and before the view is rendered and would use AutoMapper to replace the model with the corresponding view model.

Categories

Resources