Customized StringLength Validation doesn't work - asp.net-mvc

I added Customized StringLength class in my project
using System;
using System.ComponentModel.DataAnnotations;
namespace Argussoft.BI.DAL.Validation
{
public class PasswordLengthAttribute : StringLengthAttribute
{
public PasswordLengthAttribute(int maximumLength)
: base(maximumLength)
{
}
public override bool IsValid(object value)
{
string val = Convert.ToString(value);
if (val.Length < MinimumLength)
ErrorMessage = "Минимальная длина пароля 5 символов";
if (val.Length > MaximumLength)
ErrorMessage = "Максимальная длина пароля 20 символов";
return base.IsValid(value);
}
}
}
It's my class CreateUserDto
public class CreateUserDto
{
[Required(ErrorMessage = "Введите пароль")]
[PasswordLength(User.PasswordMaxLength, MinimumLength = User.PasswordMinLength)]
[RegularExpression(User.PasswordRegularExpression, ErrorMessage = "Пароль может содержать только латинские символы, дефисы, подчеркивания, точки")]
public virtual String Password { get; set; }
}
And it's my controller
[HttpPost]
public ActionResult CreateUser(CreateUserDto dto)
{
if (!Request.IsAjaxRequest()) return View("_AddUserDialog", dto);
if (!ModelState.IsValid) {FillViewBags(); return PartialView("_AddUserDialog", dto);}
string hash;
string salt;
CryptoUtils.SetHashAndSalt(dto.Password, out hash, out salt);
UserService.CreateUser(
new User ()
{
Name = dto.Name,
Email = dto.Email,
FullName = dto.FullName,
Role = UserService.SetRole(dto.Role),
Phone = dto.Phone,
Status = UserService.SetUserStatus(dto.Status),
PasswordHash = hash,
PasswordSalt = salt
});
return Json(new { Message = string.Format("Пользователь {0} успешно создан", dto.Name) });
}
but if I try to enter the password is less than 5 characters or more than 20, ModelState.IsValid == false, but
I don't get any error message when the password is less than 5 characters or more than 20. What's wrong?

Related

asp.net mvc model state errors keys

