MVC5 ActionResult model being passed as empty - asp.net-mvc

I have read plenty of posts about this issue but I can't seem to find a solution that fits with my implementation. I'm giving MVC another attempt (I'm a webforms guy). The model being passed to my ActionResult is basically empty when it should be populated. I'm starring at the sample that works and I can find no differences. It seems to be something impossible to debug too. Any pointers will be greatfully appreciated.
View:
#model WebApplication1.Models.SiteViewModel
#{
ViewBag.Title = "Delete your site";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<p>Are you sure you want to delete this site?</p>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false)
<ul class="list-group">
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Name)</b></div>
<div class="col-md-6">#Model.Name</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Phase)</b></div>
<div class="col-md-6">#Model.Phase</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Type)</b></div>
<div class="col-md-6">#Model.Type</div>
</div>
</li>
</ul>
<button type="submit" class="btn btn-danger">Delete</button>
#Html.ActionLink("Back to List", "Index", null, new { #class = "btn btn-default" })
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Model:
public class SiteViewModel
{
public SiteViewModel()
{
Services = new List<ServiceModel>();
}
public SiteViewModel(SiteModel site)
{
this.SiteId = site.SiteId;
this.Name = site.Name;
this.Type = site.Type;
this.Phase = site.Phase;
this.Services = site.Services;
}
public int SiteId { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
public SchoolType Type { get; set; }
public SchoolPhase Phase { get; set; }
public DateTime? Deleted { get; set; }
public virtual ICollection<ServiceModel> Services { get; set; }
}
Controller Action:
[HttpPost]
public ActionResult Delete(SiteViewModel model)
{
var site = siteRepository.GetById(model.SiteId);
if (site == null) { throw new ArgumentException(string.Format("Site with Id [{0}] does not exist", model.SiteId)); }
try
{
siteRepository.SoftDeleteAndSubmit(site);
base.SetSuccessMessage("The site has been (soft) deleted.");
return RedirectToAction("Index");
}
catch (Exception ex)
{
base.SetErrorMessage("Whoops! Couldn't delete the site. The error was [{0}]", ex.Message);
}
return View(model);
}
Thanks,
Chris.

If you want a fully-populated model you'll need to use form elements or the form helper functions to post your data.
#using(Html.BeginForm())
{
<input type="text" value="#Model.Name" />
// or
Html.TextBoxFor(m => m.Name)
...
}
The example you linked relies on a URL routing rule to match a model's parameter. So you need to rename SiteId to Id or add/modify a routing rule.
If you only need the id then I would just pass that parameter as it will make your intent more obvious and is less prone to breaking.
#using(Html.BeginForm())
{
#Html.HiddenFor(m => m.Id)
<button type="submit">Delete</button>
}
[HttpPost]
public ActionResult Delete(int id)
{
var site = siteRepository.GetById(id);
...
}

Related

EF Core ModelSate Invalid because form is passing foreign key name and value attributes

Very new to MVC Core and C# and just as I think I'm getting the hang of something there's a new curve ball. I have a form which is based on a model which has a foreign key. When I submit the form to the controller the modelState is invalid because the form is passing something back which isn't in the model it is based on. Here is the model:
public partial class Agreement
{
public Agreement()
{
AgreementAmendments = new HashSet<AgreementAmendment>();
Bundles = new HashSet<Bundle>();
Invoices = new HashSet<Invoice>();
}
public int Id { get; set; }
public int OrgId { get; set; }
public string AgreementNumber { get; set; } = null!;
public string? IrespondReference { get; set; }
public string? DocumentLink { get; set; }
public virtual Organization Org { get; set; }
public virtual ICollection<AgreementAmendment> AgreementAmendments { get; set; }
public virtual ICollection<Bundle> Bundles { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
This is the Get Create Action Method:
public IActionResult Create()
{
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "ShortName");
return View();
}
This is the form:
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrgId" class="control-label">Organization</label>
<select asp-for="OrgId" class ="form-control" asp-items="ViewBag.OrgId"></select>
</div>
<div class="form-group">
<label asp-for="AgreementNumber" class="control-label">Agreement Number</label>
<input asp-for="AgreementNumber" class="form-control" />
<span asp-validation-for="AgreementNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IrespondReference" class="control-label">Internal Reference</label>
<input asp-for="IrespondReference" class="form-control" />
<span asp-validation-for="IrespondReference" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DocumentLink" class="control-label">Document Link</label>
<input asp-for="DocumentLink" class="form-control" />
<span asp-validation-for="DocumentLink" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
And this is the HttpPost Create Action Method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("OrgId,AgreementNumber,IrespondReference,DocumentLink")] Agreement agreement)
{
if (ModelState.IsValid)
{
_context.Add(agreement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "Id", agreement.OrgId);
return View();
}
When I look at the results of the ModelState it shows an error with the Org Key but as far as I can see the form should just be returning the OrgId as per the model. Can someone please let me know where I am going wrong.
Created a View Model for Agreements to handle the form input and then passed that to the base Agreement Model which seems like unnecessary work. Why can't EF Core handle this stuff without having to constantly build View Models just because there is a foreign key?
Anyway, this is the final HttpPost code for others who run into the same issue:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(AgreementWriteViewModel newagreement)
{
if (ModelState.IsValid)
{
var model = new Agreement
{
OrgId = newagreement.OrgId,
AgreementNumber = newagreement.AgreementNumber,
IrespondReference = newagreement.IrespondReference,
DocumentLink = newagreement.DocumentLink,
};
_context.Add(model);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "ShortName", newagreement.OrgId);
return View();
}

Creating a comment-like box in ASP.NET Core

I'm trying to create a comment and like box like the one shown in this tutorial what are the main changes I need to make in order to make it work with ASP.NET Core 3.1
https://www.c-sharpcorner.com/blogs/how-to-create-a-comment-box-session-as-like-facebook-comment-in-asp-net
To create a Facebook-like comment box in ASP.NET Coreļ¼Œ I do a demo to make it, referring to official documentation to configure database:https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/adding-model?view=aspnetcore-5.0&tabs=visual-studio
1.To Create the comment box, we need to create User, Reply, Comment models to add some tables ,and add some ViewModels.
User.cs:
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Email { get; set; }
public string ImageUrl { get; set; }
public DateTime? CreateOn { get; set; }
}
Reply.cs:
public class Reply
{
[Key]
public int Id { get; set; }
[Required]
public string Text { get; set; }
public DateTime? CreateOn { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
public int CommentId { get; set; }
[ForeignKey("CommentId")]
public virtual Comment Comment{get;set;}
}
Comment.cs:
public class Comment
{
[Key]
public int Id { get; set; }
[Required]
public string Text { get; set; }
public DateTime? CreateOn { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
public ICollection<Reply> Replies { get; set; }
public ICollection<User> Users { get; set; }
}
LoginVM.cs:
public class LoginVM
{
[Required]
[Key]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
RegisterVM.cs:
public class RegisterVM
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
[Compare("Password")]
public string ConfirmPassword { get; set; }
[Required]
public string Email { get; set; }
}
ReplyVM.cs:
public class ReplyVM
{
public string Reply { get; set; }
public int CID { get; set; }
}
Installs the EF Core package
From the Tools menu, select NuGet Package Manager > Manage NuGet Packages for solution.
[Note]My core3.1 installed 3.1.21 version
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design
Microsoft.EntityFrameworkCore.Proxies
Microsoft.Extensions.Configuration.JSON
creates a database context class: Models/ApplicationDbContext.cs
public class ApplicationDbContext: DbContext
{
public ApplicationDbContext(DbContextOptions options) :base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Reply> Replies { get; set; }
public DbSet<commentbox.ViewModels.LoginVM> LoginVM { get; set; }
}
4.Add codes in Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.AddControllersWithViews();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ApplicationDbContext")));
}
5.Add app.UseSession(); in Startup.Configure
6.Add a connection string to the appsettings.json file:
"ConnectionStrings": {
"ApplicationDbContext": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDbContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"}
7.In the Package Manager Console (PMC), enter the following commands separately:
Add-Migration InitialCreate
Update-Database
8.Add HomeController to set userId=0 when people first Login without register, add AccountController to manage user login or not, add ChatRoomController to manage user post comments or reply.
HomeController.cs:
public class HomeController : Controller
{ ....
public IActionResult Index()
{
ISession session = HttpContext.Session;
session.SetInt32("UserId", 0);
return View();
}
...
}
AccountController.cs:
public class AccountController : Controller
{
private readonly ApplicationDbContext db;
public AccountController(ApplicationDbContext context)
{
db = context;
}
//GET:Account/Register
[HttpGet]
public IActionResult Register()
{
return View();
}
//GET:Account/Register
[HttpPost]
public IActionResult Register(RegisterVM obj)
{
bool UserExistis=db.Users.Any(x=>x.UserName==obj.UserName);
if(UserExistis)
{
ViewBag.UserNameMessage = "This UserName is already in use, try another";
return View();
}
bool EmailExistis = db.Users.Any(y => y.Email == obj.Email);
if (EmailExistis)
{
ViewBag.EmailMessage = "This Email is already in use, try another";
return View();
}
//if username and email is unique, then we save or register the user
User u = new User();
u.UserName = obj.UserName;
u.Password = obj.Password;
u.Email = obj.Email;
u.ImageUrl = "";
u.CreateOn = DateTime.Now;
db.Users.Add(u);
db.SaveChanges();
return RedirectToAction("Index","ChatRoom");
}
//GET:Account/Login
[HttpGet]
public IActionResult Login()
{
return View();
}
//GET:Account/Login
[HttpPost]
public IActionResult Login(LoginVM obj)
{
bool existis = db.Users.Any(u => u.UserName == obj.UserName&&u.Password==obj.Password);
if(existis)
{
ISession session = HttpContext.Session;
session.SetInt32("UserId", db.Users.Single(x => x.UserName == obj.UserName).Id);
return RedirectToAction("Index","ChatRoom");
}
//if invalid credentials
ViewBag.Message = "Invalid Credentials!";
return View();
}
}
ChatRoomController.cs:
public class ChatRoomController: Controller
{
private readonly ApplicationDbContext db;
public ChatRoomController(ApplicationDbContext context)
{
db = context;
}
public IActionResult Index()
{
var comments = db.Comments.Include(x => x.Replies).ThenInclude(x=>x.User).OrderByDescending(x => x.CreateOn)
.ToList();
return View(comments);
}
//Post:ChatRoom/PostReply
[HttpPost]
public ActionResult PostReply(ReplyVM obj)
{
ISession session = HttpContext.Session;
int userId =(int)session.GetInt32("UserId");
if (userId==0)
{
return RedirectToAction("Login", "Account");
}
Reply r = new Reply();
r.Text = obj.Reply;
r.CommentId = obj.CID;
r.UserId =userId;
r.CreateOn = DateTime.Now;
db.Replies.Add(r);
db.SaveChanges();
return RedirectToAction("Index");
}
//Post:ChatRoom/PostComment
[HttpPost]
public ActionResult PostComment(string CommentText)
{
ISession session = HttpContext.Session;
int userId = (int)session.GetInt32("UserId");
if (userId == 0)
{
return RedirectToAction("Login", "Account");
}
Comment c = new Comment();
c.Text = CommentText;
c.CreateOn = DateTime.Now;
c.UserId = userId;
db.Comments.Add(c);
db.SaveChanges();
return RedirectToAction("Index");
}
}
9.Add views: Login.cshtml(for Login action in AccountController), Register.cshtml(for Register action in AccountController), index.cshtml(for Index action in ChatRoomController).
Login.cshtml:
#model commentbox.ViewModels.LoginVM
#{
ViewData["Title"] = "Login";
}
<h1>Login</h1>
#Html.AntiForgeryToken()
<div class="row">
<div class="col-md-4">
<form asp-action="Login">
<h4 class="text-danger">#ViewBag.Message</h4>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserName" class="control-label"></label>
<input asp-for="UserName" class="form-control" />
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</form>
</div>
</div>
Register.cshtml:
#model commentbox.ViewModels.RegisterVM
#{
ViewData["Title"] = "Register";
}
<h1 class="text-success">Register User</h1>
<div class="row">
<div class="col-md-4">
<form asp-action="Register">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserName" class="control-label"></label>
<input asp-for="UserName" class="form-control" />
<span asp-validation-for="UserName" class="text-danger"></span>
<p class="text-danger">#ViewBag.UserNameMessage </p>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></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="ConfirmPassword" class="control-label"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
<p class="text-danger">#ViewBag.EmailMessage </p>
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-primary" style="border-radius:20px"/>
</div>
</form>
</div>
</div>
Index.cshtml:
#model IEnumerable<commentbox.Models.Comment>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<h2 class="text-success text-center"> someone's post or some other things</h2>
#using (Html.BeginForm("PostComment", "ChatRoom", FormMethod.Post))
{
<input type="text" name="CommentText" placeholder="Type new comment..."
style="width:700px; height:60px; font-size:20px; margin-top:10px" />
<br />
<input type="submit" value="Post Comment" class="btn btn-success "
style="margin-top: 10px;margin-bottom: 10px " />
}
<table class="table text-center">
<tbody>
#foreach (var comment in Model)
{
<tr style="border:1px solid black;">
<td>
<span style="margin-right:15px;font-size:16px;color:green">
#Html.DisplayFor(modelItem => comment.User.UserName)
</span>
<span style="font-size:20px">
#Html.DisplayFor(modelItem => comment.Text)
</span>
<span style="margin-left:10px">
#Html.DisplayFor(modelItem => comment.CreateOn)
</span>
#foreach (var reply in comment.Replies)
{
<br />
<span style="margin-right:15px;font-size:16px;color:blue">
#Html.DisplayFor(modelItem => reply.User.UserName)
</span>
<span style="font-size:19px">
#reply.Text
</span>
<span style="margin-left:10px">
#reply.CreateOn
</span>
}
<br />
#using (Html.BeginForm("PostReply", "ChatRoom", FormMethod.Post))
{<input type="text" name="Reply" placeholder="Type reply..."
style="width:100%; height:60px; font-size:20px; margin-top:10px" />
<br />
<input type="hidden" name="CID" value="#comment.Id" />
<input type="submit" value="Post Reply" class="btn btn-success" style="margin-top :10px" />
}
</td>
</tr>
}
</tbody>
</table>
10.Add link in _Layout.cshtml.
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login">Login</a>
</li>

