The LOB Chronicles Episode 9: The Repository Pattern + Source Code Is In!

by Evan Hutnick | Comments 13

In the last episode we were exploring how MEF created a unique issue in loading assemblies and missing dependencies as well as how we utilized a creative synchronous approach to ensuring assemblies were downloaded before loading modules. This week we’re pushing full speed ahead with the application and what we have to show (more on that later), but before we start highlighting the UI work being done I thought it would be good to take an episode to discuss how we’re handling data within the application. As you might have guessed from the title this involves the Repository Pattern.

Repository Pattern + RepositoryBase

One of the primary reasons behind using the Repository Pattern is to ensure consistency in how we are accessing data in our application. A key dilemma faced by many developers in larger teams is handling data, often times resulting in different takes on how data access should be implemented and persisted throughout different portions of the application, which can lead to a maintenance nightmare if not done right. Imagine being the person responsible for picking up a paused project, only to realize that each of the ten modules handles accessing data and CRUD operations slightly differently. Before you get too freaked out – find your zen moment and try to relax, this is the exact issue we’re looking to avoid. If you remember back to when I was talking about the Infrastructure project, you can think of a repository as the infrastructure for data, ensuring that all needed data can be accessed in the application but abstracting the actual access away into a repository so regardless of what operations need to be done in-between server and client the methods for accessing and retrieving data are consistent.

As a base for this (no pun intended) our development team created a RepositoryBase abstract class that all data access will build upon. Here is the current code for RepositoryBase.cs in its entirety:

public abstract class RepositoryBase
{
    private CRMDomainContext context = new CRMDomainContext();
    public CRMDomainContext Context
    {
        get
        {
            return this.context;
        }
    }
 
    public void SaveOrUpdateEntities()
    {
        this.Context.SubmitChanges();
    }
 
    protected void LoadQuery<T> (EntityQuery<T> query , Action<IEnumerable<T>> callback)
        where T: Entity
    {
        if (this.IsContextLoading())
        {
            return;
        }
 
        EventHandler onCompleted = null;
        onCompleted = (s, args) =>
        {
            var lo = s as LoadOperation<T>;
            callback(lo.Entities);
            lo.Completed -= onCompleted;
        };
 
        var loadOperation = this.Context.Load<T>(query);
        loadOperation.Completed += onCompleted;
    }
 
    protected bool IsContextLoading()
    {
        return this.Context.IsLoading;
    }
}

You can immediately see from the first public property and method that we’re providing a very generic way to access the CRMDomainContext as well as to call SubmitChanges. Developers know that any time they call the SaveOrUpdateEntities method this will handle any operations that need to be taken care of regardless of where they are in the application or what additional logic needs to be applied. Need to save from the Companies, Activities, Contacts, or Opportunities modules? SaveOrUpdateEntities.

The LoadQuery generic method is where things start to look interesting – but when you step through the code it all just sort of makes sense. We check to see if the context is actively loading via the IsContextLoading protected bool, providing it is not the then we create an instance of an onCompleted event handler to return entities via the callback parameter. No matter what we need to access, this method provides the basis for returning an IEnumerable that we’ll use to surface data to the UI.

ActivityRepository : RepositoryBase

While all this talk is great, seeing how we’re starting to use our new repository will help put this all into perspective. Continuing with the tradition of pulling code directly from TFS while it is work-in-progress (our developers are going to love me if I keep doing this :D), here is the ActivityRepository:

[Export]
public class ActivityRepository : RepositoryBase
{
    public void GetActivities(Action<IEnumerable<Activity>> callback)
    {
        this.LoadQuery<Activity>(this.Context.GetActivitiesQuery(), callback);
    }
 
    public void GetActivitiesByCompany(Company company, Action<IEnumerable<Activity>> callback)
    {
        if (company == null)
        {
            callback(Enumerable.Empty<Activity>());
            return;
        }
 
        var companyID = company.CompanyID;
        this.LoadQuery<Activity>(this.Context.GetActivitiesByCompanyIDQuery(companyID), callback);
    }
 
    public void GetActivitiesByContact(Contact contact, Action<IEnumerable<Activity>> callback)
    {
        if (contact == null)
        {
            callback(Enumerable.Empty<Activity>());
            return;
        }
 
        var contactID = contact.ContactID;
        this.LoadQuery<Activity>(this.Context.GetActivitiesByContactIDQuery(contactID), callback);
    }
}

Looking at all three methods, notice anything in common? If you said “Yes I see a common use of LoadQuery!” then you win the prize (found in the next section). This is where having a repository comes in handy and why that generic loading method is so powerful – no matter what data we are pulling from our domain context the individual module repositories never have to look at the CRMDomainContext or know it exists. All we’re changing is the parameters that go into the method whereas the callback will return data for whatever it is being used for in the module. Neither the UI nor the ActivityRepository cares that data comes from CRMDataContext, creating more of that loosely coupled goodness that we love in good software design.

Code, Code, We’ve Got Code!

And now the moment you have all been waiting for – we have some code to download! As I mentioned in the previous section this is still very work-in-progress, so I’ll utilize a Build-style word of caution:

