Asp Net Core - Entity Framework - ViewBag - Related Data - CRUD - entity-framework

I am creating a simple Location and Country relationship and i have been able to link the to tables and i am able to pull the related data, the issue is that when i try post the form the database does not update with the ID of the country.
When monitoring the POST action via Firefox Developer tools i can see the ID of the country is being posted.
LocationsController
public class LocationsController : Controller
{
private readonly DataContext _context;
public LocationsController(DataContext context)
{
_context = context;
}
// DropDown: Populate the Dropdown lists
private void PopulateCountryDropDownList(object selectedCountry = null)
{
var countriesQuery = from c in _context.Countries
orderby c.CountryID
select c;
ViewBag.CountryID = new SelectList(countriesQuery.AsNoTracking(), "CountryID", "Title");
}
// GET: Locations/Create
public IActionResult Create()
{
PopulateCountryDropDownList();
return View();
}
// POST: Locations/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Location location)
{
if (ModelState.IsValid)
{
_context.Add(location);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(location);
}
}
Location
using System.ComponentModel.DataAnnotations;
namespace RaceCaribbean.Models
{
public class Location
{
public int LocationID { get; set; }
public string Title { get; set; }
public Country Country { get; set; }
}
}
Country
using System.ComponentModel.DataAnnotations;
namespace RaceCaribbean.Models
{
public class Country
{
public int CountryID { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Code { get; set; }
}
}
Create.cshtml
#model RaceCaribbean.Models.Location
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form asp-action="Create">
<div class="form-horizontal">
<h4>Location</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Country" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="Country" class="form-control" asp-items="ViewBag.CountryID">
<option selected="selected" value="">-- Select Country --</option>
</select>
<span asp-validation-for="Country" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Related

Data storing to the associated table in ASP.NET Core 5 MVC

I have three tables 'Movie', 'Genre' and it's associated table 'MovieGenre'. What I have to do is to combine the value of Movie table with Genre table and to display the Name value from Genre table with Movie table values, Using the third associated table MovieGenre.
public partial class Movie
{
public int MovieId { get; set; }
[Required]
public string Title { get; set; }
public string Description { get; set; }
public string Storyline { get; set; }
public int? Year { get; set; }
[DataType(DataType.Date)]
[Validators(ErrorMessage = "Date must be after or equal to current date")]
[Display(Name = "Release Date")]
public DateTime? ReleaseDate { get; set; }
public int? Runtime { get; set; }
[Display(Name = "Movie Type")]
[Column(TypeName = "nvarchar(20)")]
public MovieType MovieType { get; set; }
public ICollection<Genre> Genres { get; set; }
}
public class Genre
{
[Key]
public int GenreId { get; set; }
[Display(Name="Genre name")]
public string Name { get; set; }
public ICollection<Movie> Movies { get; set; }
}
public class MovieGenre
{
public int MovieGenreId { get; set; }
public int MovieId { get; set; }
public Movie Movie { get; set; }
public int GenreId { get; set; }
public Genre Genre { get; set; }
}
This is the Context page for this
public partial class MovieContext : DbContext
{
public MovieContext(DbContextOptions<MovieContext> options)
: base(options)
{
}
public virtual DbSet<Movie> Movies { get; set; }
public virtual DbSet<Genre> Genre { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");
modelBuilder.Entity<MovieGenre>().HasKey(mg => new { mg.MovieId, mg.GenreId });
modelBuilder.Entity<Movie>()
.HasMany(p => p.Genres)
.WithMany(p => p.Movies)
.UsingEntity(j => j.ToTable("MovieGenre"));
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
I used database-first method. I have created all other part and it is working correctly when I enter values to the table from database. But what I want to achieve is to store the foreign key values 'MovieId' and 'GenreId' to the 'MovieGenre' table when creating a new movie.
This is the code in create action method. How can I store the 'MovieId' and 'GenreId' to the 'MovieGenre' table from this code?
public async Task<IActionResult> Create([Bind("Title,Description,Storyline,Year,ReleaseDate,Runtime,MovieType")] Movie movies)
{
if (ModelState.IsValid)
{
_context.Add(movies);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewBag.GenreId = new MultiSelectList(_context.Genre.ToList(), "GenreId", "Name");
return View(movies);
}
Below is the create Action view code, here I used check boxes for the Genre, I want to enter the GenreId from this somehow to the associate table too .
<div class="row">
<div class="col-md-4 center">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Storyline" class="control-label"></label>
<input asp-for="Storyline" class="form-control" />
<span asp-validation-for="Storyline" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Year" class="control-label"></label>
<input asp-for="Year" class="form-control" />
<span asp-validation-for="Year" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Runtime" class="control-label"></label>
<input asp-for="Runtime" class="form-control" />
<span asp-validation-for="Runtime" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieType" class="control-label"></label>
<select asp-for="MovieType" class="form-control" asp-items="Html.GetEnumSelectList<MovieType>()"></select>
<span asp-validation-for="MovieType" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Movie Genre</label>
<div class="col-md-10">
<div class="checkbox">
#foreach (var item in (MultiSelectList)ViewBag.GenreId)
{
<input type="checkbox" value="#item.Value" id="GenreId" name="GenreId" />#item.Text
}
</div>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
<a class="btn btn-primary" asp-action="Index">Back to List</a>
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
But what I want to achieve is to store the foreign key values 'MovieId' and 'GenreId' to the 'MovieGenre' table when creating a new movie.
You don't really need MovieId because it is auto-generated when SaveChanges{Async} is called, thus is available only after that call.
But you have explicit join entity MovieGenre with both navigation and explicit FK properties, so you can utilize the navigation property to specify the new Movie and let EF Core handle the actual FK for you. For the other FK you can just assign the existing Genre key (Id).
There are two ways to add a new MovieGenre record in this scenario. Both require creating MovieGenre instance and filling in the navigation and FK properties:
var movieGenre = new MovieGenre
{
Movie = movies,
GenreId = genreId // ?? - see below
};
Just not sure where GenreId is coming from here
Create([Bind("MovieId,Title,Description,Storyline,Year,ReleaseDate,Runtime,MovieType, GenreId")] Movie movies
since the Movie class doesn't have GenreId property, so you have to figure that out (eventually revisit the DTO/ViewModel used for communication with the controller).
Once you have that, you either add it to the Movie.MovieGenre collection:
movies.MovieGenre = new List<MovieGenre> { movieGenre };
_context.Add(movies);
or just add it directly to the db context (set):
_context.Add(movies);
_context.Add(movieGenre);
For this particular scenario the explicit join entity helps a lot. If you instead used the EF Core 5.0+ implicit join entity, implementing that operation would require a different approach.
I have found the right code for this. If anyone need the solution here my GitHub repo with the complete code.
https://github.com/Iam-Goku/ProjectMovie.git

How to update a MVC Form before Submit

So i have 2 Viewbags, they each have a list of values from a database table, the first Viewbag have all possible values of a database column, while the other have only the values corresponding to the selected value in the first Viewbag.
I have the logic for the search.
but i need to have the form update after selecting one value, since they both need to be in the same form, it is not searching for the second value.
OBS:i am only using the controllers, and cshtml views, not razor pages.
Here is a simple demo how to create cascading selectlist in asp.net core mvc:
Model:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubCategory
{
public int Id { get; set; }
public int CategoryId { get; set; }
public string SubCategoryName { get; set; }
}
View(Index.cshtml):
<div>
<div style="float:left;width:40%">
<form id="form">
<div class="form-group row">
<label>Category Name</label>
<div class="col-12">
<select id="CategoryId" class="custom-select mr-sm-2"
asp-items="#(new SelectList( #ViewBag.Category,"Id","Name"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div class="form-group row">
<label>SubCategory Name</label>
<div class="col-12">
<select id="SubCategoryId" class="custom-select mr-sm-2"
asp-items="#(new SelectList(string.Empty,"Id","SubCategoryName"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div>
<input type="button" value="Search" />
</div>
</form>
</div>
</div>
#section Scripts
{
<script>
$(function () {
$('#CategoryId').change(function () {
var data = $("#CategoryId").val();
$.ajax({
url: '/Home/GetSubCategory?CategoryId=' + data,
type: 'Get',
success: function (data) {
var items = "";
$.each(data, function (i, item) {
items += "<option value='" + item.value + "'>" + item.text + "</option>";
});
$('#SubCategoryId').html(items);
}
})
});
})
</script>
}
Controller:
public class HomeController : Controller
{
private readonly MvcProj3Context _context;
public HomeController(MvcProj3Context context)
{
_context = context;
}
public IActionResult Index()
{
ViewBag.Category = _context.Category.ToList();
return View();
}
public JsonResult GetSubCategory(int CategoryId)
{
ViewBag.SubCategory = (from m in _context.SubCategory
where m.CategoryId == CategoryId
select m).ToList();
return Json(new SelectList(ViewBag.SubCategory, "Id", "SubCategoryName"));
}
}
Result:
The problem is, that the .cshtml file is completely rendered before its send to your browser. Therefore you cannot change it with C# code after its sent to the browser.
You could use blazor if you want to do it with c#, or you could do it with javascript.

Need walk-through of how to create a user and assign a role to it in one form (ASP.NET Core MVC 3+)

Using ASP.NET Core MVC 3.1.
Added security using the identity scaffolding.
Created default groups following the instructions from eShopOnWeb (https://github.com/dotnet-architecture/eShopOnWeb/blob/master/src/Infrastructure/Identity/AppIdentityDbContextSeed.cs)
Able to seed the database and create
3 groups: Admins, Managers, Users
3 users: Admin, Manager, User
assign the user to respective group.
I need instructions on how to accomplish assigning the roles to users from the User Management form (MVC pattern) at the time when I create a user or need to edit the roles for the users. Also I need MVC pattern not Razor pages like here https://github.com/bhrugen/Uplift/blob/master/Uplift/Areas/Identity/Pages/Account/Register.cshtml.cs
I presume I need to create the ViewModel which would include entries from dbo.AspNetUsers, dbo.AspNetRoles, dbo.AspNetUserRoles for that but not sure what exactly I need and how to perform.
Here is the desired functionality of the form
Here is a simple working demo , you could refer to
Models
public class ApplicationUser: IdentityUser
{
public DateTime BirthDate { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class RegisterVM
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Birth date")]
public DateTime BirthDate { get; set; }
public string City { get; set; }
public string Country { get; set; }
[Display(Name ="Management role")]
public string role { get; set; }
public List<IdentityRole> RoleList { get; set; }
}
DbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole, string>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<ApplicationUser> ApplicationUser { get; set; }
}
Register view
#model MVC3_1Identity.Models.ViewModels.RegisterVM
<div class="row">
<div class="col-md-4">
<form method="post">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="BirthDate"></label>
<input asp-for="BirthDate" class="form-control" />
<span asp-validation-for="BirthDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="City"></label>
<input asp-for="City" class="form-control" />
<span asp-validation-for="City" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Country"></label>
<input asp-for="Country" class="form-control" />
<span asp-validation-for="Country" class="text-danger"></span>
</div>
<div class="custom-checkbox">
<label asp-for="role"></label>
#for (var i = 0; i < Model.RoleList.Count; i++)
{
<input asp-for="role" type="radio" value="#Model.RoleList[i].Name" />
<label asp-for="#Model.RoleList[i].Name">#Model.RoleList[i].Name</label>
}
<span asp-validation-for="role" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
</div>
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Controller
public IActionResult Register()
{
var model = new RegisterVM();
model.RoleList = _roleManager.Roles.ToList();
return View(model);
}
[HttpPost]
public async Task<IActionResult> Register(RegisterVM model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
BirthDate=model.BirthDate,
City=model.City,
Country =model.Country,
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_userManager.AddToRoleAsync(user,model.role).Wait();
_logger.LogInformation("User created a new account with password and role.");
// other logic you want
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
return View();
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser ,IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
}
Result:

How to View data from a different table and Insert data in to another table from one view page using Asp.net core MVC?

Scenario
I am going to create a simple interface to insert donations from students using Asp.net Core MVC and EF. According to the App, before entering donation I have to display student information (from Student table) by using student's Admission number (Add_No).
Then I have to insert donation data using the same view but using a different submit button to the table Welfare.
Problem:
I tried it as following ways as shown in my code blocks. Insertion is successful. But I couldn't show student info on the screen even though I retrieve them.
Code for View.cshtml
#model Student_Management.Models.Welfare
#{
ViewData["Title"] = "Wcreate";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h4 align="center">Welfare Donation Entry Form</h4>
<hr />
<table>
<tr>
<td>
#using (Html.BeginForm("getStudentInfo", "Payments", FormMethod.Post))
{
<div class="form-group">
<label asp-for="Add_No" class="control-label"></label>
<input asp-for="Add_No" class="form-control" id="t1" name="A" />
<span asp-validation-for="Add_No" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="getStudentInfo" class="btn btn-primary" id="btngetStudentInfo" />
</div>
}
</td>
<td>
<ul>
#if (ViewBag.students != null)
{
#foreach (var std in ViewBag.students)
{
<li>
#std
</li>
}
}
</ul>
</td>
</tr>
</table>
Code for Controller
public class PaymentsController : Controller
{
private readonly ConnectionString _context;
public PaymentsController(ConnectionString context)
{
_context = context;
}
public IActionResult Wcreate(Welfare welfare, string A)
{
int maxR_No = _context.Welfare.Max(p => p.R_No);
maxR_No = maxR_No + 1;
welfare.R_No = maxR_No;
if (ModelState.IsValid)
{
_context.Add(welfare);
_context.SaveChanges();
ModelState.Clear();
}
return View();
}
public ActionResult getStudentInfo(string A)
{
var items = _context.Students.Where(x => x.Add_No == A)
.Select(x => new
{
P1 = x.F_Name,
P2 = x.Class + "-" + x.ClassNo
});//.ToList();
ViewData["students"] = items;
return RedirectToAction("Wcreate");
}
namespace Student_Management.Models
{
public class Welfare
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int R_No { get; set; }
[Required]
public string Add_No { get; set; }
[Required]
public string Grade { get; set; }
[Required]
public string STClassNo { get; set; }
[Required]
public string Month { get; set; }
[Required]
public string Year { get; set; }
[Required]
public string Rs { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
public string cmt { get; set; }
As hint given by Jasen, I modified my action method and view as follows.
<td>
#using (Html.BeginForm("getStudentInfo", "Payments", FormMethod.Post))
{
<div class="form-group">
<label asp-for="Add_No" class="control-label"></label>
<input asp-for="Add_No" class="form-control" id="t1" name="A" />
<span asp-validation-for="Add_No" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="getStudentInfo" class="btn btn-primary" id="btngetStudentInfo" />
</div>
}
</td>
<p>#TempData["info"]</p>
<p>#(TempData["info2"] +"-" + TempData["info3"])</p>
Action method
public ActionResult getStudentInfo(string A)
{
var items = _context.Students.Where(x => x.Add_No == A)
.Select(x => new
{
P1 = x.F_Name,
P2 = x.Class,
p3=x.ClassNo
}).ToList();
TempData["info"] = items[0].P1;
TempData["info2"] = items[0].P2;
TempData["info3"] = items[0].p3;
return RedirectToAction("Wcreate");
}

DropDown list [asp.net core 2.0]

Hi im having problem with my view
as you can see there is an input and a select option
<div class="form-group">
<label asp-for="DomWasLoc" class="control-label"></label>
<input asp-for="DomWasLoc" class="form-control" id="firstname" name="firstname" />
<select class="form-control"
id="name" name="name"
asp-items="#(new SelectList(ViewBag.LocationList, "LocName","LocName"))">
<option value="">- Select -</option>
</select>
<span asp-validation-for="DomWasLoc" class="text-danger"></span>
</div>
every time you select an item on on the dropdownList the selected item value is inputted in the input box.
but every time i click submit i always shows this error
AspNetCore._Views_DomesticWastes_Create_cshtml+d__25.MoveNext() in Create.cshtml
+
asp-items="#(new SelectList(ViewBag.LocationList, "LocName","LocName"))">
what should i do so that when i click submit it ignore the select and just add the data from input box?
thanks in advance guys! :)
There is no need to combine input and select for the same field DomWasLoc. You could bind the DomWasLoc directly to select.
A demo code like below:
<div class="form-group">
<label asp-for="DomWasLoc" class="control-label"></label>
#*<input asp-for="DomWasLoc" class="form-control" />*#
<select class="form-control"
asp-for="DomWasLoc"
asp-items="#(new SelectList(ViewBag.LocationList, "LocName","LocName"))">
<option value="">- Select -</option>
</select>
<span asp-validation-for="DomWasLoc" class="text-danger"></span>
</div>
Update:
Complte Code:
1. Model
public class Location
{
public int Id { get; set; }
public string LocName { get; set; }
}
public class DomesticWaste
{
public int Id { get; set; }
public string DomWasLoc { get; set; }
}
Controller
// GET: DomesticWastes/Create
public IActionResult Create()
{
List<Location> locationlist = new List<Location>() {
new Location{ Id = 1, LocName = "L1" },
new Location{ Id = 2,LocName = "L2" },
new Location{ Id = 3,LocName = "L3" }
};
ViewBag.LocationList = locationlist;
return View();
}
// POST: DomesticWastes/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,DomWasLoc")] DomesticWaste domesticWaste)
{
if (ModelState.IsValid)
{
_context.Add(domesticWaste);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(domesticWaste);
}