Comments with replies won't show properly (ASP.NET MVC)

(Reposted question, since the other one was put on hold and then edited but not reopened)
I have a problem with showing comment replies in my comment section on my website. I have made it so there is a Original Comment and that comment can have subcomment (replies) and the way I have set up my code it does work, but if there are 2 original comments and 1 reply on in one section, then it shows the reply og both of them, even though I've coded it to only show on a specific original comment.
Comment model:
namespace ComicbookWebpage.Models
{
public class ComicComment
{
public int Id { get; set; }
public string Comment { get; set; }
public DateTime Posted { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
public int ComicId { get; set; }
public Comic Comic { get; set; }
public List<SubComicComment> SubComicComments { get; set; }
}
}
SubComment model (reply):
namespace ComicbookWebpage.Models
{
public class SubComicComment
{
public int Id { get; set; }
public string CommentText { get; set; }
public DateTime Posted { get; set; }
public SubComicComment() {
Posted = DateTime.Now;
}
public string UserId { get; set; }
public ApplicationUser User { get; set; }
public int ComicId { get; set; }
public Comic Comic { get; set; }
public int OriginalCommentId { get; set; }
public ComicComment ComicComment { get; set; }
}
}
Here's my viewmodel I use for all my data (vm):
namespace ComicbookWebpage.Models.ViewModels
{
public class ComicVM
{
public Comic Comic { get; set; }
public Series Series { get; set; }
public List<ComicComment> ComicComments { get; set; }
public List<SubComicComment> SubComicComments { get; set; }
}
}
So as you can see there is an "OriginalCommentId" in my subcomments table, so that I can tell my subcomments what original comment they belong to, so they're only shown under that specific comment. But the problem is like I said above that it shows my subcomment under 2 different original comments on the same page, if the page has 2 original comments, here's an image:
(Image) Comments in view (Browser SS)
On the right side of every comment, you can see an ID, it's the ID that the comment has and you can clearly see that the ID 9 has a subcomment with ID 2, which is totally wrong according to my coding. Because I'm telling my list to render the data where the original comment id is the same as subcomment's OriginalCommentId, so they should both have ID 9, but the subcomment has ID 2 for some reason...
Here's the controller code (Look at vm.SubComicComments):
public ActionResult Comic(int id)
{
ComicVM vm = new ComicVM();
vm.Comic = db.Comics.Include(m => m.Series).Where(m => m.Id == id).FirstOrDefault();
vm.Series = db.Series.FirstOrDefault();
vm.ComicComments = db.ComicComments.Where(m => m.Comic.Id == id).ToList();
vm.SubComicComments = db.SubComicComments.Where(m => m.ComicId == id && m.ComicComment.Id == m.OriginalCommentId).ToList();
db.Users.ToList();
return View(vm);
}
And here's the view code:
#using Microsoft.AspNet.Identity
#using System.Data.Entity;
#model ComicbookWebpage.Models.ViewModels.ComicVM
#{
ViewBag.Title = #Model.Comic.Title;
}
<a class="btn btn-default" href="/Series/Details/#Model.Comic.SeriesId"><i class="glyphicon glyphicon-menu-left"></i> Back</a>
<hr />
<h5><b>Title:</b> #Model.Comic.Title</h5>
<h5><b>Series:</b> #Model.Comic.Series.Title</h5>
<h5><b>Pages:</b> #Model.Comic.PageAmount</h5>
<hr />
<h4><i class="glyphicon glyphicon-comment"></i> Leave a comment:</h4>
<br />
#if (User.Identity.IsAuthenticated)
{
<div class="col-sm-1">
<div class="thumbnail">
<img class="img-responsive user-photo" src="https://ssl.gstatic.com/accounts/ui/avatar_2x.png">
</div><!-- /thumbnail -->
</div><!-- /col-sm-1 -->
<div class="col-sm-5">
<form action="/Series/Comic/#Model.Comic.Id" method="post">
<input type="hidden" name="Posted" value="#DateTime.Now" />
<input type="hidden" name="UserId" value="#User.Identity.GetUserId()" required />
<input type="hidden" name="ComicId" value="#Model.Comic.Id" />
<textarea class="form-control form-text" type="text" name="Comment" placeholder="Type your comment..." required></textarea>
<br />
<button type="submit" class="btn bg-dark">Send</button>
</form>
</div><!-- /col-sm-5 -->
}
else
{
<h5>You have to be logged in to post a comment.</h5>
<p>Click here to login</p>
}
<div class="row">
<div class="col-md-12">
#if (Model.ComicComments.Count > 0)
{
<h4>(#Model.ComicComments.Count) Comments:</h4>
}
else
{
<h4>0 Comments:</h4>
<p>There are currently no comments posted on this comic book.</p>
}
</div>
</div>
#foreach (var Comment in Model.ComicComments.Where(m => m.ComicId == m.Comic.Id))
{
<div class="comments-container">
<ul id="comments-list" class="comments-list">
<li>
<div class="comment-main-level">
<!-- Avatar -->
<div class="comment-avatar"><img src="https://i9.photobucket.com/albums/a88/creaticode/avatar_1_zps8e1c80cd.jpg" alt=""></div>
<!-- Contenedor del Comentario -->
<div class="comment-box">
<div class="comment-head">
<h6 class="comment-name by-author">#Comment.User.UserName</h6>
<span>posted on #Comment.Posted.ToShortDateString()</span><i>ID: #Comment.Id</i>
</div>
<div class="comment-content">
#Comment.Comment
</div>
</div>
</div>
<!-- Respuestas de los comentarios -->
<ul class="comments-list reply-list">
#if (Model.SubComicComments.Count > 0)
{
foreach (var SubComment in Model.SubComicComments.Where(m => m.OriginalCommentId == m.ComicComment.Id))
{
<li>
<!-- Avatar -->
<div class="comment-avatar"><img src="https://i9.photobucket.com/albums/a88/creaticode/avatar_2_zps7de12f8b.jpg" alt=""></div>
<!-- Contenedor del Comentario -->
<div class="comment-box">
<div class="comment-head">
<h6 class="comment-name">#SubComment.User.UserName</h6>
<span>posted on #SubComment.Posted.ToShortDateString()</span><i>ID: #SubComment.OriginalCommentId</i>
</div>
<div class="comment-content">
#SubComment.CommentText
</div>
</div>
</li>
}
}
</ul>
</li>
</ul>
</div>
}
If you guys can figure out what the heck is wrong here, I would appreciate it. To me the code is pretty logical and should work, but it doesn't, and I've tried so many things but no luck.
Thank you in advance.
For your SubComments foreach statement:
foreach (var SubComment in Model.SubComicComments.Where(m => m.OriginalCommentId == m.ComicComment.Id))
Should be:
foreach (var SubComment in Model.SubComicComments.Where(m => m.OriginalCommentId == Comment.Id))
No? You want to check SubComment.OriginalCommentId against the id in the Comment variable declared in your enclosing Comments iteration.
As an aside, in your first foreach statement, I don't think the where clause is doing anything:
#foreach (var Comment in Model.ComicComments.Where(m => m.ComicId == m.Comic.Id))
ComicID == Comid.Id should always be true as long as your includes have loaded...

asp.net mvc viewmodel getting error public definition for getenumerator

I can get my Products model to my view ok. but I need to pass it in a ViewModel so I can interact with data from several models. When I bind my view to ProductPageViewModel then I get the "no public definition for GetEnumerator" error. I have tried all manner of IEnumerable options and cannot find the right combination.
I included controller and view that work when the view is bound to the model.
The full error is
CS1579: foreach statement cannot operate on variables of type JaniesWebLive.ViewModel.ProductPageViewModel' because
JaniesWebLive.ViewModel.ProductPageViewModel' does not contain a public definition for 'GetEnumerator'
Triggered at #foreach (var item in Model)
Products.cs
public class Products
{
[Key]
public int WpId { get; set; }
public string Category { get; set; }
public string WpProductId { get; set; }
public string ProdDescShort { get; set; }
public string ProdDescLong { get; set; }
public string ProductMedium { get; set; }
public decimal? Price1 { get; set; }
}
ProductPageViewModel.cs
public class ProductPageViewModel
{
public IEnumerable<Products> Products { get; set; }
public Contact Contacts { get; set; }
public string Message { get; set; }
/*more models to be added */
}
ProductsController.cs
public class ProductsController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult ShortList(string prodname)
{
var products = db.DbProducts.Where(m => m.Category == "homepage");
var model = new ViewModel.ProductPageViewModel
{
Products = products,
Message = "Thanks for your business! "
/* more models to be added */
};
return View(model);
}
}
ShortList.cshtml
#model IEnumerable<JaniesWebLive.ViewModel.ProductPageViewModel>
#foreach (var item in Model)
{
<div class="row">
<div class="col-md-4 col-sm-6 col-xs-12">
<p><img class="product-image" src=#Html.DisplayFor(modelItem => item.ProductMedium) /></p><br />
<h3>#Html.DisplayFor(modelItem => item.WpProductId)</h3>
<h5>#Html.DisplayFor(modelItem => item.Price1)</h5>
<h6>#Html.DisplayFor(modelItem => item.ProdDescShort)</h6>
<p class="text-center">#Html.DisplayFor(modelItem => item.ProdDescLong)</p>
<p>
<button class="text-right btn btn-link">SHORT LIST</button>
</p>
</div>
</div>
}
Working controller and view
public ActionResult LongList(string prodname)
{
var model = db.DbProducts.Where(m => m.Category == "homepage");
return View(model);
}
LongList.cshtml
#model JaniesWebLive.Models.Products
#foreach (var item in Model)
{
<div class="row">
<div class="col-md-4 col-sm-6 col-xs-12">
<p><img class="product-image" src=#Html.DisplayFor(modelItem => item.ProductMedium) /></p>
<h3>#Html.DisplayFor(modelItem => item.WpProductId)</h3>
<h5>#Html.DisplayFor(modelItem => item.Price1)</h5>
<h6>#Html.DisplayFor(modelItem => item.ProdDescShort)</h6>
<p class="text-center">#Html.DisplayFor(modelItem => item.ProdDescLong)</p>
<p>
<button class="text-right btn btn-link">LONG LIST</button>
</p>
</div>
</div>
}
you need to change the view to something like
#model JaniesWebLive.ViewModel.ProductPageViewModel
#foreach (var item in Model.Products )
{
<div class="row">
<div class="col-md-4 col-sm-6 col-xs-12">
<p><img class="product-image" src=#Html.DisplayFor(modelItem => item.ProductMedium) /></p><br />
<h3>#Html.DisplayFor(modelItem => item.WpProductId)</h3>
<h5>#Html.DisplayFor(modelItem => item.Price1)</h5>
<h6>#Html.DisplayFor(modelItem => item.ProdDescShort)</h6>
<p class="text-center">#Html.DisplayFor(modelItem => item.ProdDescLong)</p>
<p>
<button class="text-right btn btn-link">SHORT LIST</button>
</p>
</div>
</div>
}
since upr view model is a single object which contain a list property you need to itterate the property not the whole model

