Domain Object Validation with AOP

by John 6/4/2010 7:23:00 PM

A common requirement when developing applications is the ability to validate domain objects and display the subsequent validation errors on the user interface.

One method of implementing this is through the use of the IDataErrorInfo interface. This interface expects the class to implement two properties, Error and Item

 

Shown below is a class with 2 properties upon which I would like to perform validation 

 

ClassE = public class (IDataErrorInfo)

     { Methods }
    public constructor;


     { Properties }
    public property AnotherStringProperty: String;
    public property StringProperty: String;

 

    public property Error: String;
    public property Item[columnName: String]: String read get_Item;
 

end;

 Shown below is the implementation of the get_item method

 

method ClassE.get_Item(columnName: String): String;
begin
    var str: String;
    if ((String.Compare('AnotherStringProperty', columnName) = 0) and (not String.IsNullOrEmpty(self.AnotherStringProperty) and iif((self.AnotherStringProperty.Length > 5), (self.AnotherStringProperty.Length >= 10), true))) then
        str := 'AnotherStringProperty must fall between 5 and 10';
    if ((String.Compare('StringProperty', columnName) = 0) and (not String.IsNullOrEmpty(self.StringProperty) and (self.StringProperty.Length >= 10))) then
        str := 'StringProperty must be less than 10';
    begin
        result := str;
        exit
    end
end;

In this method I have specific validation 

1) AnotherStringProperty must be between 5 and 10 characters

2) StringProperty must be longer than 9 characters.

 

It gets pretty tedious having to implement this and there are usually alot of common validation requirements that can be shared across domain objects. For example

1) String lengths

2) Numbers falling between specific ranges

 

In this blog post I will be presenting a solution to this problem using the AOP framework called Cirrus in Delphi Prism.

 

Consider an Employee object as shown below

 

  Employee=public class

  private
    method ValidateBirthDate:Boolean;

  public

    property Id:Integer;
    property Firstname:String;
    property Lastname:String;
    property BirthDate:DateTime;

    property Salary:Decimal;

    property SomeNumber:Int32;

    method IsSalaryValid():Boolean;

  end;
 

It has some basic validation requirements

 

1) An Id is required

2) The firstname and lastnames must be set

3) The date of birth must be valid

4) The BirthDate must be valid. I treated this as "complex", so the class has a method to validate this

 

Shown below is how I decorate the class with aspects using my validation framework to meet the validation requirements listed above.

  [aspect:Prism.StandardAspects.Validation.Aspects.Validation]
  Employee=public class

  private
    method ValidateBirthDate:Boolean;

  public

    [aspect:Prism.StandardAspects.Validation.Aspects.NumericHasValue('An Id is Required')]
    property Id:Integer;
    [aspect:Prism.StandardAspects.Validation.Aspects.StringNullOrEmpty()]
    property Firstname:String;
    [aspect:Prism.StandardAspects.Validation.Aspects.StringNullOrEmpty('You must fill in the employees lastname')]
    property Lastname:String;
    [aspect:Prism.StandardAspects.Validation.Aspects.ValidationMethodForProperty('ValidateBirthDate','The date of birth must be valid')]
    property BirthDate:DateTime;

    property Salary:Decimal;

    [aspect:Prism.StandardAspects.Validation.Aspects.ValidationMethod('We are not paying this employee')]
    method IsSalaryValid():Boolean;

  end;
 

Each validation aspect takes parameters to display messages in the user interface if the property fails validation. If there are requirements not met by the supplied aspects you can implement your own methods.

In the code above the method validateBirthDate is used to validate the BirthDate property. 

At compile time the IDataErrorInfo interface is implemented on the domain object. An IsValid method is also exposed and can be used in non user interface code.

The IDataErrorInfo interface is suppported in WPF, Silverlight 4 and ASP.Net MVC. Domain objects can be bound to the user interface elements, validation can be supported

and validation errors can be displayed to the user while they are editing. Shown below are screen shots of when validation has failed in each environment.

The source code for the aspects and examples can be found at the RemObjects code repository 

This is an open source project that contains a number of aspects developed using the Cirrus AOP framework for Delphi Prism.

Show below is the Solution open in Visual Studio 2010. A word of warning you will need Delphi Prism 2011 to build the solution

 

 

 

I would like to highlight several projects in the Solution. Its split between implementation and examples.

Prism.StandardAspects.Validation
base framework added to projects consuming the validation aspects
Prism.StandardAspects.Validation.Silverlight  As above but for Silverlight
Prism.StandardAspects.ValidationAspects The Aspects
   
ValidationDomain
 A domain object using validation
ValidationDomain.Tests  NUnit tests for the framework
IDataErrorInfoWindowsApplication
 A WPF application showing the domain object bound to the UI
ValidationMVCApplication
 An ASP.Net MVC application showing editing and displaying of domain objects
ValidationSilverlightApplication
 A Silverlight 4 application, with the domain object bound to the UI
   

A combination of the NUnit tests and examples should give a good starting point.

Feedback is welcome either through the comments or email.

Cheers

John

Currently rated 3.0 by 2 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Delphi Prism | Cirrus | Silverlight | AOP | aspnetmvc | WPF

