Telerik blogs

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

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.

Comments

Comments are disabled in preview mode.