Linq To SQL : LinqContext per Unit of Work

11 11 2008

To implement a Linq To SQL Oriented solution, there are many ways to construct your application. But only one interested me : the Context per Unit of Work Architecture.

When using Linq To SQL, you need to instanciate a Context which contains your entities tables and allow you to apply query methods. But when your application is construct with many layers, you only can pass Context through methods to keep the same context during your business operations. It is the same problem when a transaction is required(managed by Linq To SQL Context though optimistic transaction).

When using the Unit of Work architecture proposed by Microsoft and few developpers, most of the common operation is easier. Then, I’ll explain the main idea :

I’ll suppose that we have a 2 layers application, called Web & BLL(contains DAL through Linq To SQL Schema).

We want to :

  • Work with a unique Linq To SQL Context during a HTTP Request(so only one per user request)
  • Access to the Context in Web layer(indirectly) and BLL layer(directly)
  • Denied access to the Context in code-behind

The first step is to create a controller class in the BLL layer to access to the context.

public class MyDataContext

{

public static IDataContextStorage MyDataContextStorage;

public static LinqContext Current

{

get

{

return MyDataContextStorage.Current;

}

set

{

MyDataContextStorage.Current = value;

}

}

public static void Save()

{

Current.SubmitChanges();

}

}

And create the following interface:

public interface IDataContextStorage

{

LinqContext Current { get; set; }

}

Now, we have a controller which can be called by the Web and BLL Layer. Accessing from web is not a good practice, so we can change to internal if we want to restrict access to BLL only and set the Current Property type return to object. However, I don’t like to cast each time I need my context, so a leave it like that.

MyDataContext is the wrapper to access to the Context. but we now need to store your context during your HTTP Request. We need to create the following class that allow us to store the context and access it for each user :

public class HttpDataContextStorage:IDataContextStorage

{

public MyBLL.LinqContext Current

{

get {

if ((MyBLL.LinqContext)HttpContext.Current.Items["DataContext"] == null)

HttpContext.Current.Items["DataContext"] = new MyBLL.LinqContext();

return (MyBLL.LinqContext)HttpContext.Current.Items["DataContext"]; }

set { HttpContext.Current.Items["DataContext"] = value; }

}

}

This class need to be placed in App_Code in Web layer(there are some HttpContext reference, so…).

For each application request, we are going to create a Context and destroy it when request is finished. Write in your global.asax.cs:

void Application_BeginRequest(object sender, EventArgs e)

{

MyDataContext.MyDataContextStorage = new HttpDataContextStorage();

}

void Application_EndRequest(object sender, EventArgs e)

{

MyDataContext.Current.Dispose();

}

Ok, all is defined.

when we need to execute a Linq to SQL Query, we only need to write :

MyDataContext.Current.Users.Where(u=>u.Id==1);

“Users” is a entity table for this sample.

When saving is required, do: MyDataContext.Save() or MyDataContext.Current.SubmitChanges()

With this strucutre, Linq Context is always available when you need it and allow to perform transaction through many methods calling.

However, some issues are known, as :

  • Serialization : If you do a full loading of Context, binding with associated object can provoke a serialization failure.
  • It is a HTTP Unit of work concept. If you want to perform asynchronous tasks, the Context will not be available. So keep in mind to build your MyDataContext Controller with features that allow custom Context loading.

This Linq To SQL Context Architecture is a simple sample to show the main idea. Sure, It can be improved to restrict the layers access, check if we use the HTTP Linq Context or a New Context for asynchronous tasks…

Reminder: A Linq Overview Ticket is available here to see others architectures and statistics.





Linq To SQL Generic Controller

20 10 2008

With the power of Linq language, it’s easy to use Linq To SQL to build 95% of the data access interrogation.

But you will notify that 2/3 of your data access methods is always the same, as GetById, GetAll, Insert, DeleteById…

So Microsoft staff has written a class named GenericController that allow you to centralize all standard methods in one class. You only need to inherit from it and a good part of your job is done.

http://code.msdn.microsoft.com/multitierlinqtosql

If you look more deeper in this pack, you will find some other classes written to instanciate one LinqContext by Query or a Linq To SQL Debugger(see also my ticket on DebugWriter).





Linq To SQL : How to order with a string typed expression ?

23 09 2008

After using Linq To SQL queries, I was quickly confronted to sort my data with a string expression provided, for example, by a gridview when a user require it. At this moment, I understood that the strongly typed system used with Linq cannot help me for this task… Understanding the fact that allowing a string expression representing an object property is contrary to Linq purpose, I began to search with our best friend ”Google” a soluce to resolve quickly my problem. But the best that I can obtain was a “switch case solution” which, according to the string property name, build the linq query sort filter with the associated object property. Immediatly, I guess my switch case code with 8-10 columns… No…

So, I started to write an extended method “OrderBy” allowing to take a string sort expression. Here the result.

/// <summary>

/// Extends method which allow to sort by string field name.

/// Allow to use a relative object definition for sorting (ex:LinkedObject.FieldsName1)

/// </summary>

/// <typeparam name=”TEntity”>Current Object type for query</typeparam>

/// <param name=”source”>list of defined object</param>

/// <param name=”sortExpression”>string name of the field we want to sort by</param>

/// <returns>Query sorted by sortExpression</returns>

public static IQueryable<TEntity> OrderBy<TEntity>(

    this IQueryable<TEntity> source, string sortExpression) where TEntity : class

