In MVC2, how do I validate fields that aren't in my data model? - asp.net-mvc-2

I am playing with MVC2 in VS 2010 and am really getting to like it. In a sandbox application that I've started from scratch, my database is represented in an ADO.NET entity data model and have done much of the validation for fields in my data model using Scott Guthrie's "buddy class" approach which has worked very well.
However, in a user registration form that I have designed and am experimenting with, I'd like to add a 'confirm email address' or a 'confirm password' field. Since these fields obviously wouldn't exist in my data model, how would I validate these fields client side and server side?
I would like to implement something like 'Html.ValidationMessageFor', but these fields don't exist in the data model. Any help would be greatly appreciated.

I use view models. I don't create the data model instance to persist until the view model is valid.
Below is a simple example. Notice that some of the properties are data models, but the validation properties only exist on this view model.(the base isn't pertinent here)
public class ProblemAddToDepartmentProductView : ViewModel
{
public Problem Problem { get; set; }
public IList<Product> AllProducts { get; set; }
public IList<Department> AllDepartments { get; set; }
public string ProblemId { get; set; }
public string ProblemName { get; set; }
[DisplayName("Choose the product:")]
[Required(ErrorMessage = "Select the Product.")]
public string SelectedProduct { get; set; }
public SelectList GetProducts()
{
var selectList = new SelectList(AllProducts, "Id", "Name");
return selectList;
}
[DisplayName("Choose the department using this problem for that product:")]
[Required(ErrorMessage = "Select the Department.")]
public string SelectedDepartment { get; set; }
public SelectList GetDepartments()
{
var selectList = new SelectList(AllDepartments, "Id", "Name");
return selectList;
}
internal class ProductSelect
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}
It will also help to see it wired on the page:
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%= Html.LabelFor(x => x.SelectedProduct) %>
</div>
<div class="editor-field">
<%= Html.DropDownListFor(x => x.SelectedProduct, Model.GetProducts(),"--Select One--") %>
<%= Html.ValidationMessageFor(x => x.SelectedProduct)%>
</div>
<div class="editor-label">
<%= Html.LabelFor(x => x.SelectedDepartment) %>
</div>
<div class="editor-field">
<%= Html.DropDownListFor(x => x.SelectedDepartment, Model.GetDepartments(),"--Select One--") %>
<%= Html.ValidationMessageFor(x => x.SelectedDepartment)%>
</div>
<p>
<input type="submit" value="Add Selected" />
</p>
</fieldset>
I also do this so the model will have these values if the validation fails, to pull back in the needed data for the drop downs:
p.ProblemId) %>
<%= Html.HiddenFor(p => p.ProblemName) %>

Client-Side:
Using Javascript Validation
and/or
Server-Side:
Validate in Controller (Using FormCollection) - or
Create "CustomViewModel" Class that encapsulates all validation strongly type your view - or
You could add two string properties to your Model. Doing so will allow you to populate an instance of your model in a Controller and validate appropriately in your Model/s...

Related

Insert into DB from ASP.Net form

