MVC3 drop down list confusion - entity-framework

I'm using MVC3 with EF 4.1 and trying to edit a model which has a drop down list which is the reference to a parent object. Here are the models:
public class Section
{
public Guid SectionId { get; set; }
public string Title { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
public class Article
{
public Guid ArticleId { get; set; }
public DateTime? DatePosted { get; set; }
public string Title { get; set; }
public string ArticleBody { get; set; }
public Section Section { get; set; }
}
Here's the controller action to render the GET part of the edit:
public ActionResult Edit(Guid id)
{
Article article = db.Articles.Find(id);
var sections = db.Sections.ToList();
var secIndex = sections.IndexOf(article.Section);
ViewBag.SectionId = new SelectList(sections, "SectionId", "Title", secIndex);
return View(article);
}
And the View
#model CollstreamWebsite.Models.Article
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Article</legend>
#Html.HiddenFor(model => model.ArticleId)
<div class="editor-label">
#Html.LabelFor(model => model.DatePosted)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DatePosted)
#Html.ValidationMessageFor(model => model.DatePosted)
</div>
...
<div class="editor-label">
#Html.LabelFor(model => model.Section)
</div>
<div class="editor-field">
#Html.DropDownList("SectionId")
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
And finally the POST action for the edit
[HttpPost]
public ActionResult Edit(Article article)
{
if (ModelState.IsValid)
{
db.Entry(article).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(article);
}
The problem I have is that when the HttpPost Edit comes back, article.Section is null. How do I force the View to bind the Section to the article being edited.
Any help appreciated.

Don't push your Model straight to your View. Use ViewModel instead.
Something like this:
ViewModel
public class EditArticleViewModel
{
///All the properties for your Article
///The SelectListItems for your Sections
public List<SelectListItem> Sections{ get; set; }
public String SelectedSection{ get; set; }
}
Edit Get
[HttpGet]
public ActionResult Edit(Guid id)
{
EditArticleViewModel oEditArticleViewModel = new EditArticleViewModel();
//Fill in the SelectLists
List<SelectListItem> Sections= new List<SelectListItem>();
Sections.Add(new SelectListItem() { Text = "TheSelectedSection", Value = SectionId.ToString(), Selected = true});
foreach(Section otherSection in AllPossibleSections)
{
Sections.Add(new SelectListItem() { Text = otherSection.Title, Value = otherSection.Id, Selected = false});
}
oEditArticleViewModel.Sections = Sections;
return View(oEditArticleViewModel );
}
Your View
#Html.DropDownListFor(model => model.SelectedSection, Model.Sections)
//All other needed properties with their textboxes etc.
Edit Post
[HttpPost]
public ActionResult Register(EditArticleViewModel oPostedViewModel)
{
if (ModelState.IsValid)
{
//Get the Article and fill in the new properties etc.
//You can get the selectedSection from the SelectedSection Property, just cast it to a Guid.
RedirectToAction("Index", "Home");
}
//Something went wrong, redisplay the form for correction.
//Make sure to fill in the SelectListItems again.
return View(oPostedViewModel);
}
Hope it helps

Related

Can't access to foreign key properties using Entity Framework in Razor Pages project

I'm trying to display foreign key properties values using class that should have access to it:
#foreach (var item in Model.UserIssues)
{
<div class="card scroll" style="width:22rem;">
<div class="card-header p-4">
<div class="d-flex justify-content-between">
<div>
<p class="card-label mb-0"></p>
<h5>#Html.DisplayFor(i => item.Case.CaseNumber)</h5>
</div>
<div>
<a class="btn button-idle-add" data-bs-toggle="tooltip" data-bs-title="Dodaj Pracownię" asp-route-id="#item.Id" asp-page="/Cases/AddLaboratory"><i class="fa-solid fa-plus"></i></a>
</div>
</div>
<div class="d-flex justify-content-between mt-3">
<div>
<p class="card-input">#Html.DisplayFor(e => item.Case.Principal)</p>
</div>
<div>
<p class="card-input">#Html.DisplayFor(e => item.Case.Date)</p>
</div>
</div>
</div>
<div class="card-body">
<div class="m-1 p-3 card-lab">
<div class="d-flex justify-content-between align-items-center">
<p>#Html.DisplayFor(e => item.IssueNumber)</p>
<p class="pe-3 ps-3" style="border-radius:15px; background-color: palegreen;"></p>
</div>
<p>#Html.DisplayFor(e => item.Specialist.Laboratory)</p>
<p>#Html.DisplayFor(e => item.Specialist.FullName)</p>
</div>
</div>
</div>
}
public class IndexModel : PageModel
{
private readonly IRepository<Issue> issueRepository;
private readonly IRepository<Specialist> specialistRepository;
public IndexModel(IRepository<Issue> issueRepository, IRepository<Specialist> specialistRepository)
{
this.issueRepository = issueRepository;
this.specialistRepository = specialistRepository;
}
public List<Issue> AllIssues { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var loggedUser = specialistRepository.GetAll().FirstOrDefault(u => u.Login == User.Identity.Name);
UserIssues = issueRepository.GetAll().Where(i => i.Specialist.Id == loggedUser.Id).ToList();
return Page();
}
}
So for Specialist it works just fine:
#Html.DisplayFor(e => item.Specialist.FullName)
But for the Case it doesn't work, nothing is displayed:
#Html.DisplayFor(i => item.Case.CaseNumber)
Here are my models for Entity Framework setup:
public class Case
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string CaseNumber { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string Principal { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string Description { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string Date { get; set; }
public ICollection<Issue> Issues { get; set; }
}
public class Issue
{
[Key]
public int Id { get; set; }
public string Comment { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string IssueNumber { get; set; }
public Case Case { get; set; }
public Specialist Specialist { get; set; }
}
public class Specialist : IdentityUser
{
[Required]
public string Login { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Laboratory { get; set; }
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
public ICollection<Issue> Issues { get; set; }
}
Is my Entity Framework setup wrong? How can I display property values for the Case entity?
Using Entity Framework in case you want to get a specialist with id e775704b-5298-4173-82c8-c15d884e0695 and include the issues for this specialist you can use the include method in order to get the associated data.
Specialist? specialist = context.Specialists.Where(m => m.Id == "e775704b-5298-4173-82c8-c15d884e0695").Include(m => m.Issues).FirstOrDefault();
if (specialist != null)
{
foreach (var issue in specialist.Issues)
{
string issueNumber = issue.IssueNumber;
}
}
You can do the same thing for the entire list like this.
List<Specialist> specialists = context.Specialists.Include(m => m.Issues).ToList();
foreach (var specialist in specialists)
{
foreach (var issue in specialist.Issues)
{
string issueNumber = issue.IssueNumber;
}
}

Many-to-many relationship and multi select in asp.net core

I'm trying to learn asp.net core with razor and I'm trying to make a videogame database to keep a track of finished games, games I haven't played yet, etc.
But I have a problem. I have a table Game and a table Developer. Since a game can have many developers and a developer can have many games y made a third table DeveloperXGame.
They are something like this
public class Game
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Developer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DeveloperXGame
{
public int DeveloperId { get; set; }
public int JuegoId { get; set; }
public Developer Developer { get; set; }
public Game Game { get; set; }
}
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Game> Game { get; set; }
public DbSet<Developer> Developer { get; set; }
public DbSet<DeveloperXGame> DeveloperXGame { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DeveloperXGame>()
.HasKey(m => new { m.DeveloperId, m.GameId });
}
}
I already did the pages for the developers so I first create them manually. Now I'm trying to create the games and I want to show a select where I can select one or more developers of that list (next step will be to try to add them with ajax through the games page if they don't exists). But I'm lost beyond this point.
I don't know how to load the list of developers in that list and later on post how to save those selected items in the table DeveloperXGame.
Thanks
You can remove public DbSet<DeveloperXGame> DeveloperXGame { get; set; } from your context.
Index.cshtml.cs
public class IndexModel : PageModel
{
private readonly ApplicationDbContext _context;
public IndexModel(ApplicationDbContext context)
{
_context = context;
}
public Game Game { get; set; }
public IEnumerable<int> Developers { get; set; }
public IEnumerable<SelectListItem> DeveloperList { get; set; }
public IActionResult OnGet()
{
var developers = from m in _context.Developers
select m;
DeveloperList = developers.Select(m => new SelectListItem { Value = m.Id.ToString(), Text = m.Name });
return Page();
}
}
Here is the view Index.cshtml
#page
#model RazorPages.TestGame.Pages.Games.IndexModel
#{
ViewData["Title"] = "Index";
}
<div class="row">
<div class="col-md-4">
<form method="post">
<div class="form-group">
<label asp-for="Game.Name" class="control-label"></label>
<input asp-for="Game.Name" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Developers" class="control-label"></label>
<select asp-for="Developers" asp-items="Model.DeveloperList">
<option value="">All</option>
</select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
You can also use ViewData to pass your developers list in your view. You can go through this sample web as in official doc here.Hope it helps you to get started!

Unable to update DbEnitity.! EnityValidation occurs! Customer parameter returns null when trying to edit or save it

I am using codefirst approach and entity framework to update my Customer by changing the name but EnityValidation Error occurs.As I try to debug it using trycatch block it shows the name field is required.
Error message while debugging
I used data annotation while using entity framework to my model class.
public class Customer
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[Display(Name ="Date Of Birth")]
[DisplayFormat(ApplyFormatInEditMode =true,DataFormatString ="{0:dd/MM/yyyy}")]
public DateTime? Birthdate { get; set; }
public bool IsSubscribedToNewsletter { get; set; }
public MembershipType MembershipType { get; set; }
[Display(Name ="MembershipType")]
public byte MembershipTypeId { get; set; }
}
[Action-Save in CustomersController]
[HttpPost]
public ActionResult Save(Customer customer)
{
if (customer.Id == 0)
_context.Customers.Add(customer);
else
{
var customerInDb = _context.Customers.Single(c => c.Id == customer.Id);
customerInDb.Name = customer.Name;
customerInDb.Birthdate = customer.Birthdate;
customerInDb.IsSubscribedToNewsletter = customer.IsSubscribedToNewsletter;
customerInDb.MembershipTypeId = customer.MembershipTypeId;
}
try
{
_context.SaveChanges();
}
catch (DbEntityValidationException e)
{
Console.WriteLine(e);
}
return RedirectToAction("Index", "Customers");
}
[Edit-Action in CustomerController]
public ActionResult Edit(int id)
{
var customer = _context.Customers.SingleOrDefault(c => c.Id == id);
if (customer == null)
{
return HttpNotFound();
}
var viewModel = new CustomerFormViewModel
{
Customers = customer,
MembershipTypes = _context.MembershipType.ToList()
};
return View("CustomerForm", viewModel);
}
[New-Action CustomerController]
public ActionResult New()
{
var membershipType = _context.MembershipType.ToList();
var viewModel = new CustomerFormViewModel
{
MembershipTypes = membershipType
};
return View("CustomerForm", viewModel);
}
[CustomerFormViewModel -CustomerController]
public class CustomerFormViewModel
{
public IEnumerable<MembershipType> MembershipTypes { get; set; }
public Customer Customers { get; set; }
}
View-CustomerForm
#using (Html.BeginForm("Save","Customers"))
{
<div class="form-group">
#Html.LabelFor(c => c.Customers.Name)
#Html.TextBoxFor(c => c.Customers.Name,new{ #class="form-control"})
</div>
<div class="form-group">
#Html.LabelFor(c => c.Customers.Birthdate)
#Html.TextBoxFor(c => c.Customers.Birthdate,"{0:d MMM yyyy}" ,new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(c => c.Customers.MembershipTypeId)
#Html.DropDownListFor(c => c.Customers.MembershipTypeId,new SelectList(Model.MembershipTypes,"Id","Name"),"Select MembershipType",new { #class = "form-control" })
</div>
<div class="form-check">
<label>
#Html.CheckBoxFor(c => c.Customers.IsSubscribedToNewsletter) Subscribed To NewsLetter?
</label>
</div>
#Html.HiddenFor(c => c.Customers.Id)
<button type="submit" class="btn btn-primary">Save</button>
But this code works for another parameter "Movies".The movie object get passed.
[movies-model]
public class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public Genre Genre { get; set; }
public byte GenreId { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/mm/yyyy}")]
public DateTime ReleaseDate { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/mm/yyyy}")]
public DateTime DateAdded { get; set; }
public byte NumberInStock { get; set; }
}
[Edit-Action MovieController]
public ActionResult Edit(int id)
{
var movie = _context.Movies.SingleOrDefault(m => m.Id == id);
if (movie == null)
{
return HttpNotFound();
}
var viewModel = new MovieFormViewModel
{
Movie = movie,
Genres = _context.Genre.ToList()
};
return View("MovieForm",viewModel);
}
[New-Action MovieController]
public ActionResult New()
{
var genre = _context.Genre.ToList();
var viewModel = new MovieFormViewModel()
{
Genres = genre
};
return View("MovieForm", viewModel);
}
[Save-Action MovieController]
[HttpPost]
public ActionResult Save(Movie movie)
{
if(movie.Id==0)
{
movie.DateAdded = DateTime.Now;
_context.Movies.Add(movie);
}
else
{
var movieInDb = _context.Movies.SingleOrDefault(m => m.Id == movie.Id);
movieInDb.Name = movie.Name;
movieInDb.NumberInStock = movie.NumberInStock;
movieInDb.GenreId = movie.GenreId;
movieInDb.ReleaseDate = movie.ReleaseDate;
}
_context.SaveChanges();
return RedirectToAction("Index","Movies");
}
[MovieFormviewModel]
public class MovieFormViewModel
{
public Movie Movie { get; set; }
public IEnumerable<Genre> Genres { get; set; }
public string Title
{
get
{
if(Movie != null && Movie.Id !=0)
{
return "Edit Movie";
}
else
{
return "New Movie";
}
}
}
}
[movieForm-View]
#model Vidly.ViewModels.MovieFormViewModel
#{
ViewBag.Title = Model.Title;
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>New Movie</h2>
#using (Html.BeginForm("Save", "Movies"))
{
<div class="form-group">
#Html.LabelFor(m => m.Movie.Name)
#Html.TextBoxFor(m => m.Movie.Name,new { #class="form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Movie.ReleaseDate)
#Html.TextBoxFor(m => m.Movie.ReleaseDate, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Movie.GenreId)
#Html.DropDownListFor(m => m.Movie.GenreId,new SelectList(Model.Genres,"Id","Name"),"Select Genre", new { #class = "form-control"})
</div>
<div class="form-group">
#Html.LabelFor(m => m.Movie.NumberInStock)
#Html.TextBoxFor(m => m.Movie.NumberInStock, new { #class = "form-control" })
</div>
#Html.HiddenFor(m => m.Movie.Id)
<button type="submit" class="btn btn-primary">Save</button>
}
**How can i remove this error?Why is the object not getting passed for customers but for movies!!Both are basically same! **

How to save an image to Database using MVC 4

So I have a project which is a Shopping Cart, I have to save images to the database instead of uploading them to the server, here is my model
namespace ShoppingCart.Models
{
[Bind(Exclude = "ItemID")]
public class Item
{
[ScaffoldColumn(false)]
public int ItemID { get; set; }
[DisplayName("Category")]
public int CategoryID { get; set; }
[DisplayName("Brand")]
public int BrandID { get; set; }
[Required(ErrorMessage = "A Name is required")]
[StringLength(160)]
public string Title { get; set; }
public string Description { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 100.00,
ErrorMessage = "Price must be between 0.01 and 500.00")]
public decimal Price { get; set; }
[DisplayName("Album Art URL")]
[StringLength(1024)]
public string ItemArtUrl { get; set; }
public byte[] Picture { get; set; }
public virtual Category Category { get; set; }
public virtual Brand Brand { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
}
So Im unsure how to go about the controller to insert images or the view to display them, I have search for information about this but I cant really find anything, Im using entity framework code first.
There are two easy ways to do images -- one is to simply return the image itself in the controller:
[HttpGet]
[AllowAnonymous]
public ActionResult ViewImage(int id)
{
var item = _shoppingCartRepository.GetItem(id);
byte[] buffer = item.Picture;
return File(buffer, "image/jpg", string.Format("{0}.jpg", id));
}
And the view would just reference it:
<img src="Home/ViewImage/10" />
Additionally, you can include it in the ViewModel:
viewModel.ImageToShow = Convert.ToBase64String(item.Picture);
and in the view:
#Html.Raw("<img src=\"data:image/jpeg;base64," + viewModel.ImageToShow + "\" />");
For the data-store, you would simply use a byte array (varbinary(max)) or blob or any compatible type.
Uploading images
Here, an object called HeaderImage is an EntityFramework EntityObject. The controller would look something like:
[HttpPost]
public ActionResult UploadImages(HttpPostedFileBase[] uploadImages)
{
if (uploadImages.Count() <= 1)
{
return RedirectToAction("BrowseImages");
}
foreach (var image in uploadImages)
{
if (image.ContentLength > 0)
{
byte[] imageData = null;
using (var binaryReader = new BinaryReader(image.InputStream))
{
imageData = binaryReader.ReadBytes(image.ContentLength);
}
var headerImage = new HeaderImage
{
ImageData = imageData,
ImageName = image.FileName,
IsActive = true
};
imageRepository.AddHeaderImage(headerImage);
}
}
return RedirectToAction("BrowseImages");
}
The View would look something like:
#using (Html.BeginForm("UploadImages", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="row">
<span class="span4">
<input type="file" name="uploadImages" multiple="multiple" class="input-files"/>
</span>
<span class="span2">
<input type="submit" name="button" value="Upload" class="btn btn-upload" />
</span>
</div>
}

MVC4 Entity Framework 5 Many-To-Many Save Entity to Database

Hi I've been stuck to long on this problem. I've looked at a lot of examples but i cant find what im looking for. Any help is appreciated.
I use the Code-First approach.
I've enabled migration and the database is fine [Student] - [StudentCourse] - [Course].
Scenario: I have two entites -> Student
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Course> Courses { get; set; }
}
And Course
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Student> Students { get; set; }
}
Nothing fancy about that... Ive created a ViewModel ->
public class StudentCourseViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<Course> Courses { get; set; }
}
View:
#model Project.Web.Models.StudentCourseViewModel #{
ViewBag.Title = "Edit"; }
Edit
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Student</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Courses)
</div>
<div class="editor-field">
#for (int i = 0; i < Model.Students.Count(); i++)
{
<div style="border: dotted 1px; padding: 5px; margin: 10px;">
#Html.HiddenFor(s => s.Students[i].Id)
#Html.LabelFor(s => s.Students[i].Name[i + 1])
#Html.EditorFor(s => s.Students[i].Name)
</div>
}
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset> }
Controller Action:
[HttpPost]
public ActionResult Edit(CourseStudentViewModel model)
{
var course = db.Courses.Find(model.CourseId);
course.Name = model.CourseName;
course.Description = model.CourseDescription;
course.Students = model.Students;
if (ModelState.IsValid)
{
db.Entry(course).State = System.Data.EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
(Maby this is were i go wrong...)
Anyway, I want to create a new Student with optional many courses (textboxes -> courseName)
How should i do this?
The main issue is that i always get null values (student is fine, List of courses = NULL) back from my view [httpPost]Create -action.
I'm in need of guidance how to make this approach possible.
Thx J!
Your entities are not setup correctly for a many-to-many relationship. You need another entity to handle the many-to-many mapping. It would look something like this.
public class StudentsToCourses
{
public int StudentId {get; set;}
public int CourseId {get; set;}
public virtual Student Student {get; set;}
public virtual Course Course {get; set;}
}
Then your student model should be changed to this.
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<StudentsToCourses> Courses { get; set; }
}
And your coursed model changed to this.
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<StudentsToCourses> Students { get; set; }
}
You also need to setup the foreign key relationship using the Fluent API. It would look something like this.
public class StudentsToCoursesConfiguration : EntityTypeConfiguration<StudentsToCourses>
{
internal StudentsToCoursesConfiguration ()
{
this.HasKey(p => new {p.StudentId, p.CourseId});
this.HasRequired(p => p.Student)
.WithMany(p => p.Courses)
.HasForeignKey(p => p.StudentId);
this.HasRequired(p => p.Course)
.WithMany(r => r.Students)
.HasForeignKey(p => p.CourseId);
}
}