Demystifying The Code

Patterns-Based Silverlight Development – Part II – Repository and Validation

Introduction

In this post I will provide a brief overview of the Repository pattern, implement a Repository in our sample application, establish our server-side validation and add our test project. 

 

Acknowledgements

Most of what you will see in this post follows very closely with the code ScottGu implemented in his NerdDinner tutorial.  In fact, I would highly recommend reading that if you haven’t already.  I will implement the repository, fake repository and validation using the implementations he laid out in that chapter.  I am a big fan of ScottGu’s samples.  I feel they are very effective in walking that delicate balance between simple to understand and useful.

I will deviate a bit from the Gu’s repository in order to implement the Pipeline pattern in the next post.  Further, I will likely dedicate a post in this series to implementing Rhino Mocks for our MockRepository. 

 

The Repository Pattern

“A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.” – by Edward Hieatt and Rob Mee, http://martinfowler.com/eaaCatalog/repository.html

The major benefits of implementing the Repository pattern are twofold.  First, it abstracts away the data persistence implementation.  This gives you the ability to swap back ends out seamlessly.  This can be especially advantageous today.  You may well choose to swap out your back end database for a cloud-based data source.  Second, and in my opinion more often relevant, is that it facilitates unit testing by providing a layer where you can inject a fake or mock back end.  This allows you to test your code without having a dependence on a database.  This will be very clear in the next post.

 

Implementing the Repository

Add the ITicketRepository Interface

We will use an interface to define the signature of our repository, so the first thing we need to do is to add the ITicketRepository class.  Then we need to add all of the method signatures.  Here it is:

    public interface ITicketRepository
    {
        IQueryable<Ticket> GetTickets();
        IQueryable<SeverityLevel> GetSeverityLevels();

        void Add(Ticket ticket);
        void Delete(Ticket ticket);

        void Save();
    }

Add the TicketRepository implementation

    public class TicketRepository
    {
        HelpDeskDataContext db = new HelpDeskDataContext();

        public IQueryable<Ticket> GetTickets()
        {
            return db.Tickets;
        }

        public IQueryable<SeverityLevel> GetSeverityLevels()
        {
            return db.SeverityLevels;
        }

        public void Add(Ticket ticket)
        {
            db.Tickets.InsertOnSubmit(ticket);
        }

        public void Delete(Ticket ticket)
        {
            db.Tickets.DeleteOnSubmit(ticket);
        }

        public void Save()
        {
            db.SubmitChanges();
        }
    }

There are a few things to notice here.  The first thing is that we are delegating all of the work to the LINQ to SQL framework.  That is why you don’t see much data access code here.  Had I chosen to use the Entity Framework, we would have implemented it here, as well. 

The second, and related, is that we are returning an IQueryable<T> from our fetch operations.  When you compose a query with IQueryable, the clauses you add (where, select, order by, etc) are stored in an expression tree in memory.  What is cool about this is you can continue to add expressions into the tree up until the time you fetch the data.  For example, if a method calls GetTickets(), they will get an IQueryable<Ticket>.  They could then add additional clauses into that query.  For instance, they could add where clause that limits the tickets to those with a high severity level.  We will take full advantage of this when we implement the Pipeline pattern in the next post.  Why did I mention it here and not wait until I talked about the Pipeline pattern?  I did so because, you might notice that for Tickets, there is only 1 fetch: GetTickets().  We don’t have a GetTicketsPage(int pageNum, int pageSize)  that returns pages of tickets or a GetTicketsBySeverityLevel(SeverityLevel level).  This is by design.  We will implement that functionality in the Pipeline.  As far as the Repository is concerned, all we need to do is return the IQueryable<T>.

 

Partial Classes and Partial Methods Described

Partial Classes

You may have noticed that the Ticket and the SeverityLevel class that the L2S (LINQ to SQL) designer created were marked as partial classes:

public partial class SeverityLevel : …
public partial class Ticket : …

Partial classes allow you to split the definition of a class into multiple files.  At compile time, all of the files are compiled together.  As far as the compiler is concerned, they may as well have existed in one file.  This is a big help when working with designer-generated files.  If you were to write some custom logic (say validation logic – hint, hint) directly in the generated file, what would happen when you made a change in the designer and the code was re-generated?  Your hard work would be overwritten.  However, if you were to add your code in another file in a partial class, it would remain.  Nice.

Partial Methods

Partial Classes (or partial Structs for that matter) can contain partial methods.  With a partial method, one Partial Class or Struct contains the signature of the method:

