Why do Validation Errors persist? - asp.net-mvc

A user submits registration information to the form:
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Nbk">Nbk:</label>
<%= Html.TextBox("Nbk",null, new {Disabled="Disabled" })%>
<%= Html.ValidationMessage("Nbk", "*") %>
</p>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name") %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Email">Email:</label>
<%= Html.TextBox("Email") %>
<%= Html.ValidationMessage("Email", "*") %>
</p>
<p>
<label for="MailCode">MailCode:</label>
<%= Html.TextBox("MailCode") %>
<%= Html.ValidationMessage("MailCode", "*") %>
</p>
<p>
<label for="TelephoneNumber">TelephoneNumber:</label>
<%= Html.TextBox("TelephoneNumber") %>
<%= Html.ValidationMessage("TelephoneNumber", "*") %>
</p>
<p>
<label for="OrganizationId">OrganizationId:</label>
<%= Html.TextBox("OrganizationId") %>
<%= Html.ValidationMessage("OrganizationId", "*") %>
</p>
<p>
<label for="OrganizationSponsorId">OrganizationSponsorId:</label>
<%= Html.TextBox("OrganizationSponsorId") %>
<%= Html.ValidationMessage("OrganizationSponsorId", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
Controller:
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(DefectSeverityAssessmentBusiness.ModelRegistration registration)
{
registration.Nbk = StateController.GetNbk(Request);
try
{
var errors = DataAnnotationsValidationRunner.GetErrors(registration);
if (errors.Any())
foreach (var item in errors)
{
if( ModelState[item.PropertyName].Errors.Count==0)
ModelState.AddModelError(item.PropertyName, item.ErrorMessage);
//ModelState.SetModelValue(item.PropertyName,ViewData[item.PropertyName].ToValueProvider());
}
if (ModelState.IsValid)
{
_RegistrationRepository.CreateRegistration(registration);
return RedirectToAction("Index", "Assessment");
}
}
catch (Exception exception)
{
ModelState.AddModelError("Exception", exception);
}
return View();
}
controller factory:
ControllerBuilder.Current.SetControllerFactory(new Models.InMemoryRepositories.InMemoryControllerFactory());
public class InMemoryControllerFactory : IControllerFactory
{
private readonly Dictionary<string, IController> _controllers = new Dictionary<string, IController>();
private readonly Dictionary<string, Func<IController>> _controllerFactoryDictionary =
new Dictionary<string, Func<IController>>();
public InMemoryControllerFactory()
{
InitializeDictionary();
}
private void InitializeDictionary()
{
AddFactory(typeof(Controllers.HomeController), () => new Controllers.HomeController(
new Models.InMemoryRepositories.Registration.InMemoryRegistrationRepository()));
AddFactory(typeof(Controllers.RegistrationController),() => new Controllers.RegistrationController(
new Models.InMemoryRepositories.Registration.InMemoryRegistrationRepository()));
AddFactory(typeof(Controllers.AssessmentController),()=> new Controllers.AssessmentController(
new Models.InMemoryRepositories.Registration.InMemoryDefectRepository(),
new Models.InMemoryRepositories.Registration.InMemoryAssessmentRepository())
);
}
private void AddFactory(Type type, Func<IController> creator)
{
const string Str_Controller = "Controller";
var fullname = type.Name;
Debug.Assert(fullname.EndsWith(Str_Controller));
var controllerName= fullname.Substring(0, fullname.Length - Str_Controller.Length);
Func<string, Func<IController>> controllerFactoryFunc = (_controllerName) =>
{
return () =>
{
//var controllerName=ControllerNameFunc(type);
if (!_controllers.ContainsKey(_controllerName))
_controllers.Add(_controllerName, creator());
return _controllers[_controllerName];
};
};
_controllerFactoryDictionary.Add(controllerName, controllerFactoryFunc(controllerName));
}
#region IControllerFactory Members
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return _controllerFactoryDictionary[controllerName]();
}
/// <summary>
/// Code via http://nayyeri.net/custom-controller-factory-in-asp-net-mvc
/// </summary>
/// <param name="controller"></param>
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
(controller as IDisposable).Dispose();
else
controller = null;
}
#endregion
}
Through the controller with an invalid value at first, then a valid one, but the error message stays and modelstate stays invalid. Why does this happen? i'm using the default model binder, but the included controller factory.