I have an issue when I want to insert an object into the database.
My model is Colis class which has a foreign key to ZoneReserve (ZoneReserveId), which has a foreign key on Reserve (ReserveId).
In my form I choose an existing ZoneReserve and Reserve, but when I post my form, new lines are created in DB, in table ZoneReserve and Reserve. Entity framework do not retrieve the existing line or I don't know...
I don't know if I'm clear enough, sorry for my english ;)
Do you have any advice ? I'm stuck et I tried everything :(
Thank you guys
Colis Model Class :
public class Colis
{
public int ColisId { get; set; }
[Display(Name = "Code barre du colis")]
public string CodeBarreColis { get; set; }
public bool IndAVendreColis { get; set; }
public virtual TypeColis TypeColis { get; set; }
[Display(Name = "Type de colis")]
public int TypeColisId { get; set; }
public ZoneReserve ZoneReserve { get; set; }
[Display(Name = "Emplacement du colis")]
public int ZoneReserveId { get; set;
}
ZoneReserve Model Class :
public class ZoneReserve
{
public int Id { get; set; }
public string NomZoneReserve { get; set; }
public Reserve Reserve { get; set; }
public int ReserveId { get; set; }
}
Reserve Model Class :
public class Reserve
{
[Display(Name = "Réserve")]
public int Id { get; set; }
public string NomReserve { get; set; }
}
My Action in ColisController :
[HttpPost]
public ActionResult CreerColis(Colis colis)
{
_context.Colis.Add(colis);
_context.SaveChanges();
return RedirectToAction("ListeColis");
}
My Form in the view :
#using (Html.BeginForm("CreerColis", "Colis"))
{
<div class="form-group">
#Html.LabelFor(m => m.Colis.CodeBarreColis)
#Html.TextBoxFor(m => m.Colis.CodeBarreColis, new { #class = "form-control" })
</div>
<div class="checkbox">
<label>
#Html.CheckBoxFor(m => m.Colis.IndAVendreColis) A vendre ?
</label>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.TypeColisId)
#Html.DropDownListFor(m => m.Colis.TypeColisId, new SelectList(Model.TypeColis, "Id", "NomTypeColis"), "Selectionner un type", new { #class = "form -control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserve.Reserve.Id)
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Reserve.Id, new SelectList(Model.Reserve, "Id", "NomReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserveId)
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Id, new SelectList(Model.ZoneReserve, "Id", "NomZoneReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
<button type="submit" class="bt, btn-primary">Enregistrer</button>
}
<script src="~/Scripts/jquery-3.4.1.min.js"></script>
<script>
$(document).ready(function () {
$("#Colis_ZoneReserve_Reserve_Id").change(function () {
$.get("/ZoneReserve/ListeZoneReserveParReserve", { ReserveId: $("#Colis_ZoneReserve_Reserve_Id").val() }, function (data) {
$("#Colis_ZoneReserve_Id").empty();
$.each(data, function (index, row) {
$("#Colis_ZoneReserve_Id").append("<option value='" + row.Id + "'>" + row.NomZoneReserve+ "</option>")
});
});
})
});
</script>
It looks like your razor page is posting info about navigation properties of the Colis object to the controller and creating the full objects instead of creating a new Colis object with just the int foreign key specified.
As is, when posted, '''colis.ZoneReserve''' is not null nor is '''colis.ZoneReserve.Reserve''' reference which tells entity framework to create those related object as well when you .Add(colis) to the context.
[HttpPost]
public ActionResult CreerColis(Colis colis)
{
_context.Colis.Add(colis);
_context.SaveChanges();
return RedirectToAction("ListeColis");
}
You are POSTing unintended parameters to your controller, specifically '''Colis.ZoneReserve.Id''' and '''Colis.ZoneReserve.Reserve.Id''' as you BOUND TO in your razor page (see comments in code):
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserve.Reserve.Id)
<!-- DropDownListFor m.Colis.ZoneReserve.ReserveId will send that (navigation path) value to the server. //-->
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Reserve.Id, new SelectList(Model.Reserve, "Id", "NomReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserveId)
<!-- DropDownListFor m.Colis.ZoneResearch.Id will send that navigation property to the server //-->
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Id, new SelectList(Model.ZoneReserve, "Id", "NomZoneReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
To fix your razor page (and not send unintended values to the server)
change the first drop down list for Reserve to NOT be for anything it'll bind to on the server (you don't even need to POST it's value if you can strip it before submit), one way is to change it's name to something meaningless such as "UnnecessaryData" that won't map in the controller when posted (pseudo-code, not tested)
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserve.Reserve.Id)
#Html.DropDownList(new SelectList(Model.Reserve, "Id", "NomReserve"),
"Selectionner une zone",
new { #class = "form-control", name = "UnnecessaryData" })
</div>
Change the second drop-down-list to map to the correct property on the Colis object, notice all I did was change m => m.Colis.ZoneReserve.Id to the FK property of Colis m => m.Colis.ZoneReserveId:
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserveId)
#Html.DropDownListFor(m => m.Colis.ZoneReserveId, new SelectList(Model.ZoneReserve, "Id", "NomZoneReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
When you POST the form, your Colis object in the controller should have a NULL ZoneReserve property and a non-zero ZoneReserveId property - this will prevent the other data records from being created by entity framework.
Note: You can also simply strip the data from the Colis in the POST controller - but that doesn't correct your implementation on the client razor page that's sending unintended structure to the server in the POST method.
Also note: Because you don't validate that the navigation properties of Colis are NULL in the controller, a malicious user COULD create a lot of crap data on the server by POSTing full object tree data that'll be added with the controller method as implemented.

Displaying Validations using dataannotaion

I am new to Entity Framework, I have created a EDM using Model first appraoch and applied validations using datannotations, everything is working fine but all the validations are displayed at one place but i would like to show validation error messages beside the respective field.
The code i written is as below
public partial class Hardware_services_repairs
{
public class hardwaremetadata
{
[Required]
[RegularExpression(#"^[a-zA-Z]+$", ErrorMessage="Numbers and Special characters are not allowed")]
public string CompanyName { get; set; }
[Required]
[RegularExpression(#"^[a-zA-Z]+$", ErrorMessage="Numbers and Special characters are not allowed")]
public string ContactName { get; set; }
//[Required]
[RegularExpression(#"^[0-9+-]+$",ErrorMessage="Only numbers are allowed")]
public string PhoneNumber { get; set; }
//[Required]
[RegularExpression(#"^[0-9+-]+$", ErrorMessage = "Only numbers are allowed")]
public string MobileNumber { get; set; }
//[Required]
[RegularExpression(#"^[A-Za-z0-9#: ]+$", ErrorMessage = "Special Characters are not allowed")]
public string Address { get; set; }
}
}
The output is as shown below
List of validation errors
Numbers and Special characters are not allowed
Numbers and Special characters are not allowed
Only numbers are allowed
Only numbers are allowed
Special Characters are not allowed
But i want every error message separtely beside the respective field, kindly let me the solution
Thanks in advance
Maybe, the problem is in your View.
The parameter "excludePropertyErrors" of the method "ValidationSummary" must be set to "true":
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
Then, for each field, you have to write something like this:
<div>
#Html.LabelFor(model => model.CompanyName)
</div>
<div>
#Html.EditorFor(model => model.CompanyName)
#Html.ValidationMessageFor(model => model.CompanyName)
</div>
You do not have to add #Html.validationSummary
Add an validationMessageFor to every field you want to validate and show the validation error
<div>
#Html.LabelFor(model => model.CompanyName)
</div>
<div>
#Html.EditorFor(model => model.CompanyName)
#Html.ValidationMessageFor(model => model.CompanyName)
</div>

LINQ to Entities error when trying to bind a dropdownlist

I am trying to create a viewmodel and add a SelectListItem to allow me to bind a dropdown list.
I have a very basic viewmodel that looks like this
public class CreatePurchaseViewModel
{
public Product Product { get; set; }
public IEnumerable<SelectListItem> Products { get; set; }
public int SelectedProductId { get; set; }
public DateTime OrderDate { get; set; }
public bool OrderSent { get; set; }
}
My controller looks like this
[HttpGet]
public ActionResult Create()
{
var model = new CreatePurchaseViewModel
{
Products = context.Products.Select(x =>
new SelectListItem()
{
Text = x.ProductName,
Value = x.ProductID
})
};
return View(model);
}
However it complains that Value = x.Product cant convert type int to string. So if I add a .ToString it compiles ok but when I try load the view I get an error
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
My View
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreatePurchaseViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.SelectedProductId)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.SelectedProductId,Model.Products)
#Html.ValidationMessageFor(model => model.SelectedProductId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.OrderDate)
</div>
<div class="editor-field">
#Html.TextBoxFor(model=>model.OrderDate)
#Html.ValidationMessageFor(model => model.OrderDate)
</div>
<div>
Sent
#Html.RadioButtonFor(model => model.OrderSent, "Sent", new { #checked = true })
Not Sent
#Html.RadioButtonFor(model=>model.OrderSent,"Not Sent")
Im pretty new to both entity framework and mvc so any help would be great.
Thank you
You haven't specified how does your Product model look like but we can assume that the ProductID property is integer so you might need to convert it to a string:
[HttpGet]
public ActionResult Create()
{
var model = new CreatePurchaseViewModel
{
Products = context.Products.Select(x =>
new SelectListItem
{
Text = x.ProductName,
Value = x.ProductID.ToString()
}
)
};
return View(model);
}

ASP.NET MVC Dynamic List Binding

I have a strongly typed mvc page which I wont to bind a unorder list to a list of objects. So in mvc view it might look something like
<% foreach (var item in Model.WhatYouDoL) { %>
<li><%: Html.Encode(item.Text) %><input type="hidden" name="WhatYouDoL[0].Reference" /></li>
<% } %>
My view model might look something like
public class ViewModelQuoteWhatYouDoInMotorTrade
{
public List<WhatYouDo> WhatYouDoL { get; set; }
}
and my list contains object like
public struct WhatYouDo
{
public decimal Percent { get; set; }
public string Reference { get; set; }
public string Text { get; set; }
}
This binds ok providing I use WhatYouDoL[0].Reference with the index ([0]) which when loading I can set with an index. The problem is I want to add and remove from this list on the client side. So I might have some js which adds and extra list item and removes the current. This means I have to somehow manage the indexes in the name and keep them in order and non duplicate on the client side. Does anyone know if there is a way to get around using the index in the name.
Thanks in advance.
There is, probably, a mistake:
<% foreach (var item in Model.WhatYouDoL) { %>
<li><%: Html.Encode(item.Text) %><input type="hidden" name="WhatYouDoL[0].Reference" /></li>
<% } %>
Maybe it should be:
<% foreach (var item in Model.WhatYouDoL) { %>
<li><%: item.Text %><input type="hidden" name="<%: item.Reference %>" /></li>
<% } %>
You don't need to encode as long as you use <: proof

Data is empty in Post method asp.net mvc 2

I`m trying to display classes that have properties of type some custom class inside it.
Model:
public class ComplexParent
{
public SimpleChild First { get; set; }
public SimpleChild Second { get; set; }
}
public class SimpleChild
{
public int Id { get; set; }
public string ChildName { get; set; }
public string ChildDescription { get; set; }
}
Controller:
public ActionResult Testify(int id)
{
ComplexParent par = new ComplexParent();
par.First = new SimpleChild() { Id = id };
par.Second = new SimpleChild()
{
Id = id + 1,
ChildName = "Bob",
ChildDescription = "Second"
};
return View("Testify", par);
}
[HttpPost]
public ActionResult Testify(ComplexParent pComplexParent)
{
return View("Testify", pComplexParent);
}
View:
<% using (Html.BeginForm())
{%>
<fieldset>
<legend>Fields</legend>
<%: Html.EditorFor(x => x.First) %>
<br />
<%: Html.EditorFor(x => x.Second.ChildName)%>
<br/>
<br/>
<br/>
<% Html.RenderPartial("SimpleChild", Model.First); %>
<p>
<input type="submit" value="Watch me :-)" />
</p>
</fieldset>
<% } %>
When it comes to Get it works just fine, I can see all the data. But on post pComplexParent parameter is empty (both properties of a complex class are nulls). Probably Im missing something here, but could not get this to work ...
Small addition: view part that only shows editor for name makes Second child not null and name is set to Bob. But I dont understand how to make it just with EditorFor or DisplayFor methods.
UPDATE: Thanks to Darin Dimitrov, who kindly went throught all my code and found what caused this problem. The exact problem was that if you are using display template, asp.net mvc 2 doesnt post any values back and if whole template is has nothing to post back object is null. I still thinking of the way how to get the data, even if you don`t want to edit it. But using editor template does the thing and I have all objects filled with proper data now.
Your view is a bit of a mess. You are using editor templates along with partials for the first child. It is not very clear what fields are included inside the form. I would recommend you using only editor templates:
Model:
public class ComplexParent
{
public SimpleChild First { get; set; }
public SimpleChild Second { get; set; }
}
public class SimpleChild
{
public int Id { get; set; }
public string ChildName { get; set; }
public string ChildDescription { get; set; }
}
Controller:
[HandleError]
public class HomeController : Controller
{
public ActionResult Testify(int id)
{
var par = new ComplexParent();
par.First = new SimpleChild() { Id = id };
par.Second = new SimpleChild()
{
Id = id + 1,
ChildName = "Bob",
ChildDescription = "Second"
};
return View(par);
}
[HttpPost]
public ActionResult Testify(ComplexParent pComplexParent)
{
return View(pComplexParent);
}
}
View:
<% using (Html.BeginForm()) { %>
<%: Html.EditorFor(x => x.First) %>
<%: Html.EditorFor(x => x.Second) %>
<input type="submit" value="Watch me :-)" />
<% } %>
Editor template for SimpleChild (~/Views/Home/EditorTemplates/SimpleChild.ascx):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SomeNs.Models.SimpleChild>" %>
<%: Html.HiddenFor(x => x.Id) %>
<%: Html.EditorFor(x => x.ChildName) %>
<%: Html.EditorFor(x => x.ChildDescription) %>
Now if you want to have different editor templates for the two child properties you could either specify the editor template name when including it:
<%: Html.EditorFor(x => x.First, "FirstChildEditor") %>
which corresponds to ~/Views/Home/EditorTemplates/FirstChildEditor.ascx or use an [UIHint] attribute at your model:
public class ComplexParent
{
[UIHint("FirstChildEditor")]
public SimpleChild First { get; set; }
public SimpleChild Second { get; set; }
}
My recommendation is not to use Html.RenderPartial for generating input fields because their names will be hardcoded and won't bind properly depending on your objects hierarchy.