//Definition
partial void OnValidate(System.Data.Linq.ChangeAction action);

The same partial Class (Struct) or more commonly another can contain the implementation:

partial void OnValidate(ChangeAction action)
{
    if (!IsValid)
        throw new ValidationException("Rule violations prevent saving");
}

This again is very useful in designer-generated code.  It is a great point of extensibility.  A very cool thing about partial methods is that if there is no implementation, the method and any calls to it are pulled out at compile time.  However, if there is an implementation, the call to that method will occur.

 

Server Side Validation

Create a Partial Class

As you might imagine, we are going to implement the server-side validation logic for our Ticket in a partial class.  All you need to do is:

  1. Create a class:  Right-click the HelpDesk.Data project –> Add –> Class 
  2. Name it Ticket.cs

  3. Mark it public partial

Implement our Validation

As I stated in the opening summary, I am going to use the simple validation implementation that ScottGu laid out in his NerdDinner tutorial (page 37).  I will provide a bit of an overview here, but for a more thorough discussion see Scott’s tutorial.  To start with, take a look at this code:

RuleViolation.cs

public class RuleViolation
{
    public string ErrorMessage { get; private set; }
    public string PropertyName { get; private set; }
    public RuleViolation(string errorMessage)
    {
        ErrorMessage = errorMessage;
    }
    public RuleViolation(string errorMessage, string propertyName)
    {
        ErrorMessage = errorMessage;
        PropertyName = propertyName;
    }
}

RuleViolation is a simple class with 2 primitives that allow you to indicate the ErrorMessage and optionally the name of the Property that caused the violation.

Ticket.cs

 



public partial class Ticket
{
    public const string ShortDescriptionRequired = "Short Description is required";
    public const string LongDescriptionRequired = "Long Description is required";
    public const string ShortDescriptionTooLong = "Short Description cannot exceed 50 characters";
    public const string LongDescriptionTooLong = "Long Description cannot exceed 500 characters";
    public const string TicketDateRequired = "Ticket Date is required";
    public const string TicketDateCannotExceedCurrentDate =
        "Ticket Date cannot be greater than the current date";

    public bool IsValid
    {
        get { return (GetRuleViolations().Count() == 0); }
    }

    public IEnumerable<RuleViolation> GetRuleViolations()
    {
        if (String.IsNullOrEmpty(ShortDescription))
            yield return new RuleViolation(ShortDescriptionRequired, "ShortDescription");
        if (!String.IsNullOrEmpty(ShortDescription) && ShortDescription.Trim().Length > 50)
            yield return new RuleViolation(ShortDescriptionTooLong, "ShortDescription");
        if (String.IsNullOrEmpty(LongDescription))
            yield return new RuleViolation(LongDescriptionRequired, "LongDescription");
        if (!String.IsNullOrEmpty(LongDescription) && LongDescription.Trim().Length > 500)
            yield return new RuleViolation(LongDescriptionTooLong, "LongDescription");
        if (this.TicketDate == null)
            yield return new RuleViolation(TicketDateRequired, "TicketDate");
        if (this.TicketDate > DateTime.Now)
            yield return new RuleViolation(TicketDateCannotExceedCurrentDate, "TicketDate");
        yield break;
    }

    partial void OnValidate(ChangeAction action)
    {
        if (!IsValid)
            throw new ValidationException("Rule violations prevent saving");
    }
}

 

(For ease of reading, I used constants for the error message text instead of the preferred method of using resource files.)

Take a look at the GetRuleViolations method.  This method can be called and it will return a collection of each and every RuleViolation (thanks to the yield return implementation).  Each condition (if statement) will be evaluated in order.  For each one, In the event the condition is true, the new RuleViolation will be provided to the enumerator (the method returns IEnumerable<RuleViolation>). 

Inside of the method, I have added some sample validation checks.  Clearly, this is not a complete list.  Again, the goal of this series is not to build the complete application, rather, illustrate how to apply certain patterns.  You can clearly see that you could write whatever custom validation you wanted here.  In my examples, I made sure some properties weren’t null, I did some string length checking and made sure a date falls within a range (less than current).  The important thing to realize is that if there are more than one violation, the complete list will be returned.  The other thing to know is that anyone at anytime can call GetRuleViolations without the penalty of an exception being thrown if there are any violations.

The IsValid helper method simply returns true if there are any rule violations.  Again, this can be called without fear of an exception.

