How do I implement ASP.NET MVC 2 validation that checks the database? - validation

All examples that I can find do something like this:
[Required]
public string Title { get; set; }
That's great for simple cases, but what about something that checks the database or something else server side?
For example, say I have a movie database and I want to allow people to rate it. How could I tell if someone has already rated a movie, as I'd only want them to rate a movie once.
I would think it would be something like:
public IEnumerable<string> ValidateUserHasNotAlreadyRatedMovie(User currentUser, Guid movieId)
{
if(movieHasAlreadyBeenRated)
{
yield return "Movie been rated man!";
}
}
Now then I'd call this with something like:
var errors = new List<string>();
errors.AddRange(ValidateUserHasNotAlreadyRatedMovie(topic, comment));
if(errors.Any())
{
throw new SomeTypeOfCustomExtension??(errors);
}
Do I just need to extend Exception for a custom SomeTypeOfCustomExtension above, or is there something already built? Am I doing this the ASP.NET MVC 2 way?
After that how do I put it into the model state and let the view know something's wrong?

See this it may help
Remote Validation with ASP.NET MVC 2
http://bradwilson.typepad.com/blog/2010/01/remote-validation-with-aspnet-mvc-2.html

The ASP.NET 2.0 MVC way of doing custom validation is described here. It basically shows how to write a custom validation attribute. Here is an example of one such custom attribute that checks the database for name uniqueness:
public class UniqueNameAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
string str = (string)value;
if (String.IsNullOrEmpty(str))
return true;
using (XDataContext vt = new XDataContext())
{
return !(vt.Users.Where(x => x.Username.Equals(str)).Any());
}
}
}
The ASP.NET 1.0 way (that doesn't require custom attributes) is described here.

Related

Custom ASP.NET Core MVC validation response

ASP.NET Core MVC has a great model binding & model validation subsystem which supports almost any scenario. But when developing APIs things can go a little more complicated.
Suppose we have a model class with a property XYZ which is annotated with [MinLength(5)].
public class ViewModel
{
[MinLength(5)]
public string XYZ { get; set; }
}
If anything goes wrong with this property what MVC will give you is something like this:
{ "XYZ": [ "The field XYZ must be a string or array type with minimum length of '5'" ] }
But this is not what the client needs! The client needs an object with specific details. She will create her own message however she wants to:
{ "error": "minLength", "property": "XYZ", "minimum": 5 }
Possible Solutions:
You can use InvalidModelStateResponseFactory to generate customized responses. It gives you the ActionContext which contains the ModelState property. But all you can do is to process error messages which are pure strings! That could lead to some problems.
Another option is to completely disable the MVC Validation and implement one for yourself.
I appreciate any other solutions.
For general validation message, it is pure string. And for minLength and minimum are different for different validation attribute. I am wondering how the client will check the different node.
For server side, InvalidModelStateResponseFactory would be better to return json object. and you need to check the ValidationAttribute for return different object like
services.Configure<ApiBehaviorOptions>(o =>
{
o.InvalidModelStateResponseFactory = actionContext =>
{
var error = new Dictionary<string, string>();
foreach (var key in actionContext.ModelState.Keys)
{
foreach (var parameter in actionContext.ActionDescriptor.Parameters)
{
var prop = parameter.ParameterType.GetProperty(key);
if (prop != null)
{
var attr = prop.GetCustomAttributes(typeof(ValidationAttribute), false).FirstOrDefault() as ValidationAttribute;
if (attr is MinLengthAttribute minLengthAttribute)
{
error.Add("Error", "minLength");
error.Add("Property", key);
error.Add("minimum", minLengthAttribute.Length.ToString());
}
}
}
}
return new BadRequestObjectResult(error);
};
});

Get full name of Complex Type from ModelClientValidationRequiredIfRule method in custom ValidationAttribute

I am using the example at The Complete Guide To Validation In ASP.NET MVC 3 to create a RequiredIf validation attribute (it's about 1/3 down the page under the heading of "A more complex custom validator"). It all works fine with the exception of one scenario, and that is if I have the need to validate against a complex type. For example, I have the following model:
public class MemberDetailModel
{
public int MemberId { get; set; }
// Other model properties here
public MemberAddressModel HomeAddress { get; set; }
public MemberAddressModel WorkAddress { get; set; }
}
public class MemberAddressModel
{
public bool DontUse { get; set; }
// Other model properties here
[RequiredIf("DontUse", Comparison.IsEqualTo, false)]
public string StreetAddress1 { get; set; }
}
The problem is that when the attribute validation for the StreetAddress property is rendered, it get's decorated with the attribute of data-val-requiredif-other="DontUse". Unfortunately, since the address is a sub-type of the main model, it needs to be decorated with a name of HomeAddress_DontUse and not just DontUse.
Strangely enough, the validation works fine for server-side validation, but client-side unobtrusive validation fails with an JS error because JS can't find the object with a name of just "DontUse".
Therefore, I need to find a way to change the ModelClientValidationRequiredIfRule method to know that the property it is validating is a sub-type of a parent type, and if so, prepend the ParentType_ to the "otherProperty" field (e.g. otherProperty becomes HomeAddress_DontUse.
I have tried passing in typeof(MemberAddressModel) as a parameter of the attribute, but even when debugging the attribute creation, I can't seem to find any reference to the parent type of HomeAddress or WorkAddress from that type.
Based on the suggestion from The Flower Guy, I was able to come up with the following which seems to work. I simply modified the following in the customValidation.js file:
jQuery.validator.addMethod("requiredif", function (value, element, params) {
if ($(element).val() != '') return true;
var prefix = getModelPrefix(element.name); // NEW LINE
var $other = $('#' + prefix + params.other); // MODIFIED LINE
var otherVal = ($other.attr('type').toUpperCase() == "CHECKBOX") ? ($other.attr("checked") ? "true" : "false") : $other.val();
return params.comp == 'isequalto' ? (otherVal != params.value) : (otherVal == params.value);
});
I also added the following method to that file (within the JQuery block so as to be only privately accessible):
function getModelPrefix(fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1).replace(".","_");
}
Cannot do it exactly right now, but the problem is in the client javascript function:
jQuery.validator.addMethod("requiredif" ...
The js is not sophisticated enough to cope with complex view models where there may be a model prefix. If you take a look at Microsoft's jquery.validate.unobstrusive.js (in the Scripts folder over every MVC3 application), you will find some useful methods including getModelPrefix and appendModelPrefix. You can take a similar approach and change the requiredIf validation method - take a look at the equalto method in jquery.validate.unobstrusive.js for a helping hand.

ASP.NET MVC 3 multiple Models to single Form using DB

I have a question.
My question actually extends from this one:
Shortly - what I want to get: 3 models, and 1 super model for this specific view. This super model fills(properly) IENumerable, IENumerable, IENumerable, to use them in View part. (as far as I understand it, at least...)
In this other topic Dan Revell proposed verry nice and elegant solution, but this solution does not fetch data from DB itself...
Question:
What must be done to get data in this model from DB, not from "new" instance constructors?
While using this approach tried to fetch data from DBContext. And got some problems in it ) I can't understand when (or how) to create my DBContext... Or how to access one that is created by application...
Tried to create it forcefully in Controller, like
using (var Db = new thetaskermvc.Models.TaskerDBContext())
{
var themodel = new thetaskermvc.Models.TotalView();
//Jobbers
themodel.Jobberz = new Dictionary<int, thetaskermvc.Models.Jobbers>();
var jobbers = from Jobbers in Db.Jobbers.OrderBy(g => g.jobb_name) select Jobbers;
foreach (Models.Jobbers ad in jobbers)
{
themodel.Jobberz.Add(ad.jobb_id,
new Models.Jobbers(ad.jobb_id, ad.jobb_name, ad.jobb_from, ad.jobb_carma, ad.jobb_status, ad.jobb_balance, ad.jobb_time));
}
if (themodel.Jobberz.Count == 0)
{
themodel.Jobberz.Add(-1, new Models.Jobbers(0, "NOTHING FOUND",DateTime.Now,0,"",0,0));
}
}
But as created that way Context stops it's existence (?) after passing data away from controller - I can't use it any other way but to get all data inside this controller, and fill data in model by direct add into collections in it (while use of IENumerable would fetch data on-demand, as far as I get it).
So.. If it ain't hard please enlighten me about - is it Ok to use such approach, or there is some other "common" way? Becaus beside it's clumsiness - this approach works...
PS I'm quite new to Asp, yet...
I have one view model per view with data from multiple tables (if required). On my view I have data that needs to be loaded from 2 different database tables. In my grant application controller I have the following:
private readonly IBankService bankService;
private readonly IAccountTypeService accountTypeService;
public GrantApplicationController(IBankService bankService, IAccountTypeService accountTypeService)
{
// Check incoming parameters for null values
this.bankService = bankService;
this.accountTypeService = accountTypeService;
}
In my Create action method I populate my banks and account types (to be used in drop downs) like this (different tables):
public ActionResult Create()
{
GrantApplicationCreateViewModel viewModel = new GrantApplicationCreateViewModel
{
Banks = bankService.FindAll(),
AccountTypes = accountTypeService.FindAll()
}
// Do what ever else you need to get done
return View(viewModel);
}
My partial view model would like this:
public class GrantApplicationCreateViewModel
{
public int BankId { get; set; }
public IEnumerable<Bank> Banks { get; set; }
public int AccountTypeId { get; set; }
public IEnumerable<AccountType> AccountTypes { get; set; }
// Other properties
}
In my repository class I would use the database context like this (I use Entity Framework code first):
public class BankRepository : IBankRepository
{
HefContext db = new HefContext
public IEnumerable<Bank> FindAll()
{
return db.Banks.OrderBy(x => x.Name);
}
}
In my database context class:
public class HefContext : DbContext
{
public DbSet<Bank> Banks { get; set; }
public DbSet<AccountType> AccountTypes { get; set; }
}
Doing it this way you can have one view model that has data from multiple sources. I hope this answers your question? If you need more explanation please let me know :)
You may want to have a look at this post, it explains (with a sample project) how an ideal MVC application architecture should be.
In your code sample above, your shouldn't have any references to DbContexts in a controller. Controller's job is to control the flow of requests not to connect to the DB and perform Model population.

MVC 3 with Dynamic Data - applying data types to Dynamic Data

I have an MVC 3 C# / ADO.NET / Dynamic Data app set up and working(ish). To set it up I created an MVC 3 app, added the Dynamic Data components in, split out Presentation, Business and Data in to three projects, set the references to match the MVC pattern and set up the routes and scaffolding.
List, Edit and Insert all work with the standard DD page templates, however I've hit a wall getting the Presentation Layer to apply data type attributes to the data displayed in Gridview and Details views, particularly for URLs, which I want be typed as DataType.Url and so use the associated DD display attributes.
Have tried setting up a meta data class for the Link table and applying something like:
[DataType(DataType.Url)]
public object URL {get; set;}
(the Url field in table "Link" is "URL")
.. within a partial class, which is something I read about for pure DD sites.
Can anyone point me in the right direction, or tell me if is this even possible?
Many Thanks.
Yes this is possible. I would write a custom FieldTemplate for Urls. Using the UIHint metadata, you can assign a custom fieldtemplate to the column. Something like this (untested):
public partial class FooUrl : System.Web.DynamicData.FieldTemplateUserControl
{
string getUrl()
{
var metadata = MetadataAttributes.OfType<DataTypeAttribute>().FirstOrDefault();
if (metadata == null)
return FieldValueString;
switch (metadata.DataType)
{
case DataType.Url:
string url = FieldValueString;
if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
return url;
return "http://" + FieldValueString;
case DataType.EmailAddress:
return "mailto:" + FieldValueString;
default:
throw new Exception("Unknown DataType");
}
}
protected override void OnDataBinding(EventArgs e)
{
HyperLinkUrl.NavigateUrl = getUrl();
}
public override Control DataControl
{
get
{
return HyperLinkUrl;
}
}
}
}
I hope this helps.