This code is a Developer Preview, so there may still be bugs and kinks in the logic and implementation and it is not guaranteed to run on all system. Utilize this as a resource, read through the code to see how we do X, Y, and Z, even comment on choices that have been made in coding style, but keep in mind that this != the final product.

That out of the way, we’ve got some prerequisites that are necessary for running this locally. Ensure you have the following installed if you want to try running this on your machine:

I was going to write some long spiel about all the greatness our team has put into this and to expect even more great things to come as we ramp up towards the release of this demo – but at this point all anyone really wants is a source link, so...

Download the LOB Chronicles Work-In-Progress Developer Preview

And remember – feedback, feedback, feedback!

Wrapping Up

In theory if you’re a developer you stopped at the download link above and are now in Visual Studio and not reading this, but just in case you are I’d like to reiterate that we would love to hear feedback on everything you’ve heard about in the previous 9 episodes and any thoughts on the code you now see before you. As a project in very active development we can consider suggestions or potential features to see if they will fit into the development schedule, otherwise we want to know how valuable this resource is to you and, after reviewing the code, if you would like to see more things from us like this in the future. Also, if you need to review anything we’ve done, be sure to stop on over to the LOB Chronicles webpage to get a quick recap of previous episode as well as general information on this project and our goals.

Enjoy the code and stay tuned for more!

About the author

Evan Hutnick

Evan Hutnick

works as a Developer Evangelist for Telerik specializing in Silverlight and WPF in addition to being a Microsoft MVP for Silverlight. After years as a development enthusiast in .Net technologies, he has been able to excel in XAML development helping to provide samples and expertise in these cutting edge technologies. You can find him on Twitter @EvanHutnick.

13 Comments

Dave
Doh! Cannot get the code to compile. It's a problem with ORM (it's always a problem with ORM). Error msg (truncated) is
Could not load file or assembly 'Telerik.OpenAccess.35.Extensions, Version=2011.1.411.2, Culture=neutral, PublicKeyToken=7ce17eeaf1d59342' or one of its dependencies.
Note the version number. Too sleepy to forge on. Hope it is an easy one to clear up.
Steve
Managed to get the code compiled, but cannot mount the database, seems to dislike the version differences, the Database I think is 661 and the Server is 662. It states that there is "no downgrade option available". Will look into this further over the weekend, have to work now. 
Dave
OK. I installed the latest version of Open Access and re-configured the references. It compiled!
But now I have encountered the same issue as Steve. If anyone figures out how to resolve that, I'd love to hear. Google has not been so helpful.
Casual Developer
Why are you not setting up a bBitbucket or github repository that we can see the code evolve? The feedback, feedback, feedback would be much easier as well. Just my thoughts.
Casual Developer
Why are you not setting up a bBitbucket or github repository that we can see the code evolve? The feedback, feedback, feedback would be much easier as well. Just my thoughts.
Casual Developer
@Steve, @Dave
I had the same issue. Never used the Telerik ORM before, but it looks like you have to use the same DB in the DataAccess and the web project. I just created the database from the model in my local SQL Express instance. After connecting both projects to the same DB instance it's working now.
Casual Developer
I'd really like to see unit testing and blendability in this project. That would make it a real reference project for MVVM (and Telerik's stuff). Any thoughts on that?  
Dave
OK. Finally. There's an xml file in the DataAccess project called CRMDomainModel.rlinq. In that file, you'll find a hard coded file path to the CRM.mdf file - on Evan's machine. I changed that path to point to the mdf file on my machine and it hit the database without any exceptions.
Dave
OK. Finally. There's an xml file in the DataAccess project called CRMDomainModel.rlinq. In that file, you'll find a hard coded file path to the CRM.mdf file - on Evan's machine. I changed that path to point to the mdf file on my machine and it hit the database without any exceptions.
Evan
Apologies for some of the hassles with getting this running folks, I guess it is living up to the Developer Preview warning!  For the SQL R2 versioning errors, I've read quite a few threads over the weekend on this topic as my home PC has SQL R2 and is getting a version issue, so that is still a work in progress if you have R2 installed and it is finding your 2k8 version.
RE: Github or other repository, we're working this out of TFS and it is within the same space that we work on other internal projects from for the sake of our own internal workflow, so we'll look into the possibility of doing a more public repository for a future endeavor.
RE: Unit testing and blendability, there are plans in the Q3 timeframe (so after mid-November'ish) to produce some content on Silverlight and Test Studio (functional testing) as well as JustMock (unit testing), if you have any requests on the types of things you want to see there send them over to me at hutnick at telerik dotcom. 
We'll look to mitigate some of the issues with this release in the next version (beta?), but thank you all for the feedback we've received already!
Gareth
Evan,
Is there a reason why this couldn't be written using just MVVM and not force use developers to learn Prism which can be daunting to say the least.
Gareth
Evan,
Is there a reason why this couldn't be written using just MVVM and not force us developers to learn Prism which can be daunting to say the least.
Russell
Would it be possible to put this demo online? It would be great to look at the projects evolution and interact with it without having to install components I wont have a license for (namely OpenAccess ORM - I could install the trial but that would run out as we use something different).

Comments

  1.    
      
      
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)
Read more articles by Evan Hutnick - or - read latest articles in Developer Tools