Saving Viewmodel data to the Database in ASP.NET MVC - asp.net-mvc

I am new to ASP.net MVC and am using a viewmodel rather than viewbags to populate my dropdowns since I've seen most people recommend against them. I have a slick UI that does cascading dropdowns and autocompletes (not shown here) but I can't seem to get my data saved back to the database.
Models:
public partial class Car
{
public int CarID { get; set; }
public string CarName { get; set; }
public int ModelID { get; set; }
public int ManufacturerID { get; set; }
public int CarColorID { get; set; }
public Nullable<decimal> Price { get; set; }
public string Description { get; set; }
public virtual CarColor CarColor { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public virtual CarModel CarModel { get; set; }
}
public partial class CarColor
{
public CarColor()
{
this.Cars = new HashSet<Car>();
}
public int ColorID { get; set; }
public string ColorName { get; set; }
public virtual ICollection<Car> Cars { get; set; }
}
public partial class CarModel
{
public CarModel()
{
this.Cars = new HashSet<Car>();
}
public int CarModelID { get; set; }
public int ManufacturerID { get; set; }
public string CarModelName { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
}
public partial class Manufacturer
{
public Manufacturer()
{
this.Cars = new HashSet<Car>();
this.Manufacturer1 = new HashSet<Manufacturer>();
this.CarModels = new HashSet<CarModel>();
}
public int ManufacturerID { get; set; }
public string ManufacturerName { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public virtual ICollection<Manufacturer> Manufacturer1 { get; set; }
public virtual Manufacturer Manufacturer2 { get; set; }
public virtual ICollection<CarModel> CarModels { get; set; }
}
ViewModel:
public class AnotherTestViewModel
{
public Car car { get; set; }
public IEnumerable<SelectListItem> CarModels { get; set; }
public IEnumerable<SelectListItem> Manufacturers { get; set; }
public IEnumerable<SelectListItem> CarColors { get; set; }
}
Controller:
public ActionResult Create()
{
var model = new AnotherTestViewModel();
using (new CarTestEntities())
{
model.CarModels = db.CarModels.ToList().Select(x => new SelectListItem
{
Value = x.CarModelID.ToString(),
Text = x.CarModelName
});
model.Manufacturers = db.Manufacturers.ToList().Select(x => new SelectListItem
{
Value = x.ManufacturerID.ToString(),
Text = x.ManufacturerName
});
model.CarColors = db.CarColors.ToList().Select(x => new SelectListItem
{
Value = x.ColorID.ToString(),
Text = x.ColorName
});
}
return View(model);
}
//
// POST: /AnotherTest/Create
[HttpPost]
public ActionResult Create(AnotherTestViewModel model)
{
if (ModelState.IsValid)
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Details", "AnotherTestViewModel", new { id = model.car.CarID });
}
return View();
}
I saw a few recommendations to use Automapper because EntityState.Modified won't work, but I'm not sure how to configure it because using the code below didn't work.
Mapper.CreateMap<AnotherTestViewModel, Car>();
Mapper.CreateMap<Car, AnotherTestViewModel>();
var newCar = Mapper.Map<AnotherTestViewModel, Car>(model);
Any ideas?

Your view model should not be interacting with the database. View Models should only be used in the presentation layer (user interface) - hence the term "View" model. You should have another model (data model) that interacts with your database. Then you should have some type of service layer that handles your conversion between your view model and your data model (and vice versa). Your data model is the model generated by Entity Framework (which I assume is what you are using). To handle updates to your database, you need to instantiate a data context, grab the data entity from your database, make changes to that entity, and call save changes all in that data context. The data context will keep track of all changes to your entities and apply the necessary changes to your database when you call "save changes".
Example:
public void UpdateCar(CarViewModel viewModel)
{
using (DataContext context = new DataContext())
{
CarEntity dataModel = context.CarEntities.where(x => x.Id == viewModel.Id).First();
dataModel.Name = viewModel.Name;
dataModel.Type = viewModel.Type;
context.SaveChanges();
}
}
In this example, context will keep track of any changes to "dataModel". When "context.SaveChanges" is called, those changes will automatically be applied to the database.

Related

Update existing entity framework with database (by adding new tables from db)

Hi i have an entity and i am gonna add two tables from database named as country and state.
There is a relation between these two tables based on CountryId.
I used the "Update Model from database ..." to add these two entity types.
I have manually written two classes for these two entity-types given as below:-
public partial class Country
{
//[Key] //[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int CountryID { get; set; }
public string CountryName { get; set; }
}
public partial class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public int CountryID { get; set; }
}
public DbSet<Country> Countries { get; set; }
public DbSet<State> States { get; set; }
Controller to fetch coutries and states :-
public JsonResult GetCountries()
{
List<Country> allCountry = new List<Country>();
using (SunilEntities dc = new SunilEntities())
{
allCountry = dc.Countries.OrderBy(a => a.CountryName).ToList();
}
return new JsonResult { Data = allCountry, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public JsonResult GetStates(int countryID)
{
List<State> allState = new List<State>();
using (SunilEntities dc = new SunilEntities())
{
allState = dc.States.Where(a => a.CountryID.Equals(countryID)).OrderBy(a => a.StateName).ToList();
}
return new JsonResult { Data = allState, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
But I am getting an error "The entity type Country is not part of the model for the current context".
What should be the exact classes to be used to use these two tables in my controller?
Is there any way I can get automated classes after entity being updated with newer tables?
Under your yourmodel.edmx file there is yourmodel.tt and it generates relevant classes, thus there is no need to write these classes. By using relevant namespace you can use them.
I got the solution change models as below:-
public partial class Country
{
public Country()
{
this.States = new HashSet<State>();
}
public int CountryID { get; set; }
public string CountryName { get; set; }
public virtual ICollection<State> States { get; set; }
}
public partial class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public Nullable<int> CountryID { get; set; }
public virtual Country Country { get; set; }
public virtual State State1 { get; set; }
public virtual State State2 { get; set; }
}
and change controller as given below:-
public JsonResult GetCountries()
{
using (SunilEntities dc = new SunilEntities())
{
var ret = dc.Countries.Select(x => new { x.CountryID, x.CountryName }).ToList();
return Json(ret, JsonRequestBehavior.AllowGet);
}
}
// Fetch State by Country ID
public JsonResult GetStates(int countryID)
{
using (SunilEntities dc = new SunilEntities())
{
var ret = dc.States.Where(x => x.CountryID == countryID).Select(x => new { x.StateID, x.StateName }).ToList();
return Json(ret, JsonRequestBehavior.AllowGet);
}
}

How do I use an edit viewmodel in MVC?

I have been struggling with this for some time. I have a model and an edit view model so I can allow the user to both see the image that was uploaded before and upload a replacement. Everything works fine until I get to the db.Entry portion. The error is:
The entity type EditCardViewModel is not part of the model for the current context.
If I try to add EditCardViewModel to the DbContext, it wants a key and a table, which isn't going to happen. The ViewModel is just a way to pass data. How do I tell it to use the Cards context when saving from this ViewModel?
Controller Edit Get:
public ActionResult Edit(int id = 0)
{
Card card = db.Cards.Find(id);
ViewData["Abilities"] = card.CardAbilities.Select(a => a.AbilityID);
if (card == null)
{
return HttpNotFound();
}
var editview = new EditCardViewModel(card);
{
}
return View(editview);
}
Controller Edit Post:
[HttpPost]
public ActionResult Edit(EditCardViewModel card)
{
if (ModelState.IsValid)
{
if(card.ImageUpload != null)
{
string savedFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Images");
savedFileName = Path.Combine(savedFileName, Path.GetFileName(card.ImageUpload.FileName));
card.ImageUpload.SaveAs(savedFileName);
card.Cards.ImageUrl = "\\Images\\" + Path.GetFileName(card.ImageUpload.FileName);
}
db.Entry(card).State = EntityState.Modified; //ERROR - Entity Type is not part of context
db.SaveChanges();
Edit ViewModel:
public class EditCardViewModel
{
public Card Cards { get; set; }
public HttpPostedFileBase ImageUpload { get; set; }
public IEnumerable<SelectListItem> Abilities { get; set; }
public int[] SelectedAbilities { get; set; }
public IEnumerable<SelectListItem> Rarities { get; set; }
public int SelectedRarities { get; set; }
public IEnumerable<SelectListItem> MainTypes { get; set; }
public int SelectedMainTypes { get; set; }
public IEnumerable<SelectListItem> SubTypes { get; set; }
public int SelectedSubTypes { get; set; }
public IEnumerable<SelectList> CardSets { get; set; }
public int SelectedCardSets { get; set; }
public Rarity Rarity { get; set; }
public MainType MainType { get; set; }
public SubType SubType { get; set; }
public CardSet CardSet { get; set; }
public EditCardViewModel() { } //NEEDED OR PARAMETERLESS CONSTRUCTOR ERROR
public EditCardViewModel(Card card) //NEEDED OR CANNOT PASS CARD MODEL
{
Cards = card;
}
}
The problem is that your view model is not recognised by Entity Framework - it has no idea that EditCardViewModel is meant to be a representation of a Card. It's a bit unclear from your view model exactly what you are doing with it but you either need to create a new Card object and use that:
var newCard = new Card
{
Id = card.Id //for example
};
Or possibly use the Cards property of your view model as that is of the correct type.

Populating navigation properties of navigation properties

How do I populate a navigation property with specific value?
I have 3 models, Game, UserTeam, User, defined below. I have a razor view which uses the model IEnumerable. This view loops over the Games, and within that loop, loops over the UserTeams. So far, so good.
Within the UserTeam loop, I want to access the User properties, but they are null. How do I populate the User navigation property for each UserTeam object? Do I need a constructor with a parameter in the UserTeam model?
Models
public class Game
{
public Game()
{
UserTeams = new HashSet<UserTeam>();
}
public int Id { get; set; }
public int CreatorId { get; set; }
public string Name { get; set; }
public int CurrentOrderPosition { get; set; }
public virtual UserProfile Creator { get; set; }
public virtual ICollection<UserTeam> UserTeams { get; set; }
}
public class UserTeam
{
public UserTeam()
{
User = new UserProfile();
}
public int Id { get; set; }
public int UserId { get; set; }
public int GameId { get; set; }
public int OrderPosition { get; set; }
public virtual UserProfile User { get; set; }
public virtual Game Game { get; set; }
public virtual IList<UserTeam_Player> UserTeam_Players { get; set; }
}
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string test { get; set; }
public UserProfile()
{
UserTeams = new HashSet<UserTeam>();
}
public virtual ICollection<UserTeam> UserTeams { get; set; }
[ForeignKey("CreatorId")]
public virtual ICollection<Game> Games { get; set; }
}
Loop in my Razor view (Model is IEnumerable)
#foreach (var item in Model) {
#foreach (var userteam in item.UserTeams) {
#Html.ActionLink("Join game as"+userteam.User.UserName, "JoinGame", new { gameid = item.Id, userid=userteam.UserId })
}
}
Method in my repository that returns the Games
public IEnumerable<Game> GetAllGames()
{
using (DataContext)
{
var gm = DataContext.Games.Include("UserTeams").ToList();
return gm;
}
}
You would need to include this in your repository method. If you are using eager loading then it would be something like
var gm = DataContext.Games
.Include(x => x.UserTeams)
.Include(x => x.UserTeams.Select(y => y.User))
.ToList();
I have not done this without using LINQ for my queries, but I assume it would be something like:
var gm = DataContext.Games.Include("UserTeams.User").ToList();
Hopefully this helps you out

Model or ViewModel in a ListViewModel

Should T be a for example Customer or CustomerViewModel ?
The annotations bound to Mvc namespace are on the ListViewModel so actually I could pass the Customer object. What do you think?
public class ListViewModel<T>
{
[Required(ErrorMessage="No item selected.")]
public int[] SelectedIds { get; set; }
public IEnumerable<T> DisplayList { get; set; }
}
UPDATE
[HttpGet]
public ActionResult Open()
{
IEnumerable<Testplan> testplans = _testplanDataProvider.GetTestplans();
OpenTestplanListViewModel viewModel = new OpenTestplanListViewModel(testplans);
return PartialView(viewModel);
}
public class OpenTestplanListViewModel
{
public OpenTestplanListViewModel(IEnumerable<Testplan> testplans)
{
var testplanViewModels = testplans.Select(t => new TestplanViewModel
{
Name = string.Format("{0}-{1}-{2}-{3}", t.Release.Name, t.Template.Name, t.CreatedAt, t.CreatedBy),
TestplanId = t.TestplanId,
});
DisplayList = testplanViewModels;
}
[Required(ErrorMessage = "No item selected.")]
public int[] SelectedIds { get; set; }
public string Name { get; set; }
public IEnumerable<TestplanViewModel> DisplayList { get; private set; }
}
public class TestplanViewModel
{
public int TestplanId { get; set; }
public string Name { get; set; }
}
public class Testplan
{
public int TestplanId { get; set; }
public int TemplateId { get; set; }
public int ReleaseId { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
public Template Template { get; set; }
public Release Release { get; set; }
}
T should ideally be a view model. Having a view model referencing domain models is some kind of a hybrid view model, not a real one. But if you think that in this specific case the domain model will be exactly the same as the view model then you could keep it as well.

How to design a ViewModel for a todo list application?

I am creating a simple todo application which has two entities, tasks and categories.
To create a task, choosing a category is a must. For this, I figured I would need a ViewModel.
Here is the Task entity
public class Task
{
public int taskId { get; set; }
public int categoryId { get; set; }
public string taskName { get; set; }
public bool isCompleted { get; set; }
public DateTime creationDate { get; set; }
public DateTime completionDate { get; set; }
public string remarks { get; set; }
public string completionRemarks { get; set; }
}
Here is the Category entity
public class Category
{
public int categoryId { get; set; }
public string categoryName { get; set; }
}
How can I design a TaskCategoryViewModel so that I can bind the category in the CreateTask view?
Edit: I am using classic ADO.NET instead of Entity Framework or LINQ to SQL.
Kishor,
the best bet is have model that hods definition for your task and for category (all in one)
here is how everything hangs together.
where
IEnumerable<SelectListItem> Categories
is used for creating drop down list which is ready to use
<%= Html.DropDownListFor(model=>model.NewTask.categoryId, Model.Categories) %>
this will create you nice dropdown list
private IEnumerable<Category> GetCategories
{
get
{
List<Category> categories = new List<Category>
{
new Category() {categoryId = 1, categoryName = "test1"},
new Category() {categoryId = 2, categoryName = "category2"}
};
return categories;
}
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult CreateTask()
{
TaskModel taskModel = new TaskModel();
LoadCategoriesForModel(taskModel);
return View(taskModel);
}
private void LoadCategoriesForModel(TaskModel taskModel)
{
taskModel.Categories =
GetCategories.Select(
x =>
new SelectListItem()
{Text = x.categoryName, Value = x.categoryId.ToString(CultureInfo.InvariantCulture)});
}
public ActionResult CreateTask(TaskModel taskModel)
{
if (ModelState.IsValid)
{
// do your logic for saving
return RedirectToAction("Index");
}
else
{
LoadCategoriesForModel(taskModel);
return View(taskModel);
}
}
/// <summary>
/// your model for creation
/// </summary>
public class TaskModel
{
public Task NewTask { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
/// <summary>
/// Task
/// </summary>
public class Task
{
public int taskId { get; set; }
public int categoryId { get; set; }
public string taskName { get; set; }
public bool isCompleted { get; set; }
public DateTime creationDate { get; set; }
public DateTime completionDate { get; set; }
public string remarks { get; set; }
public string completionRemarks { get; set; }
}
/// <summary>
/// Category
/// </summary>
public class Category
{
public int categoryId { get; set; }
public string categoryName { get; set; }
}
In the TaskViewModel (I would prefer naming it CreateTaskViewModel) create property for categories select list
public IEnumerable<SelectListItem> CategoriesSelectList;
In controller, bind that property before returning view (note that this also should be done in post handler, when ModelState is invalid)
public ViewResult Create()
{
CreateTaskViewModel model = new CreateTaskViewModel();
model.CategoriesSelectList = _repository.AllCategories().Select(x=> new SelectListItem(){ Text = x.CategoryName, Value = x.CategoryId.ToString();}
}
And finally, in the view
Html.DropDownListFor(model => model.CategoryId, Model.CategoriesSelectList)
Edit:
In your code, _repository.AllCategories() should be replaced by your data access code, that returns object having type IEnumerable<Category>. It actually does not matter which data access technology you use. And do not forget to add the using System.Linq; statement to your controller file, if it's missing.

Resources