Introduction
A generic Entity Framework repository pattern implementation.
Usage
Unzip Database\Database.7z, attach to LocalDB, or restore to your favorite SQL Server instance and change the connection string in App.Config.
Open solution and run the application.
Base Repository
The base repository exists in Common\AbstractRepository.cs, it handles the basic CRUD operations, for example, methods like Find with pagination, Create, Update, Delete.
The Find
method, which has many overloading, handles pagination, sorting, total count. You can use these kinds of generic methods for all the entities.
It supports async call and also in the parent-child table scenario, you can pass the include
parameter to do the join
table.
Derived Repository
For certain business logic, you may need derived repository from the base repository to handle more business logic.
In the sample, you may find Repository\LogRepository.cs which is a repository for Log. In this example, I don’t have additional business logic, so it just looks like this:
public class LogRepository : AbstractRepository<Log, int>
{
public LogRepository(EFContext context)
: base(context)
{
}
}
And in the caller code, you can just use it like:
var repo = uow.GetLogRepository();
var result = repo.Find( GetSpecification(),
pageIndex,
pageSize,
List<SortDescriptor> sortings,
l => l.Level);
Unit of Work
To implement the transaction concept, multiple repository share a common EFContext. You may find the example in Common\EFUnitOfWork.cs. For simplicity, I don’t use and inject dependency, just to demonstrate the concept.
Specification Pattern
In Find
method of the generic repository, it accepts an ISpecification interface
. You can define some specification for the query to fix the criteria and you may have better control of the query, for example:
public class LogSearchSpecification : ISpecification<Log>
{
public string LevelName { get; set; }
public string Message { get; set; }
public Expression<Func<Log, bool>> ToExpression()
{
return log => (log.Level.Name == LevelName || LevelName == "") &&
(log.Message.Contains(Message) || Message == "");
}
public bool IsSatisfiedBy(Log entity)
{
return (entity.Level.Name == LevelName || LevelName == "") &&
(entity.Message.Contains(Message) || Message == "");
}
}
Entity Generation
I prefer the database first approach because for a complex system, I like to start from the database design and generate the POCO classes. Of course, you may use the code first approach, there’s no limitation.
In the Entity\EF.GenericRepository.Entity.edmx, you can do reverse engineering to generate class from database.
Logging
I like to check what SQL the Entity Framework generates so that I can make sure there won’t be some performance issue for example in the for
loop, query the database in each iteration.
In the Common\EFContext.cs, there’s a switch EnableTraceSql
, it will populate every SQL statement which entity framework sends to database. And I use NLog to store the SQL statements in the Log
table. Every time you hit the Search button in the WinForm example, you may find there are some logs inserted into the Log
table.
Sample UI to Test
After preparing the database, you can directly compile and run the sample application.
CodeProject