{

    var type = typeof(TEntity);

    // Remember that for ascending order GridView just returns the column name and

    // for descending it returns column name followed by DESC keyword 

    // Therefore we need to examine the sortExpression and separate out Column Name and

    // order (ASC/DESC) 

    string[] expressionParts = sortExpression.Split(‘ ‘); // Assuming sortExpression is like [ColumnName DESC] or [ColumnName] 

    string orderByProperty = expressionParts[0];

    string sortDirection = “ASC”;

    string methodName = “OrderBy”;

 

    //if sortDirection is descending 

    if (expressionParts.Length > 1 && expressionParts[1] == “DESC”)

    {

        sortDirection = “Descending”;

        methodName += sortDirection; // Add sort direction at the end of Method name 

    }

    MethodCallExpression resultExp = null;

    if (!orderByProperty.Contains(“.”))

    {

        var property = type.GetProperty(orderByProperty);

        var parameter = Expression.Parameter(type, “p”);

        var propertyAccess = Expression.MakeMemberAccess(parameter, property);

        var orderByExp = Expression.Lambda(propertyAccess, parameter);

        resultExp = Expression.Call(typeof(Queryable), methodName,

                        new Type[] { type, property.PropertyType },

                        source.Expression, Expression.Quote(orderByExp));

    }

    else

    {

        Type relationType = type.GetProperty(orderByProperty.Split(‘.’)[0]).PropertyType;

        PropertyInfo relationProperty = type.GetProperty(orderByProperty.Split(‘.’)[0]);

        PropertyInfo relationProperty2 = relationType.GetProperty(orderByProperty.Split(‘.’)[1]);

        var parameter = Expression.Parameter(type, “p”);

        var propertyAccess = Expression.MakeMemberAccess(parameter, relationProperty);

        var propertyAccess2 = Expression.MakeMemberAccess(propertyAccess, relationProperty2);

        var orderByExp = Expression.Lambda(propertyAccess2, parameter);

        resultExp = Expression.Call(typeof(Queryable), methodName,

                        new Type[] { type, relationProperty2.PropertyType },

                        source.Expression, Expression.Quote(orderByExp));

    }

    return source.Provider.CreateQuery<TEntity>(resultExp);

}

/// <summary>

/// Allow to add another sorting on a query with a string representation of the field to sort by.

/// </summary>

/// <typeparam name=”TEntity”>Current Object type for query</typeparam>

/// <param name=”source”>list of defined object</param>

/// <param name=”sortExpression”>string name of the field we want to sort by</param>

/// <returns>Query sorted by sortExpression</returns>

public static IOrderedQueryable<TEntity> ThenBy<TEntity>(

    this IOrderedQueryable<TEntity> source, string sortExpression) where TEntity : class

{

    var type = typeof(TEntity);

    // Remember that for ascending order GridView just returns the column name and

    // for descending it returns column name followed by DESC keyword 

    // Therefore we need to examine the sortExpression and separate out Column Name and

    // order (ASC/DESC) 

    string[] expressionParts = sortExpression.Split(‘ ‘); // Assuming sortExpression is like [ColumnName DESC] or [ColumnName] 

    string orderByProperty = expressionParts[0];

    string sortDirection = “ASC”;

    string methodName = “ThenBy”;

 

    //if sortDirection is descending 

    if (expressionParts.Length > 1 && expressionParts[1] == “DESC”)

    {

        sortDirection = “Descending”;

        methodName += sortDirection; // Add sort direction at the end of Method name 

    }

    MethodCallExpression resultExp = null;

    if (!orderByProperty.Contains(“.”))

    {

        var property = type.GetProperty(orderByProperty);

        var parameter = Expression.Parameter(type, “p”);

        var propertyAccess = Expression.MakeMemberAccess(parameter, property);

        var orderByExp = Expression.Lambda(propertyAccess, parameter);

        resultExp = Expression.Call(typeof(Queryable), methodName,

                        new Type[] { type, property.PropertyType },

                        source.Expression, Expression.Quote(orderByExp));

    }

    else

    {

        Type relationType = type.GetProperty(orderByProperty.Split(‘.’)[0]).PropertyType;

        PropertyInfo relationProperty = type.GetProperty(orderByProperty.Split(‘.’)[0]);

        PropertyInfo relationProperty2 = relationType.GetProperty(orderByProperty.Split(‘.’)[1]);

        var parameter = Expression.Parameter(type, “p”);

        var propertyAccess = Expression.MakeMemberAccess(parameter, relationProperty);

        var propertyAccess2 = Expression.MakeMemberAccess(propertyAccess, relationProperty2);

        var orderByExp = Expression.Lambda(propertyAccess2, parameter);

        resultExp = Expression.Call(typeof(Queryable), methodName,

                        new Type[] { type, relationProperty2.PropertyType },

                        source.Expression, Expression.Quote(orderByExp));

    }

    return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExp);

}





Linq Overview

23 09 2008

During my migration from .NET 2.0 to .NET 3.0/3.5, my first tought was : ‘With .NET 3.5, I can used Linq To SQL and stop to bore me with all the code generation tools”. I don’t want to crush the glory of tools like Raptier or Code Smith, but an ORM produce by Microsoft can be better in generation time(class generation and integration) and simplicity. It was true(for common uses). But better than that. Linq offers many others functionalities(strongly typed, composite queries, relashionship between objects and many more). And I began to forget all about others problems when the Graal was not here…

After a month of research, I have started to write a document on my Linq feedback and I share it today :-)

linq