Я бы предложил эксперимент, который я испытывал только на прошлой неделе или около того.
Исходя из этого вдохновения я создаю DTO, которые проверяют немного иначе, чем в подходе DataAnnotations
. Образец DTO:
public class Contact : DomainBase, IModelObject
{
public int ID { get; set; }
public string Name { get; set; }
public LazyList<ContactDetail> Details { get; set; }
public DateTime Updated { get; set; }
protected override void ConfigureRules()
{
base.AddRule(new ValidationRule()
{
Properties = new string[] { "name" },
Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
validator = () => this.Name.IsRequired300LenNoSpecial()
});
base.AddRule(new ValidationRule()
{
Properties = new string[] { "updated" },
Description = "required",
validator = () => this.Updated.IsRequired()
});
}
}
Это может выглядеть больше работы, чем DataAnnotations
, ну, конечно, это так, но это не так уж и много. Я думаю, что это более презентабельно в классе (у меня есть некоторые действительно уродливые классы DTO с атрибутами DataAnnotations
- вы даже больше не можете видеть свойства). И сила анонимных делегатов в этом приложении почти достойна книг (так что я открываю).
Базовый класс:
public partial class DomainBase : IDataErrorInfo
{
private IList<ValidationRule> _rules = new List<ValidationRule>();
public DomainBase()
{
// populate the _rules collection
this.ConfigureRules();
}
protected virtual void ConfigureRules()
{
// no rules if not overridden
}
protected void AddRule(ValidationRule rule)
{
this._rules.Add(rule);
}
#region IDataErrorInfo Members
public string Error
{
get { return String.Empty; } // Validation should call the indexer so return "" here
} // ..we dont need to support this property.
public string this[string columnName]
{
get
{
// get all the rules that apply to the property being validated
var rulesThatApply = this._rules
.Where(r => r.Properties.Contains(columnName));
// get a list of error messages from the rules
StringBuilder errorMessages = new StringBuilder();
foreach (ValidationRule rule in rulesThatApply)
if (!rule.validator.Invoke()) // if validator returns false then the rule is broken
if (errorMessages.ToString() == String.Empty)
errorMessages.Append(rule.Description);
else
errorMessages.AppendFormat("\r\n{0}", rule.Description);
return errorMessages.ToString();
}
}
#endregion
}
ValidationRule
и мои функции проверки:
public class ValidationRule
{
public string[] Properties { get; set; }
public string Description { get; set; }
public Func<bool> validator { get; set; }
}
/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
#region IsRequired
public static bool IsRequired(this String str)
{
return !str.IsNullOrTrimEmpty();
}
public static bool IsRequired(this int num)
{
return num != 0;
}
public static bool IsRequired(this long num)
{
return num != 0;
}
public static bool IsRequired(this double num)
{
return num != 0;
}
public static bool IsRequired(this Decimal num)
{
return num != 0;
}
public static bool IsRequired(this DateTime date)
{
return date != DateTime.MinValue;
}
#endregion
#region String Lengths
public static bool IsLengthLessThanOrEqual(this String str, int length)
{
return str.Length <= length;
}
public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
{
return !str.IsNullOrTrimEmpty() && (str.Length <= length);
}
public static bool IsRequired300LenNoSpecial(this String str)
{
return !str.IsNullOrTrimEmpty() &&
str.RegexMatch(@"^[- \r\n\\\.!:*,@$%&""?\(\)\w']{1,300}$",
RegexOptions.Multiline) == str;
}
#endregion
}
Если мой код выглядит плохо, это потому, что я работаю над этим подходом к проверке только последние несколько дней. Мне нужна эта идея, чтобы удовлетворить несколько требований:
- Мне нужно поддерживать интерфейс
IDataErrorInfo
, чтобы мой уровень MVC проверялся автоматически
- Мне нужно иметь возможность поддерживать сложные сценарии проверки (я полагаю, весь смысл вашего вопроса): я хочу иметь возможность проверки по нескольким свойствам одного и того же объекта (т. Е. StartDate и FinishDate); свойства из разных / нескольких / связанных объектов, которые я бы имел в графе объектов; и даже о других вещах, о которых я еще не думал.
- Мне нужно поддержать идею ошибки, применяемой к более чем одному свойству
- В рамках моего путешествия по TDD и DDD я хочу, чтобы мои доменные объекты описывали больше моего «домена», чем методы уровня моего сервиса, поэтому введение этих сложных условий в объекты модели (а не в DTO), кажется, достигает этого
Этот подход, я думаю, даст мне то, что я хочу, и, может быть, вы тоже.
Я бы вообразил, если бы вы прыгнули со мной на борт по этому поводу, что мы были бы довольно "сами по себе", но это могло бы того стоить. Я читал о новых возможностях проверки в MVC 2 , но он все еще не соответствует приведенному выше списку пожеланий без пользовательской модификации.
Надеюсь, это поможет.