The last method is the OnValidate partial method.  As described earlier, the OnValidate signature was defined in the Ticket partial class that the L2S designer generated.  Because we have added the implementation in our Ticket partial class, the method will be called by the LINQ to SQL framework prior to persisting any data.  This gives us a chance to validate the data (thus the name).  We simply call our IsValid method.  If it is not valid (there is at least one RuleViolation), we throw a ValidationException.  This is a slight deviation from what the Gu did in NerdDinner.com.  he threw an ApplicationException.  ValidationException is a simple class that implements Exception (see below).  You will see why I throw a ValidationException when I get to the post on implementing our WCF services.

ValidationException.cs

public class ValidationException : Exception
{
    public ValidationException() { }
    public ValidationException(string message) : base(message) { }
    public ValidationException(string message, Exception inner) : base(message, inner) { }
    protected ValidationException(
      SerializationInfo info,
      StreamingContext context)
        : base(info, context) { }
}

 

Testing our Validation Logic

Well, we have implemented our Repository and our server-side validation logic for Tickets (at least some validation logic).  The next thing I want to do is to add a Test project and add some methods that test our validation logic.  Again, I’m only going to add a couple which should be enough to illustrate the process.

Add a Test Project

  1. Right-Click on the Server Solution Folder –> Add –> New Project
  2. Under Visual C# –> Choose ‘Test’ –> Choose ‘Test Project’
  3. Name it HelpDesk.Server.Tests
  4. Add a reference to HelpDesk.Data
  5. Delete the stubbed out test files:
    • AuthoringTests.txt
    • ManualTest1.mht
    • UnitTest1.cs

Add a Test Class

  1. Right-Click on the HelpDesk.Server.Tests Project –> Add –> New Item
  2. Under Visual C# Items –> Choose ‘Unit Test’
  3. Name it TicketTests
  4. Delete the stubbed out code

Add a couple tests to Test our Validation logic

Now we just need to add the tests to test our logic.  Take a look:

[TestClass]
public class TicketTests
{
    [TestMethod]
    public void Ticket_Should_Be_Invalid_When_ShortDescription_Is_Empty()
    {
        Ticket ticket = new Ticket();
        ticket.TicketID = 1;
        ticket.LongDescription = "Test";
        ticket.IsOpen = false;
        ticket.SeverityLevel = new SeverityLevel() { SeverityLevelID = 1, Level = "High" };
        ticket.SeverityLevelID = 1;
        ticket.TicketDate = DateTime.Now;

        //Make sure ShortDescription is empty
        ticket.ShortDescription = "";

        bool isValid = ticket.IsValid;

        Assert.IsFalse(isValid);
    }

    [TestMethod]
    public void Ticket_Should_Be_Invalid_When_ShortDescription_Is_GreaterThan_50_In_This_Case_60()
    {
        Ticket ticket = new Ticket();
        ticket.TicketID = 1;
        ticket.LongDescription = "Test";
        ticket.IsOpen = false;
        ticket.SeverityLevel = new SeverityLevel() { SeverityLevelID = 1, Level = "High" };
        ticket.SeverityLevelID = 1;
        ticket.TicketDate = DateTime.Now;

        //Set the ShortDescription to 60
        ticket.ShortDescription = "123456789012345678901234567890123456789012345678901234567890";

        bool isValid = ticket.IsValid;

        Assert.IsFalse(isValid);
    }

    [TestMethod]
    public void Ticket_Should_Be_Valid_When_All_Properties_Are_Correct()
    {
        Ticket ticket = new Ticket();
        ticket.TicketID = 1;
        ticket.ShortDescription = "Test";
        ticket.LongDescription = "Test";
        ticket.IsOpen = false;
        ticket.SeverityLevel = new SeverityLevel() { SeverityLevelID = 1, Level = "High" };
        ticket.SeverityLevelID = 1;
        ticket.TicketDate = DateTime.Now;

        bool isValid = ticket.IsValid;

        Assert.IsTrue(isValid);
    }
}

As you can see, my method names are very verbose.  This is by design.  When you have a bunch of tests, it is nice to simply look at the name of a failed test and know what is wrong.  Because we are testing the validation logic, we are able to simply new up a new Ticket and set (or fail to set) the appropriate properties that will define our tests.  We don’t need to go through the Repository (yet). 

Here is what it looks like when I run the tests:

image

image

 

Sample Code

You can get the sample code for Building the Repository and Validation here.  (You will need to be a registered user of the site to get the code)

 

What’s Next

In the next post, I will implement the Filter layer.  At that point I will need to go through my Repository (or my Fake / Mock repository) when testing.

Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!

Demystifying The Code