From 908b2fe3edb304f832df95e1e19f6af3799510a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 26 Jan 2018 16:07:45 +0300 Subject: [PATCH] Added repository doc. --- docs/Data-Transfer-Objects.md | 3 + docs/Object-To-Object-Mapping.md | 3 + docs/Repositories.md | 109 ++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 docs/Data-Transfer-Objects.md create mode 100644 docs/Object-To-Object-Mapping.md diff --git a/docs/Data-Transfer-Objects.md b/docs/Data-Transfer-Objects.md new file mode 100644 index 0000000000..b0d80246f7 --- /dev/null +++ b/docs/Data-Transfer-Objects.md @@ -0,0 +1,3 @@ +## Data Transfer Objects + +TODO \ No newline at end of file diff --git a/docs/Object-To-Object-Mapping.md b/docs/Object-To-Object-Mapping.md new file mode 100644 index 0000000000..b0d80246f7 --- /dev/null +++ b/docs/Object-To-Object-Mapping.md @@ -0,0 +1,3 @@ +## Data Transfer Objects + +TODO \ No newline at end of file diff --git a/docs/Repositories.md b/docs/Repositories.md index 1b89fa6443..969b0c70f6 100644 --- a/docs/Repositories.md +++ b/docs/Repositories.md @@ -6,5 +6,112 @@ Repositories, in practice, are used to perform database operations for domain ob ### Generic Repositories -ABP can provide a default generic repository for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository` into your service and perform standard CRUD operations. Example usage: +ABP can provide a **default generic repository** for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository` into your service and perform standard **CRUD** operations. Example usage: + +````C# +public class PersonAppService : ApplicationService +{ + private readonly IRepository _personRepository; + + public PersonAppService(IRepository personRepository) + { + _personRepository = personRepository; + } + + public async Task Create(CreatePersonDto input) + { + var person = new Person { Name = input.Name, Age = input.Age }; + + await _personRepository.InsertAsync(person); + } + + public List GetList(string nameFilter) + { + var people = _personRepository + .Where(p => p.Name.Contains(nameFilter)) + .ToList(); + + return people + .Select(p => new PersonDto {Id = p.Id, Name = p.Name, Age = p.Age}) + .ToList(); + } +} +```` + +In this example; + +* `PersonAppService` simply injects `IRepository` in it's constructor. +* `Create` method uses `InsertAsync` to save a newly created entity. +* `GetList` method uses the standard LINQ `Where` and `ToList` methods to filter and get a list of people from the data source. + +> The example above uses hand-made mapping between [entities](Entities.md) and [DTO](Data-Transfer-Objects.md)s. See [object to object mapping document](Object-To-Object-Mapping.md) for an automatic way of mapping. + +Generic Repositories provides some standard CRUD features out of the box: + +* Providers `Insert` method to save a new entity. +* Providers `Update` and `Delete` methods to update or delete an entity by entity object or it's id. +* Provides `Delete` method to delete multiple entities by a filter. +* Implements `IQueryable`, so you can use LINQ and extension methods like `FirstOrDefault`, `Where`, `OrderBy`, `ToList` and so on... +* Have **sync** and **async** versions for all methods. + +#### Generic Repository without a Primary Key + +If your entity does not has an Id primary key (it may have a composite primary key for instance) then you can not use the `IRepository` defined above. In that case, you can inject and use `IRepository` for your entity. + +> `IRepository` has a few missing methods those normally works with the `Id` property of an entity. Because of the entity has no `Id` property in that case, these methods are not available. One example is the `Get` method that gets an id and returns the entity with given id. However, you can still use `IQueryable` features to query entities by standard LINQ methods. + +### Basic Repositories + +Standard `IRepository` interface extends standard `IQueryable` and you can freely query using standard LINQ methods. However, some ORM providers or database systems may not support standard `IQueryable` interface. + +ABP provides `IBasicRepository` and `IBasicRepository` interfaces to support such scenarios. You can extend these interfaces (and optionally derive from `BasicRepositoryBase`) to create custom repositories for your entities. + +Depending to `IBasicRepository` but not depending to `IRepository` has an advantage to make possible to work with all data sources even if they don't support `IQueryable`. But major vendors, like Entity Framework, NHibernate or MongoDb already support `IQueryable`. + +So, working with `IRepository` is the **suggested** way for typical applications. But reusable module developers may consider `IBasicRepository` to support wide range of data sources. + +### Custom Repositories + +Default generic repositories will be sufficient for most cases. However, you may need to create a custom repository class for your entity. + +#### Custom Repository Example + +ABP does not force you to implement any interface or inherit from any base class for a repository. It can be just a simple POCO class. However, it's suggested to inherit existing repository interface and classes to make your work easier and get the standard methods out of the box. + +##### Custom Repository Interface + +First, define an interface in your domain layer: + +```c# +public interface IPersonRepository : IRepository +{ + Task FindByNameAsync(string name); +} +``` + +This interface extends `IRepository` to take advantage of pre-built repository functionality. + +##### Custom Repository Implementation + +A custom repository tightly depends on the data access tool you are using. In this example, we will use Entity Framework Core: + +````C# +public class PersonRepository : EfCoreRepository, IPersonRepository +{ + public PersonRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public async Task FindByNameAsync(string name) + { + return await DbContext.Set() + .Where(p => p.Name == name) + .FirstOrDefaultAsync(); + } +} +```` + +You can directly access to the data access provider (`DbContext` in this case) to perform operations. See [entity framework integration document](Entity-Framework-Core.md) for more about custom repositories based on EF Core.