Click here to Skip to main content
15,867,568 members
Articles / Web Development / IIS

WCF by Example - Chapter II - Repositories

Rate me:
Please Sign up or sign in to vote.
4.82/5 (23 votes)
5 Nov 2010CPOL5 min read 96.9K   56   16
Introducing the RepositoryLocator pattern.

Previous

Next

Chapter IChapter III

The Series

WCF by example is a series of articles that describe how to design and develop a WPF client using WCF for communication and NHibernate for persistence purposes. The series introduction describes the scope of the articles and discusses the architect solution at a high level.

Chapter Overview

In the previous chapter, we introduced a basic implementation of the Repository which requires to be implemented for each entity in our domain. As we indicated, this component is key for persistence purposes for our domain layer and services as they need to extensively use these components. As a result, it is a good approach to streamline our design in this area so we can provide a comprehensive level of service without compromising coupling between the business logic and our back-end persistence implementation. This chapter tries to justify the creation of a new sort of service: the repository locator. We will see how the use of Generics will prove to be indispensable in this pattern. The pattern strives on removing duplication and keeping a simple facade for services and domain entities.

The source code for this chapter can be found at CodePlex change set 67243. The latest code for the eDirectory solution can also be found at Codeplex.

Current Design Issues

Having concrete repositories for each entity is a costly way to provide persistence functionality to our business layer. We need a more flexible design. The current design requires the entities knowing the type of repository to use. In the previous chapter, we had:

C#
public static Customer Create(IRepository<customer> repository, CustomerDto operation)
{
    ...
}

Although the use of an interface using Generics seems to be appropriate, we saw that the implementation is costly when creating the in-memory implementation. If we have tens of entities, this is a very expensive approach. Services suffer from the same problem, the need to hold an instance of the repository.

Introducing the Service Locator

We need to provide a single implementation for our domain layer and services that can be used in a transparent manner without having to develop individual implementations for each entity type. We also need a design that can provide a mechanism for articulating specialised calls to the back-end, if required, for different purposes like reporting, performance, and so on. (We are not covering this aspect in this chapter though.) The proposed pattern exposes generic methods instead of being a generic class. It serves as a proxy to the back-end repositories, providing a transparent mechanism for our services and entities to execute persistence calls.

We are going to leave our IRepository as it was designed in the previous chapter, but we are adding a new interface: IRepositoryLocator:

RepositoryLocatorInterface.png

It is worth noting that both interfaces are very similar; they expose the same functionality, but the new implementation uses generic methods:

C#
public interface IRepositoryLocator
{
    #region CRUD operations
    
    TEntity Save<TEntity>(TEntity instance);
    void Update<TEntity>(TEntity instance);
    void Remove<TEntity>(TEntity instance);
    
    #endregion

    #region Retrieval Operations

    TEntity GetById<TEntity>(long id);
    IQueryable<TEntity> FindAll<TEntity>();

    #endregion

    IRepository<T> GetRepository<T>();
}

The last method (GetRepository) is not really required in the interface, but it is critical for the base implementation, so for clarity purposes, we will expose it on the interface.

The solution will provide two implementations of the RepositoryLocator, the first one for the in-memory, and the second for NHibernate. Both implementations share common functionality that we will place in a base class: RepositoryLocatorBase.

C#
public abstract class RepositoryLocatorBase
    : IRepositoryLocator
{

    #region IRepositoryLocator Members

    public TEntity Save<TEntity>(TEntity instance)
    {
        return GetRepository<TEntity>().Save(instance);
    }

    public void Update<TEntity>(TEntity instance)
    {
        GetRepository<TEntity>().Update(instance);
    }

    public void Remove<TEntity>(TEntity instance)
    {
        GetRepository<TEntity>().Remove(instance);
    }

    public TEntity GetById<TEntity>(long id)
    {
        return GetRepository<TEntity>().GetById(id);
    }

    public IQueryable<TEntity> FindAll<TEntity>()
    {
        return GetRepository<TEntity>().FindAll();
    }

    public abstract IRepository<T> GetRepository<T>();

    #endregion
}

The beauty of the above implementation is that entities and services delegate onto the RepositoryLocator for finding out the correct repository. Concrete implementations of the base class will provide the mechanism to retrieve the correct back-end repository.

Re-factor of Entities and Services

Now we can re-factor the Customer entity to use the RepositoryLocator instead:

CustomerCreateReFactor.gif

We also replace the Customer repository in the service with RepositoryLocator:

ServiceRefactor.png

Re-factor of the In-memory Repositories

In the previous chapter, we defined the RepositoryEntityStore and RepositoryCustomer. We want to define a single implementation of the IRepository for our in-memory implementation that is valid for all our entities. Unfortunately, the in-memory implementation requires a mechanism for generating the entity PK, a function that NHibernate resolves delegating to the back-end database. Therefore, a few changes are required in our entities to facilitate a common facade; a new interface is declared:

C#
public interface IEntity
{
    long Id { get; }
}

As we mentioned before, we are assuming that all our entities have a numeric PK. We are creating an abstract class implementing this interface: EntityBase. At this point, it is very simple; we may extend it in later chapters:

C#
public abstract class EntityBase
        :IEntity
{
    public virtual long Id { get; protected set; }
}

All entities inherit from this base class, therefore the Customer class will look like:

