Saturday, October 09, 2004 - Posts

Incorporating a Repository in the Virtual Cellar

A couple of quick links to throw out there

NCover - Code Coverage Tool.
NCoverBrowser - A tool created by Jeff Key to easily see your code coverage file.
Michael Swanson has a post about integrating MSBuild into a Continuous Integration environment

Virtual Cellar

So, I've been busy over the last few days implementing some classes in my architecture so I really haven't had time to post to my blog. In this post I'll discuss my implementation. I've been following the Repository discussions put forth by Steve Maine and Steve Eichert, as well as re-reading some sections in Martin Fowler's PoEAA book. Incidentally he's been busy adding further information here.

As you may (or may not recall), I laid out my initial application architecture in this post. I'd like to revisit that architecture in this post and add some concrete code samples as well. The figure below illustrates my "version 2" of the Virtual Cellar architecture, and it illustrates what I've currently coded so far

As you can see there is not much change in the lower data layers, these classes belong to the Lorengo.VirtualCellar.Data namespace, and use the provider model for isolating the data storage specific code. In the figure above, the ProducerRepository uses the IProducerDataProvider interface for handling the CRUD operations on Producer instances. The AccessVirtualCellarDataProvider uses System.Data.OleDb classes in it's implementation.

The ProducerRepository sets it's IProducerDataProvider _producerDataProvider member by calling the GetProducerDataProvider method.

_producerDataProvider = (VirtualCellarDataProviderBase.Instance()).GetProducerDataProvider();

The GetProducerDataProvider looks at the .config file for the application and loads the appropriate class (in this case the AccessVirtualCellarDataProvider). This code was patterned after the model used in .Text

<virtualCellar defaultProvider="AccessVirtualCellarDataProvider" defaultLanguage="en-en" >
  <providers>
    <clear/>
    <add name = "AccessVirtualCellarDataProvider" 
	 type = "Lorengo.VirtualCellar.Data.AccessVirtualCellarDataProvider, Lorengo.VirtualCellar.Data" 
	 connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Source=Lorengo.VirtualCellar.DB.Access\VirtualCellar.mdb;"
    />
  </providers>
</virtualCellar>

The ProducerRepository implements the IRepository interface. Currently only two methods are defined

public interface IRepository
{
    RepositoryItemBase Load( int id );
    void Save( RepositoryItemBase ri );
}

You'll notice that these methods reference the RepositoryItemBase class, which all classes wishing to be stored in a repository must inherit from. Looking at the class diagram you can see that the RepositoryItemBase class declares two attributes that support <<get>> and <<set>> operations. The "Id" property which is unique by repository for that specific instance, and the RepositoryState (I'll have to think of a better name, perhaps ItemState) which is an enum as defined below.

public enum RepositoryItemState
{
    New = 0,	// Item is new and not yet stored in the repository
    Dirty = 1,	// Item has changed since it was last retrieved from the repository
    Clean = 2	// Item is unchanged since it was last retrieved from the repository
}

The RepositoryItemState enum is used by the class implementing the IRepository interface to determine whether the RepositoryItem should be created or updated in the repository.

The RepositoryItemBase class allows the setting of these two attributes while the Producer class uses the c# "new" modifier to provide a new implementation which prevents this. Only the Repository for this class should be able to modify these attributes. This scheme only works if the developer of new business domain classes chooses to add the new implementation, it is not enforced by the design. A better solution would be to derive a new class from RepositoryItemBase which already hides the setter and move the Repository classes to their own assembly, then mark the RepositoryItemBase classes as protected internal to prevent business domain classes from overriding their behavior, for now however I will leave these classes as is.

So looking at some inital code for the ProducerRepository.Save()

/// <summary>
/// Saves the Producer to the repository
/// </summary>
/// <param name="ri"></param>
public void Save( RepositoryItemBase ri )
{
    Producer p = (Producer)ri;

    if( p == null )
    {
        throw new ArgumentNullException( "Object to save cannot be null." );
    }

    switch( ri.RepositoryState )
    {
        case RepositoryItemState.New:
        {
            ri.Id = this.ProducerDataProvider.Add( p.Name );
            ri.RepositoryState = RepositoryItemState.Clean
            break;
        }

        case RepositoryItemState.Dirty:
        {
            this.ProducerDataProvider.Update( p.Id, p.Name);
            ri.RepositoryState = RepositoryItemState.Clean
            break;
        }

        case RepositoryItemState.Clean:
        {
            // No need to do anything
            break;
        }

        default:
        {
           break;
        }
    }
}

Based on the RepositoryState we call either the Add() or Update method. Also since we are referencing the RepositoryItemBase class this allows us to set the .Id for newly created objects and modify the .RepositoryState for reflecting its Clean state. We also know that since this is a ProducerRepository we can upcast the ri instance to a Producer instance for the purpose of retrieving the Producer.Name information.

Now, this code is not perfect, we should be doing a check to make sure we are indeed getting a Producer object and throw an exception if we did not get one. I should break out refactor the different cases into their own private methods, and enclose the calls to the DataProviders in try...catch blocks to ensure that RepositoryState is updated correctly.

So what would some code look like? Well I've written multiple test cases for each of these new classes (actually I wrote those first). Here's the Save tests for the ProducerRepository class.

/// <summary>
/// Tests to make sure a newly created Producer can be saved
/// </summary>
[Test]
public void SaveProducer()
{
    Producer p1 = new Producer();
    p1.Name = "Hightower Cellars";
    _repository.Save( p1 );

    Producer p2 = (Producer)_repository.Load( p1.Id ); 

    Assert.IsNotNull( p2 );
    Assert.AreEqual( p1.Id, p2.Id );
}

/// <summary>
/// Tests to make sure a null argument exception is thrown
/// for a null producer
/// </summary>
[Test]
[ExpectedException( typeof(ArgumentNullException) )]
public void SaveNullProducer()
{
    Producer p = null;
    _repository.Save( p );
}

You'll notice that I explicitly cast the returned value from the Load() method because the IRepository signatures only return types of RepositoryItemBase. For this test, I really don't have to do that because I'm not checking any specific Producer values, but I did anyway. I could also investigate using the "as" operator or wait until C# 2.0 and use generics. Another option would be to perform an Encapsulate Downcast refactor. So many options!

For this post I just wanted get my first implementation of the design done. I'll look at adding overloads to the Load and Save methods to deal with collections, and deal with the problems of multiple aggregate roots. Furthermore I will want to add some instrumentation to the code.

with 1 Comments