MVC Model Range Validator? - asp.net-mvc-3

i wnat to validate the datetime, My Code is:
[Range(typeof(DateTime),
DateTime.Now.AddYears(-65).ToShortDateString(),
DateTime.Now.AddYears(-18).ToShortDateString(),
ErrorMessage = "Value for {0} must be between {1} and {2}")]
public DateTime Birthday { get; set; }
but i get the error:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
please help me?

This means the values for the Range attribute can't be determined at some later time, it has to be determined at compile time. DateTime.Now isn't a constant, it changes depending on when the code runs.
What you want is a custom DataAnnotation validator. Here's an example of how to build one:
How to create Custom Data Annotation Validators
Put your date validation logic in IsValid()
Here's an implementation. I also am using DateTime.Subtract() as opposed to negative years.
public class DateRangeAttribute : ValidationAttribute
{
public int FirstDateYears { get; set; }
public int SecondDateYears { get; set; }
public DateRangeAttribute()
{
FirstDateYears = 65;
SecondDateYears = 18;
}
public override bool IsValid(object value)
{
DateTime date = DateTime.Parse(value); // assuming it's in a parsable string format
if (date >= DateTime.Now.AddYears(-FirstDateYears)) && date <= DateTime.Now.AddYears(-SecondDateYears)))
{
return true;
}
return false;
}
}
Usage is:
[DateRange(ErrorMessage = "Must be between 18 and 65 years ago")]
public DateTime Birthday { get; set; }
It's also generic so you can specify new range values for the years.
[DateRange(FirstDateYears = 20, SecondDateYears = 10, ErrorMessage = "Must be between 10 and 20 years ago")]
public DateTime Birthday { get; set; }

Related

Dynamic LINQ: Comparing Nested Data With Parent Property

I've a class with following structure:
public class BestWayContext
{
public Preference Preference { get; set; }
public DateTime DueDate { get; set; }
public List<ServiceRate> ServiceRate { get; set; }
}
public class ServiceRate
{
public int Id { get; set; }
public string Carrier { get; set; }
public string Service { get; set; }
public decimal Rate { get; set; }
public DateTime DeliveryDate { get; set; }
}
and I've dynamic linq expression string
"Preference != null && ServiceRate.Any(Carrier == Preference.Carrier)"
and I want to convert above string in Dynamic LINQ as follows:
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda<BestWayContext, bool>(condition, null).Compile();
But it showing following error:
Please correct me what am I doing wrong?
It looks like you wanted to do something like this:
var bwc = new BestWayContext
{
Preference = new Preference { Carrier = "test" },
DueDate = DateTime.Now,
ServiceRate = new List<ServiceRate>
{
new ServiceRate
{
Carrier = "test",
DeliveryDate = DateTime.Now,
Id = 2,
Rate = 100,
Service = "testService"
}
}
};
string condition = "Preference != null && ServiceRate.Any(Carrier == #0)";
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda<BestWayContext, bool>(condition, bwc.Preference.Carrier).Compile();
bool res = expression(bwc); // true
bwc.ServiceRate.First().Carrier = "test1"; // just for testing this -> there is only one so I've used first
res = expression(bwc); // false
You want to use Preference which belong to BestWayContext but you didn't tell the compiler about that. If i write your expression on Linq i will do as follows:
[List of BestWayContext].Where(f => f.Preference != null && f.ServiceRate.Where(g => g.Carrier == f.Preference.Carrier)
);
As you see i specified to use Preference of BestWayContext.

Validate age by date of birth using Enterprise Library Validation Application Block

I have the following situation:
A domain model that has a property BithDay.
I want to be able to verify that the age (that will be computed accordingally to the birthday) is lower than 150 years.
Can I do that by using the built in validtors or I have to build my own?
Can someoane provide me an example of DomainValidator?
You can use a RelativeDateTimeValidator to validate an age based on a Birth Date. For example:
public class Person
{
[RelativeDateTimeValidator(-150, DateTimeUnit.Year, RangeBoundaryType.Inclusive,
0, DateTimeUnit.Year, RangeBoundaryType.Ignore,
MessageTemplate="Person must be less than 150 years old.")]
public DateTime BirthDate
{
get;
set;
}
}
// 150 Year old person
Person p = new Person() { BirthDate = DateTime.Now.AddYears(-150) };
var validator = ValidationFactory.CreateValidator<Person>();
ValidationResults vrs = validator.Validate(p);
foreach (ValidationResult vr in vrs)
{
Console.WriteLine(vr.Message);
}
This will print: "Person must be less than 150 years old."
You can try something like this:
public class Person
{
public DateTime BirthDate { get; set; }
[RangeValidator(0, RangeBoundaryType.Inclusive, 150, RangeBoundaryType.Exclusive,
MessageTemplate="Person must be less than 150 years old.")]
public int Age
{
get { return (DateTime.Now - this.BirthDate).Days / 365; }
}
}

