Skip to main content

Using Interfaces, Generics and the Repository Pattern

I’ve been using the repository pattern to insulate my apps from the LINQ to SQL so that I can easily change when (or if) MS kills L2S. The nice thing about this is that I can write a generic repository the encompasses some of the tasks so that the code is consistent for all repositories.

A little background first. We name all our tables the plural of the objects contained and the first column is an integer primary key named in the singular followed by a “Key” suffix. For example, the Customers table has a first column of CustomerKey. This key name is then used in all related tables as the foreign key column. This makes it very easy to come in behind someone and figure out the data structure.

This convention gets in the way of making life easier for me because all items have a different name for the primary key. To fix this, created an interface called IPrimaryKey, shown below.

public interface IPrimaryKey
{
    int PrimaryKey { get; set; }
}

In my database model, I change the name from CustomerKey to PrimaryKey.

image  image

I then create an interface for Customer and a partial class implementing the ICustomer interface. Note that ICustomer adds IPrimaryKey, so any class implementing ICustomer will also have a PrimaryKey.

public interface ICustomer
     :IPrimaryKey
{
     string FirstName { get; set; }
     string LastName { get; set; }
     string Address { get; set; }
     string City { get; set; }
}

partial class Customer
     : ICustomer
{
}

I can now create an interface for my repository and a generic base class that can be inherited by all my other repositories. Creating an interface allows me to use InversionOfConrol to keep my classes flexible, especially for testing.

public interface IRepository<T> where T : IPrimaryKey
{    
    T GetByKey(int key);    

    void Save(T entity);
}

public abstract class RepositoryBase<T>
    : IRepository<T> where T : IPrimaryKey
{    
    protected BogusDbDataContext dc;
    protected IQueryable<T> SourceTable;    

    public T GetByKey(int key)
    {
        return (from x in SourceTable
                where x.PrimaryKey == key                
                select x).SingleOrDefault();    
    }    

    public IQueryable<T> List()    
    {        
        return from x in SourceTable select x;    
    }    

    public abstract void Save(T entity);
}

And finally here is my customers repository.

public class CustomersRepository
    : RepositoryBase<ICustomer>, ICustomersRepository
{
    public CustomersRepository()
    {
        dc = new BogusDbDataContext();
        SourceTable = dc.Customers;
    }

    public override void Save(ICustomer entity)
    {
        dc.Customers.InsertOnSubmit((Customer)entity);
    }
}

Notice that customers has no code to retrieve an entity by it’s primary key. That code exists in RepositoryBase. All my other repositories can use the same code.

Here is a mock repository and some tests to see how it all fits together.

public class CustomersRepositoryMock
    : RepositoryBase<ICustomer>, ICustomersRepository
{
    private List<ICustomer> customers;

    public CustomersRepositoryMock()
    {
        customers = new List<ICustomer>();

        customers.Add(new Customer() { PrimaryKey = 1, FirstName = "John", LastName = "Public", Address = "123 Main", City = "Somewhereville" });
        customers.Add(new Customer() { PrimaryKey = 2, FirstName = "Larry", LastName = "Jones", Address = "456 Oak", City = "Somewhereville" });
        customers.Add(new Customer() { PrimaryKey = 3, FirstName = "Fred", LastName = "Smith", Address = "789 Grand", City = "Somewhereville" });
        customers.Add(new Customer() { PrimaryKey = 4, FirstName = "Barb", LastName = "Johnson", Address = "1234 Cedar", City = "Somewhereville" });

        this.SourceTable = customers.AsQueryable();
    }

    public override void Save(ICustomer entity)
    {

    }
}
[TestMethod()]
public void GetByKeyTest()
{
    CustomersRepositoryMock repository = new CustomersRepositoryMock();
    int key = 3;
    ICustomer target = repository.GetByKey(key);

    Assert.AreEqual(key, target.PrimaryKey);
}

[TestMethod()]
public void ListTest()
{
    CustomersRepositoryMock repository = new CustomersRepositoryMock();
    IQueryable<ICustomer> target = 
        from x in repository.List()
            where x.LastName.StartsWith("J")
            orderby x.LastName
            select x;

    Assert.AreEqual(2, target.Count());
    Assert.AreEqual("Johnson", target.First().LastName);
    Assert.AreEqual("Jones", target.Last().LastName);
}
The L2S data context needs to be handled appropriately. I treat the repositories as a unit of work in that I dispose of them almost immediately after use. In this way, I don’t get in trouble with load options. I also try to make sure that I return concrete objects except where I use IQueryable methods. This prevents unwanted problems with a data context that no longer exists.

There are other interfaces that can be used such as one for Beginning / Ending dates and IsActive. Again, write the code in RepositoryBase and all the derived repositories can use the same code.

Comments

Popular posts from this blog

Migrating Legacy Apps to the New SimpleMembership Provider

Asp.Net MVC4 uses the new SimpleMembership provider, changes the table structure and adds a new hashing algorithm. The reasons for the changes can be found in this article by Jon Galloway. This article shows how to migrate your existing apps to the new provider.I’m assuming that you stored your passwords in the unrecoverable SHA-1 format. If you didn’t, then you’ll have to change a couple of things. All of my apps are done this way so… I’m also assuming that you have created the basic skeleton of the new app and ran it once so the correct tables will be created.First, we’ll look at the new tables. Previously, we had all of those aspnet_xxxxxx tables. Here’s the new ones.UserProfileContains all of the elements relevant to the user. This is a combination of the aspnet_Users table and the aspnet_Profiles table.webpages_MembershipStores the password info when not using OAuth, Live, Facebook, etc. This table is somewhat of a match to the aspnet_Membership table.webpages_OAuthMembershipStor…

JavaScript function to automatically add slashes to date

In converting an old Windows app to a browser app, the user wanted to be able to enter dates without the slashes. Here's a simple jscript: 1:// Function to convert short date string (MMddyy) 2:// or (MMddyyyy) to a date string (mm/dd/yyyy). 3:// txtBox is the actual textbox control 4:// with the value to be processed. 5:function FixShortDate(txtBox) { 6:if (txtBox == null) { 7:return'' } 8: 9:var re = new RegExp(/(\d{6})(\d{2})?/); 10: 11:if (re.test(txtBox.value)) 12: { 13:if (txtBox.value.length == 8) { 14: txtBox.value = txtBox.value.substring(0, 2) + '/' + txtBox.value.substring(2, 4) + '/' + txtBox.value.substring(4, 8) 15: } 16: 17:if (txtBox.value.length == 6) { 18:if (txtBox.value.substring(4, 6) < 20)

Get Asp.Net Profile properties from Sql

Ever wanted to include the profile information from an Asp.Net profile in a query? It’s not that hard once you understand the structure. I’ve written a little function that does all the work. Note: I’m using Sql Server as my repository.

First we need to understand how the profile data is stored. Looking at the aspnet_Profile table, we can see that it stores the information in two columns: PropertyNames and PropertyValuesString.

Looking at PropertyNames we can see that it has a basic structure of Property Name, Data Type, Starting Position and Length. For example, in the string “FirstName:S:0:4:Phone:S:4:10:LastName:S:14:5:” we can see that FirstName is of type string, starts at position 0 and has a length of 4. Notice the zero base for the starting position, we need to correct for that in our function. This means in the PropertyValuesString “John2175551212Smith”, we would start with the first position and proceed 4 characters to get the name.

You might be thinking …