ValidationMessage asp.net mvc - c#

I have a question about ModelState.AddModelError method and about the ValidationMessage method.
I am new to ASP.NET MVC and I am a little confused.
I wrote this code:
public ActionResult update(FormCollection collection)
{
int oos = 0;
try
{
oos = int.Parse(collection[0]);
}
catch
{
}
data d = new data();
TryUpdateModel(d , collection.ToValueProvider());
if (ModelState.IsValid)
{
return View("index",d);
}
else
{
ModelState.AddModelError("Date", "Wronge Date");
d.Id = 50;
return View("index",d);
}
}
and this code in the view side
#{
ViewBag.Title = "index";
}
<h2>index</h2>
#TempData["Hi"]
#Html.ValidationMessage("fullname")
#using (Html.BeginForm())
{
#Html.AntiForgeryToken() #Html.TextBox("id", 70)
#Html.TextBox("Date", "3/2/1991 12:00:00 ص")
#Html.ValidationMessage("Date","Please insert the correct Date Format")
<input type="submit">
}
My questions are, why the message Please insert the correct Date Format appears directly when I rune the index while still I did not submit the form, why when I submit the form with error in the date format,the same message appear but not the message that I set to the Date Key in the update method which is Wronge Date.
maybe still I do not understand those two methods so I hope to find somebody to explain them to me.
explnation with example or reference would be appreciated

Please look at http://www.asp.net/mvc/overview/getting-started/introduction/adding-validation
Because you have already entered a message in the View it will use that over your error that you are adding from the controller. As for your fullname you do not have a message set yet, only the placehoder for the field.

Related

How do I handle multi line AddModelError Errors?

I'm trying to check for multiple errors on my form. Here is the code I have:
var hasErrors = false;
var sb = new StringBuilder();
if (string.IsNullOrEmpty(creditCard.CardNumber))
{
hasErrors = true;
sb.AppendLine("Credit card number is required.");
//ModelState.AddModelError("PaymentAmount", "Credit card number is required.");
}
if (string.IsNullOrEmpty(creditCard.ExpirationDateMonth) || string.IsNullOrEmpty(creditCard.ExpirationDateYear))
{
hasErrors = true;
// ModelState.AddModelError("PaymentAmount", "Expiration date is required.");
sb.AppendLine("Expiration date is required.");
}
if (string.IsNullOrEmpty(creditCard.NameOnCard))
{
hasErrors = true;
// ModelState.AddModelError("PaymentAmount", "Name is required.");
sb.AppendLine("Name is required.");
}
decimal amt = 0;
creditCard.PaymentAmount = creditCard.PaymentAmount.Replace("$", string.Empty);
if (!decimal.TryParse(creditCard.PaymentAmount, out amt))
{
hasErrors = true;
//ModelState.AddModelError("PaymentAmount","Amount is invalid.");
sb.AppendLine("Amount is invalid.");
}
if (hasErrors)
{
ModelState.AddModelError("PaymentAmount", sb.ToString().Replace(Environment.NewLine,"<br>"));
return View("CreditCard", creditCard);
}
I'm trying to get AddModelError to display in multiple lines but I'm not having any luck. It's displaying the <br> as text on the screen instead of rending a break.
I had it where the error was being submitted individually but you'd have to submit the form multiple times before you got the errors on screen. That's why the AddModelError is commented out in each line.
Is there a way to display multiple lines on the AddModelError or is there a better way to handle this?
Thanks for the help!
You should call ModelState.AddModelError for each of the errors you have in your controller, IMHO, it is not a good practice to mix your validation logic with the way things are rendered in the user interface. In fact, the MVC pattern is all about separating the three concerns, the model (data), the controller (logic, such as validation) and the views (the user interface).
So I would do something like this:
if (string.IsNullOrEmpty(creditCard.CardNumber))
{
ModelState.AddModelError("PaymentAmount", "Credit card number is required.");
}
if (string.IsNullOrEmpty(creditCard.ExpirationDateMonth) || string.IsNullOrEmpty(creditCard.ExpirationDateYear))
{
ModelState.AddModelError("PaymentAmount", "Expiration date is required.");
}
if (string.IsNullOrEmpty(creditCard.NameOnCard))
{
ModelState.AddModelError("PaymentAmount", "Name is required.");
}
[…]
Then in your view, you can use the following HTML helper to render each error in a list:
If you are using ASP.NET Core:
<div asp-validation-summary="ValidationSummary.ModelOnly"></div>
If you are using the previous versions of ASP.NET MVC:
#Html.ValidationSummary()
This will generate HTML that you can style using CSS.
See here for more info if you are using asp.net core or here for an example if you are using the previous version of ASP.NET MVC.
If you want to display the errors in a different way you can access the errors directly in your view or even better, roll your own helper, see the answers to this question: How do I get the collection of Model State Errors in ASP.NET MVC?

