Repositories
Repositories, in practice, are used to perform database operations for domain objects (see Entities). Generally, a separated repository is used for each aggregate root or entity.
ABP can provide a default generic repository for each aggregate root or entity. You can into your service and perform standard CRUD operations.
Example usage of a default generic repository:
See the “IQueryable & Async Operations“ section below to understand how you can use async extension methods, like
ToListAsync()
(which is strongly suggested) instead ofToList()
.
In this example;
PersonAppService
simply injectsIRepository<Person, Guid>
in it’s constructor.Create
method usesInsertAsync
to save a newly created entity.GetList
method uses the standard LINQWhere
andToList
methods to filter and get a list of people from the data source.
Generic Repositories provides some standard CRUD features out of the box:
- Provides
Insert
method to save a new entity. - Provides
Update
andDelete
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<TEntity>
, so you can use LINQ and extension methods likeFirstOrDefault
,Where
,OrderBy
,ToList
and so on…
Standard IRepository<TEntity, TKey>
interface extends standard IQueryable<TEntity>
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<TEntity, TPrimaryKey>
and IBasicRepository<TEntity>
interfaces to support such scenarios. You can extend these interfaces (and optionally derive from BasicRepositoryBase
) to create custom repositories for your entities.
Depending on IBasicRepository
but not depending on 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 a wider range of data sources.
Read Only Repositories
There are also IReadOnlyRepository<TEntity, TKey>
and IReadOnlyBasicRepository<Tentity, TKey>
interfaces for who only want to depend on querying capabilities of the repositories.
If your entity does not have an Id primary key (it may have a composite primary key for instance) then you cannot use the IRepository<TEntity, TKey>
(or basic/readonly versions) defined above. In that case, you can inject and use IRepository<TEntity>
for your entity.
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:
public interface IPersonRepository : IRepository<Person, Guid>
{
Task<Person> FindByNameAsync(string name);
This interface extends IRepository<Person, Guid>
to take advantage of pre-built repository functionality.
Custom Repository Implementation
A custom repository is tightly coupled to the data access tool type you are using. In this example, we will use Entity Framework Core:
You can directly access the data access provider (DbContext
in this case) to perform operations.
IRepository
inherits from IQueryable
, that means you can directly use LINQ extension methods on it, as shown in the example of the “Generic Repositories“ section above.
Example: Using the Where(...)
and the ToList()
extension methods
var people = _personRepository
.Where(p => p.Name.Contains(nameFilter))
.ToList
, Count()
… are standard extension methods defined in the System.Linq
namespace (see all).
You normally want to use .ToListAsync()
, .CountAsync()
… instead, to be able to write a truly async code.
However, you see that you can’t use these async extension methods in your application or domain layer when you create a new project using the standard , because;
- These async methods are not standard LINQ methods and they are defined in the Microsoft.EntityFrameworkCore NuGet package.
- The standard template doesn’t have a reference to the EF Core package from the domain and application layers, to be independent from the database provider.
Based on your requirements and development model, you have the following options to be able to use the async methods.
Using async methods is strongly suggested! Don’t use sync LINQ methods while executing database queries to be able to develop a scalable application.
When you add the NuGet package to your project, you can take full power of the EF Core extension methods.
Example: Directly using the ToListAsync()
after adding the EF Core package
This method is suggested;
- If you are developing an application and you don’t plan to change EF Core in the future, or you can tolerate it if you need to change later. We believe that’s reasonable if you are developing a final application.
MongoDB Case
If you are using MongoDB, you need to add the NuGet package to your project. Even in this case, you can’t directly use async LINQ extensions (like ToListAsync
) because MongoDB doesn’t provide async extension methods for IQueryable<T>
, but provides for IMongoQueryable<T>
. You need to cast the query to IMongoQueryable<T>
first to be able to use the async extension methods.
Example: Cast IQueryable<T>
to IMongoQueryable<T>
and use ToListAsync()
var people = ((IMongoQueryable<Person>)_personRepository
.Where(p => p.Name.Contains(nameFilter)))
.ToListAsync();
Option-2: Custom Repository Methods
You can always create custom repository methods and use the database provider specific APIs, like async extension methods here. See or MongoDb document for more info about the custom repositories.
This method is suggested;
- If you want to completely isolate your domain & application layers from the database provider.
- If you develop a reusable and don’t want to force to a specific database provider, which should be done as a best practice.
IAsyncQueryableExecuter
is a service that is used to execute an IQueryable<T>
object asynchronously without depending on the actual database provider.
Example: Inject & use the IAsyncQueryableExecuter.ToListAsync()
method
ApplicationService
andDomainService
base classes already haveAsyncExecuter
properties pre-injected and usable without needing an explicit constructor injection.
ABP Framework executes the query asynchronously using the actual database provider’s API. While that is not a usual way to execute a query, it is the best way to use the async API without depending on the database provider.
This method is suggested;
For example, ABP Framework uses the IAsyncQueryableExecuter
in the CrudAppService
base class (see the document).