How does one do MVC2/EF4 EntityCollection validation w/ data annotations?

I have finally gotten over one hurdle and can now successfully create new model data. Now there's another catch - validation. Most of the validation seems easy enough as a lot of my model data are scalar values. There is a many-to-many relationship I link to, however, so I'm not sure how to go about validating that. My model is (once again):
Game (only listing the relevant columns):
GameID - int (primary key, auto-incr)
Platform:
PlatformID - int (primary key, auto-incr)
Name - string
GamePlatform (not a visible entity):
GameID - int (foreign key from Games)
PlatformID - int (foreign key from Platforms)
And my Create method (yes, I know it's sloppy and amateurish - I am an amateur and trying to learn. I'll definitely add error checking to it. I'm just trying to get the big picture of the view->controller->validation->persist-in-db/show errors process down):
public ActionResult CreateReview([Bind(prefix = "GameData")]Game newGame, int[] PlatformIDs)
{
try
{
foreach(int i in PlatformIDs)
{
Platform plat = _siteDB.Platforms.Single(p => p.PlatformID == i);
newGame.Platforms.Add(plat);
}
newGame.LastModified = Datetime.Now;
_siteDB.Games.AddObject(newGame);
_siteDB.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The array of PlatformIDs are supplied by a group of checkboxes within my view. For my Game to be valid, it must be associated with at least one Platform. I'm just not sure how to validate that with data annotations, or if it's even possible to do so. Any help would be greatly appreciated.
If I understand your question correctly, your int[] potentially contains ints associated with the ID of a Platform in your DB and you want to make sure your int[] contains at least one valid PlatformID, correct?
Immediately you could do just a simple check prior to going into your logic:
// If there aren't any IDs in Platform that are in PlatformIDs...
if (!_siteDB.Platforms.Any(p => PlatformIDs.Contains(p.PlatformID)))
Return RedirectToAction("Index");
// And probably tell the user to check a box, if they did,
// One of your checkboxes isn't matching up with your PlatformIDs
Ideally what you'd want to do is add the int[] to your model so you can check model validation. Since databased don't typically store int[], add it to your Game model. The EF probably put your DB Entities in your Models folder and if you look at them, you'll see they're partial classes. So add this in your Models folder:
public partial class Game
{
public Dictionary<int, bool> SupportedPlatforms { get; set; }// Edited
}
// Also add this which you'll see why below
public partial class Platform
{
public static bool IsValidPlatformID(int PlatformID)
{
using (SiteDBEntities _siteDB = new SiteDBEntities())
return _siteDB.Platforms.Any(p => p.PlatformID.Equals(PlatformID));
}
}
Then add a custom ValidationAttribute class:
public ContainsValidPlatformIDAttribute : ValidationAttribute
{
public ContainsValidPlatformIDAttribute() { }
public override bool IsValid(object value)
{
Dictionary<int, bool> supportedPlatforms = (Dictionary<int, bool>)value;
if (value == null)
return true;
foreach (int i in values)
{
if (supportedPlatforms.Values.Any(b => b.Equals(true)))// Edited
return false;
}
return true;
}
Now decorate your Property with it in the Game class:
[ContainsValidPlatformID(Error = "You did not select a valid Platform.")]
public Dictionary<int, bool> SupportedPlatforms { get; set; }// Edited
(Edited)Now instead of hard coding a checkbox for each platform, add this instead:
<%: Html.CheckboxFor(model => model.SupportedPlatforms[0]) %>
<%: Html.ValidationMessageFor(model => model.SupportedPlatforms[0]) %>
(Edited)Now your checkboxes are tied to the Model, you can validate the model in the controller, and you can remove the int[] argument from your Action method. This has all been coded from my head into this editor so you may need to tweak some things here and there but this is the direction you should be heading in when working with Models in Views.
Also, check out what Scott Guthrie has written on the topic of MVC Model Validation in his blog. Hopefully with my sample and Scott's blog, you'll be pointed in the right direction.

Resources