C#
public class Customer
       :EntityBase
{
    protected Customer() { }

    public virtual string FirstName { get; protected set; }
    public virtual string LastName { get; protected set; }
    public virtual string Telephone { get; protected set; }

    public static Customer Create(IRepositoryLocator locator, CustomerDto operation)
    {
    ...
    }
}

Please note that the properties have been declared virtual to comply with the lazy load functionality in NHibernate. At this point, we can re-factor our in-memory Repository. We are consolidating the functionality we defined in two classes into one single class; the re-factoring is somehow extensive, so we will discuss only the more relevant aspects:

C#
public class RepositoryEntityStore<TEntity>
    :IRepository<TEntity>
{
    protected readonly IDictionary<long, TEntity> RepositoryMap = 
                       new Dictionary<long, TEntity>();

    #region IRepository<TEntity> Members

    public TEntity Save(TEntity instance)
    {
        IEntity entityInstance = GetEntityInstance(instance);
        if (entityInstance.Id != 0)
        {
            throw new ApplicationException("Entity instance cannot " + 
                      "be saved, the Id field was not cero");
        }
        GetNewId(instance);
        RepositoryMap.Add(entityInstance.Id, instance);
        return instance;
    }

    public void Update(TEntity instance) { ... }

    public void Remove(TEntity instance)
    {
        IEntity entityInstance = GetEntityInstance(instance);
        RepositoryMap.Remove(entityInstance.Id);
    }

    public TEntity GetById(long id)
    {
        return RepositoryMap[id];
    }

    public IQueryable<TEntity> FindAll()
    {
        return RepositoryMap.Values.AsQueryable();
    }

    #endregion

    #region Helper Methods

    private void GetNewId(TEntity instance) { ... }

    private readonly IDictionary<Type, MethodInfo> Setters = 
                     new Dictionary<Type, MethodInfo>();

    private MethodInfo GetSetter(Type type) { ... }

    private IEntity GetEntityInstance(TEntity instance)
    {
        var entityInstance = instance as IEntity;
        if (entityInstance == null)
            throw new ArgumentException("Passed instance is not an IEntity");
        return entityInstance;
    }

    #endregion
}

The Save method is expected to generate the Entity ID property; the private helper methods work out the new ID and update the entity. At this point, we can discard our RepositoryCustomer that is not needed anymore and re-factor our tests.

Note [Nov-2010]: Classes in this assembly were later renamed with the InMemory suffix.

Chapter Summary

We discussed some of the problems of the pattern used in the previous chapter, and the need for a service that provides persistence functionality that does not require additional work if a new entity is added to our entity. The RepositoryLocator is a neat approach that provides a simple layer of abstraction between the business and the persistence layer in an elegant manner.

In the next chapter, we leave the persistence layer to focus on the communication standards, and provide the baseline for the messaging process between the client and server components.

History

  • 4-Nov-2010: Source code revision was done as a result of some typos.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Ireland Ireland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionRepositories with DB4O (OODBMS) Pin
sangrampp16-Mar-14 23:51
sangrampp16-Mar-14 23:51 
AnswerRe: Repositories with DB4O (OODBMS) Pin
Enrique Albert18-Mar-14 0:31
Enrique Albert18-Mar-14 0:31 
QuestionQuestion Pin
Parhs22-Oct-12 8:00
Parhs22-Oct-12 8:00 
AnswerRe: Question Pin
Enrique Albert22-Oct-12 10:11
Enrique Albert22-Oct-12 10:11 
QuestionFactory method with Dao pattern Pin
e90096e14-May-12 4:40
e90096e14-May-12 4:40 
AnswerRe: Factory method with Dao pattern Pin
Enrique Albert22-Oct-12 10:17
Enrique Albert22-Oct-12 10:17 
QuestionIRepositoryLocator should only focus on getting repository Pin
farcryzry4-Aug-11 22:04
farcryzry4-Aug-11 22:04 
AnswerRe: IRepositoryLocator should only focus on getting repository Pin
Enrique Albert4-Aug-11 22:25
Enrique Albert4-Aug-11 22:25 
QuestionAre there any design diagrams about the Repository Locator? Pin
kate08245-Jan-11 16:26
kate08245-Jan-11 16:26 
AnswerRe: Are there any design diagrams about the Repository Locator? Pin
Enrique Albert5-Jan-11 17:47
Enrique Albert5-Jan-11 17:47 
GeneralThere is some mistyped code Pin
Jacek Spólnik3-Nov-10 8:12
professionalJacek Spólnik3-Nov-10 8:12 
GeneralRe: There is some mistyped code [modified] Pin
Enrique Albert3-Nov-10 16:49
Enrique Albert3-Nov-10 16:49 
QuestionHow would you implement Pin
rittersh8-Oct-10 10:38
rittersh8-Oct-10 10:38 
AnswerRe: How would you implement Pin
Enrique Albert8-Oct-10 23:39
Enrique Albert8-Oct-10 23:39 
GeneralWrong interpretation for Locator Pattern Pin
Tawani Anyangwe12-Jul-10 5:14
Tawani Anyangwe12-Jul-10 5:14 
GeneralRe: Wrong interpretation for Locator Pattern Pin
Enrique Albert12-Jul-10 17:02
Enrique Albert12-Jul-10 17:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.