I have 2 properties contractor1 and contractor2 in a model, how can I use a single remote validation for both of them
[Display(Name ="Contractor 1:")]
[Remote("ValidateContractor", "Contracts")]
public string Cntrctr1 {get; set;}
[Display(Name = "Contractor 2:")]
[Remote("ValidateContractor", "Contracts")]`enter code here`
public string Cntrctr2 {get; set;}
Remote Validation function in the Controller
public JsonResult ValidateContractor1(string Cntrctr)
{
var valid = Validations.ValidateContractor(Cntrctr);
if (!valid)
{return Json("Enter correct contractor", JsonRequestBehavior.AllowGet);}
else{return Json(true, JsonRequestBehavior.AllowGet);}
}
public static bool ValidateContractor(string CntrctrNM)
{
bool valid;
using (var entities = new CAATS_Entities())
{
var result = (from t in entities.PS_VENDOR_V
where (t.VNDR_1_NM).Equals(CntrctrNM)
select t).FirstOrDefault();
if (result != null)
{
valid = true;
}
else
{
valid = false;
}
}
return valid;
}
This doesn't work. Can you please help me with this?
When remote validation is called, the querystring key is the name of the field, e.g. in your case /Contracts/ValidateContractor1?Cntrctr1=foo. You need a more dynamic solution.
One way you can do this is to not have any parameters in ValidateContractor1 and just grab the first query string value instead. This isn't tested but should work for you:
public JsonResult ValidateContractor1()
{
// gets the name of the property being validated, e.g. "Cntrctr1"
string fieldName = Request.QueryString.Keys[0];
// gets the value to validate
string Cntrctr = Request.QueryString[fieldName];
// carry on as before
var valid = Validations.ValidateContractor(Cntrctr);
if (!valid)
{return Json("Enter correct contractor", JsonRequestBehavior.AllowGet);}
else{return Json(true, JsonRequestBehavior.AllowGet);}
}
Adding onto Rhumborls answer if you find his method not to work it might be because you're using forms; if this is the case you need to use the Form attribute instead of the QueryString as so.
public JsonResult ValidateContractor()
{
// gets the name of the property being validated, e.g. "Cntrctr1"
string fieldName = Request.Form.Keys[0];
// gets the value to validate
string Cntrctr = Request.Form[fieldName];
// carry on as before
var valid = Validations.ValidateContractor(Cntrctr);
if (!valid)
{return Json("Enter correct contractor", JsonRequestBehavior.AllowGet);}
else{return Json(true, JsonRequestBehavior.AllowGet);}
}
Related
Could someone give me an advice on this issue, so what am I doing is receiving username and password as parameters.
If that is ok (checking credentials) I want to return QR code of it, and if not i want to return string (or boolean or void).
Is their a solution for this? So is it possible not to know return statement?
Create a class whose object will be returned. In that class you can add properties.
class ReturnClass
{
public string QRCode { get; set; }
public bool IsOK { get; set; }
}
public ReturnClass MainMethod()
{
ReturnClass mrc = new ReturnClass();
// Do checks and populate value of ReturnClass
return mrc;
}
Just return an object. Then you can return whatever object you like and check on the requester's side what type was returned.
public object returnObject(string user, string pwd)
{
if(checkCredentials(user,pwd))
return new QRcode(usr,pwd);
else
return false;
}
(I realize this question is very similar to How to whitelist/blacklist child object fields in the ModelBinder/UpdateModel method? but my situation is slightly different and there may be a better solution available now that wasn't then.)
Our company sells web-based software that is extremely configurable by the end-user. The nature of this flexibility means that we must do a number of things at run time that would normally be done at compile time.
There are some rather complex rules regarding who has read or read/write access to most everything.
For instance, take this model that we would like to create:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace j6.Business.Site.Models
{
public class ModelBindModel
{
[Required]
[Whitelist(ReadAccess = true, WriteAccess = true)]
public string FirstName { get; set; }
[Whitelist(ReadAccess = true, WriteAccess = true)]
public string MiddleName { get; set; }
[Required]
[Whitelist(ReadAccess = true, WriteAccess = true)]
public string LastName { get; set; }
[Required]
[Whitelist(ReadAccess = User.CanReadSalary, WriteAccess = User.CanWriteSalary)]
public string Salary { get; set; }
[Required]
[Whitelist(ReadAccess = User.CanReadSsn, WriteAccess = User.CanWriteSsn)]
public string Ssn { get; set; }
[Required]
public string SirNotAppearingOnThisPage { get; set; }
}
}
In the controller, it is not difficult to "unbind" things manually.
var resetValue = null;
modelState.Remove(field);
pi = model.GetType().GetProperty(field);
if (pi == null)
{
throw new Exception("An exception occured in ModelHelper.RemoveUnwanted. Field " +
field +
" does not exist in the model " + model.GetType().FullName);
}
// Set the default value.
pi.SetValue(model, resetValue, null);
Using HTML helpers, I can easily access the model metadata and suppress rendering of any fields the user does not have access to.
The kicker: I can't figure out how to access the model metadata anywhere in the CONTROLLER itself to prevent over-posting.
Note that using [Bind(Include...)] is not a functional solution, at least not without additional support. The properties to Include are run-time (not compile time) dependent, and excluding the property does not remove it from the validation.
ViewData.Model is null
ViewData.ModelMetaData is null
[AllowAnonymous]
[HttpPost]
// [Bind(Exclude = "Dummy1" + ",Dummy2")]
public ViewResult Index(ModelBindModel dto)
{
zzz.ModelHelper.RemoveUnwanted(ModelState, dto, new string[] {"Salary", "Ssn"});
ViewBag.Method = "Post";
if (!ModelState.IsValid)
{
return View(dto);
}
return View(dto);
}
Any suggestions on how to access the Model MetaData from the controller? Or a better way to whitelist properties at run time?
Update:
I borrowed a page from this rather excellent resource:
http://www.dotnetcurry.com/ShowArticle.aspx?ID=687
With a model that looks like this:
[Required]
[WhiteList(ReadAccessRule = "Nope", WriteAccessRule = "Nope")]
public string FirstName { get; set; }
[Required]
[WhiteList(ReadAccessRule = "Database.CanRead.Key", WriteAccessRule = "Database.CanWrite.Key")]
public string LastName { get; set; }
The class:
public class WhiteList : Attribute
{
public string ReadAccessRule { get; set; }
public string WriteAccessRule { get; set; }
public Dictionary<string, object> OptionalAttributes()
{
var options = new Dictionary<string, object>();
var canRead = false;
if (ReadAccessRule != "")
{
options.Add("readaccessrule", ReadAccessRule);
}
if (WriteAccessRule != "")
{
options.Add("writeaccessrule", WriteAccessRule);
}
if (ReadAccessRule == "Database.CanRead.Key")
{
canRead = true;
}
options.Add("canread", canRead);
options.Add("always", "be there");
return options;
}
}
And adding these lines to the MetadataProvider class mentioned in the link:
var whiteListValues = attributes.OfType<WhiteList>().FirstOrDefault();
if (whiteListValues != null)
{
metadata.AdditionalValues.Add("WhiteList", whiteListValues.OptionalAttributes());
}
Finally, the heart of the system:
public static void DemandFieldAuthorization<T>(ModelStateDictionary modelState, T model)
{
var metaData = ModelMetadataProviders
.Current
.GetMetadataForType(null, model.GetType());
var props = model.GetType().GetProperties();
foreach (var p in metaData.Properties)
{
if (p.AdditionalValues.ContainsKey("WhiteList"))
{
var whiteListDictionary = (Dictionary<string, object>) p.AdditionalValues["WhiteList"];
var key = "canread";
if (whiteListDictionary.ContainsKey(key))
{
var value = (bool) whiteListDictionary[key];
if (!value)
{
RemoveUnwanted(modelState, model, p.PropertyName);
}
}
}
}
}
To recap my interpretation of your question:
Field access is dynamic; some users may be able to write to a field and some may not.
You have a solution to control this in the view.
You want to prevent a malicious form submission from sending restricted properties, which the model binder will then assign to your model.
Perhaps something like this?
// control general access to the method with attributes
[HttpPost, SomeOtherAttributes]
public ViewResult Edit( Foo model ){
// presumably, you must know the user to apply permissions?
DemandFieldAuthorization( model, user );
// if the prior call didn't throw, continue as usual
if (!ModelState.IsValid){
return View(dto);
}
return View(dto);
}
private void DemandFieldAuthorization<T>( T model, User user ){
// read the model's property metadata
// check the user's permissions
// check the actual POST message
// throw if unauthorized
}
I wrote an extension method a year or so ago that has stood me in good stead a couple of times since. I hope this is of some help, despite not being perhaps the full solution for you. It essentially only allows validation on the fields that have been present on the form sent to the controller:
internal static void ValidateOnlyIncomingFields(this ModelStateDictionary modelStateDictionary, FormCollection formCollection)
{
IEnumerable<string> keysWithNoIncomingValue = null;
IValueProvider valueProvider = null;
try
{
// Transform into a value provider for linq/iteration.
valueProvider = formCollection.ToValueProvider();
// Get all validation keys from the model that haven't just been on screen...
keysWithNoIncomingValue = modelStateDictionary.Keys.Where(keyString => !valueProvider.ContainsPrefix(keyString));
// ...and clear them.
foreach (string errorKey in keysWithNoIncomingValue)
modelStateDictionary[errorKey].Errors.Clear();
}
catch (Exception exception)
{
Functions.LogError(exception);
}
}
Usage:
ModelState.ValidateOnlyIncomingFields(formCollection);
And you'll need a FormCollection parameter on your ActionResult declaration, of course:
public ActionResult MyAction (FormCollection formCollection) {
Is it possible to have unobtrusive validation to make a field required but only if other properties change?
For Example
[Required]
public Decimal Income {get; set;}
[Required]
public Decimal Tax {get; set;}
//Required if tax or income changes
public string ChangeReason {get; set;}
I thought about having multiple backing store fields and writing a Custom Validator to compare these, but wondered if anyone had a better suggestion?
Custom Validator is the way to go. I had to build something similar a while back.
I'd set up a hidden value - "Changed" - set it to true whenever the user mods the fields of interest.
Set a RequiredIf validator on the 2 properties of interest:
[RequiredIf("Changed", true, ErrorMessage = "Required")]
The code for a RequiredIf validator is shown below:
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private RequiredAttribute _innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// get a reference to the property this validation depends upon
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(this.DependentProperty);
if (field != null)
{
// get the value of the dependent property
var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);
// compare the value against the target value
if ((dependentvalue == null && this.TargetValue == null) ||
(dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
{
// match => means we should try validating this field
if (!_innerAttribute.IsValid(value))
// validation failed - return an error
return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
string depProp = BuildDependentPropertyId(metadata, context as ViewContext);
// find the value on the control we depend on;
// if it's a bool, format it javascript style
// (the default is True or False!)
string targetValue = (this.TargetValue ?? "").ToString();
if (this.TargetValue.GetType() == typeof(bool))
targetValue = targetValue.ToLower();
rule.ValidationParameters.Add("dependentproperty", depProp);
rule.ValidationParameters.Add("targetvalue", targetValue);
yield return rule;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
// build the ID of the property
string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
// unfortunately this will have the name of the current field appended to the beginning,
// because the TemplateInfo's context has had this fieldname appended to it. Instead, we
// want to get the context as though it was one level higher (i.e. outside the current property,
// which is the containing object (our Person), and hence the same level as the dependent property.
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField))
// strip it off again
depProp = depProp.Substring(thisField.Length);
return depProp;
}
}
Javascript:
/// <reference path="jquery-1.4.4-vsdoc.js" />
/// <reference path="jquery.validate.unobtrusive.js" />
$.validator.addMethod('requiredif',
function (value, element, parameters) {
var id = '#' + parameters['dependentproperty'];
// get the target value (as a string,
// as that's what actual value will be)
var targetvalue = parameters['targetvalue'];
targetvalue =
(targetvalue == null ? '' : targetvalue).toString();
// get the actual value of the target control
// note - this probably needs to cater for more
// control types, e.g. radios
var control = $(id);
var controltype = control.attr('type');
var actualvalue =
controltype === 'checkbox' ?
control.attr('checked').toString() :
control.val();
// if the condition is true, reuse the existing
// required field validator functionality
if (targetvalue === actualvalue)
return $.validator.methods.required.call(
this, value, element, parameters);
return true;
}
);
$.validator.unobtrusive.adapters.add(
'requiredif',
['dependentproperty', 'targetvalue'],
function (options) {
options.rules['requiredif'] = {
dependentproperty: options.params['dependentproperty'],
targetvalue: options.params['targetvalue']
};
options.messages['requiredif'] = options.message;
});
It is possible. You can write your own attribute, to do this exactly.
It basically requires two steps:
Write your own attribute, make it inherit ValidationAttribute amd implement IClientValidatable
Write a Jquery validation adapter to support it
A good working sample is described in this post.
I used a similar approach to create a dependency validation (one field could have values only if another was filled)
I need a Login function(login is just an example, any other frequently used method can be fine) which takes email and password as parameter and asks DB if there is such a user. If yes, it has to return customer_id(int), if no, it will return the message why login could not happen(ex:no such an email address).
I also do not wanna rewrite the login function everytime. I want to write it once in a common project which I can use in my every project and reuse it. But i am trying to find out the best practice for this. So far, i thought something like below, but the problem for me is that i cannot return customerID which i will get in codebehind in my projects(any other project) and open a session variable with it. I only can return strings in below structure. I also thought returning a Dic, but this also is wrong I guess because if bool(key) happens to be true, customerID is not a string(value). Can you help me please learning the correct way of using common functions with no need to think the returning messages and variables twice? Thanks a lot
public class UserFunctions
{
private enum Info
{
//thought of returning codes??
LoginSuccess = 401,
NoMatchPasswordEmail = 402,
InvalidEmail = 403,
};
public string TryLogin(string email, string password)
{
bool isValidEmail = Validation.ValidEmail(email);
if (isValidEmail == false)
{
return Result(Info.InvalidEmail);
// returning a message here
}
Customers customer = new Customers();
customer.email = email;
customer.password = password;
DataTable dtCustomer = customer.SelectExisting();
if (dtCustomer.Rows.Count > 0)
{
int customerID = int.Parse(dtCustomer.Rows[0]["CustomerID"].ToString());
return Result(Info.LoginSuccess);
// Here I cant return the customerID. I dont wanna open a session here because this function has no such a job. Its other projects button events job I guess
}
else
{
return Result(Info.NoMatchPasswordEmail);
}
}
private string Result(Info input)
{
switch (input)
{
case Info.NoMatchPasswordEmail:
return "Email ve şifre bilgisi uyuşmamaktadır";
case Info.InvalidEmail:
return "Geçerli bir email adresi girmelisiniz";
case Info.LoginSuccess:
return "Başarılı Login";
}
return "";
}
}
You may want to consider returning an instance of a custom class.
public class LoginResult
{
public Info Result { get; set; }
public int CustomerId { get; set;}
}
Modify your TryLogin method to return an instance of LoginResult.
Base your application flow on the result:
var loginResult = TryLogin(..., ...);
switch (loginResult.Result)
{
case Info.LoginSuccess:
var customerId = loginResult.CustomerId;
//do your duty
break;
case Info.NoMatchPasswordEmail:
//Yell at them
break;
...
}
You could try Creating an event and then the calling code can register to the event before attempting to login.
For example:
public class UserFunctions
{
private enum Info
{
LoginSuccess = 401,
NoMatchPasswordEmail = 402,
InvalidEmail = 403,
};
public delegate void LoginAttemptArgs(object sender, Info result, int CustomerID);//Define the delegate paramters to pass to the objects registered to the event.
public event LoginAttemptArgs LoginAttempt;//The event name and what delegate to use.
public void TryLogin(string email, string password)
{
bool isValidEmail = Validation.ValidEmail(email);
if (isValidEmail == false)
{
OnLoginAttempt(Info.InvalidEmail, -1);
}
Customers customer = new Customers();
customer.email = email;
customer.password = password;
DataTable dtCustomer = customer.SelectExisting();
if (dtCustomer.Rows.Count > 0)
{
int customerID = int.Parse(dtCustomer.Rows[0]["CustomerID"].ToString());
OnLoginAttempt(Info.LoginSuccess, customerID);
}
else
{
OnLoginAttempt(Info.NoMatchPasswordEmail, -1);
}
}
private void OnLoginAttempt(Info info, int CustomerID)
{
if (LoginAttempt != null)//If something has registered to this event
LoginAttempt(this, info, CustomerID);
}
}
I wouldn't compile a string to return, I would return the enum result and let the calling code do what it likes with the result. reading an enum is much quicker than parsing returned string.
Edit: Typos and i missed an event call... .Twice
Update:
I have a dropdownlist with seven different options like (date, subject, press, cia, media...) and user will select one of the item from the dropdownlist and click on search button to get the results.
Here is my repository class GetInquiries method which accepts 7 parameters but it will pass only one parameters at a time to the stored procedure and in case of Date (it will pass both from/to)
public List<Inquiry> GetInquiries(string fromDate, string toDate,
string subject, string press,
string cia, string media,
string status)
Here is what I have come up with passing the parameters to GetInquiries:
if (!string.IsNullOrEmpty(this.txtSubject.Text.Trim()))
{
e.Result = reporterRepo.GetInquiries(null,null,txtSubject.Text,null,null,null,null);
}
else
{
e.Result = reporterRepo.GetInquiries(null,null,null,null,null,null,null);
}
else if (!string.IsNullOrEmpty(this.fromDate.Text.Trim()))
e.Result = reporterRepo.GetInquiries(fromDate.Text,null,null,null,null,null,null)
.......................
...................
....................
I have to continue seven times with (if else if conditions) for all seven parameters.
Is there a way I can do it more clearly and more readability?
Well, depending on your exact situation, this might help:
e.Result = reporterRepo.GetInquiries(null, null,
string.IsNullOrEmpty(this.txtSubject.Text.Trim()) ? null : txtSubject.Text,
null, null, null, null);
Or if you're using C# 4 and can modify GetInquiries, you could possibly make all the parameters optional and use named arguments to specify which one you're actually providing.
Or write methods such as GetInquiriesByName, GetInquiriesBySubject etc to avoid overloading with clashing parameter types.
EDIT: If there's a dropdown, it sounds like you should be using the value of that to determine the appropriate branch to take (i.e. what to search on) and then go from there.
If you are using .NET 4.0 then you take advantge of optional and default parameters. If you're using an earlier framework version, then try creating overloasd of the routine:
public List<Inquiry> GetInquiries(string fromDate, string toDate,
string subject, string press,
string cia, string media)
{
// Pass empty string for status.
return this.GetInquiries(fromDate, toDate, subject, press, cia, media, String.Empty)
}
public List<Inquiry> GetInquiries(string fromDate, string toDate,
string subject, string press,
string cia, string media,
string status)
var subjectText = this.txtSubject.Text.Trim();
var dateText = this.fromDate.Text.Trim();
if (subjectText.Length == 0) subjectText = null;
if (dateText .Length == 0) dateText = null;
e.Result = reporterRepo.GetInquiries(dateText ,null,subjectText,
null,null,null,null);
string.IsNullOrEmpty(this.txtSubject.Text.Trim()))
Note that this.txtSubject.Text.Trim() will never be null, and if this.txtSubject.Text is null, this will throw an exception.
You reflection to invoke the method GetInquiries with just the n-th argument set.
public List<Inquiry> GetInquiries(int numparam, string myparameter)
{
object[] params = new object[7];
params[numparam] = myparameter;
reporterRepo.Gettype().GetMethod("GetInquiries").Invoke(reporterRepo, params);
}
I have not tested the code but you get the idea.
public class Inquiries
{
private SqlParameter[] _parameters;
public DateTime FromDate { get{ //return value from parameters array} set{ // add a new parameter to your array } }
public DateTime ToDate { get{ //return value from parameters array} set{ // add a new parameter to your array } }
public String Subject { get{ //return value from parameters array} set{ // add a new parameter to your array } }
public String Press { get{ //return value from parameters array} set{ // add a new parameter to your array } }
public String Cia { get{ //return value from parameters array} set{ // add a new parameter to your array } }
public String Media { get{ //return value from parameters array} set{ // add a new parameter to your array } }
public List<Inquiry> Get()
{
// Your dal code using the parameters array
}
}
Then make Inquiries a member level in the formthat's going to get inquiries, and with the changes you do, set the properties appropriately, unless you're only going to do one property at a time then just create the class and toss it each time you use it as:
List<Inquiry> myInquires = new Inquiry() { Subject = txtSubject.Text }.Get();
What about using some sort of a specification pattern
So in your example the usage would be
var querySpecification = new QuerySpecification();
if (!string.IsNullOrEmpty(this.txtSubject.Text.Trim()))
{
querySpecification = new WhereSubject().Is(txtSubject);
}
else if (!string.IsNullOrEmpty(this.fromDate.Text.Trim()))
{
querySpecification = new WhereFromDate().Is(fromDate);
}
else
{
querySpecification = new All();
}
e.Result = reporterRepo.GetInquiries(querySpecification);
WhereSubject, WhereFromDate and All derives from a common IReportReposSpec.
public interface IReportReposSpec
{
void Is(object value)
// stored proc translation of the spec
string Param {get;}
string DbType {get;}
string Value {get;}
}
The implementation of reporterRepo.GetInquiries could be as such
public List<Inquiry> GetInquiries(IReportReposSpec spec)
{
var cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
// for simplicity i'm going to use a pseudo code from here on
cmd.AddParam(spec.Param, spec.DbType, spec.Value));
//...
}