Implementing Dependency Injection using AOP

by John 5/21/2009 1:00:00 PM

Lately I've been making use of dependency injection. I've been using it in the development of applications to switch between test and production services without impacting the calling code. I have been doing this so that I might test specific parts of the code through unit testing without having to worry about setting up a database.

I'm using StructureMap in my example, which is intended to illustrate how one might go about performing dependency injection.  In the example below I've create an Invoice object. IRepository is an interface and TestRepository is a data access object which returns Invoices. Service is a class which makes uses Invoice Repositories. By using interfaces, I can at a later date with minimal change replace TestRepository with another class that performs data access against a database.

 

  Invoice=public class

  end;

  IRepository=public interface

    method FindAll:List<Invoice>;

  end;

  TestRepository=public class(IRepository)

  public

    method FindAll:List<Invoice>;

  end;

  Service=public class

  public

    constructor (repository:IRepository);

    method FindAllInvoices:List<Invoice>;

  end;

implementation

 

class method ConsoleApp.Main;

begin

  StructureMapConfiguration.BuildInstancesOf<IRepository>().TheDefaultIsConcreteType<TestRepository>();

  var service := ObjectFactory.GetInstance<Service>();

  var invoices:=service.FindAllInvoices;

end;

constructor Service(repository:IRepository);

begin

  self.repository:=repository;

end;

method Service.FindAllInvoices:List<Invoice>;

begin

result:=self.Repository.FindAll;

end; 

method Repository.FindAll:List<Invoice>;

begin

  var invoices:=new List<Invoice>;

  invoices.Add(new Invoice);

  invoices.Add(new Invoice);

  invoices.Add(new Invoice);

  result:=invoices;

end;

 

In the main method of ConsoleApp I'm doing a number of things.

1) Registering TestRepository as the type to return when an instance of IRepository is requested.

2) Requesting an instance of Service with dependencies injected.

3) Calling the FindAllInvoices on the service 

 

Its important to note that I could of done step (1) using a configuration file, which would better illustrate the power of dependency injection. I could switch between

a test and production version of the Repository with no changes to the code.

The downside I see of dependency injection is that your tied to creating instances of objects using factory method. Your also to a certain extent have a dependency on the  library your using, in this case StructureMap.

This being a continuation of my previous post, I'm going to demonstrate I think I quite an elegant solution to these two problems using AOP.

As in the previous post you can find the Aspect and an example at the Delphi Prism Aspects library.

If you open the solution, there are two projects to take a look at. Prism.StandardAspects.DependencyInjection which contains the PropertyDependencyInjector aspect and an example project showing how two use it which is called PropertyInjectorConsoleApplication.

Shown below is a sample of the code and its usage. In program.pas from the console application you can find the declaration of the InvoiceService. This time the aspect is intended for properties and I have highlighted the declaration below.

 

 InvoiceService=public class

  public

    [aspect:PropertyDependencyInjector]

    property ItemRepository:LineItemRepository;

    [aspect:PropertyDependencyInjector]

    property InvoiceRepository:InvoiceRepository;

    method GetAllInvoices:List<Invoice>;

  end;

 

Shown below is a code sample using the Service

 

    StructureMapConfiguration.BuildInstancesOf<IRepository<Invoice>>().TheDefaultIsConcreteType<InvoiceRepository>();

    StructureMapConfiguration.BuildInstancesOf<IRepository<LineItem>>().TheDefaultIsConcreteType<LineItemRepository>();

 

    var service:=new InvoiceService;

 

    if(assigned(service.InvoiceRepository))then

    begin

      var allInvoices:=service.InvoiceRepository.FindAll;

 

      if(allInvoices.Count=1)then

      begin

        Console.WriteLine(String.Format('Invoice Id{0}', allInvoices[0].Id));

      end

      else

      begin

        Console.WriteLine('No invoices ?');

      end;

    end

    else

    begin

      Console.WriteLine('No invoice repository');

    end;

In the above code example, the only way you would know the dependency injection is taking place are the two lines calling BuildInstancesOf

Otherwise your creating InvoiceService objects in the normal manner and calling the properties without even knowing anything is going on in the background.

 

Behind the scenes the aspect is modifying the getters and setters to call StructureMap. If you use Reflector to look at the final code  it should look like this

 

method InvoiceService.get_InvoiceRepository: InvoiceRepository;

begin

    if not self.@p_InvoiceRepository_injected then begin

        self.@p_InvoiceRepository_real := ObjectFactory.GetInstance<InvoiceRepository>;

        self.@p_InvoiceRepository_injected := true

    end;

    begin

        result := self.@p_InvoiceRepository_real;

        exit

    end

end;

Notice how the actual creation of property is delegated to StructureMap.

 

In conclusion, I think this is pretty cool. 

 

 

 

 

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

AOP | IOC | Cirrus | Delphi Prism

Powered by BlogEngine.NET 1.3.0.0
Theme by Mads Kristensen

About the author

Name of author John Moshakis
I'm a software developer living in Toronto..

E-mail me Send mail

Calendar

<<  November 2017  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

Pages

    Recent posts

    Recent comments

    Authors

    Tags

    Don't show

      Disclaimer

      The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

      © Copyright 2017

      Sign in