Validation attributed is cached

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.

AutoMapper doesn't map calculated field to scalar

I am trying to separate my MVC3 project into a proper DAL/Domain/ViewModel architecture, but I'm running into a problem with AutoMapper and mapping calculated fields from my domain to my view model.
Here's an example of what I'm trying to do:
Interface
public interface IRequirement
{
int Id { get; set; }
... bunch of others
public decimal PlanOct { get; set; }
public decimal PlanNov { get; set; }
public decimal PlanDec { get; set; }
... and so on
decimal PlanQ1 { get; }
... etc
decimal PlanYear { get; }
... repeat for ActualOct, ActualNov ... ActualQ1 ... ActualYear...
}
Domain Model
public class Requirement : IRequirement
{
public int Id { get; set; }
... bunch of others
public decimal PlanOct { get; set; }
public decimal PlanNov { get; set; }
public decimal PlanDec { get; set; }
... and so on
public decimal PlanQ1 { get { return PlanOct + PlanNov + PlanDec; } }
... etc
public decimal PlanYear { get { return PlanQ1 + PlanQ2 + PlanQ3 + PlanQ4; } }
... repeat for ActualOct, ActualNov ... ActualQ1 ... ActualYear...
}
There are also VarianceX properties, i.e. VarianceOct which is calculated as (PlanOct - ActualOct), etc.
My view model looks almost exactly the same, except instead of calculated fields it has the default getter/setter syntax, for example:
public decimal PlanQ1 { get; set; }
My AutoMapper config in Global.asax looks like this:
Mapper.CreateMap<Domain.Abstract.IRequirement, Models.Requirement.Details>();
This works fine on all properties except the calculated ones. None of my calculated fields (i.e. *Q1, *Q2, *Q3, *Q4, *Year, and all the Variance* fields) are actually mapped -- they all show up with the default value of 0.00.
I'm pretty stumped on this, and I'm also a novice at this and AutoMapper, so maybe I missed something. My intuition is that since the property signatures aren't identical (i.e. the domain object has only a non-default getter and no setter, while the view model has default getter and setter) then AutoMapper isn't picking it up. But I also did this:
Mapper.CreateMap<Domain.Abstract.IRequirement, Models.Requirement.Details>()
.ForMember(dest => dest.PlanQ1, opt => opt.MapFrom(src => src.PlanQ1);
And it still resolved to 0. I confirmed this in the debugger as well.
What am I doing wrong?
Thanks in advance.
EDIT 1
After following Wal's advice I ran the test and it worked, so I began working backwards one step at a time, first pasting in the Field1/Field2/Field3 parts into the interface/domain/view model classes and verifying it worked in my controller, then changing one thing at a time. What I found is that, since I am dealing with decimal types, if I hard-code in integer or double values then I get zero, but if I cast to a decimal or use a decimal literal then it works. But only if I manually set them, not if I pull the values from the database.
In other words, this works (i.e. PlanQ1 = 6):
var D = new Requirement { PlanOct = (decimal) 1.0, PlanNov = (decimal) 2.0, PlanDec = (decimal) 3.0 };
var V = Mapper.Map<IRequirement, Details>(D);
And this works:
var D = new Requirement { PlanOct = 1M, PlanNov = 2M, PlanDec = 3M };
var V = Mapper.Map<IRequirement, Details>(D);
But this does not (pulling a single domain object from a repository object, that in turn pulls from SQL Server using Entity Framework):
var D = requirementRepository.Requirement(5);
var V = Mapper.Map<IRequirement, Details>(D);
With the above all I get is 0 for PlanQ1 and PlanYear. I verified that PlanOct = 1, PlanNov = 2, and PlanDec = 3 in the domain object (D). I also verified that the type in all objects, including the EF generated object, is decimal, and the SQL Server type is decimal. I even tried mapping to a created view model, just to rule that out, and I still get 0 for PlanQ1 and PlanYear:
var D = requirementRepository.Requirement(5);
var V = new Details();
Mapper.Map<IRequirement, Details>(D, V);
is PlanQ1 a member of IRequirement ? you have implied it is by your last code snippet but if it isn't then you will get the behavior exactly as you describe.
Consider a simplied example of what you are doing:
public interface IFoo
{
string Field1 { get; set; }
string Field2 { get; set; }
//string Field3 { get; }
}
public class Foo1 : IFoo
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get { return Field1 + Field2; } }
}
public class Foo2
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
}
Note in this instance I have omitted Field3 from the interface; now when I run the following the mapping fails
[TestMethod]
public void Map()
{
Mapper.CreateMap<IFoo, Foo2>();
var foo1 = new Foo1() { Field1 = "field1", Field2 = "field2" };
var foo2 = new Foo2();
Mapper.Map(foo1, foo2);
Assert.AreEqual("field1field2", foo2.Field3);//fails, not mapped
}
So if I comment in Field3 from IFoo everything works again. Check this simplified example with your code.
Consider #Wal post, Try this Map,
Mapper.CreateMap<IFoo, Foo2>()
.ForMember(destination => destination.Field3, options => options.MapFrom(source => source.Field1 + source.Field2));
And
[TestMethod]
public void Map()
{
Mapper.CreateMap<IFoo, Foo2>()
.ForMember(destination => destination.Field3, options => options.MapFrom(source => source.Field1 + source.Field2));
var foo1 = new Foo1() { Field1 = "field1", Field2 = "field2" };
var foo2 = new Foo2();
Mapper.Map(foo1, foo2);
Assert.AreEqual("field1field2", foo2.Field3); // True
}
Just realized this was left unanswered so I wanted to close it out. Technically it is unanswered because I couldn't get Automapper to play nice in this scenario for some reason. What I wound up doing is going back and creating a couple of mapping methods inside my repository, one to map a single instance of the DAL object to the IRequirement object, and one to map a collection. Then in the repository instead of calling Mapper.Map I just call my custom mapping methods and it works perfectly.
I still don't understand why this doesn't work, but I've run into a few other classes where Automapper just throws up and I have to manually map at least one or two fields, though Automapper does take care of the rest in those cases.
I'm sure there's something about it I just don't see yet. But in any case, falling back to partial or fully manual mapping was my workaround.

