> IConstraintProvider<UserDTO> because when I try to evaluate IsValid I would get an invalid cast.
Needs a bit of work but the code looks a bit like this:
var rulesToApplyToTarget = new DomainRuleSet<TTargetOfRules>();
foreach (IPropertyMappingDefinition propertyMappingDefinition in propertyMappings)
{
var ruleDefinitionsForProperty = rulesToMap.RulesFor(propertyMappingDefinition.FromProperty);
ruleDefinitionsForProperty.ForEach(ruleDefinition =>
{
var newRuleDefinition =
ruleDefinition.CloneForUseOn<TTargetOfRules>(propertyMappingDefinition.ToProperty);
rulesToApplyToTarget.Add(newRuleDefinition);
});
}
Worth a bit of explanation, which probably indicates I should refactor, but essentially propertyMappings describes the mappings between the domain and DTO, each knows which property it is mapping to/from (ignore value objects for now). So I work out the rules for each property, and as you can see above for each of them we can create a new rule definition by calling CloneForUseOn:
public IReflectionBasedRuleDefinition<TTarget> CloneForUseOn<TTarget>(PropertyInfo property)
{
return new ReflectionBasedRuleDefinition<TTarget, TProperty>(property, this.Rule);
}
public IDomainRule<TProperty> Rule { get; set; }
So you've got a new RelectionBasedRuleDefinition but setup for the property on the target, at run-time its executed like this against the target (the DTO):
public bool IsSatisfiedBy(TContainingProperty model)
{
TProperty toValidate = (TProperty)this.PropertyRuleRelatesTo.GetValue(model, null);
return this.Rule.IsSatisfiedBy(toValidate);
}
That cast can ofcourse fail. Anyway I'd fully accept its a bit convoluted, though actually using it is easy and all this is hidden away. Having said that its not been put through its paces on a big project yet and obviously there might be a better solution, and perhaps after trying it for a while I'll decide that just duplicating the rules on the DTO is fine.
2009/7/3 Peter Morris <mrpmorris@...>
> If you are interested I could blog about the approach, its not perfect but I far prefer it to most other approaches I've seen/used.I'd certainly be interested, more information is always better and besides I can always discard it if I think it is rubbish :-)
What I wanted to do at one point was to have my IConstraintProvider<UserDTO> do something like this
return
from ucp in UserConstraintProvider.GetConstraints()
where
ucp.FieldName == "FullName"
|| ucp.FieldName == "EmailAddress"
select
ucp;
The problem is that IConstraintProvider<T> returns IConstraint, but these are implemented like so
public class StringIsNotNullConstraint<TInstance> :
BaseConstraint<TInstance, string>
{
public StringIsNotNullConstraint(.....Func<TInstance, string> getValue)
{
....
}
public bool IsValid(object instance)
{
string valueToValidate = GetValue((User)instance);
return valueToValidate != null;
}
}
So IConstraintProvider<User> will return for example StringIsNotNull<User>, so I cannot directly apply those to the result of IConstraintProvider<UserDTO> because when I try to evaluate IsValid I would get an invalid cast.
I like having my constraints as generic because then I can apply them to different classes...
new StringIsNotNullConstraint<User>(u => u.Title);
new StringIsNotNullConstraint<User>(u => u.FirstName);
new StringIsNotNullConstraint<User>(u => u.LastName);
or
new StringIsNotNullConstraint<Country>(u => u.IsoCode);
new StringIsNotNullConstraint<Country>(u => u.Name);
but its strength is also its weakness in this case, so I am torn!
Let me know when your blog is done :-)
Pete