ASP .NET MVC 3 Data Annotations GreaterThan LowerThan for DateTime and int - datetime

I would like to know what is the easiest way to have a "Greater Than" & "Lower Than" validation on a ASP.NET MVC 3 form?
I use unobtrusive JavaScript for client validation.
I have two DateTime properties (StartDate & EndDate) and I need a validation to be sure that the EndDate is greater than the StartDate.
I have another similar case with another form on which I have a MinValue (int) & MaxValue (int).
Does this type of validation exist by default? Or does someone know an article which explains how to implement it?

Could look at the dataannotationsextensions it does Min/Max for int
Also have a look at a foolproof validation it inlcudes GreaterThan comparison for numeric/datetime etc

You can simply do this with custom validation.
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
public class DateGreaterThanAttribute : ValidationAttribute
{
string otherPropertyName;
public DateGreaterThanAttribute(string otherPropertyName, string errorMessage)
: base(errorMessage)
{
this.otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
try
{
// Using reflection we can get a reference to the other date property, in this example the project start date
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
// Let's check that otherProperty is of type DateTime as we expect it to be
if (otherPropertyInfo.PropertyType.Equals(new DateTime().GetType()))
{
DateTime toValidate = (DateTime)value;
DateTime referenceProperty = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
// if the end date is lower than the start date, than the validationResult will be set to false and return
// a properly formatted error message
if (toValidate.CompareTo(referenceProperty) < 1)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
else
{
validationResult = new ValidationResult("An error occurred while validating the property. OtherProperty is not of type DateTime");
}
}
catch (Exception ex)
{
// Do stuff, i.e. log the exception
// Let it go through the upper levels, something bad happened
throw ex;
}
return validationResult;
}
}
and use it in model like
[DisplayName("Start date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime StartDate { get; set; }
[DisplayName("Estimated end date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
[DateGreaterThan("StartDate", "End Date end date must not exceed start date")]
public DateTime EndDate { get; set; }
This works well with server side validation.For client side validaion you can write the method like GetClientValidationRules like
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
//string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
string errorMessage = ErrorMessageString;
// The value we set here are needed by the jQuery adapter
ModelClientValidationRule dateGreaterThanRule = new ModelClientValidationRule();
dateGreaterThanRule.ErrorMessage = errorMessage;
dateGreaterThanRule.ValidationType = "dategreaterthan"; // This is the name the jQuery adapter will use
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
dateGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);
yield return dateGreaterThanRule;
}
Now simply in view
$.validator.addMethod("dategreaterthan", function (value, element, params) {
return Date.parse(value) > Date.parse($(params).val());
});
$.validator.unobtrusive.adapters.add("dategreaterthan", ["otherpropertyname"], function (options) {
options.rules["dategreaterthan"] = "#" + options.params.otherpropertyname;
options.messages["dategreaterthan"] = options.message;
});
You can find more details in this link

I don't know if writing your own validator class is the "easiest" way, but that's what I did.
Usage:
<DataType(DataType.Date)>
Public Property StartDate() As DateTime
<DataType(DataType.Date)>
<DateGreaterThanEqual("StartDate", "end date must be after start date")>
Public Property EndDate() As DateTime
Class:
<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)>
Public Class DateGreaterThanEqualAttribute
Inherits ValidationAttribute
Public Sub New(ByVal compareDate As String, ByVal errorMessage As String)
MyBase.New(errorMessage)
_compareDate = compareDate
End Sub
Public ReadOnly Property CompareDate() As String
Get
Return _compareDate
End Get
End Property
Private ReadOnly _compareDate As String
Protected Overrides Function IsValid(ByVal value As Object, ByVal context As ValidationContext) As ValidationResult
If value Is Nothing Then
' no need to do or check anything
Return Nothing
End If
' find the other property we need to compare with using reflection
Dim compareToValue = Nothing
Dim propAsDate As Date
Try
compareToValue = context.ObjectType.GetProperty(CompareDate).GetValue(context.ObjectInstance, Nothing).ToString
propAsDate = CDate(compareToValue)
Catch
Try
Dim dp As String = CompareDate.Substring(CompareDate.LastIndexOf(".") + 1)
compareToValue = context.ObjectType.GetProperty(dp).GetValue(context.ObjectInstance, Nothing).ToString
propAsDate = CDate(compareToValue)
Catch
compareToValue = Nothing
End Try
End Try
If compareToValue Is Nothing Then
'date is not supplied or not valid
Return Nothing
End If
If value < compareToValue Then
Return New ValidationResult(FormatErrorMessage(context.DisplayName))
End If
Return Nothing
End Function
End Class

Take a look at the answer of this thread,
There is a lib called MVC.ValidationToolkit.
Though I'm not sure whether it works in case of DateTime fields.

You can use the DateGreaterThanEqual attribute in your model. Here is a snippet of code that I used to validate two fields in my form.
[DataType(DataType.Date)]
[DisplayName("From Date")]
public DateTime? StartDate { get; set; }
[DataType(DataType.Date)]
[DisplayName("To Date")]
[DateGreaterThanEqual("StartDate")]
public DateTime? EndDate { get; set; }

Related

Web Api adding underscore prefix to json property names

This is what I get
{"_ProviderId":476,"_FirstName":" ","_LastName":"Nam Of Provders","_Specialty":"Pediatrics"}
api code
Public Class ProviderSimpleModel
Public Property ProviderId As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property Specialty As String
Public ReadOnly Property Name() As String
Get
Return If(FirstName, "").Trim() + " " + If(LastName, "").Trim()
End Get
End Property
End Class
<HttpGet>
Public Function GetProviderSimpleList(Optional id As Integer = 0) As List(Of ProviderSimpleModel)
Dim db As New LinqConsole.MyDataContext
Dim q = From pr In db.Providers
Where pr.ProviderId = id OrElse id = 0
Select New ProviderSimpleModel With
{
.ProviderId = pr.ProviderId,
.FirstName = pr.FirstName,
.LastName = pr.LastName,
.Specialty = pr.Specialty
}
Dim list = q.OrderBy(Function(x) x.FirstName).ThenBy(Function(x) x.LastName).ToList()
db.Dispose()
Return list
End Function
why is this happening and how to stop it?
Updated Newtonsoft from 10.x to 13.x, issue solved.

Xamarin.Forms 2 DatePickers Don't allow end date to be before start date

I have two datepickers bound to an object inheriting from INotifyPropertyChanged - here is the code for the DateTime properties
private DateTime _startDate;
public DateTime StartDate
{
get { return _startDate; }
set
{
if (DateTime.Compare(value, _startDate) == 0)
{
return;
}
_startDate = value;
OnPropertyChanged();
}
}
//public DateTime StartDate { get; set; }
private DateTime _endDate;
public DateTime EndDate
{
get { return _endDate; }
set
{
if (DateTime.Compare(value, _endDate) == 0)
{
return;
}
_endDate = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
in the forms page I have this
DatePicker startDate = new DatePicker
{
BindingContext = uCoSafe,
Format = "D"
};
startDate.SetBinding(DatePicker.DateProperty, new Binding("StartDate", BindingMode.OneWayToSource));
startDate.PropertyChanged +=(sender, e) =>
{
uCoSafe.EndDate = uCoSafe.StartDate.AddDays(1);
};
MyCoLabel endDateLbl = new MyCoLabel(1, "End Date");
DatePicker endDate = new DatePicker
{
BindingContext = uCoSafe,
Format = "D"
};
endDate.SetBinding(DatePicker.DateProperty, new Binding("StartDate", BindingMode.TwoWay));
This all works fine. If I change the start date then the end date is updated.
I also want it, if the end date is changed manually then it can't be set before the start date.
i tried this
endDate.PropertyChanged += (sender, e) =>
{
if (uCoSafe.EndDate.Date <= uCoSafe.StartDate.Date)
{
uCoSafe.EndDate = uCoSafe.StartDate.AddDays(1);
DisplayAlert("End Date Error", "End date cannot be earlier than the start date, please try again", "OK");
}
}
But it only triggers when the startDate is changed (when I don't want the message) but not when the datepicker for endDate is manually changed.
Any suggestions greatly appreciated
Your binding for endDate looks wrong. You wrote:
endDate.SetBinding(DatePicker.DateProperty, new Binding("StartDate", BindingMode.TwoWay));
And you probably meant:
endDate.SetBinding(DatePicker.DateProperty, new Binding("EndDate", BindingMode.TwoWay));
^^^^^^^^^
And this should get you back on the track.
Now, what I would do in this case, instead of displaying an alert in case of error, is just prevent the user to make such error, by Binding the MinimumDateProperty of the endDate to the uCoSafe.StartDate. That will probably make a better user experience at the end, still IMHO.

DataBinding a DateTimePicker raises "DataBinding cannot find a row in the list that is suitable for all bindings."

I have a simple test application which reproduces an error I encountered recently. Basically I have a simple WinForm with databound TextBox and DateTimePicker controls, and a button. When I execute the code below (on the button click), I get the error "DataBinding cannot find a row in the list that is suitable for all bindings". If I move the DataSource assignment into the form's constructor, I don't get the error.
If I remove the data binding for the DateTimePicker, it works fine.
Can anyone explain what the problem is ?
public partial class Form1 : Form
{
private BindingSource bs;
public Form1()
{
InitializeComponent();
button1.Click += new EventHandler(button1_Click);
bs = new BindingSource();
bs.DataSource = typeof(Thing);
this.textBox1.DataBindings.Add("Text", bs, "MyString");
this.dateTimePicker1.DataBindings.Add(new Binding("Value", bs, "MyDate"));
//Thing thing = new Thing { MyString = "Hello", MyNumber = 123, MyDate = DateTime.Parse("01-Jan-1970") };
//bs.DataSource = thing;
}
private void button1_Click(object sender, EventArgs e)
{
Thing thing = new Thing { MyString = "Hello", MyNumber = 123, MyDate = DateTime.Parse("01-Jan-1970") };
bs.DataSource = thing;
}
}
public partial class Thing
{
public String MyString { get; set; }
public Int32 MyNumber { get; set; }
public DateTime MyDate { get; set; }
}
}
Thanks
Edit:
It seems that if I change the data binding for the DateTimePicker control such that I bind to the "Text" property, the problem goes away. I don't understand why that would be though, because "Value" is valid for data binding.

Specified cast is not valid in EmployeeUtilityClass

i have a table empinf
fields
Empid int
EmpName Nvarchar
Salary Numeric
i have a EmployeeUtility class which contains a Method Getdetails
public EmployeeDetails Getdetails(int employeeId)
{
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("SelectEmployee", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id",SqlDbType.Int,4));
cmd.Parameters["#Id"].Value=employeeId;
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (!reader.HasRows) return null;
reader.Read();
EmployeeDetails emp = new EmployeeDetails((int)reader["EmpId"], (string)reader["EmpName"], (int)reader["Salary"]);
reader.Close();
return emp;
}
catch (SqlException e)
{
throw new ApplicationException("Data error.");
}
finally
{
con.Close();
}
}
EmployeeDetails emp=new EmployeeDetails((int)reader["EmpId"],(string)reader["EmpName"],(int)reader["Salary"]);
it shows an error in this line indicating Specified cast is not valid. please need assistance in it.
my employeeDetails class is as follows:
public class EmployeeDetails
{
private int employeeID;
public int EmployeeID
{
get { return employeeID; }
set { employeeID = value; }
}
private string employeeName;
public string EmployeeName
{
get { return employeeName; }
set { employeeName = value; }
}
private int salary;
public int Salary
{
get { return salary; }
set { salary = value; }
}
public EmployeeDetails(int employeeId, string employeeName, int salary)
{
EmployeeID = employeeID;
EmployeeName = employeeName;
Salary = salary;
}
public EmployeeDetails()
{
// TODO: Complete member initialization
}
}
I'm assuming that this error is a runtime exception, and not a compiler error?
The problem is not with your EmployeeDetails class - but on one of these
(int)reader["EmpId"], (string)reader["EmpName"], (int)reader["Salary"]
One or more of the values being populated into the data reader cannot be converted into int or string, either because of a bad type or because it's null.
Since EmpId is likely to be an Identity column, and therefore not null, my money is on either the EmpName column being null or the Salary column actually containing a float, or being null. In the second case, you could use an int?, unless it's just simply dodgy data, in which case make sure the column is not nullable.
You can use the Convert class for converting column values to .net types - because they will actually be Sql types. That said, those types - SqlInt32, for example - does support casting directly to int, for example, but it will also throw an exception if the underlying value is null. That said, so will Convert; so I suppose either will actually do.
This is mostly a guess, but it could be this:
(int)reader["Salary"]
Numeric is a decimal data type, not an integer. But you're trying to directly cast it to an integer. If the Salary value in the database is a decimal, try changing the Salary value on the object to a decimal as well.
Note that you can easily debug this and narrow down the problem in the future by simply removing one field at a time and re-running the test. When you remove a field and it works, re-add the field and it again doesn't work, you've found the culprit.
Note also that your exception handling is woefully poor form:
catch (SqlException e)
{
throw new ApplicationException("Data error.");
}
Not only do you completely ignore the actual exception, but you replace it with another that's entirely devoid of context. You should at least log the exception that occurs.
Try this:
EmployeeDetails emp=new EmployeeDetails(Convert.ToInt32(reader["EmpId"]),Convert.ToString(reader["EmpName"]),Convert.ToInt32(reader["Salary"]));
//this assumes that you don't have NULL values in any of those columns, because if you do, it will fail. It would be more safe to check for DBNull.Value before trying to Cast these values.

ASP.NET: URI handling

I'm writing a method which, let's say, given 1 and hello should return http://something.com/?something=1&hello=en.
I could hack this together pretty easily, but what abstraction functionality does ASP.NET 3.5 provide for building URIs? I'd like something like:
URI uri = new URI("~/Hello.aspx"); // E.g. ResolveUrl is used here
uri.QueryString.Set("something", "1");
uri.QueryString.Set("hello", "en");
return uri.ToString(); // /Hello.aspx?something=1&hello=en
I found the Uri class which sounds highly relevant, but I can't find anything which does the above really. Any ideas?
(For what it's worth, the order of the parameters doesn't matter to me.)
Edited to correct massively incorrect code
Based on this answer to a similar question you could easily do something like:
UriBuilder ub = new UriBuilder();
// You might want to take more care here, and set the host, scheme and port too
ub.Path = ResolveUrl("~/hello.aspx"); // Assumes we're on a page or control.
// Using var gets around internal nature of HttpValueCollection
var coll = HttpUtility.ParseQueryString(string.Empty);
coll["something"] = "1";
coll["hello"] = "en";
ub.Query = coll.ToString();
return ub.ToString();
// This returned the following on the VS development server:
// http://localhost/Hello.aspx?something=1&hello=en
This will also urlencode the collection, so:
coll["Something"] = "1";
coll["hello"] = "en&that";
Will output:
Something=1&hello=en%26that
As far I know nothing here. So everybody has its own implementation.
Example from LinqToTwitter.
internal static string BuildQueryString(IEnumerable<KeyValuePair<string, string>> parameters)
{
if (parameters == null)
{
throw new ArgumentNullException("parameters");
}
StringBuilder builder = new StringBuilder();
foreach (var pair in parameters.Where(p => !string.IsNullOrEmpty(p.Value)))
{
if (builder.Length > 0)
{
builder.Append("&");
}
builder.Append(Uri.EscapeDataString(pair.Key));
builder.Append("=");
builder.Append(Uri.EscapeDataString(pair.Value));
}
return builder.ToString();
}
UPDATE:
You can also create extension method:
public static UriBuilder AddArgument(this UriBuilder builder, string key, string value)
{
#region Contract
Contract.Requires(builder != null);
Contract.Requires(key != null);
Contract.Requires(value != null);
#endregion
var query = builder.Query;
if (query.Length > 0)
{
query = query.Substring(1) + "&";
}
query += Uri.EscapeDataString(key) + "="
+ Uri.EscapeDataString(value);
builder.Query = query;
return builder;
}
And usage:
var b = new UriBuilder();
b.AddArgument("test", "test");
Please note that everything here is untested.
Just combined answers=>
public static class UriBuilderExtensions
{
public static void AddQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key)) throw new ArgumentNullException
("Key '{0}' already exists!".FormatWith(key));
x.Add(key, value);
b.Query = x.ToString();
}
public static void EditQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x[key] = value;
else throw new ArgumentNullException
("Key '{0}' does not exists!".FormatWith(key));
b.Query = x.ToString();
}
public static void AddOrEditQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x[key] = value;
else
x.Add(key, value);
b.Query = x.ToString();
}
public static void DeleteQueryArgument(this UriBuilder b, string key)
{
key = Uri.EscapeDataString(key);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x.Remove(key);
b.Query = x.ToString();
}
}
Half baked code. But should work well enough.
There's also the UriBuilder class
This is something that might appeal to you- recently at work I was looking at a way to "type" commonly used URL query string variables and so developed this interface:
'Represent a named parameter that is passed from page-to-page via a range of methods- query strings, HTTP contexts, cookies, session, etc.
Public Interface INamedParam
'A key that uniquely identfies this parameter in any HTTP value collection (query string, context, session, etc.)
ReadOnly Property Key() As String
'The default value of the paramter.
ReadOnly Property DefaultValue() As Object
End Interface
You can then implement this interface to describe a query string parameter, such an implementation for your "Hello" param might look like this:
Public Class HelloParam
Implements INamedParam
Public ReadOnly Property DefaultValue() As Object Implements INamedParam.DefaultValue
Get
Return "0"
End Get
End Property
Public ReadOnly Property Key() As String Implements INamedParam.Key
Get
Return "hello"
End Get
End Property
End Class
I developed a small (and very, very basic) class to help build URLs using these strongly typed parameters:
Public Class ParametrizedHttpUrlBuilder
Private _RelativePath As String
Private _QueryString As String
Sub New(ByVal relativePath As String)
_RelativePath = relativePath
_QueryString = ""
End Sub
Public Sub AddQueryParameterValue(ByVal param As INamedParam, ByVal value As Object)
Dim sb As New Text.StringBuilder(30)
If _QueryString.Length > 0 Then
sb.Append("&")
End If
sb.AppendFormat("{0}={1}", param.Key, value.ToString())
_QueryString &= sb.ToString()
End Sub
Public Property RelativePath() As String
Get
Return _RelativePath
End Get
Set(ByVal value As String)
If value Is Nothing Then
_RelativePath = ""
End If
_RelativePath = value
End Set
End Property
Public ReadOnly Property Query() As String
Get
Return _QueryString
End Get
End Property
Public ReadOnly Property PathAndQuery() As String
Get
Return _RelativePath & "?" & _QueryString
End Get
End Property
End Class
Here's my version (needs .NET4 or a ToArray() call on the Select)
var items = new Dictionary<string,string> { { "Name", "Will" }, { "Age", "99" }};
String query = String.Join("&", items.Select(i => String.Concat(i.Key, "=", i.Value)));
I thought the use of Dictionary might mean the items can get reordered, but that doesn't actually seem to be happening in experiments here - not sure what that's about.

Resources