LINQ is a strongly typed way of querying data. LINQ-to-SQL
is a dialect of LINQ that allows the querying of a SQL Server database. It also
includes object/relational mapping and tools for generating domain model
classes from a database schema. LINQ is an excellent addition to object/relational
mappings tools because it facilitates strongly typed queries.
Listing 1
IList<Shape> threeSidedShapes = _genericShapeRepository.FindAll(
shape => shape.NumberOfSides == 3).Take(5).ToList();
IRepository<T>
The generic interface IRepository<T> defines the
methods that are required on each repository.
Listing 2
public interface IRepository<T> where T : class
{
/// <summary>
/// Return all instances of type T.
/// </summary>
/// <returns></returns>
IEnumerable<T> All();
/// <summary>
/// Return all instances of type T that match the expression exp.
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
IEnumerable<T> FindAll(Func<T, bool> exp);
/// <summary>Returns the single entity matching the expression.
/// Throws an exception if there is not exactly one such entity.</summary>
/// <param name="exp"></param><returns></returns>
T Single(Func<T, bool> exp);
/// <summary>Returns the first element satisfying the condition.</summary>
/// <param name="exp"></param><returns></returns>
T First(Func<T, bool> exp);
/// <summary>
/// Mark an entity to be deleted when the context is saved.
/// </summary>
/// <param name="entity"></param>
void MarkForDeletion(T entity);
/// <summary>
/// Create a new instance of type T.
/// </summary>
/// <returns></returns>
T CreateInstance();
/// <summary>Persist the data context.</summary>
void SaveAll();
}
Repository<T>
IRepository<T> is implemented by a generic repository
base class, Repository<T>. Repository<T> is a base implementation
that provides data access functionality for all entities. If an entity (T) does
not require a specialized repository then its data access will be done through
Repository<T>.
Listing 3
public class Repository<T> : IRepository<T>
where T : class
{
protected IDataContextFactory _dataContextFactory;
/// <summary>
/// Return all instances of type T.
/// </summary>
/// <returns></returns>
public IEnumerable<T> All()
{
return GetTable;
}
/// <summary>
/// Return all instances of type T that match the expression exp.
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
public IEnumerable<T> FindAll(Func<T, bool> exp)
{
return GetTable.Where<T>(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="exp"></param><returns></returns>
public T Single(Func<T, bool> exp)
{
return GetTable.Single(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="exp"></param><returns></returns>
public T First(Func<T, bool> exp)
{
return GetTable.First(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="entity"></param>
public virtual void MarkForDeletion(T entity)
{
_dataContextFactory.Context.GetTable<T>().DeleteOnSubmit(entity);
}
/// <summary>
/// Create a new instance of type T.
/// </summary>
/// <returns></returns>
public virtual T CreateInstance()
{
T entity = Activator.CreateInstance<T>();
GetTable.InsertOnSubmit(entity);
return entity;
}
/// <summary>See IRepository.</summary>
public void SaveAll()
{
_dataContextFactory.SaveAll();
}
public Repository(IDataContextFactory dataContextFactory)
{
_dataContextFactory = dataContextFactory;
}
#region Properties
private string PrimaryKeyName
{
get { return TableMetadata.RowType.IdentityMembers[0].Name; }
}
private System.Data.Linq.Table<T> GetTable
{
get { return _dataContextFactory.Context.GetTable<T>(); }
}
private System.Data.Linq.Mapping.MetaTable TableMetadata
{
get { return _dataContextFactory.Context.Mapping.GetTable(typeof(T)); }
}
private System.Data.Linq.Mapping.MetaType ClassMetadata
{
get { return _dataContextFactory.Context.Mapping.GetMetaType(typeof(T)); }
}
#endregion
}
IShapeRepository & ShapeRepository
It is usually desirable to provide more specialized
repositories for entity classes. If our domain includes a shape entity, we
might like to have a ShapeRepository with a RetrieveByNumberOfSides(int
sideCount) method. Such a class would be exposed to consumers as a specialized
interface IShapeRepository.
Listing 4
public interface IShapeRepository : IRepository<Shape>
{
Shape RetrieveByNumberOfSides(int sideCount)
}
public class ShapeRepository : Repository<Shape>, IShapeRepository
{
public Shape RetrieveByNumberOfSides(int sideCount)
{
return FindAll(shape => shape.NumberOfSides == sideCount);
}
}
Usage
We now have a fully functioning, decoupled repository
implementation. A class might use the repositories as follows:
Listing 5
public class ApplicationService
{
private IRepository<Shape> _genericShapeRepository;
private IShapeRepository _specializedShapeRepository;
public ApplicationService(IRepository<Shape> genericShapeRepository,
IShapeRepository specializedShapeRepository)
{
_genericShapeRepository = genericShapeRepository;
_specializedShapeRepository = specializedShapeRepository;
}
public void DoSomethingWithTheGenericRepository()
{
IList<Shape> threeSidedShapes = _genericShapeRepository.FindAll(
shape => shape.NumberOfSides == 3).ToList();
_genericShapeRepository.MarkForDeletion(threeSidedShapes[0]);
_genericShapeRepository.SaveAll();
}
public void DoSomethingWithTheSpecializedRepository()
{
IList<Shape> threeSidedShapes =
_specializedShapeRepository.RetrieveByNumberOfSides(3).ToList();
_specializedShapeRepository.MarkForDeletion(threeSidedShapes[0]);
_specializedShapeRepository.SaveAll();
}
}