I have an action that accepts multiple values to filter from. These values are optional. I set default values. The default values are overriding the values I pass in.
If I pass in:
minAge:40, maxAge:40. Both values are set to 0.
This is the Action on my controller:
[HttpGet]
public ActionResult DataTableUpdate(string firstName = "", string lastName = "", int minAge = 0, int maxAge = 0, string currentState = "")
{
List<DataMember> data = DataMemberCache.GetMembers().FindAll(d => (d.FirstName.Contains(firstName)) && (d.LastName.Contains(lastName)) && (d.Age < minAge) && (d.Age > maxAge) && (d.CurrentState.Contains(currentState)));
return PartialView("_DataTable", data);
}
See here: Optional parameters in the MVC framework are handled by using nullable-type arguments for controller action methods.
For example, if a method can take a date as part of the query string but you want to the default to be today's date if the query string parameter is missing, you can use code like that in the following example:
public ActionResult ShowArticles(DateTime? date)
{
if(!date.HasValue)
{
date = DateTime.Now;
}
// ...
}
So, your code must change to this one:
[HttpGet]
public ActionResult DataTableUpdate(string firstName, string lastName, int? minAge, int? maxAge, string currentState)
{
firstName = firstName ?? "";
lastName = lastName ?? "";
minAge = minAge ?? 0;
maxAge = maxAge ?? 0;
currentState = currentState ?? "";
List<DataMember> data = DataMemberCache.GetMembers().FindAll(d => (d.FirstName.Contains(firstName)) && (d.LastName.Contains(lastName)) && (d.Age < minAge) && (d.Age > maxAge) && (d.CurrentState.Contains(currentState)));
return PartialView("_DataTable", data);
}
Related
[HttpPost]
public ActionResult crbtmis(string submitbuttonoperator, DateTime fromdate, DateTime todate, string operatorname)
{
DateTime dfd = fromdate;
DateTime dtd = todate;
string soprtr = operatorname;
if (Session["user"] != null && Session["user"].ToString() == "MISADMIN")
{
switch (submitbuttonoperator)
{
case "Export":
return (ExportOprtrlist( fromdate, todate, operatorname));
case "Search":
return (SearchByOperator());
default:
return (View("LogOn"));
}
}
else
{
return RedirectToAction("LogOn");
}
}
I have defined three variable named as dfd,dtd and soprtr and want to use these variable into another ActionResult. Now please help me how will I do it?
In your action, you can use other action that contains some parameters:
public ActionResult crbtmis(string submitbuttonoperator, DateTime fromdate, DateTime todate, string operatorname)
{
//..your code
return RedirectToAction("OtherAction", new { dfd = dfd, dtd = dtd, soprtr = soprtr });
}
I have following code in custom validation attribute called DateRange:
private DateTime _minDate = DateTime.Today.AddYears(-100);
private DateTime _maxDate = DateTime.MaxValue;
// String representation of the Min Date (yyyy/MM/dd)
public string Min
{
get { return FormatDate(_minDate, DateTime.Today.AddYears(-100)); }
set { _minDate = value == "Today" ? DateTime.Today : ParseDate(value, DateTime.Today.AddYears(-100)); }
}
// String representation of the Max Date (yyyy/MM/dd)
public string Max
{
get { return FormatDate(_maxDate, DateTime.MaxValue); }
set { _maxDate = value == "Today" ? DateTime.Today : ParseDate(value, DateTime.MaxValue); }
}
Then I write this attribute in metadata on some property of entity model like this:
[DateRange(Max = "Today")]
public string SomeDateProperty { get; set; };
I set breakpoint on Max property's getter. First time I open view, breakpoint is activated and DateTime.Today is got. Consequent refresh of the view does not activate breakpoint and old value is got. I think it's caching validation attribute. My question is: Is this because of caching? If it is, then how to disable it? Thanks in advance
The constructor for the custom attributes only get hit once, no idea how to turn off any sort of caching. The way I got round this for my scenario, was to only deal with the date calculation in the "IsValid" Method.
I created a date in the past attribute, that needed the date to be in the past, but you could set how long in the past was valid.
public class DateInPastAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "'{0}' must be in the past.";
public int DaysInPastAllowed { get; set; }
public DateInPastAttribute(int daysInPastAllowed)
: base(DefaultErrorMessage)
{
this.DaysInPastAllowed = daysInPastAllowed;
}
public override bool IsValid(object value)
{
if (!(value is DateTime))
{
return true;
}
DateTime maxDate = DateTime.Now;
DateTime minDate = maxDate.AddDays(this.DaysInPastAllowed * -1);
DateTime dateValue = (DateTime)value;
return
minDate <= dateValue &&
dateValue <= maxDate;
}
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentCulture, this.ErrorMessageString, name);
}
}
You can then use it in your view model like this:
[DateInPast(365)]
public DateTime DateReceived { get; set; }
Which would allow a date to be entered within the last year. You could amend this for the scenario that you require.
I have a enum that holds countries:
public enum CountryEnum
{
[Display(Name = "AF", ResourceType = typeof(Global))]
AF,
[Display(Name = "AL", ResourceType = typeof(Global))]
AL,
[Display(Name = "DZ", ResourceType = typeof(Global))]
DZ,
};
As you can see I make use of the DataAnnotations to localise the values.
Now I want to display a dropdownlist with all of the localised country names. I come up with this code:
public static string GetDisplayName<TEnum>(TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DisplayAttribute[] attributes =
(DisplayAttribute[])fi.GetCustomAttributes(
typeof(DisplayAttribute), false);
if ((attributes != null) && (attributes.Length > 0))
return attributes[0].Name;
else
return value.ToString();
}
I have a Html helper that makes use of the above method:
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
Type enumType = GetNonNullableModelType(metadata);
IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();
IEnumerable<SelectListItem> items = from value in values
select new SelectListItem
{
Text = GetDisplayName(value),
Value = value.ToString(),
Selected = value.Equals(metadata.Model)
};
// If the enum is nullable, add an 'empty' item to the collection
if (metadata.IsNullableValueType)
items = SingleEmptyItem.Concat(items);
return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
}
The DropDown renders correctly, however, GetDisplayName doesn't return the localised value, it just display the name attribute (e.g. AF for the first entry).
How can I modify GetDisplayName method to return the localised value?
You need to update your GetDisplayName method to use the GetName() method rather than the Name property of the DisplayAttribute.
Like this:
public static string GetDisplayName<TEnum>(TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DisplayAttribute[] attributes = (DisplayAttribute[])fi.
GetCustomAttributes(typeof(DisplayAttribute), false);
if ((attributes != null) && (attributes.Length > 0))
return attributes[0].GetName();
else
return value.ToString();
}
From the MSDN documentation for DisplayAttribute.Name:
Do not use this property to get the value of the Name property. Use
the GetName method instead.
The MSDN documentation for the GetName() method has this to say:
Returns the localized string for the Name property, if the
ResourceType property has been specified and the Name property
represents a resource key; otherwise, the non-localized value of the
Name property.
I was having a similar problem, having setup the Display Attribute as you did (easier since classes use the same attribute to load localizations), so took your initial code and tweaked it a bit, now it is displaying the localization string as expected. I don't think it is the most clever or optimized way of doing it, but it works. Hopefully that's what you intended.
public static string GetDisplayName<TEnum>(TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DisplayAttribute[] attributes = (DisplayAttribute[])fi.
GetCustomAttributes(typeof(DisplayAttribute), false);
if ((attributes != null) && (attributes.Length > 0))
{
string key = attributes[0].Name;
string localizedString = attributes[0].ResourceType.GetProperty(key).GetValue("").ToString();
return localizedString;
}
else
return value.ToString();
}
i want to add two queryString in current request URL if it is not exsist
Edited:
I tried this ---
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.QueryString["mid"] == null && filterContext.RequestContext.HttpContext.Request.QueryString["mod"] == null)
{
mid = Convert.ToString(HttpUtility.ParseQueryString(filterContext.RequestContext.HttpContext.Request.UrlReferrer.Query)["mid"]);
mod = Convert.ToString(HttpUtility.ParseQueryString(filterContext.RequestContext.HttpContext.Request.UrlReferrer.Query)["mod"]);
if (!string.IsNullOrEmpty(mid) || !string.IsNullOrEmpty(mod))
{
RouteValueDictionary redirecttargetDictionary = new RouteValueDictionary();
NameValueCollection Qstr = null;
if (filterContext.HttpContext.Request.RequestType == "GET")
{
Qstr = HttpUtility.ParseQueryString(filterContext.HttpContext.Request.Url.Query);
foreach (string item in Qstr)
{
redirecttargetDictionary.Add(item, Qstr[item]);
}
if (Qstr["mid"] == null)
{
redirecttargetDictionary.Add("mid", mid);
}
if (Qstr["mod"] == null)
{
redirecttargetDictionary.Add("mod", mod);
}
filterContext.Result = new RedirectToRouteResult(redirecttargetDictionary);
}
}
}
This code works fine.
-check for quesrystring exsist if no
-then take qstring value from 'Urlreferrer'
-if request type is GET
-then add two querystring with exsisting querystring
But but I having problem if querystring in current request has null value url like http://localhost:53372/question/index?type=&stage.if this ll b the current request url then code ll check for mid n mod qstring as its not exsist it will add both the qstring with exsisting type n stage but value ll b null then before hitting action again OnActionExecuting() get call this time first 'if' is not true and then index method get call but I m not getting type n stage querystring which suppose to null I found url in address bar is http://localhost:53372/question/index?mid=1&mod=5.
If I remove filter the I got URL http://localhost:53372/question/index?type=&stage=&mid=1&mod=5 here I m getting both stage n type querystring though its null.
Edited--
[HttpGet]
public ActionResult Index(ProjectType? projectType, int? projectStageID, int? page)
{
int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
IPagedList<ProjectModule> moduleList = null;
IDictionary<string, string> queryParam = new Dictionary<string, string>();
queryParam["ProjectType"] = string.Empty;
queryParam["ProjectStageID"] = string.Empty;
ViewBag.ProjectType = null;
ViewBag.ProjectStage = null;
ViewBag.message = "";
if ((projectType != null || projectType.HasValue) && projectStageID.HasValue && page.HasValue)
{
queryParam["ProjectType"] = Request.QueryString["ProjectType"];
queryParam["ProjectStageID"] = Request.QueryString["ProjectStageID"];
//
//Set View Bag
//
ViewBag.ProjectType = Enum.Parse(typeof(ProjectType), Request.QueryString["ProjectType"]);
ViewBag.ProjectStage = Convert.ToInt32(Request.QueryString["ProjectStageID"]);
//
// Set Query String formcollection for Pagging purpose
//
ViewBag.QueryParam = queryParam;
moduleList = new ProjectModuleService().GetProjectModulesByProjectStageID(Convert.ToInt32(Request.QueryString["ProjectStageID"])).ToPagedList(currentPageIndex, Utility.ConstantHelper.AdminDefaultPageSize); ;
if (moduleList.Count == 0)
{
ViewBag.message = "Record(s) not found.";
}
return View(moduleList);
}
else
{
if (!string.IsNullOrEmpty(Request.QueryString["ProjectType"]) && !string.IsNullOrEmpty(Request.QueryString["ProjectStageID"]) && !string.Equals(Request.QueryString["ProjectStageID"], "0"))
{
if (!string.IsNullOrEmpty(Request.Params.Get("Index")))
{
queryParam["ProjectType"] = Request.QueryString["ProjectType"];
queryParam["ProjectStageID"] = Request.QueryString["ProjectStageID"];
//
//Set View Bag
//
ViewBag.ProjectType = Enum.Parse(typeof(ProjectType), Request.QueryString["ProjectType"]);
ViewBag.ProjectStage = Convert.ToInt32(Request.QueryString["ProjectStageID"]);
//
// Set Query String formcollection for Pagging purpose
//
ViewBag.QueryParam = queryParam;
moduleList = new ProjectModuleService().GetProjectModulesByProjectStageID(Convert.ToInt32(Request.QueryString["ProjectStageID"])).ToPagedList(currentPageIndex, Utility.ConstantHelper.AdminDefaultPageSize); ;
if (moduleList.Count == 0)
{
ViewBag.message = "Record(s) not found.";
}
return View(moduleList);
}
else if (!string.IsNullOrEmpty(Request.Params.Get("Create")))
{
return RedirectToAction("Create", new { projectStageID = Convert.ToInt32(Request.QueryString["ProjectStageID"]) });
//return RedirectToAction("Create", new { projectStageID = Convert.ToInt32(Request.QueryString["ProjectStageID"]), projectType = Enum.Parse(typeof(ProjectType), Request.QueryString["ProjectType"]) });
}
}
if (Request.QueryString["ProjectType"] == "" || Request.QueryString["ProjectStageID"] == "" || string.Equals(Request.QueryString["ProjectStageID"], "0"))
{
ViewBag.message = "Please select all fields.";
return View();
}
return View();
}
}
What I m doing wrong?
Here what I understand that if there are no querystring in the URL,you need to add two query string parameters. If you are adding this two querystring parameters explicitly than, it will be default value for that queryparameters.
It's better to provide default value in action method instead of adding queryparameters in URL. You can achieve by following code.
public ActionResult ComposeEmail(int queryParameter1 = 0,string queryParameter2 = string.Empty)
{
}
Do let me know, if you have any query.
What do I want to achieve:
To show a seperate validation message for failing minimum age check and one for maximum age check
To store the minimum and maximum age in one area as integers. Not in js/ validator... only in the model. which I hope to change to look at a config file.
For the validation to work with jquery unobtrusive and server side, and to be in one place against the model (and obv some jquery)
To be enabled using data annotations
I wanted to check against DOB as a datetime, not have the user put in there age as an int. If i did I could have used [Min] notation and [Max] notation for age. It wasn't good enough.
Why didn't I use a range notation. I wanted to fire a different validation message for each min fail and max fail. I looked into this and had no look. I'd also have to pass in the range as a datetime and its static or something so I couldn't have done DateTime.Now.AddYears(-90) for instance.
My problems
I'm a noob at MVC, JQuery validation and the whole MVC architecture!
What I've come up with works. However, as you can see there is alot repeated code, I'd like to conform to DRY.
My first hack was to pass in the value that i'm checking against into the validation message. I got around this by doing...
[MaximumAgeCheck(90,"You have to be at most {0} to apply")]
and inside the validation attribute
private readonly int _min;
private readonly string _defaultErrorMessage = "";
public MinimumAgeCheck(int min, string defaultErrorMessage)
: base(defaultErrorMessage)
{
_min = min;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", _min.ToString());
}
and I used it for instance like so..
return new ValidationResult(_defaultErrorMessage);
I know this isn't the right way to do it, and wondered what is the best way to do this?
Second hack!
I'm passing in two validation parameters which I want to be able to access in the jQuery.validator.addMethod... method.
I tried to access these parameters by doing the following... params.[thevalueiadded], params[0]... etc, I even logged out params into console.log but it never showed me all the params, only the first value as a string!
My work around was to store the javascript variables at the top and load them from the adapters.add.
I'm probabily making little sense so here is the code, that works...I warn you, it is messy!
Model property and data annotation
[Required(ErrorMessage = "Date of birth required")]
[Display(Name = "Date of Birth")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
[DataType(DataType.DateTime, ErrorMessage = "Date of birth should be in dd/mm/yyyy format")]
[MinimumAgeCheck(18,"You have to be at least {0} to apply")]
[MaximumAgeCheck(90,"You have to be at most {0} to apply")]
public DateTime? DateOfBirth { get; set; }
Minimum Age Check and Maximum age check
validation attributes
public class MinimumAgeCheck : ValidationAttribute, IClientValidatable
{
private readonly int _min;
private readonly string _defaultErrorMessage = "";
public MinimumAgeCheck(int min, string defaultErrorMessage)
: base(defaultErrorMessage)
{
_min = min;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", _min.ToString());
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= _min && dtAge.Year <= 30))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, _min);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule mcvrTwo = new ModelClientValidationRule();
mcvrTwo.ValidationType = "checkminimumage";
mcvrTwo.ErrorMessage = _defaultErrorMessage;
mcvrTwo.ValidationParameters.Add("todaysdate", DateTime.Now.ToString("dd/MM/yyyy"));
mcvrTwo.ValidationParameters.Add("lowerage", _min.ToString());
return new List<ModelClientValidationRule> { mcvrTwo };
}
}
public class MaximumAgeCheck : ValidationAttribute, IClientValidatable
{
private readonly int Max;
private readonly string _defaultErrorMessage = "";
public MaximumAgeCheck(int max, string defaultErrorMessage)
: base(defaultErrorMessage)
{
Max = max;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", Max.ToString());
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= Max && dtAge.Year <= 30))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,Max);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule mcvrTwo = new ModelClientValidationRule();
mcvrTwo.ValidationType = "checkmaximumage";
mcvrTwo.ErrorMessage = _defaultErrorMessage;
mcvrTwo.ValidationParameters.Add("todaysdate", DateTime.Now.ToString("dd/MM/yyyy"));
mcvrTwo.ValidationParameters.Add("upperage", Max.ToString());
return new List<ModelClientValidationRule> { mcvrTwo };
}
}
The Jquery
(function ($) {
var mintodaysDateVal;
var maxtodaysDateVal;
var lowerageVal;
var upperageVal;
jQuery.validator.unobtrusive.adapters.add("checkminimumage", ['lowerage', 'todaysdate', 'upperage'], function (options) {
options.rules["checkminimumage"] = options.params;
mintodaysDateVal = options.params.todaysdate;
lowerageVal = options.params.lowerage;
options.messages["checkminimumage"] = options.message;
});
jQuery.validator.addMethod("checkminimumage", function (value, element, params) {
var currDate = mintodaysDateVal;
var sdoc = currDate.split('/');
var dobDate = value;
var sdob = dobDate.split('/');
//pass year,month,date in new Date object.
var vDOB = new Date(sdob[2], sdob[1] - 1, sdob[0]);
var vDOC = new Date(sdoc[2], sdoc[1] - 1, sdoc[0]);
//getAge user define function to calculate age.
var vYrs = getAge(vDOB, vDOC);
var result = false;
if (vYrs >= lowerageVal) { result = true; }
return result;
});
jQuery.validator.unobtrusive.adapters.add("checkmaximumage", ['lowerage', 'todaysdate', 'upperage'], function (options) {
options.rules["checkmaximumage"] = options.params;
maxtodaysDateVal = options.params.todaysdate;
upperageVal = options.params.upperage;
options.messages["checkmaximumage"] = options.message;
});
jQuery.validator.addMethod("checkmaximumage", function (value, element, params) {
var currDate = maxtodaysDateVal;
var sdoc = currDate.split('/');
var dobDate = value;
var sdob = dobDate.split('/');
var vDOB = new Date(sdob[2], sdob[1] - 1, sdob[0]);
var vDOC = new Date(sdoc[2], sdoc[1] - 1, sdoc[0]);
var vYrs = getAge(vDOB, vDOC);
var result = false;
if (vYrs <= upperageVal) { result = true; }
return result;
});
function getAge(oldDate, currDate) {
return currDate.getFullYear() - oldDate.getFullYear();
}
} (jQuery));
I hope this makes sense, I've read it over and its quite garbled... so i'll be happy to answer any comments.
Really useful code, but the ValidationResult IsValid method has some bugs. It doesn't handle future dates or blank dates. Plus it seems to have a hard coded limit to age 30 in - looks to be debug code? Anyway, I addressed those issues for my code and came up with the below:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
return new ValidationResult(_defaultErrorMessage);
}
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
if (lTicks < 0)
{
return new ValidationResult(_defaultErrorMessage);
}
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= _min ))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
Take a look at the MVC Foolproof Validation library. You can find it in NuGet.
It has pretty much all the validation you need and is added via data annotations. It will intergrate nicely into the unobtrusive client side validation.