So I discovered an interesting problem.
I have a model like this:
public class ApplicantModel
{
[Display(Name = "Firstname", ResourceType = typeof(Resources))]
[MaxLength(50, ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
[Required(ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
public string Firstname { get; set; }
[Display(Name = "Surname", ResourceType = typeof(Resources))]
[MaxLength(50, ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
[Required(ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
public string Surname { get; set; }
}
that is all fine, and when I check the Model state and there is an error on a model I get something like this:
errors:
[{
Key = FirstApplicant.Firstname
Value = ["First name is required field"]
},
{
Key = FirstApplicant.Surname
Value = ["Surname name is required field"]
}].
That is also fine.
Edit:
This is the c# ModelState object visualized as JSON object. Real object looks like this:
ModelState
{System.Web.Mvc.ModelStateDictionary}
Count: 2
IsReadOnly: false
IsValid: false
Keys: Count = 2
Values: Count = 2
Results View: Expanding the Results View will enumerate the IEnumerable
However my question is. Is it possible to somehow change the key? I know that the key is created as the name of object and then the name property on that object.
So it makes sense, but is there any way how to change this default behavior? Or do I have to change the names of objects?
Edit2:
What I am trying to achieve here is that I have a c# ViewModel and knockout ViewModel. and when you do server side validations you get this dictionary of keys and values which I serialize and send to client.
And then I call this function on it on client:
var errors = #Html.Raw(Json.Encode(Model.Errors));
function showErrors(serializedErrors) {
var errors = JSON.parse(serializedErrors);
for (var i = 0; i < errors.length; i++) {
var error = errors[i];
var key = error.Key;
var property = eval("masterModel." + key);
property.setError(error.Value.ErrorMessage);
property.isModified(true);
}
}
showErrors(errors);
And this would work fine if the view model property names match on the server and on client. But for example on server side I have a FirstApplicant.FirstName and on a client side it is ApplicantOne.firstname. Thank you all for help and comments. I hope I explained my problem in more detail this time.
in the end I found a solution to this problem. It is a bit complicated but it works.
First I've created an attribute.
public class ClientNameAttribute : Attribute, IMetadataAware
{
public ClientNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["ClientName"] = this.Name;
}
}
Notice that this attribute also implements IMetadataAware
Next step was to create Html helper, so I could call this in a view.
public static class HtmlHelperExtensions
{
public static string CustomModelState<T>(this HtmlHelper<T> helper)
{
var errors = helper.ViewData.ModelState.Select(
m => new { Key = GenerateClientName(m.Key, helper), Value = m.Value.Errors.FirstOrDefault() }).Where(e=> e.Value != null);
return Json.Encode(errors);
}
private static string GenerateClientName<T>(string key, HtmlHelper<T> helper)
{
StringBuilder builder = new StringBuilder();
int periodIndex = -1;
do
{
periodIndex = key.IndexOf('.', periodIndex + 1);
string part = key.Substring(0, periodIndex==-1 ? key.Length : periodIndex);
var partMetadata = ModelMetadata.FromStringExpression(part, helper.ViewData);
object clientName;
if (builder.Length > 0)
{
builder.Append('.');
}
if (partMetadata.AdditionalValues.TryGetValue("ClientName", out clientName))
{
builder.Append(clientName);
}
else
{
builder.Append(partMetadata.PropertyName);
}
}
while (periodIndex != -1);
return builder.ToString();
}
}
CustomModelState is a method that I call in a view.
like this:
var errors = #Html.Raw(Html.CustomModelState());
if (errors.length > 0) {
showErrors("masterModel",errors);
}
this will give you nicely formated errors, with your custom names of properties.
And here are tests for it:
public class TestModel
{
[Required]
public string Normal { get; set; }
[ClientName("Other")]
[Required]
public string Changed { get; set; }
[ClientName("Complicated")]
public TestModelTwo TestModelTwo { get; set; }
}
public class TestModelTwo
{
public string PropertyOne { get; set; }
[ClientName("Two")]
public string PropertyTwo{ get; set; }
}
[TestClass]
public class HtmlHelperExtensionsTests
{
[TestMethod]
public void CustomModelStateTests()
{
var model = new TestModel();
var page = new ViewPage();
page.ViewData.Model = model;
page.ViewData.ModelState.AddModelError("Normal", "Error1");
page.ViewData.ModelState.AddModelError("Changed", "Error2");
HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
var custom = helper.CustomModelState();
string expectedResult =
"[{\"Key\":\"Normal\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Other\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
Assert.AreEqual(expectedResult, custom);
}
[TestMethod]
public void CustomModelStateTests_ObjectProperty_With_ClientName()
{
var model = new TestModel();
model.TestModelTwo = new TestModelTwo();
var page = new ViewPage();
page.ViewData.Model = model;
page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyOne", "Error1");
page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyTwo", "Error2");
HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
var custom = helper.CustomModelState();
string expectedResult =
"[{\"Key\":\"Complicated.PropertyOne\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Complicated.Two\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
Assert.AreEqual(expectedResult, custom);
}
}

MVC: Custom attribute not populating in view

I am writing a custom attribute to validate that a first and last name does not exceed a certain amount of characters, but the error message is not displaying like it does for out-of-the-box annotations.
Here is my implementation.
public class User
{
[Required(ErrorMessage = "Last Name is required.")]
[RegularExpression(#"^[a-zA-Z'\s]{1,50}$", ErrorMessage = "Please enter a valid last name.")]
[FullNameMaxLength("FirstName")]
public string LastName { get; set; }
}
public class FullNameMaxLengthAttribute : ValidationAttribute
{
private string _firstName;
public FullNameMaxLengthAttribute(string firstName)
{
_firstName = firstName;
}
protected override ValidationResult IsValid(object lastName, ValidationContext validationContext)
{
clsUserRegistration userRegistrationContext = (clsUserRegistration)validationContext.ObjectInstance;
if (lastName != null)
{
string strValue = lastName.ToString();
PropertyInfo propInfo = validationContext.ObjectType.GetProperty(_firstName);
if (propInfo == null)
return new ValidationResult(String.Format("Property {0} is undefined.", _firstName));
var fieldValue = propInfo.GetValue(validationContext.ObjectInstance, null).ToString();
if (strValue.Length + fieldValue.Length > 53)
{
return new ValidationResult("First and last names are too long!");
}
return ValidationResult.Success;
}
return null;
}
}
In my view, I have a ValidationMessageFor, and it works fine with non-custom attributes. When I step through my model, it returns the ValidationMessage, but I cannot see that error message. Any thoughts?
The above is just the "back-end" validation. This for example does still work when user's browser has JavaScript turned off - the page will post back regardless of errors but then show the form again with validation messages on it.
For "front-end" validation, you need something along these lines:
public class FullNameMaxLengthAttribute : ValidationAttribute, IClientValidatable
{
// Your Properties and IsValid method here
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = String.IsNullOrEmpty(ErrorMessage) ? FormatErrorMessage(metadata.DisplayName) : ErrorMessage,
ValidationType = "fullnamemaxlength"
};
rule.ValidationParameters["firstname"] = FirstName;
rule.ValidationParameters["maxlength"] = 53;
yield return rule;
}
}
And then in JavaScript that is added to the page:
if (jQuery.validator) {
jQuery.validator.addMethod("fullnamemaxlength", function(value, element, param) {
var name = param.firstname;
var max = param.maxlength;
return name.length > max;
});
jQuery.validator.unobtrusive.adapters.add("fullnamemaxlength", ["firstname", "maxlength"], function (options) {
options.rules.fullnamemaxlength = {};
options.rules.fullnamemaxlength.firstname = options.params.firstname;
options.rules.fullnamemaxlength.maxlength = options.params.maximum;
options.messages.fullnamemaxlength = options.message;
}
);
}
Note this sits OUTSIDE of document.ready() { };
Something similar here: client-side validation in custom validation attribute - asp.net mvc 4

Change Validation For a Property in ASP.NET MVC 3 by Condition

This is my Model:
[RegularExpression(#"^08[589][0-9]{8}$", ErrorMessage = "Invalid Number!")]
public string Phone { get; set; }
[ForeignKey]
public long PhoneType { get; set; } // 1-CellPhone , 2-Phone
So I think to change RegularExpression Validation by Change PhoneType if I want say more specific:
if user select CellPhone from DropDownList the validation be
[RegularExpression(#"^08[589][0-9]{8}$", ErrorMessage = "Invalid Number!")]
and if select Phone the validation be
[RegularExpression("^[1-9][0-9]{9}$", ErrorMessage = "Invalid Number!")]
What is your suggestion?
You could write a custom validation attribute:
public class PhoneAttribute : ValidationAttribute
{
private readonly string _phoneTypeProperty;
public PhoneAttribute(string phoneTyperoperty)
{
_phoneTypeProperty = phoneTyperoperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_phoneTypeProperty);
if (property == null)
{
return new ValidationResult(string.Format("Unknown property: {0}", _phoneTypeProperty));
}
var phone = Convert.ToString(value, CultureInfo.CurrentCulture);
if (string.IsNullOrEmpty(phone))
{
return null;
}
var phoneType = (long)property.GetValue(validationContext.ObjectInstance, null);
Regex regex = null;
if (phoneType == 1)
{
regex = new Regex(#"^08[589][0-9]{8}$");
}
else if (phoneType == 2)
{
regex = new Regex("^[1-9][0-9]{9}$");
}
else
{
return new ValidationResult(string.Format("Unknown phone type: {0}", phoneType));
}
var match = regex.Match(phone);
if (match.Success && match.Index == 0 && match.Length == phone.Length)
{
return null;
}
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
}
and then decorate your view model property with this attribute:
public class MyViewModel
{
[Phone("PhoneType", ErrorMessage = "Invalid Number!")]
public string Phone { get; set; }
public long PhoneType { get; set; }
}
Another possibility (and which I would more than strongly recommend) if you want to make your life easier with validation is to use FluentValidation.NET. Just look at how easier it is to define validation rules instead of writing gazzilions of lines of plumbing code and no longer be able to understand which part is plumbing and which part is actual validation. With FluentValidation.NET there's no plumbing. You express your validation requirements in a fluent way:
public class MyViewModelValidator : AbstractValidator<MyViewModel>
{
public MyViewModelValidator()
{
RuleFor(x => x.Phone)
.Matches(#"^08[589][0-9]{8}$").When(x => x.PhoneType == 1)
.Matches("^[1-9][0-9]{9}$").When(x => x.PhoneType == 2);
}
}
Simply compare this validator with the previous one.

Semi-Complex View Model Property Validation in ASP.NET MVC 3

I am struggling to complete a server-client validation solution for a semi-complex scenario. I have a core type called DateRange:
public class DateRange {
public DateRange (DateTime? start, DateTime? end) { ... }
public DateTime? Start { get; private set; }
public DateTime? End { get; private set; }
}
I have a view model like:
public class MyViewModel {
public DateRange Period { get; set; }
}
I have a %mvcproject%\Views\Shared\EditorTemplates\DateRange.cshtml like:
#model MyCore.DateRange
#Html.Editor("Start", "Date")
#Html.Editor("End", "Date")
I also have a DateRangeModelBinder to bind the two form inputs into the DateRange property. The problem I'm having is with a DateRangeRequiredAttribute:
public class DateRangeRequired : ValidationAttribute, IClientValidatable,
IMetadataAware
{
private const string DefaultErrorMessage =
"{0} is required.";
public DateRangeRequired(bool endIsRequired = true)
: base(() => DefaultErrorMessage)
{
EndIsRequired = endIsRequired;
}
public bool EndIsRequired { get; set; }
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
if (!value.GetType().IsAssignableFrom(typeof(DateRange)))
{
throw new ArgumentException("Value is not a DateRange.");
}
var dateRange = value as DateRange;
return (dateRange.Start.HasValue && !EndIsRequired) ||
(dateRange.Start.HasValue && dateRange.End.HasValue && EndIsRequired);
}
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "daterangerequired"
};
rule.ValidationParameters.Add("endisrequired", EndIsRequired.ToString().ToLower());
yield return rule;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.DataTypeName = "DateRange";
}
}
I can't get it to hook up to the two inputs. It's almost like there needs to be a ValidatorTemplate that pairs with the EditorTemplate because of the split inputs. Any ideas? Let me know if additional clarification is needed.
You haven't shown exactly how your custom DateRangeRequiredAttribute implementation looks like, so let me suggest an example:
public class DateRangeRequiredAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public DateRangeRequiredAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "Unknown property {0}", _otherProperty));
}
var otherValue = property.GetValue(validationContext.ObjectInstance, null);
if (!(value is DateTime) || !(otherValue is DateTime))
{
return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "The two properties to compare must be of type DateTime"));
}
if ((DateTime)value >= (DateTime)otherValue)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "daterange"
};
rule.ValidationParameters.Add("other", "*." + _otherProperty);
yield return rule;
}
}
then you could decorate your view model with it:
public class DateRange
{
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d}")]
[DateRangeRequired("End", ErrorMessage = "Please select a start date before the end date")]
public DateTime? Start { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d}")]
[Required]
public DateTime? End { get; set; }
}
and finally in the view register the adapter:
jQuery.validator.unobtrusive.adapters.add(
'daterange', ['other'], function (options) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
};
var appendModelPrefix = function (value, prefix) {
if (value.indexOf('*.') === 0) {
value = value.replace('*.', prefix);
}
return value;
};
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
options.rules['daterange'] = element;
if (options.message) {
options.messages['daterange'] = options.message;
}
}
);
jQuery.validator.addMethod('daterange', function (value, element, params) {
// TODO: some more advanced date checking could be applied here
// currently it uses the current browser culture setting to perform
// the parsing. If you needed to use the server side culture, this code
// could be adapted respectively
var date = new Date(value);
var otherDate = new Date($(params).val());
return date < otherDate;
}, '');
After reading this pornography, you might consider using FluentValidation.NET which renders this extremely simple validation scenario a couple of lines to implement (which is how such simple validation scenarios should be done). I would strongly recommend you this library. I am using it in all my projects because I am sick of DataAnnotations for validation. They are so pretty limited.