A Controller should be instantiated per request. Think of the ControllerContext being much like the HttpContext. It represents the state of a single request. Since you're storing controllers in a static dictionary, you're not clearing the state of the controller for each request, thus it holds onto its modelstate.
In general, you should not use the Singleton pattern with controllers because of this. Otherwise you're responsible for clearing its state on each request. You're probably not getting much of a perf benefit anyways by doing that.

Related

How add data in asp.net mvc?

If I add data to table witch has no relationships, it's all good: data is adding. But if table have relationships, this is something wrong
Here is my project, what i mean is, for example AddSt in RouteController.
http://zalil.ru/32249903
Here is controller:
[HttpGet]
public ActionResult AddSt(int RouteId)
{
var routeDetails = (from rd in db.Route
join rdd in db.RouteDetail
on rd.RouteId equals rdd.Route.RouteId ///check
where rd.RouteId == RouteId
select rdd).FirstOrDefault();
return View(routeDetails);
}
[HttpPost]
public ActionResult AddSt(RouteDetail rd)
{
try
{
if (ModelState.IsValid)
{
db.AddToRouteDetail(rd);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (Exception e)
{
ModelState.AddModelError("Error!", e);
}
return View();
}
and view:
<% using (Html.BeginForm("AddSt","Route")) {%>
<%= Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.Route.RouteId)%>
<%= Html.TextBoxFor(model => model.Station)%>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
Why I can't write model => model.RouteId ????
What's wrong?
Why TrainSheduleDBEntities table RouteDetail doesn't generate field RouteID ?
You are only selecting RouteDetail (rdd)
So you want model.RouteId
Just stick a debug on the addst action.
Have a look what's in rd.
I'm guessing that there's no valid routeid in it.

The type or namespace name 'DinnerForm' does not exist in the namespace 'NerdDinner.Models' when trying to implement partial form

Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0234: The type or namespace name 'DinnerForm' does not exist in the namespace 'NerdDinner.Models' (are you missing an assembly reference?)
Source Error:
Line 170:
Line 171: [System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()]
Line 172: public class views_dinners_create_aspx : System.Web.Mvc.ViewPage, System.Web.SessionState.IRequiresSessionState, System.Web.IHttpHandler {
Line 173:
Line 174: private static bool #__initialized;
DinnerFormViewModel.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NerdDinner.Controllers;
namespace NerdDinner.Models
{
public class DinnerFormViewModel
{
// Properties
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
// Constructor
public DinnerFormViewModel(Dinner dinner)
{
Dinner = dinner;
Countries = new SelectList(PhoneValidator.Countries, dinner.Country);
}
}
}
DinnerForm.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Controllers.DinnerFormViewModel>" %>
<%= Html.ValidationSummary("Please correct the errors and try again.") %>
<% using (Html.BeginForm()) { %>
<fieldset>
<p>
<label for="Title">Dinner Title:</label>
<%= Html.TextBox("Title", Model.Dinner.Title) %>
<%=Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="EventDate">Event Date:</label>
<%= Html.TextBox("EventDate", Model.Dinner.EventDate) %>
<%= Html.ValidationMessage("EventDate", "*") %>
</p>
<p>
<label for="Description">Description:</label>
<%= Html.TextArea("Description", Model.Dinner.Description) %>
<%= Html.ValidationMessage("Description", "*") %>
</p>
<p>
<label for="Address">Address:</label>
<%= Html.TextBox("Address", Model.Dinner.Address) %>
<%= Html.ValidationMessage("Address", "*") %>
</p>
<p>
<label for="Country">Country:</label>
<%= Html.DropDownList("Country", Model.Countries) %>
<%= Html.ValidationMessage("Country", "*") %>
</p>
<p>
<label for="ContactPhone">Contact Phone #:</label>
<%= Html.TextBox("ContactPhone", Model.Dinner.ContactPhone) %>
<%= Html.ValidationMessage("ContactPhone", "*") %>
</p>
<p>
<input type="submit" value="Save"/>
</p>
</fieldset>
<% } %>
DinnersControllers.cs (create methods)
//
// GET: /Dinners/Create
public ActionResult Create()
{
Dinner dinner = new Dinner()
{
EventDate = DateTime.Now.AddDays(7)
};
return View(new DinnerFormViewModel(dinner));
}
//
// POST: /Dinners/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
try
{
dinner.HostedBy = "SomeUser";
dinnerRepository.Add(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch
{
foreach (var issue in dinner.GetRuleViolations())
{
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(new DinnerFormViewModel(dinner));
}
}
return View(new DinnerFormViewModel(dinner));
}
Create.aspx
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.DinnerForm>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Host A Dinner
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Host a Dinner</h2>
<% Html.RenderPartial("DinnerForm"); %>
</asp:Content>
DinnerForm.ascx:
Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.DinnerFormViewModel>

MVC server side validation with ASP.net

I am trying to put server side validation for a textbox in MVC website. Here is what I have:
<% using (Html.BeginForm("WebsiteLinks", "Home", FormMethod.Get))
{%>
<%: Html.ValidationSummary("Please enter valid URL and try again.") %>
<fieldset>
<p>
<%=Html.Label("Please enter URL:") %>
<%=Html.TextBox("url")%>
<%= Html.ValidationMessage("url", "*") %>
<input type="submit" value="Crawl" />
</p>
</fieldset>
<% } %>
And in the controller I have this:
public ActionResult WebsiteLinks(string url)
{
if (Regex.IsMatch(url, #"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"))
{
ViewData["AnchorText"] = url;
return View(new Website(url, "Url"));
}
return RedirectToAction("Index");
}
The validation is working fine, but what I want to achieve is if the data is not valid, if the data is not proper url, I want to redirect to the same default page with a message probably here: <%= Html.ValidationMessage("url", "*") %> but I don't know how to do that.
Edit
After I did all the changes recommended below, I am getting an error in the header of the view page. I have Inherits="ViewPageBase<Home>" where Home is the name of the class, Home.cs in the Models folder.
In the home.cs file I have this:
namespace LAX.Models
{
public class UrlModel
{
[Required]
[DisplayName("Please enter URL:")]
[RegularExpression(#"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?")]
public string Url { get; set; }
}
}
in the controller I have:
[HttpPost]
public ActionResult WebsiteLinks(UrlModel model)
{
/*
if (Regex.IsMatch(url, #"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"))
{
ViewData["AnchorText"] = url;
return View(new Website(url, "Url"));
}
else
{
ModelState.AddModelError("url", "Error URL Format");
}
return RedirectToAction("Index");
*/
if (ModelState.IsValid)
{
ViewData["AnchorText"] = model.Url;
return View(new Website(model.Url, "Url"));
}
return RedirectToAction("Index");
}
and in the view I have:
<% using (Html.BeginForm("WebsiteLinks", "Home", FormMethod.Get))
{%>
<%: Html.ValidationSummary("Please enter valid URL and try again.") %>
<fieldset>
<p>
<%=Html.LabelFor(m => m.Url) %>
<%=Html.TextBoxFor(m => m.Url) %>
<%=Html.ValidationMessageFor(m => m.Url) %>
<input type="submit" value="Crawl" />
</p>
</fieldset>
<% } %>
Here is the error: "The type or namespace name 'Home' could not be found (are you missing a using directive or an assembly reference?)"
public ActionResult WebsiteLinks(string url)
{
if (Regex.IsMatch(url, #"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"))
{
ViewData["AnchorText"] = url;
return View(new Website(url, "Url"));
}
else
{
ModelState.AddModelError("url", "*");
}
return RedirectToAction("Index");
}
or you can make this sexier with DataAnnotations, Model, and a strongly typed View
Model:
public class UrlModel
{
[Required]
[DisplayName("Please enter URL:")]
[RegularExpression(#"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?")]
public string Url { get; set; }
}
Controller:
public ActionResult WebsiteLinks(UrlModel model)
{
if (ModelState.IsValid)
{
ViewData["AnchorText"] = model.Url;
return View(new Website(model.Url, "Url"));
}
return RedirectToAction("Index");
}
View:
<%# Page Language="C#" Inherits="ViewPageBase<UrlModel>" %>
<% using (Html.BeginForm("WebsiteLinks", "Home", FormMethod.Get)) {%>
<%: Html.ValidationSummary("Please enter valid URL and try again.") %>
<fieldset>
<p>
<%=Html.LabelFor(m => m.Url) %>
<%=Html.TextBoxFor(m => m.Url) %>
<%=Html.ValidationMessageFor(m => m.Url) %>
<input type="submit" value="Crawl" />
</p>
</fieldset>
<% } %>

Return a generic list to the MVC Controller

I have a class that looks like this;
public class item
{
public string Tradingname { get; set; }
}
I have a Partial View that inherits from the "item" class;
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<item>" %>
<%= Html.TextBoxFor(x => x.Tradingname) %>
In my view I create a number of them. Let's Say 2;
<% using (Html.BeginForm())
{ %>
<% Html.RenderPartial("TradingName", new Binding.Models.item()); %><br />
<% Html.RenderPartial("TradingName", new Binding.Models.item()); %><br />
<input type="submit" />
<%} %>
Then in my controller I was hoping to be able to write this;
[HttpPost]
public ActionResult Index(List<item> items)
or
[HttpPost]
public ActionResult Index([Bind(Prefix = "Tradingname")]List<item> items)
But I can't seem to get any data back from my partial views into my List. Anyone know how I can get a variable list of data back from a variable set of partialViews?
I would recommend you using editor templates:
Controller:
public class HomeController: Controller
{
public ActionResult Index()
{
List<item> model = ...
return View(model);
}
[HttpPost]
public ActionResult Index(List<item> items)
{
...
}
}
and in the view:
<% using (Html.BeginForm()) { %>
<%= Html.EditorForModel();
<input type="submit" />
<% } %>
and finally simply move the partial in ~/Views/Home/EditorTemplates/item.ascx (name and location are important):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<item>" %>
<%= Html.TextBoxFor(x => x.Tradingname) %><br/>

Saving a series of create forms with one submit button in ASP.Net MVC

To give some background on my issue:
I have 3 tables called Products, Packages, and PackageContents. I can go in my application and add products, and then I can create packages out of them. One of the steps of setting up a package is to create all the package contents. To do this, I've created a view that runs through all of the products and sets up a new package content for each - placing the packageID and productID in hidden fields. Then the user can select a dropdown for each one to indicate if the product is in the package, or not.
Here's my issue:
This renders a separate form for every product, each with it's own save button. I'd like to have one submit button that saves all of the new package contents at once. Can someone give me an example of how to write my POST method to loop over and save all new package contents and how I would make a button in my view that saves all of them at once?
My Model:
public class PackageContentViewModel
{
//Properties
public IEnumerable<Product> products { get; set; }
public Product product { get; set; }
public Package package { get; set; }
public PackageContent packageContent { get; set; }
//Constructor
public PackageContentViewModel(int id)
{
CustomerRepository customerRepository = new CustomerRepository();
package = customerRepository.GetPackage(id);
products = customerRepository.FindAllProducts().ToList();
foreach (var product in products)
{
PackageContent packageContent = new PackageContent();
{
packageContent.PackageID = id;
packageContent.ProductID = product.ProductID;
}
}
}
The Controller:
public ActionResult AddContents(int id)
{
Package package = customerRepository.GetPackage(id);
return View(new PackageContentViewModel(id) { });
}
The View:
<% foreach (var product in Model.products)
{ %>
<% using (Html.BeginForm())
{%>
<fieldset>
<legend><%= Html.Encode(product.ProductName) %></legend>
<%= Html.Hidden("PackageContentsID") %>
<%= Html.ValidationMessage("PackageContentsID", "*") %>
<%= Html.Hidden("PackageID", Model.package.PackageID) %>
<%= Html.ValidationMessage("PackageID", "*") %>
<%= Html.Hidden("ProductID", product.ProductID) %>
<%= Html.ValidationMessage("ProductID", "*") %>
<label for="InPackage">InPackage:</label>
<%= Html.TextBox("InPackage") %>
<%= Html.ValidationMessage("InPackage", "*") %>
<label for="Restricted">Restricted:</label>
<%= Html.TextBox("Restricted") %>
<%= Html.ValidationMessage("Restricted", "*") %>
<input type="submit" value="Create" />
</fieldset>
<% } %>
<% } %>
EDIT
I decided to post the code that ended up working for me, in case anyone else comes along and needs the same help:
My View:
<% int i = 0; using (Html.BeginForm("CreateContents", "Packages", new { id = Model.package.PackageID }))
{
foreach (var product in Model.products)
{
%>
<fieldset>
<legend><%= Html.Encode(product.ProductName)%></legend>
<%= Html.Hidden("PackageContents[" + i + "].PackageContentsID")%>
<%= Html.ValidationMessage("PackageContentsID", "*")%>
<%= Html.Hidden("PackageContents[" + i + "].PackageID", Model.package.PackageID)%>
<%= Html.ValidationMessage("PackageID", "*")%>
<%= Html.Hidden("PackageContents[" + i + "].ProductID", product.ProductID)%>
<%= Html.ValidationMessage("ProductID", "*")%>
<label for="InPackage">InPackage:</label>
<%= Html.TextBox("PackageContents[" + i + "].InPackage", "yes")%>
<%= Html.ValidationMessage("InPackage", "*")%>
<label for="Restricted">Restricted:</label>
<%= Html.TextBox("PackageContents[" + i + "].Restricted", "no")%>
<%= Html.ValidationMessage("Restricted", "*")%>
</fieldset>
<%
++i; } %>
<input type="submit" value="Add Contents" />
<% } %>
My Controller:
public RedirectToRouteResult CreateContents(int id, IList<PackageContent> PackageContents)
{
Package package = customerRepository.GetPackage(id);
foreach (var packageContent in PackageContents)
{
customerRepository.Add(packageContent);
customerRepository.Save();
}
return RedirectToAction("SetPrice", new { id = package.PackageID });
}
Here you can read about binding to a list: Model binding to a list
This article is old, in newer MVC version you don't have to define <input type="hidden" name="products.Index" value="0" />, but the rest is valid.
EDIT
Answer to comment:
If only thing that you do is select if product is in the package or not, you can use MultiSelectList.
If you want to specify quantity or other attibutes, you can write:
<% int i = 0; using (Html.BeginForm()) {%>
<% foreach (var product in Model.products) { %>
<label for="Restricted"><%= product.Name %></label>
<%= Html.Hidden("products[" + i + "].ProductId",product.id) %>
<%= Html.TextBox("products[" + i + "].Quantity",0) %>
<br/>
<% ++i; } %>
<% } %>
Post action:
[HttpPost]
public ActionResult Edit(IList<PackageContent> products)
where PackageContent is
class PackageContent
{
int ProductId{get;set;}
int Quantity{get;set;}
}

Resources