Mar 052014
 

Introduction

Hello and apologies for the long delay between articles, I am now returning to the Entity framework.  Following on from the previous article (Entity Validation) I’m back to review the latest edition of the generic data access class.

In the time between my original series of articles concerning disconnected POCO entities and a generic approach in handling them, I’ve refactored the class(es) a fair bit, to make the code a little more pleasant.  If you want to jump straight to the sample solution, I’ll place a link to it here and at the end of the article.

This article was originally published at Sanders Technology.  Check the site for updates and additional technology goodness.

This is a direct update to this former article: Flexible Data Operations.

Re-introducing the Data Access Base Class™

The concept was clear – have a base class which could be extended (by virtue of derived classes) which implements a common interface and provides very obvious functionality, such as Select, Insert, Update and Delete.

Being generic, the generic data access class is designed to work on a per-class basis, but when object graphs are introduced into the mix (courtesy of relationships or navigation properties), we end up having to deal with multiple classes.

Now, to quote an earlier article, here are the design prerogatives yet again:

Generic Implementation Design

I’m not going to go through the class function by function (too time consuming) however I do want to walk through the design decisions I made when considering this approach.

  1. Reusable

    The original intention was to conserve and protect the usage of the EF’s DbContext.  However, I also wanted an ability to encapsulate common queries in classes deriving from the generic implementation.  In the end I came up with the solution presented here.  Chances are high that there’s a more elegant approach, and I’m happy to hear from folks with suggestions.

  2. Generic

    Another key was to try and encapsulate as much of the common ‘CRUD’ functionality as possible without having to do things that were entity or type-specific.  Generally, with the exception of schema details and relationships, the approach to data access should be as uniform as possible, and so it should be with the mechanisms controlling such access.

  3. Flexible

    As always, providing a useful and flexible interface is a design goal.  There’s not much point introducing a repository or interface based design if consumers will write hacky workarounds to do something they need to do.  Hence, the exposure of IQueryable<T> return types.

  4. Extendable

    Chances are you’ll never fully anticipate all needs, and this is certainly true with persistence.  The aim here is that the generic approach can be extended fairly easily to prove realistically any capability that might be required down the track.  For example, a type-specific accessor (repository) could be implemented on top of the generic class to provide execution of stored procedures.

Class Definition

This is where the true complications start to occur – especially for disconnected or detached entities.  The Entity Framework is best used when a Data or Object context can remain instantiated and can subscribe to changes in entities which were created via the context .

Let’s take a look at what functionality is exposed by the generic class:

image
Figure 1 – The Obligatory Class Diagrams

Under the hood

To reduce the repetitiveness, I’ve combined “Insert” and “Update” into a single call (InsertOrUpdate) since the onus is on the calling code to have set the Object State for each entity correctly anyway.  That leaves us with Select, CreateQuery/GetEntities/GetEntity for reads and Delete for deletions. 

This hasn’t changed much since the earlier versions, but what *has* changed is how inserts, updates and deletes are handled by the class, behind the scenes.  The ApplyStateChanges (private) function has been refactored, and a chunk of functionality has been refactored into a separate function called ProcessEntityState.

Why refactor?  This makes the code a little more readable, and reduces the size of the ApplyStateChanges function so that it is responsible for processing.  There were also some changes to the way linked entities were handled in a many-to-one (or one-to-many) collection.

Changes to Entity Processing

Handling Object State

One big change I’ve made is how an entity set is handled when processing multiple entities.  The original implementation assumed that anything not local to the Data Context should be safely Attached, as per the below:

if (!ExistsLocal<T2>(item))
{
    dbSet.Attach(item);
}

if (item.State == ObjectState.Modified)
{
    dbSet.Attach(item);
}

However, after some more detailed testing, the code should respect the Object State which the calling code has applied to the entity (rather than relying solely on the Data Context’s local cache):

if (item.State == ObjectState.Modified)
{
    if (!ExistsLocal<T2>(item))
    {
        dbSet.Attach(item);
    }
}

Bug Fix: CreateQuery

A sample set of data I was using, went over the 100 row mark at one point, when I discovered a little faux par:

/// <summary> /// Allows freeform data queries from outside the Data Access classes,

/// without exposing the data context directly /// </summary> /// <returns>An IQueryable of type T - limited to 100 rows</returns> public IQueryable<T> CreateQuery() { IQueryable<T> _query = DataContext.Set<T>().AsQueryable<T>(); // _query = _query.Take(100); return _query; }

Applying the Take operation at this stage was actually restricting the query to the first 100 rows in the table, not restricting the final query to 100 rows.  Consequently, this was removed!

Finally

Let’s just jump right into the sample solution.  You can download a copy of the example solution

Please email me rob.sanders@sanderstechnology.com if you find any issues, have questions or leave a comment here…