How do i get Entity Frameworks foreign keys to pick up when I generate the view with the add view dialog.
My Models are like
public class System
{
#region Properties
public int SystemId { get; set; }
public string SystemName { get; set; }
#endregion
}
public class Module
{
#region Properties
public int ModuleId { get; set; }
//[Required]
[Display(Name="Module Name")]
public string ModuleName { get; set; }
[Display(Name="Date Added")]
public DateTime DateAdded { get; set; }
//[ForeignKey("CurrentSystem")]
public int SystemId { get; set; }
//[ForeignKey()]
//[ForeignKey("SystemId")]
public System System { get; set; }
#endregion
}
When i click on the Controller, then Add View, the modal opens. I select all the details that is needed and then the following is generated ( I did not include the entire view).
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Module</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ModuleName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ModuleName)
#Html.ValidationMessageFor(model => model.ModuleName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DateAdded)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateAdded)
#Html.ValidationMessageFor(model => model.DateAdded)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SystemId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SystemId)
#Html.ValidationMessageFor(model => model.SystemId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
I want the SystemId to be a dropdown and not a text field. How do i do this ?
Are you sure you created a class derived from dbContext with a property of type DBSet<System> and a property of type DBSet<module> ?
Provided you have, and you select a controller with the option "read and write actions with entity framework" and select both your class Module) and your context in the drop downs below that, MVC should generate the correct code view and controller code to display a combo for the system.
It worked for me in MVC4, at least. Remember to generate your project before trying to add a controller. I think MVc uses reflection so it needs an up-to-date assembly...
Use a view-model. MVC uses the data annotations to figure out how to display the properties. If you want your view to behave in a different manner than your model, you need to map your model to a view-model to handle the extra logic (ie a drop down where an int exists). Then generate your view from the view-model class.
The view-model adds a layer to deal with the seperations of concerns here. You want your view to be different than your model will allow so add a view model class that will take the model data and display it in a way that you want.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have worked with .Net webforms before and now working on a MVC project.
I am working on creating a user registration page. I have no idea how its MODEL should look like.
See database diagram
User registration page:
Firstname
LastName
Email adress
Subcribe newsletter ?
Password
Choose your city
.....
I think I have to do this in this task:
STEP 1:
Populate cities dropdownList (choose your city) with data from database
STEP 2:
Insert users email address in UserEmail table and set subcribe to 0 or 1
STEP 3:
Save user registration data and EmailID (get from UserEmails table), CityID into Users table
Should I make a large MODEL , like this :
Public Class Registration Model
{
Get, set:
all USEREMAIL plugs ties
all Users plugs ties
all Cities plugs ties
}
How to start with this task in MVC?
(I know how to develop this in webforms , but MVC model confuses me)
You were on the right track.You're trying to create a view which let's users complete a registertion form, this view needs to use the user model and at the same type display a list of cities in a drop down which come from a different table in the database but are related to the user.
In MVC there's a concept called viewmodel, all that it is is a simple class which combines one or more models together.
Below I created a model called SiteUser and then another model called SiteUserViewModel which inherits from the user and gives us all the user properties PLUS an additional property we can use to populate the cities.So when we display the page, we will use the SiteUserViewModel but when we post to the controller to save the user in the database we will use SiteUser:
Models:
namespace MVCTutorial.Models
{
public class SiteUser
{
[Display(Name="First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[DataType(DataType.EmailAddress)]
public string EmailAddress { get; set; }
[Display(Name = "Subscribe To Newsletter?")]
public bool SubscribeToNewsletter { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name="City")]
public int CityId { get; set; }
}
public class SiteUserViewModel : SiteUser
{
public List<SelectListItem> Cities { get; set; }
}
}
Controller:
public class SiteUserController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new SiteUserViewModel();
//Replace this with logic that reads cities from the database
var city1 = new SelectListItem { Text = "Johannesburg", Value = "1" };
var city2 = new SelectListItem { Text = "Cape Town", Value = "2" };
model.Cities = new List<SelectListItem> { city1, city2 };
return View(model);
}
[HttpPost]
public ActionResult CreateUser(SiteUser user)
{
System.Diagnostics.Debugger.Break();
//Write the code to add user to the database
return View();
}
}
View:
#model MVCTutorial.Models.SiteUserViewModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Create User</title>
</head>
<body>
#using (Html.BeginForm("CreateUser", "SiteUser"))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Site User</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubscribeToNewsletter)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SubscribeToNewsletter)
#Html.ValidationMessageFor(model => model.SubscribeToNewsletter)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CityId)
</div>
<div class="editor-field">
#Html.DropDownList("CityId", Model.Cities, "Please select one")
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
</body>
</html>
Result:
User Model
public class UserModel
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
public IEnumerable<UserPets> UserPets { get; set; }
}
User Pets Model
public class UserPetsModel
{
public PetModel Pet{ get; set; }
public bool UserHasPet { get; set; }
}
Using these 2 models I am creating an edit page where a User can come in and edit which Pets they have.
To enable them to state which pets they have I am trying to use checkboxes.
Edit Page
#model Models.UserModel
#using (Html.BeginForm())
{
<div class="form-group">
#Html.LabelFor(model => model.FirstName)
#Model.FirstName
</div>
#foreach (var userPets in Model.UserPets)
{
#Model.Pet.AnimalName
<div>
#Html.CheckBoxFor(u => userPets .UserHasPet)
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
The problem I am having is when trying to map the UserModel back to the controller action. When I press the save button, everything on the UserModel is being mapped back to the controller apart from the UserPetsModels which I believe is due to the use of the foreach.
Is there another way in which I can display a checkbox for each UserPetModel without using a foreach or a for loop.
Yes there is. You should create EditorTemplate for your UserPetsModel. It will look like:
#model UserPetsModel
#Model.Pet.AnimalName
<div>
#Html.CheckBoxFor(model => model.UserHasPet)
</div>
And then you can simply do:
#Html.EditorFor(model => model.UserPets)
EditorFor will create right binding for you. Note that you should create EditorTemplate only for UserPets and it also will work for List<UserPetsModel> and IEnumarable<UserPetsModel> with the same syntax that i show.
I would suggest replace the loop with EditorTemplate. So your
#foreach (var userPets in Model.UserPets)
{
#Model.Pet.AnimalName
<div>
#Html.CheckBoxFor(u => userPets.UserHasPet)
</div>
}
would look like:
<div class="row">
#Html.EditorFor(m => m.UserPets)
</div>
And define a view in (~/Views/Shared/EditorTemplates/UserPets.cshtml) like:
#model UserPetsModel
#Html.HiddenFor(x => x.Pet.PetId)
#Html.LabelFor(x => x.UserHasPet, Model.Pet.AnimalName)
#Html.CheckBoxFor(x => x.UserHasPet)
I have a simple application that I've loosely based off the MVC Music store used in a tutorial on asp.net that allows a user to enter details about TV shows that they are watching so they can keep track of which episode they're up to in each show.
The main entities in my application are Show:
Public class Show
{
// a few automatic properties
public int id { get; set; }
public Genre Genre { get; set; }
}
and the Genre class
Public partial class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public List<Show> Shows { get; set; }
}
My POST edit method in my controller was generated by VS for me:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Show show)
{
if (ModelState.IsValid)
{
db.Entry(show).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(show);
}
When I inspect db in the debugger, I can see that the Show objects (however many are in the database at the time. I seed it with two but usually add a third via the form) all have the genres I've entered but after the Edit action returns the view, the Genre is null while all the other values are updated. Why is this?
I thought perhaps the genre field was null because I wasn't instantiating them but where would I do that? I don't want a new genre created every time a Show's genre is set. I just want a show to have a genre reference so that I can eventually allow the user to select a genre and see all the shows they're following of that genre.
EDIT: Here's my Edit View. The Details view also looks very similar.
#model watchedCatalogue2.Models.Show
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>#Html.DisplayFor(m => m.Name)</legend>
#Html.HiddenFor(m => m.id)
<div class="editor-label">
#Html.LabelFor(m => m.Name)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Genre)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.Genre.Name)
#Html.ValidationMessageFor(m => m.Genre.Name)
</div>
<div class="editor-label">
#Html.Label("Number of episodes")
</div>
<div class="editor-field">
#Html.EditorFor(m => m.NoEpisodes)
#Html.ValidationMessageFor(m => m.NoEpisodes)
</div>
<div class="editor-label">
#Html.Label("Number of episodes watched")
</div>
<div class="editor-field">
#Html.EditorFor(m => m.NoEpisodesWatched)
#Html.ValidationMessageFor(m => m.NoEpisodesWatched)
</div>
<div class="editor-label">
#Html.Label("Watching State")
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.State, watchedCatalogue2.Models.Show.WatchStateItems)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Description)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.Description)
#Html.ValidationMessageFor(m => m.Description)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
Also, for good measure, db in my controller refers to my CatalogueEntities class:
namespace watchedCatalogue2.Models
{
public class CatalogueEntities : DbContext
{
public DbSet<Genre> Genres { get; set; }
public DbSet<Show> Shows { get; set; }
}
}
The problem is probably the fact that in MVC all the data is not retained/posted onto the POST method, but only those rendered as an editable element on the View and contained in the Model.
The simplest work-around will be to render it as a hidden field, as follows:
MVC 2.0 and earlier:
<%: Html.HiddenFor(m => m.GenreId) %>
Razor:
#Html.HiddenFor(m => m.GenreId)
If you use AJAX or something similar, you could also just send/pass the value as a parameter.
Also note, if you make use of the ModelState.IsValid functionality, it is possible and very probable that the Hidden fields would be rendered as null, and most probably as a problem. To remove this, which is not always advised, just add ModelState.Remove("TheKey");, in your case it is probably, ModelState.Remove("GenreID");
If I'm following the problem right, you need to render it on the form in some way for the data to be maintained, bearing in mind the web is stateless, if the value isn't present when the form is posted, it won't be bound back on to your view model.
On your view include in the form:
#Html.HiddenFor(model => model.GenreId)
Or depending on what your URL is like on your Get, you could change your POST method signature to include the id.
After some research, I noticed many of the model classes in the examples had references to other models that were marked as virtual. I figured I had nothing to lose so I modified my Show class so public Genre Genre { get; set; } now reads public virtual Genre Genre { get; set; }. Suddenly it all works. I'd appreciate if someone who knows why it works could explain it to me because all I've seen thus far have been references to lazy loading but nothing relating to my problem.
Thanks for the help, everyone.
On my MVC4 application I have a page where an user can be placed in a schedule. The idea is that you can select a person and that they will be entered on the schedule. The Schedule Model looks as follows:
public class Schedule {
[Key]
public int scheduleId { get; set; }
[Column(TypeName = "Date")]
[DataType(DataType.Date)]
public DateTime date { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
The User Model where the Schedule Model refers to looks like this:
public class User {
public int UserId { get; set; }
[Display(Name = "Naam")]
public string Name { get; set; }
public string Email { get; set; }
[Display(Name = "Beschikbaar")]
public bool isAvailable { get; set; }
}
An user can be placed on the scheduling page with the following controller:
public ActionResult Create(Schedule schedule)
{
if (ModelState.IsValid)
{
db.Schedules.Add(schedule);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(schedule);
}
The Schedule/Create view looks like this:
and has the following code:
<h2>Schedule a person</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Schedule</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserId)
</div>
<div class="editor-field">
#Html.DropDownList("UserId", String.Empty)
#Html.ValidationMessageFor(model => model.UserId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.date)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.date)
#Html.ValidationMessageFor(model => model.date)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.isAvailable)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.User.isAvailable)
#Html.ValidationMessageFor(model => model.User.isAvailable)
</div>
<p>
<input type="submit" value="Plan" />
</p>
</fieldset>
}
This code does place the user on the schedule on the specified date. But somehow an extra "null" user is created in the database every time an existing user is added to the schedule. See this image:
I have been breaking my head over this unwanted behaviour for the past hours. The Create action of the ScheduleController does not specify to create an extra user. I have no idea why it does do so anyway.
Does anyone here have any idea?
It's because of your .User.IsAvailabe property. This creates a new user when adding the schedule.
You'll have to attach the existing user to your schedule. Perhaps you can even get rid of UserId and use User.Id in your form.
I have a registration view in my web app. However, account registration would be unsuccessful and my drop-down menu would be outlined in red.
Here is my drop down code:
<div class="editor-label">
#Html.LabelFor(model => model.CompanyId, "Company")
</div>
<div class="editor-field">
#Html.DropDownList("Companies", "<Select Company>")
</div>
Here is my associated code in the view model:
[Display(Name = "Company")]
public int? CompanyId { get; set; }
public IEnumerable<SelectListItem> Companies { get; set; }
Here is my associated code in the controller before I return the view:
ViewBag.Companies = new SelectList(db.Companies, "CompanyId", "CompanyName");
As I debug, I find that ModelState.IsValid returns false, and therefore re-returns the view (and re-sets ViewBag.Companies in my case).
How do I complete a successful registration? I do fill out each required field within parameters. It is not required to select company. When I do not select a company, ModelState.IsValid returns true and runs through the code. However, I would like the user to have the option to associate with his company.
Thanks.
Why would you need setting ViewBag.Companies, when you've got the better approach - having select list into the model? No need to use ugly ViewBag
You should have something like this
ViewModel
public class RegisterViewModel
{
[Display(Name = "Company")]
public int? CompanyId { get; set; }
public IEnumerable<SelectListItem> Companies { get; set; }
}
Controller
[HttpGet]
public ActionResult Register()
{
RegisterViewModel viewModel = new RegisterViewModel();
viewModel.Companies = new SelectList(db.Companies, "CompanyId", "CompanyName");
return View(viewModel);
}
And in the view
<div class="editor-label">
#Html.LabelFor(model => model.CompanyId, "Company")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.CompanyId, Model.Companies, "<Select Company>")
</div>
This will bind your CompanyId on server correctly.
Your model state will always be invalid for your dropdowns if your selected variable type is not a string. Change CompanyId to string. You can still use IEnumerable Companies {get;set;}
DropDownList is kind of weird in ASP.Net MVC3. It will not bind properly to an enumerable of SelectListItem. You need to store the bound, selected choice to a primitive in the model and the list of all options in a separate field.
<div class="editor-label">
#Html.LabelFor(model => model.CompanyId, "Company")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.CompanyId, new SelectList(Model.Companies))
</div>
Then, in your model, change the IEnumerable of SelectListItems to an IEnumerable of strings.
[Display(Name = "Company")]
public string CompanyId { get; set; }
public IEnumerable<string> Companies { get; set; }
Finally, in your controller,
ViewBag.Companies = db.Companies;
Just as a sidenote, I suggest making your IEnumerable an IList instead. A dropdown menu definitely cares about the order in which the data is presented, so your model should reflect that.