
Unity auto-registered repositories on .net for EF Code first
Hey. Let's get started.
So what this article offers. You connect 2 nuget packages, implement a simple IRetrievableEntity interface for your Entity (you can simplify the task by inheriting from the finished Entity class), add 2 registration lines to the code and get the full independence from DBContext and the ability to resolve repositories for each IRetrievableEntity entity with the ability to build object-oriented (typed) queries to these repositories. Just look:
1. Install the Rikrop.Core.Data and Rikrop.Core.Data.Unity packages . The first is into a project with Entity entities, the second is into a project with a database context. For example, I used one project, it turned out the following:
2. Add the following to your IoC registrations:
RepositoryContext is a wrapper over the DBContext class, respectively, registration takes the generic parameter of the descendant from DBContext. You can register a context with the name of the connection string.
The RegisterRepositories extension method takes an Assembly input, in which POCO objects that implement IRetrievableEntity are located .
3. Implement for your POCO IRetrievableEntity. For instance:
4. Done. You can use:
It is impossible to make a mistake, since generic-parameters make sure that the correct repositories are resolved:
5. If the standard functionality offered by the IRepository interfaces and IDeactivatableRepository for any entity is not enough, you can always expand an existing implementation in a couple of simple steps. Set the interface:
Add the implementation and be sure to mark it with the attribute:
We ask Unity to find and register all extended repositories in the given assembly:
We use:
Moreover, without the need for advanced methods, you can always use the standard implementation:
The key class for working with building queries to the repository is the RepositoryQuery class . The class implements a fluent interface and allows you to do Include by Expression or by a text path (the latter can be relevant when loading properties of child collections, when the path cannot be specified through expression), filter, sort, Skip and Take.
The magic of registration is based on Reflection. When registering repositories in the assembly, all classes inherited from IRetrievableEntity <,> are found, generic arguments are taken from them, new types IRepository <,> and Repository <,> are built with the necessary generic arguments, then all this is registered using types freshly created through reflection . For extended repositories, the search is performed by the attribute:
Motivation
- There is a project with Entity framework (> = 5.0.0.0) code first.
- You love IoC but don’t like endless registrations of new entities.
- Unity is used as a container (or there is an opportunity to spend 10 minutes dopilivaniya source code for your container).
- The prospect of writing the same type of code for some reason scares you.
So what this article offers. You connect 2 nuget packages, implement a simple IRetrievableEntity interface for your Entity
var employeeRepository = container.Resolve>();
var employees = employeeRepository.Get(q =>
{
q = q.Filter(e => e.EmploymentDate >= new DateTime(2014, 9, 1));
if(excludeFired)
q = q.Filter(e => !e.Fired);
q = q.Include(e => e.Department, p => p.Department.Chief)
.OrderBy(p => p.FirstName);
});
How to quickly start using
You can use repositories without IoC, receiving bonuses of building queries and isolation from the context, but the following example and sources will provide comprehensive information about the most productive and simple application.1. Install the Rikrop.Core.Data and Rikrop.Core.Data.Unity packages . The first is into a project with Entity entities, the second is into a project with a database context. For example, I used one project, it turned out the following:
2. Add the following to your IoC registrations:
container.RegisterRepositoryContext();
//container.RegisterRepositoryContext(s => new MyDbContext(s), "myConStr");
container.RegisterRepositories(typeof(Department).Assembly);
RepositoryContext is a wrapper over the DBContext class, respectively, registration takes the generic parameter of the descendant from DBContext. You can register a context with the name of the connection string.
The RegisterRepositories extension method takes an Assembly input, in which POCO objects that implement IRetrievableEntity are located
3. Implement for your POCO IRetrievableEntity. For instance:
public class Department : Entity, IRetrievableEntity {...}
public class Employee : DeactivatableEntity, IRetrievableEntity {...}
4. Done. You can use:
var departmentRepository = container.Resolve>();
departmentRepository.Save(new Department { Name = "TestDepartment" });
var testDeps = departmentRepository.Get(q => q.Filter(dep => dep.Name.Contains("Test")));
It is impossible to make a mistake, since generic-parameters make sure that the correct repositories are resolved:
// Разрешить IDeactivatableRepository для департамента нельзя (ошибка компиляции),
// т.к. эта сущность не относледована от DeactivatableEntity.
//var departmentRepository2 = container.Resolve>();
5. If the standard functionality offered by the IRepository interfaces
public interface IPersonRepository : IDeactivatableRepository
{
void ExtensionMethod();
}
Add the implementation and be sure to mark it with the attribute:
[Repository(typeof(IPersonRepository))]
public class PersonRepository : DeactivatableRepository, IPersonRepository
{
public PersonRepository(IRepositoryContext repositoryContext)
: base(repositoryContext)
{
}
public void ExtensionMethod()
{
// Здесь у вас будет доступ к DBContext
Console.WriteLine("PersonRepository ExtensionMethod called");
}
}
We ask Unity to find and register all extended repositories in the given assembly:
// Пример регистрации "расширенных" репозиториев без указания их типа.
container.RegisterCustomRepositories(typeof(Department).Assembly);
We use:
// Извлечение "расширенного" репозитория по интерфейсу.
var personRepository = container.Resolve();
personRepository.ExtensionMethod();
Moreover, without the need for advanced methods, you can always use the standard implementation:
// Для класса Person репозиторий зарегистрирован под обоими интерфейсами, поскольку сущность наследуется от DeactivatableEntity.
var personRepository2 = container.Resolve>();
var personRepository3 = container.Resolve>();
How it works
There is a basic repository implementation that works with context through the IRepositoryContext abstraction. Accessing the dataset from the repository works thanks to the DBContext generic methods:public override DbSet Data { get { return Context.Set(); } }
The key class for working with building queries to the repository is the RepositoryQuery class . The class implements a fluent interface and allows you to do Include by Expression or by a text path (the latter can be relevant when loading properties of child collections, when the path cannot be specified through expression), filter, sort, Skip and Take.
The magic of registration is based on Reflection. When registering repositories in the assembly, all classes inherited from IRetrievableEntity <,> are found, generic arguments are taken from them, new types IRepository <,> and Repository <,> are built with the necessary generic arguments, then all this is registered using types freshly created through reflection . For extended repositories, the search is performed by the attribute:
foreach (var repositoryType in assembly.GetTypes().Where(type => type.IsClass))
{
var repositoryAttribute = repositoryType.GetCustomAttribute();
if (repositoryAttribute != null)
{
container.RegisterType(repositoryAttribute.RepositoryInterfaceType,
repositoryType, new TransientLifetimeManager());
}
}
Problems
- Only Entity framework and only Unity. The tool was created for our personal purposes and therefore it is rather difficult to find motivation to implement, for example, registrations for other containers.
- The script is suitable for use with a single DBContext - different ones will not be able to resolve the repository. This restriction does not apply to the use of Rikrop.Core.Data without Rikrop.Core.Data.Unity.
- Fixed version of Unity. If the version is not explicitly specified in the Nuget package for 4.0, then nuget will try to resolve the latest version, although it is incompatible with .net 4. If anyone knows a way to get rid of this problem, please inform in PM.
- Only .net 4.0 and 4.5.
References
- GitHub Rikrop.Core.Data
- GitHub Rikrop.Core.Data.Unity
- Special thanks to lexwings for co-writing code and advice on Unity.