Cannot implicitly convert type 'int?' to 'int'

In this query I have 3 records (int-(telefon = d.telefon), decimal-(pesel = d.pesel), decimal-(nip = d.nip)) another records are strings.
public ActionResult detail(int LoginID)
{
var user = (from d in baza.uzytkowniks
where LoginID == d.LoginID
select new uzytkownikModel {
imie = d.imie,
nazwisko = d.nazwisko,
telefon = d.telefon,
pesel = d.pesel,
nip = d.nip,
email = d.email,
adres_zamieszkania = d.adres_zamieszkania}).ToList();
ViewBag.daneuser = user;
return View();
}
And I have error:
Cannot implicitly convert type 'int?' to 'int'. An explicit conversion
exists (are you missing a cast?)
and two another errors with 'decimal?' instead 'int?'.
Model:
public class uzytkownikModel
{
[Required]
public string imie { get; set; }
[Required]
public string nazwisko { get; set; }
public decimal pesel { get; set; }
public decimal nip { get; set; }
public string adres_zamieszkania { get; set; }
public int telefon { get; set; }
public string email { get; set; }
}
Only imie and nazwisko are nonnullable, rest have allow null
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Now work. I change in model:
decimal to decimal?
and
int to int?
Thank you everyone for help
You'll need to post more code to get a definitive answer, but somewhere one of your variables is nullable, and to assign it to a non-nullable type you need to do .Value.
For example, if your object d's property imie is Nullable<int>, and that is what is causing your problem, you could do this:
imie = d.imie.Value
You will also need to watch for cases where d.imie is null. For example:
imie = d.imie.HasValue ? d.imie.Value : 0
Or, if you like the ?? operator (which evaluates to the first non-null value):
imie = d.imie ?? 0
(I am just using d.imie as an example -- there is not enough code posted to pintpoint the exact problem.)
In your uzytkownikModel class, the properties that are nullable should be declared as "int?" or "decimal?" instead of "int" and "decimal".
int? is the Nullable<int>. Since database columns may be null, the mapped properties are Nullable.
Consider this code which assigns int? values to int variables.
int? w = 2;
int? x = null;
int y = w ?? 3;
int z = x ?? 4;
The following will work:
int ID = (int)(i.ID == null ? 0 : i.ID);

Resources