Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Desktop Developer

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

    Search

    XML Feeds

    Google Ads

    « Microsoft Access Wikis on LessThanDotLittle exam question »
    comments

    One of the biggest problems you will encounter in your career as a developer is pesky users. They always seem to enter the wrong data. So we need to validate their input into your brilliant system.

    There are several ways of doing this when using a domain model.

    First, you could make sure that all your properties and constructors are designed in such a way that your object can never be in an invalid state. I wrote about this before. The biggest problem with this, is that it can get problematic really fast. I would therefore only advise such an approach for simple objects. Really simple objects.

    The better thing to do is to create a validating framework. You can read an excellent article by JP Boodhoo. He describes how to do this in a C# kind of manner. He shows how to make a validation framework and adds a Validate method to the object. This is one method to do it. But his framework works just as well for an external framework where you pass in the object to be validated. And that is what my code in VB.Net does. I also don't use predicates but the newer Func(of T).

    First the interface for the BusinessRule:

    1. Namespace Model.Validation
    2.     Public Interface IBusinessRule(Of T)
    3.         ReadOnly Property Name() As String
    4.         ReadOnly Property Description() As String
    5.         Function IsBrokenBy(ByVal Item As T) As Boolean
    6.     End Interface
    7. End Namespace

    Then an implementation for this Interface:

    1. Namespace Model.Validation
    2.     Public Class BusinessRule(Of T)
    3.         Implements IBusinessRule(Of T)
    4.  
    5.         Private _name As String
    6.         Private _description As String
    7.         Private _action As Func(Of T, Boolean)
    8.  
    9.         Public Sub New(ByVal Name As String, ByVal Description As String, ByVal Condition As Func(Of T, Boolean))
    10.             _action = Condition
    11.             _name = Name
    12.             _Description = Description
    13.         End Sub
    14.  
    15.         Public Function IsBrokenBy(ByVal Item As T) As Boolean Implements IBusinessRule(Of T).IsBrokenBy
    16.             Return Not _action(Item)
    17.         End Function
    18.  
    19.         Public ReadOnly Property Description() As String Implements IBusinessRule(Of T).Description
    20.             Get
    21.                 Return _description
    22.             End Get
    23.         End Property
    24.  
    25.         Public ReadOnly Property Name() As String Implements IBusinessRule(Of T).Name
    26.             Get
    27.                 Return _name
    28.             End Get
    29.         End Property
    30.     End Class
    31. End Namespace

    Of course you will always have more than one rule per object so we need an IBusinessRuleSet:

    1. Namespace Model.Validation
    2.     Public Interface IBusinessRuleSet(Of T)
    3.         Function Messages(ByVal Item As T) As IList(Of String)
    4.         Function IsBroken(ByVal Item As T) As Boolean
    5.         Function BrokenBy(ByVal Item As T) As IList(Of IBusinessRule(Of T))
    6.         ReadOnly Property RulesCount() As Integer
    7.         ReadOnly Property BrokenRulesCount() As Integer
    8.     End Interface
    9. End Namespace

    And an implementation:

    1. Namespace Model.Validation
    2.     Public Class BusinessRuleSet(Of T)
    3.         Implements IBusinessRuleSet(Of T)
    4.  
    5.         Private _rules As IList(Of IBusinessRule(Of T))
    6.         Private _brokenRules As IList(Of IBusinessRule(Of T))
    7.  
    8.         Public Sub New(ByVal Rules As IList(Of IBusinessRule(Of T)))
    9.             _rules = Rules
    10.         End Sub
    11.  
    12.         Public Function BrokenBy(ByVal Item As T) As System.Collections.Generic.IList(Of IBusinessRule(Of T)) Implements IBusinessRuleSet(Of T).BrokenBy
    13.             _brokenRules = New List(Of IBusinessRule(Of T))
    14.             For Each _rule In _rules
    15.                 If _rule.IsBrokenBy(Item) Then
    16.                     _brokenRules.Add(_rule)
    17.                 End If
    18.             Next
    19.             Return _brokenRules
    20.         End Function
    21.  
    22.         Public ReadOnly Property BrokenRulesCount() As Integer Implements IBusinessRuleSet(Of T).BrokenRulesCount
    23.             Get
    24.                 If _brokenRules Is Nothing Then
    25.                     Return 0
    26.                 End If
    27.                 Return _brokenRules.Count
    28.             End Get
    29.         End Property
    30.  
    31.         Public ReadOnly Property RulesCount() As Integer Implements IBusinessRuleSet(Of T).RulesCount
    32.             Get
    33.                 Return _rules.Count
    34.             End Get
    35.         End Property
    36.  
    37.         Public Function IsBroken(ByVal Item As T) As Boolean Implements IBusinessRuleSet(Of T).IsBroken
    38.             If BrokenBy(Item).Count > 0 Then Return True
    39.             Return False
    40.         End Function
    41.  
    42.         Public Function Messages(ByVal Item As T) As System.Collections.Generic.IList(Of String) Implements IBusinessRuleSet(Of T).Messages
    43.             Dim _l As New List(Of String)
    44.             For Each _rule In BrokenBy(Item)
    45.                 _l.Add(_rule.Description)
    46.             Next
    47.             Return _l
    48.         End Function
    49.     End Class
    50. End Namespace

    And now for the Validator class that you will be using all over the place if needed ;-) :

    1. Namespace Model.Validation
    2.     Public Interface IValidator(Of T)
    3.         Function IsValid(ByVal Item As T) As Boolean
    4.         Function Validate(ByVal Item As T) As IList(Of IBusinessRule(Of T))
    5.         Function Messages(ByVal Item As T) As IList(Of String)
    6.     End Interface
    7. End Namespace

    And the implementation:

    1. Imports TDB2009.Dal.Model.Common.Interfaces
    2.  
    3. Namespace Model.Validation
    4.     Public MustInherit Class Validator(Of T As IDomainEntity)
    5.         Implements IValidator(Of T)
    6.  
    7.         Private _rules As IBusinessRuleSet(Of T)
    8.  
    9.         Public Sub New(ByVal Rules As IBusinessRuleSet(Of T))
    10.             _rules = Rules
    11.         End Sub
    12.  
    13.         Public Function IsValid(ByVal Item As T) As Boolean Implements IValidator(Of T).IsValid
    14.             Return Not _rules.IsBroken(Item)
    15.         End Function
    16.  
    17.         Public Function Validate(ByVal Item As T) As IList(Of IBusinessRule(Of T)) Implements IValidator(Of T).Validate
    18.             Return _rules.BrokenBy(Item)
    19.         End Function
    20.  
    21.         Public Function Messages(ByVal Item As T) As IList(Of String) Implements IValidator(Of T).Messages
    22.             Return _rules.Messages(Item)
    23.         End Function
    24.     End Class
    25. End Namespace

    And this is an example of an implemented BusinessRuleset:

    1. Imports TDB2009.Dal.Model.Validation
    2.  
    3. Namespace Model.AnalysisManagement.Validators.Rulesets
    4.     Public Class FiberTypeRulesSet
    5.         Inherits BusinessRuleSet(Of FiberType)
    6.         Implements Interfaces.IFiberTypeRulesSet
    7.  
    8.         Public Sub New()
    9.             MyBase.New(MakeRules)
    10.         End Sub
    11.  
    12.         Private Shared Function MakeRules() As IList(Of IBusinessRule(Of FiberType))
    13.             Dim _Rules As New List(Of IBusinessRule(Of FiberType))
    14.             _Rules.Add(New BusinessRule(Of FiberType)("NotEmptyDescriptionEn", "Description En can not be empty", Function(x) Not String.IsNullOrEmpty(x.Description.En)))
    15.             _Rules.Add(New BusinessRule(Of FiberType)("NotEmptyDescriptionFr", "Description Fr can not be empty", Function(x) Not String.IsNullOrEmpty(x.Description.Fr)))
    16.             _Rules.Add(New BusinessRule(Of FiberType)("NotEmptyDescriptionNl", "Description Nl can not be empty", Function(x) Not String.IsNullOrEmpty(x.Description.Nl)))
    17.             _Rules.Add(New BusinessRule(Of FiberType)("NotEmptyKey", "Key can not be empty", Function(x) Not String.IsNullOrEmpty(x.FiberType)))
    18.             Return _Rules
    19.         End Function
    20.     End Class
    21. End Namespace

    About the Author

    User bio imageChris is awesome.
    Social SitingsTwitterHomePageLTD RSS Feed
    InstapaperVote on HN

    1 comment

    Comment from: Bruce Johnston [Visitor]
    Bruce Johnston Great post, but I’m not sure how to plug it all together. I copied all the code above and have created a object_ruleSet class, but now I want to validate the domain class from two locations. The UI for user feedback, and then from the service layer in the ObjectBus.Save(Object) method.

    I guess what I'm missing is how to use the Valuator class, it’s definition implies that it must be inherited, OK.... then what?

    Sorry I like the example and wish to use it, but I am an old VB6er and new to .net.

    Can you help please?
    02/10/10 @ 14:49

    Leave a comment


    Your email address will not be revealed on this site.

    To mislead the spambots.

    Your URL will be displayed.
    (Line breaks become <br />)
    (Name, email & website)
    (Allow users to contact you through a message form (your email will not be revealed.)