Server side pagination with MVC C# and MySql

I didn't understand how does server-side pagination works with MySql and Datatable in a C# MVC.
I created a Controlled in C#, in which I established the connection with a MySql database (I followed this example in order to do that):
public ActionResult connectDB()
{
const string DB_CONN_STR = "Server=MyServer;Port=MyPort;Uid=MyUid;Database=MyDB;";
MySqlConnection cn = new MySqlConnection(DB_CONN_STR);
try
{
string sqlCmd = "select * from t_documento";
MySqlDataAdapter adr = new MySqlDataAdapter(sqlCmd, cn);
adr.SelectCommand.CommandType = CommandType.Text;
DataTable dt = new DataTable();
adr.Fill(dt); //opens and closes the DB connection automatically !! (fetches from pool)
return Content(JsonConvert.SerializeObject(dt).ToString());
}
catch (Exception ex)
{
Console.WriteLine("{oops - {0}", ex.Message);
return Content(ex.ToString());
}
finally
{
cn.Dispose(); // return connection to pool
}
}
However, in this way, I retrieve all the records stored in that table, but I want to fill the Datatable (the inizialization of my datatable is located in a cshtml page) by implementing the pagination.
I read a lot of articles but I didn't found a clear example with a MySql DB.
Can someone help me, please?
Thank you!
Try this Example of server side pagination.
/Controllers/ProductController.cs
public class ProductController : Controller
{
public object Index(int? page)
{
var products = MyProductDataSource.FindAllProducts(); //returns IQueryable<Product> representing an unknown number of products. a thousand maybe?
var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
var onePageOfProducts = products.ToPagedList(pageNumber, 25); // will only contain 25 products max because of the pageSize
ViewBag.OnePageOfProducts = onePageOfProducts;
return View();
}
}
/Views/Products/Index.cshtml
#{
ViewBag.Title = "Product Listing"
}
#using PagedList.Mvc; //import this so we get our HTML Helper
#using PagedList; //import this so we can cast our list to IPagedList (only necessary because ViewBag is dynamic)
<!-- import the included stylesheet for some (very basic) default styling -->
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
<!-- loop through each of your products and display it however you want. we're just printing the name here -->
<h2>List of Products</h2>
<ul>
#foreach(var product in ViewBag.OnePageOfProducts){
<li>#product.Name</li>
}
</ul>
<!-- output a paging control that lets the user navigation to the previous page, next page, etc -->
#Html.PagedListPager( (IPagedList)ViewBag.OnePageOfProducts, page => Url.Action("Index", new { page }) )

How to pass the results from a HTMLDropDownList to a MVC Controller?