Send E-mail via ActionLink in Bootstrap Modal

I'm working on a small "Rent-a-car" application. I have a list of Cars that client can see and i want to implement "Order feature" on that list. Every car should have order button on his side. When the client chooses car and presses button "Order" i want to have opened Bootstrap Modal that says something like "You want to order car that client selected , please enter your personal information". Then he will enter his name, email, and phone number. When he enters that he will press "Submit" button on that modal and he will get message something like "We sent a payment link and instruction for paying on your email. Please check your email." Then the client will get email that will say "You want to order car that the client selected . Please read following instructions: Some text"
I suppose i can do this with Action Links but i don't know how to implement it in my existing code
Please note: This doesn't have to be made using Bootstrap Modals. I am opened for your suggestions. Please review my existing code.
This is my Car model:
public class Car
{
[Key]
public int CarID { get; set; }
public string Model { get; set; }
[DisplayName("Year of production")]
public int YearOfProduction { get; set; }
public string Price { get; set; }
public virtual ICollection<FilePath> FilePaths { get; set; }
[DisplayName("Air Conditioning")]
public string AirConditioning { get; set; }
[DisplayName("Engine")]
public string EngineType { get; set; }
public string Transmission { get; set; }
public string Suitcases { get; set; }
public string Seats { get; set; }
}
This is my Cars controller:
public class CarsController : Controller
{
private CarContext db = new CarContext();
// GET: Cars
public ActionResult Index()
{
return View(db.Cars.ToList());
}
// GET: Cars/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//Car car = db.Cars.Find(id);
Car car = db.Cars.Include(i => i.FilePaths).SingleOrDefault(i => i.CarID == id);
if (car == null)
{
return HttpNotFound();
}
return View(car);
}
// GET: Cars/Create
[Authorize(Roles = "Administrator")]
public ActionResult Create()
{
return View();
}
// POST: Cars/Create
[Authorize(Roles = "Administrator")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "CarID,Model,YearOfProduction,Price,AirConditioning,EngineType,Transmission, Suitcases, Seats")] Car car, HttpPostedFileBase upload)
{
if (ModelState.IsValid)
{
if (upload != null && upload.ContentLength > 0)
{
var photo = new FilePath
{
FileName = Guid.NewGuid().ToString() + System.IO.Path.GetExtension(upload.FileName), //uniqueness of the file name
FileType = FileType.Photo
};
car.FilePaths = new List<FilePath>();
upload.SaveAs(Path.Combine(Server.MapPath("~/Images/Cars"), photo.FileName));
car.FilePaths.Add(photo);
}
db.Cars.Add(car);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(car);
}
// GET: Cars/Edit/5
[Authorize(Roles = "Administrator")]
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Car car = db.Cars.Find(id);
if (car == null)
{
return HttpNotFound();
}
return View(car);
}
[Authorize(Roles = "Administrator")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "CarID,Model,YearOfProduction,Price,AirConditioning,EngineType,Transmission, Suitcases, Seats")] Car car, HttpPostedFileBase upload)
{
if (ModelState.IsValid)
{
if (upload != null && upload.ContentLength > 0)
{
var photo = new FilePath
{
FileName = Guid.NewGuid().ToString() + System.IO.Path.GetExtension(upload.FileName), //uniqueness of the file name
FileType = FileType.Photo
};
car.FilePaths = new List<FilePath>();
upload.SaveAs(Path.Combine(Server.MapPath("~/Images/Cars"), photo.FileName));
car.FilePaths.Add(photo);
}
db.Cars.Add(car);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(car);
}
// GET: Cars/Delete/5
[Authorize(Roles = "Administrator")]
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Car car = db.Cars.Find(id);
if (car == null)
{
return HttpNotFound();
}
return View(car);
}
// POST: Cars/Delete/5
[Authorize(Roles = "Administrator")]
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Car car = db.Cars.Find(id);
db.Cars.Remove(car);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
This is my Index view of the Cars controller where i put list of the cars and modals for ordering:
#model IEnumerable<Testing_identity_2.Models.Car>
#{
ViewBag.Title = "Index";
}
<h2>AVAILABLE CARS</h2>
#if (ViewContext.HttpContext.User.IsInRole("Administrator"))
{
<p>
#Html.ActionLink("Create New", "Create")
</p>
}
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Model)
</th>
<th>
#Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td class="col-md-3">
#Html.DisplayFor(modelItem => item.Model)
</td>
<td class="col-md-2">
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<button class="btn btn-default btn-sm" data-target="#orderModal" data-toggle="modal">Order</button>
#Html.ActionLink("Details", "Details", new {id = item.CarID}, new {#class = "btn btn-default btn-sm"})
#if (ViewContext.HttpContext.User.IsInRole("Administrator"))
{
#Html.ActionLink("Edit", "Edit", new {id = item.CarID}, new {#class = "btn btn-default btn-sm"})
}
#if (ViewContext.HttpContext.User.IsInRole("Administrator"))
{
#Html.ActionLink("Delete", "Delete", new {id = item.CarID}, new {#class = "btn btn-default btn-sm"})
}
</td>
</tr>
}
</table>
<div class="modal" data-keyboard="false" data-backdrop="static" id="orderModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Please enter your personal information</h4>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="inputName">Name</label>
<input class="form-control" placeholder="Name" type="text" id="inputName" />
</div>
<div class="form-group">
<label for="inputEmail">Email</label>
<input class="form-control" placeholder="Email" type="text" id="inputEmail" />
</div>
<div class="form-group">
<label for="inputPhoneNumber">Phone Number</label>
<input class="form-control" placeholder="Phone Number" type="text" id="inputPhoneNumber" />
</div>
</form>
</div>
<div class="modal-footer">
<button id="btnSubmitModal" class="btn btn-primary">Submit</button>
<button class="btn btn-primary" id="btnHideModal2">Close</button>
</div>
</div>
</div>
</div>
<div class="modal" data-keyboard="false" data-backdrop="static" id="orderModal1" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
<div class="modal-body">
<form>
<h4 class="modal-title">We sent a payment link on your email.</h4>
<br />
<h4 class="modal-title">Please check your email.</h4>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="btnHideModal1">Close</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#btnSubmitModal').click(function () {
$('#orderModal').modal('hide');
});
$('#orderModal').on('hide.bs.modal', function (){
$('#orderModal1').modal('show');
});
$('#btnHideModal1').click(function () {
$('#orderModal1').modal('hide');
});
$('#btnHideModal2').click(function () {
$('#orderModal').modal('hide');
$('#orderModal1').modal('hide');
});
});
</script>
There are several things that you are asking for. First, You need to add an actionlink tag with a binding id in your #foreach loop, for example:
#Html.Actionlink("Order car here!", "Order", new { id=item.CarID })
With the controller actionresult looking similar to this:
Public ActionResult Order(int id)
{
return View("Order");
}
Second, you want to have an order page in which the user can enter their data, using razor or angularJS. I recommend that you have a separate model for storing user data:
class client
{
public int userID { get; set; }
public string email { get; set; }
public string Name { get; set; }
public int PhoneNumber { get; set; }
}
An example Order page:
<div>
<div>
#Html.Label("emailaddress","Email address")
#Html.Editor("Email")
</div>
<div>
#Html.Label("NameLabel", "First Name")
#Html.Editor("Name")
</div>
<div>
#Html.Label("PhoneNumberLabel", "Phone Number")
#Html.Editor("PhoneNumber")
</div>
#Html.ActionLink("Submit order form", "submitOrder", new { email=Model.email, name=Model.name, PhoneNumber=Model.PhoneNumber})
</div>
PLEASE NOTE: The order form is a rough outline and will need some work to submit the forms data. The form will then submit this data to another ActionResult that will utilise the SMTP namespace(System.Net.Mail):
public ActionResult subOrder(string email, string name,
{
MailMessage mail = new MailMessage("exampleNoReponseEmail#emailexample.com", email);
SmtpClient client = new SmtpClient();
client.Port = 25;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Host = "smtp.google.com";
mail.Subject = "Car payment conformation";
mail.Subject = "Dear, " + name + ", "You want to order car that the client selected . Please read following instructions: Some text";
client.Send(mail);
return View("ConfirmPayment");
}
The confirm email view:
<h1>Email Sent!</h1>
<p>We sent a payment link and instruction for paying on your email. Please check your email.</p>
These links about sending email might also help:
1. Send e-mail via SMTP using C#
2. Sending email in .NET through Gmail
3. https://msdn.microsoft.com/en-us/library/system.net.mail.mailmessage(v=vs.110).aspx
4. https://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient(v=vs.110).aspx
I hope this helps!

Resources