Unique constraint with data annotation

I'm using the System.ComponentModel.DataAnnotations namespace to validate my domain classes. How can I create a custom attribute to validate the uniqueness of a property regardless of the database (through some interface, for example)?
This is the solution I came up with for this situation, it simply checks the table for a record with a different id that has the same value for the property being validated. It assumes that you will be using LinqToSQL, and that any table on which this kind of validation is required has a single ID column.
I'd also put a unique constraint on the underlying table in the database. This attribute allows me to put a nice error message on the form and associate it with the appropriate property.
public class UniqueAttribute : ValidationAttribute
{
public Func<DataContext> GetDataContext { get; private set; }
public string IDProperty { get; private set; }
public string Message { get; private set; }
public UniqueAttribute(Type dataContextType, string idProperty, string message)
{
IDProperty = idProperty;
Message = message;
GetDataContext = () => (DataContext)Activator.CreateInstance(dataContextType);
}
public UniqueAttribute(Type dataContextType, string idProperty, string message, string connectionString)
{
IDProperty = idProperty;
Message = message;
GetDataContext = () => (DataContext)Activator.CreateInstance(dataContextType, new object[] { connectionString });
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var idProperty = validationContext.ObjectType.GetProperty(IDProperty);
var idType = idProperty.PropertyType;
var id = idProperty.GetValue(validationContext.ObjectInstance, null);
// Unsightly hack due to validationContext.MemberName being null :(
var memberName = validationContext.ObjectType.GetProperties()
.Where(p => p.GetCustomAttributes(false).OfType<DisplayAttribute>().Any(a => a.Name == validationContext.DisplayName))
.Select(p => p.Name)
.FirstOrDefault();
if (string.IsNullOrEmpty(memberName))
{
memberName = validationContext.DisplayName;
}
// End of hack
var validateeProperty = validationContext.ObjectType.GetProperty(memberName);
var validateeType = validateeProperty.PropertyType;
var validatee = validateeProperty.GetValue(validationContext.ObjectInstance, null);
var idParameter = Expression.Constant(id, idType);
var validateeParameter = Expression.Constant(validatee, validateeType);
var objectParameter = Expression.Parameter(validationContext.ObjectType, "o");
var objectIDProperty = Expression.Property(objectParameter, idProperty);
var objectValidateeProperty = Expression.Property(objectParameter, validateeProperty);
var idCheck = Expression.NotEqual(objectIDProperty, idParameter);
var validateeCheck = Expression.Equal(objectValidateeProperty, validateeParameter);
var compositeCheck = Expression.And(idCheck, validateeCheck);
var lambda = Expression.Lambda(compositeCheck, objectParameter);
var countMethod = typeof(Queryable).GetMethods().Single(m => m.Name == "Count" && m.GetParameters().Length == 2);
var genericCountMethod = countMethod.MakeGenericMethod(validationContext.ObjectType);
using (var context = GetDataContext())
{
var table = context.GetTable(validationContext.ObjectType) as IQueryable<Models.Group>;
var count = (int)genericCountMethod.Invoke(null, new object[] { table, lambda });
if (count > 0)
{
return new ValidationResult(Message);
}
}
return null;
}
}
Example usage:
[MetadataType(typeof(UserMetadata))]
public partial class Group : IDatabaseRecord
{
public class UserMetadata
{
[Required(ErrorMessage = "Name is required")]
[StringLength(255, ErrorMessage = "Name must be under 255 characters")]
[Unique(typeof(MyDataContext), "GroupID", "Name must be unique")]
public string Name { get; set; }
}
}
just do something like this on your model
[StringLength(100)]
[Index("IX_EntidadCodigoHabilitacion", IsUnique = true)]
public string CodigoHabilitacion { get; set; }
If I am understanding you properly, you should be able to create a custom ValidationAttribute and get a context to your repository through a custom factory.
Validator:
using System.ComponentModel.DataAnnotations;
public class DBUniqueAttribute : ValidationAttribute
{
private IRepository Repository{ get; set;}
public DBUniqueAttribute()
{
this.Repository = MyRepositoryFactory.Create();
}
public override bool IsValid(object value)
{
string stringValue = Convert.ToString(value, CultureInfo.CurrentCulture);
return Repository.IsUnique(stringValue);
}
}
You would have an IRepository interface with an IsUnique() method. The MyRepositoryFactory would have a static method called Create() which would create the concrete Repository necessary for your database. If the database type changes, you only need to update the Factory to return a new Repository for your new database.
I love #daveb's solution. Unfortunately, three years later it required some pretty heavy modification for me. Here's his solution updated for EF6. Hopefully will save someone an hour or so of fiddling.
public class UniqueAttribute : ValidationAttribute
{
public UniqueAttribute(string idProperty, string message)
{
IdProperty = idProperty;
Message = message;
}
[Inject]
public DataContext DataContext { get; set; }
private string IdProperty { get; set; }
private string Message { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var objectType = validationContext.ObjectType;
if (objectType.Namespace == "System.Data.Entity.DynamicProxies")
{
objectType = objectType.BaseType;
}
var idProperty = objectType.GetProperty(IdProperty);
var idType = idProperty.PropertyType;
var id = idProperty.GetValue(validationContext.ObjectInstance, null);
var memberName = validationContext.MemberName;
var validateeProperty = objectType.GetProperty(memberName);
var validateeType = validateeProperty.PropertyType;
var validatee = validateeProperty.GetValue(validationContext.ObjectInstance, null);
var idParameter = Expression.Constant(id, idType);
var validateeParameter = Expression.Constant(validatee, validateeType);
var objectParameter = Expression.Parameter(objectType, "o");
var objectIdProperty = Expression.Property(objectParameter, idProperty);
var objectValidateeProperty = Expression.Property(objectParameter, validateeProperty);
var idCheck = Expression.NotEqual(objectIdProperty, idParameter);
var validateeCheck = Expression.Equal(objectValidateeProperty, validateeParameter);
var compositeCheck = Expression.And(idCheck, validateeCheck);
var lambda = Expression.Lambda(compositeCheck, objectParameter);
var countMethod = typeof(Queryable).GetMethods().Single(m => m.Name == "Count" && m.GetParameters().Length == 2);
var genericCountMethod = countMethod.MakeGenericMethod(objectType);
var table = DataContext.Set(objectType);
var count = (int)genericCountMethod.Invoke(null, new object[] { table, lambda });
if (count > 0)
{
return new ValidationResult(Message);
}
return null;
}
}

Resources