I have a view with a HTML.DropDownList. I am trying to figure out how to capture the selection. The selection contains two values and only one value can be selected. The value is a room number that will be a string input to my Switch in the controller.
I've got the dropdownlist working, I've got the model working and sending data to the view. I can't figure out how to pass through the value of the DropDownList.
I know I need some Jquery to trigger the POST to the controller, not sure what to write though. Any help is appreciated. Code is below.
#using (Html.BeginForm("SelectRoomNumber", "PEO"))
{
<fieldset>
Room Numbers
#Html.DropDownList("RoomList", (SelectList) ViewBag.RoomList)
<p>
<input type="submit" value="Submit" />
</p>
</fieldset>
I am calling the method SelectRoomNumber in the PEO controller.
[HttpPost]
public ActionResult SelectRoomNumber()
{
string roomNumber ="";
string readValueBv = "T";
string readValueAv = "80";
string readValueMv = "Occ";
switch (roomNumber)
{
case ("1B^1001^01"):
model.RmNum = "1B^1001^01";
model.BvInstance = 3000018;
model.AvInstance = 3000022;
model.MvInstance = 3000040;
break;
case ("1B^1002^01"):
model.RmNum = "1B^1002^01";
model.BvInstance = 3000020;
model.AvInstance = 3000023;
model.MvInstance = 3000042;
break;
default:
model.RmNum = "Room";
model.BvInstance = 0;
model.AvInstance= 0;
model.MvInstance= 0;
break;
}
//Start BACnet Message Que
_bacnetAgent.StartActivity(IpAddress);
Thread.Sleep(2000);
//Trigger Read Method BV
_bacnetAgent.Read(deviceId, BvReadBacObj, model.BvInstance, BacProp, out readValueBv);
model.BvRes = readValueBv;
//Trigger Read Method AV
_bacnetAgent.Read(deviceId, AvReadBacObj, model.AvInstance, BacProp, out readValueAv);
model.AvRes = readValueAv;
//Trigger Read Method MV
_bacnetAgent.Read(deviceId, MvReadBacObj, model.MvInstance, BacProp, out readValueMv);
model.MvRes = readValueMv;
return View("PEO", model);
}
Just Add this if you want it as integer:
[HttpPost]
public ActionResult SelectRoomNumber(int RoomList)
{
}
Otherwise as a string :
[HttpPost]
public ActionResult SelectRoomNumber(string RoomList)
{
}
RoomList is the keyname which you have entered in your view:
#Html.DropdownList("KEYNAME")
just create a variable with the same name of the attribute name of your select element.
[HttpPost]
public ActionResult SelectRoomNumber(string RoomList)
{
//rest of your code
}
MVC view works hand in hand with the associated controller, so u can pass a value or values from the view to the controller as long as the id in the view and in the controller are the same names. e.g
in your controller u have this
[HttpPost]
public ActionResult SelectRoomNumber(string RoomList)
{
}
and in your view u should have this
Html.Textbox("RoomList")

Convert ActionResult and PartialView IEnumerable results to return json objects

What is the best approach to take when converting a basic ActionResult to JSON objects and rendering them in a PartialView? My objective is to modify the application so that instead of the page rendering only the comments in the db at the time of the page request to a type of data service that updates thePartialView to add any incoming comments that may have been posted since the last page request. I think the solution I am looking for will use OData in json format and then bind that data using knockout.js, but not sure.
Here is the Controller ActionResult which returns an IEnumerable list of objects from the repository to a PartialView:
[ChildActionOnly]
public ActionResult GetCommentsById(int AId = 0)
{
if (AId == 0)
return HttpNotFound();
return PartialView("_CommentsPartial",
_unitOfWork.ArticleRepository.GetCommentsByArticleId(AId));
}
Here is a snippet of the PartialView to keep things short:
#model IEnumerable<BlogSite.Models.Comment>
#using BlogSite.Helpers;
<ul id="comments-list">
#{
foreach (var comment in Model)
{
<!--Grabs Parent Comment and then all replies w/ParentCommentId b4 grabs new Parent Comment -->
if (comment.isRoot && comment.ParentCommentId == null)
{
<!-- Comment -->
int counter = 0; foreach (var c in Model) { if (c.ParentCommentId == comment.CommentId) { counter += 1; } }
<li id="#comment.CommentId" itemscope itemtype="http://schema.org/UserComments" class="comment-container" tabindex="#comment.CommentId">
Then I call it from the Details view:
<div id="comments-panel" class="panel-box">
<div class="show-comments"><div id="upNdown"></div><span id="showNhide">Show Comments</span></div><br /> <br />
<div id="comments-partial" style="display:none;">
#Html.Action("AddComment", "Comment", new { AId = Model.ArticleId })
#Html.Action("GetCommentsById", "Article", new { AId = Model.ArticleId })
</div>
</div>
How can I make this conversion as painless as possible? Thanks in advance!
I think I gather from your question that the controller already did its work and that you simply want to "consume" the data output from it as if it were an AJAX request using the same js code. You can do this fairly easily by just serializing the data in the model using the Newtonsoft Json.NET api and extensions provided by Forloop.HtmlHelpers. These can be installed as nuget packages if you haven't already.
First, you would place this in your partial view
Note: If you don't want to install the Newtonsoft package you can replace JsonConvert.SerializeObject with the System.Web.Helpers method Json.Encode
#{
using (var context = Html.BeginScriptContext())
{
Html.AddScriptBlock("var jsonData=" + JsonConvert.SerializeObject(Model) + ";");
}
}
Then in your layout page, to ensure that your script block is rendered at the appropriate time, add this call to Html.RenderScripts
#Scripts.Render("~/bundles/jquery")
#*Add any other dependency scripts*#
#Html.RenderScripts()
#RenderSection("scripts", required: false)
This is why you need the Forloop.HtmlHelpers package, these extension methods help mitigate out-of-order script code getting rendered in the partial view before jQuery or anything else has started up.
Hope that helps

Cannot update entity framework model

I have spent nearly seven hours to figure this out and couldn't come up with a solution. So here am I, sharing this problem with you.
Please note that the following example is a simplification and subset of my original project. I tried to simplify it as much as possible for you.
To start, I have two business models:
The following EDMX diagram is as follows:
I am using MVC 4 and I have a simple page where you can enter home and away team names respectively and a save button to save these teams and the match:
CSHTML
#model TestEF.Data.Match
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>NewMatch</title>
</head>
<body>
<div>
Status: #ViewBag.Status
</div>
<div id="NewMatchFormContainer">
#using (Ajax.BeginForm(new AjaxOptions() { Url = "/Match/NewMatch", UpdateTargetId = "NewMatchFormContainer" }))
{
#Html.ValidationSummary(false)
#Html.TextBox("HomeTeamName", "", new { Name = "HomeTeam.TeamName" });
#Html.TextBox("AwayTeamName", "", new { Name = "AwayTeam.TeamName" });
<input type="submit" value="Save" />
}
</div>
</body>
</html>
Controller
public class MatchController : Controller
{
TestEFEntities _dbContext = new TestEFEntities();
public ActionResult Index()
{
return View();
}
public ActionResult NewMatch()
{
return View();
}
[HttpPost]
public ActionResult NewMatch(Match matchData)
{
try
{
if (ModelState.IsValid)
{
using (TransactionScope ts = new TransactionScope())
{
string homeTeamName = matchData.HomeTeam.TeamName;
Team existingHomeTeam = _dbContext.Teams.SingleOrDefault(i => i.TeamName == homeTeamName);
Team homeTeam = existingHomeTeam ?? matchData.HomeTeam;
homeTeam.UpdatedDate = DateTime.Now;
if (existingHomeTeam == null)
{
_dbContext.AddToTeams(homeTeam);
}
else
{
_dbContext.ObjectStateManager.ChangeObjectState(homeTeam, System.Data.EntityState.Modified);
}
string awayTeamName = matchData.AwayTeam.TeamName;
Team existingAwayTeam = _dbContext.Teams.SingleOrDefault(i => i.TeamName == awayTeamName);
Team awayTeam = existingAwayTeam ?? matchData.AwayTeam;
awayTeam.UpdatedDate = DateTime.Now;
if (existingAwayTeam == null)
{
_dbContext.AddToTeams(awayTeam);
}
else
{
_dbContext.ObjectStateManager.ChangeObjectState(awayTeam, System.Data.EntityState.Modified);
}
matchData.HomeTeam = homeTeam;
matchData.AwayTeam = awayTeam;
_dbContext.AddToMatches(matchData);
_dbContext.SaveChanges();
ts.Complete();
}
ViewBag.Status = "Success";
return PartialView(matchData);
}
else
{
ViewBag.Status = "Invalid input.";
return PartialView(matchData);
}
}
catch (Exception ex)
{
ViewBag.Status = "Error: " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message);
return PartialView(matchData);
}
}
}
As you can see inside the controller, the entered team name is compared to those in the database. If one exists, it is to be updated; else inserted. There are no problems with inserts but when an existing team name is entered inside a textbox, I get the following error message:
Cannot insert the value NULL into column 'UpdatedDate', table
'TestEF.dbo.Teams'; column does not allow nulls. INSERT fails. The
statement has been terminated.
I get this error even though inside the controller, I explicitly set the UpdateDate for records that need to be updated and set its state to Modified. However the error message says as if UpdateDate field was not set. I debugged and made sure the fields are updated correctly but in SQL Profiler UpdateDate is not set. I am very confused.
I can share the full source code if needed.
UPDATE I suspect it has something to do with Attach/Detach but I am not sure.
UPDATE 2 I have simplified the code to see whether it works and it does. Then why does the original code not work?
Team homeTeam = new Team() { TeamId = 1 };
Team awayTeam = new Team() { TeamId = 2 };
_dbContext.Teams.Attach(homeTeam);
homeTeam.UpdatedDate = DateTime.Now;
_dbContext.Teams.Attach(awayTeam);
awayTeam.UpdatedDate = DateTime.Now;
Match newMatch = new Match()
{
HomeTeam = homeTeam,
AwayTeam = awayTeam,
UpdateDate = DateTime.Now
};
_dbContext.AddToMatches(newMatch);
_dbContext.SaveChanges();
UpdatedDate does not allow nulls. Make it a nullable column in your database.
And also in your EDMX as scheien mentioned in the comment.
Your schema in EF indicates that Null value is not allowed to be entered while adding/Inserting or Updating.
Make sure you are passing the correct non nullable value.
Also you can change the schema of the table and update the model, so that null can be entered.
Set a breakpoint here: awayTeam.UpdatedDate = DateTime.Now;
Then when you run it you can tell if it's pointing to the existing team or not.
I'm pretty certain that the issue is when you are trying to do an update. In that case you haven't detached your original object, instead you're trying to reassign. Give it a try to detach your existingAwayTeam, and then attach your matchData.AwayTeam, mark it as modified, and try saving it.

Categories