<ABP framework> warehousing and abp framework warehousing
Document directory
Content of this section:
- Default Storage
- Custom warehousing
- Custom warehousing Interface
- Custom warehousing implementation
- Basic warehousing Method
- Query
- Obtain a single object
- Get Object List
- IQueryable
- Insert
- Update
- Delete
- Others
- About asynchronous Methods
- Manage database connections
- Lifecycle of a warehouse
- Best warehousing practices
The media between the domain and the ing layer uses an interface similar to a set to access objects. Generally, each entity (or aggregate root) uses a separate warehouse.
Default Storage
In the ABP, The IRepository <TEntity, TPrimaryKey> interface is implemented for a storage class. By default, a default storage is automatically created for each object type. You can directly inject IRepository <TEntity> (or IRepository <TEntity, TPrimaryKey> ). An example of how an Application Service uses warehousing to insert an entity into a database:
public class PersonAppService : IPersonAppService{ private readonly IRepository<Person> _personRepository; public PersonAppService(IRepository<Person> personRepository) { _personRepository = personRepository; } public void CreatePerson(CreatePersonInput input) { person = new Person { Name = input.Name, EmailAddress = input.EmailAddress }; _personRepository.Insert(person); }}
The PersonAppService constructor injects IRepository <Person> and uses the Insert method.
Custom warehousing
You must create a storage class only when the entity needs to create a custom warehousing method.
Custom warehousing Interface
The following example defines a warehouse for a Person object:
public interface IPersonRepository : IRepository<Person>{}
IPersonRepository extends IRepository <TEntity>, which is used to define entities with int (Int32) type Id attributes. If your object key is not an int, you can extend the IRepository <TEntity, TPrimaryKey> interface, as shown below:
public interface IPersonRepository : IRepository<Person, long>{}
Custom warehousing implementation
It is designed to be separated from the ORM (Object/relationship ing) framework, or other access database technology. Out-of-the-box warehouse implements nhib.pdf and EntityFramework. View the relevant documentation on the implementation of these frameworks:
- Nhib1_integration
- EntityFramework integration
Basic warehousing Method
Each Warehouse contains some common methods from the IRepository <TEntity> interface. Here we will take a look at most of their methods.
Query
Obtain a single object
TEntity Get(TPrimaryKey id);Task<TEntity> GetAsync(TPrimaryKey id);TEntity Single(Expression<Func<TEntity, bool>> predicate);Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate);TEntity FirstOrDefault(TPrimaryKey id);Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id);TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);TEntity Load(TPrimaryKey id);
The Get method is used to obtain an object with a given primary key (Id. If the entity with the given Id cannot be found in the database, an exception is thrown. The Single method is similar to the Get method, but accepts an expression instead of an Id. Therefore, you can write a lambda expression to obtain an object. Usage example:
var person = _personRepository.Get(42);var person = _personRepository.Single(p => p.Name == "Halil İbrahim Kalkan");
Note: Single will throw an exception when it cannot obtain entities that conform to the expression or have multiple entities that conform to the expression.
FirstOrDefault is similar, but if the entity with the given Id cannot be found, null is returned (instead of throwing an exception ). If multiple objects are found, the first object is returned.
Load does not obtain entities from the database, but creates a proxy object for delayed loading. If you only use the Id attribute, you will not actually obtain the entity from the database. It will only obtain the entity from the database when you access other attributes of the entity. For performance consideration, this method is used instead of Get. It has been implemented in nhib.pdf. If the ORM provider does not implement it, the Load method is the same as the Get method.
Returns an object list.
List<TEntity> GetAllList();Task<List<TEntity>> GetAllListAsync();List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate);IQueryable<TEntity> GetAll();
GetAllList is used to obtain all objects in the database. Its overload can filter objects, for example:
var allPeople = _personRepository.GetAllList();var somePeople = _personRepository.GetAllList(person => person.IsActive && person.Age > 42);
GetAll returns IQueryable <T>, so you can add the Linq method after this method, for example:
//Example 1var query = from person in _personRepository.GetAll() where person.IsActive orderby person.Name select person;var people = query.ToList();//Example 2:List<Person> personList2 = _personRepository.GetAll().Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();
By using the instance GetAll, you can write almost all the queries in the Linq, or even use it in a json expression.
IQueryable <T>
When you call GetAll () outside of a warehouse method, there must be an open database connection because IQueryable <T> is delayed. It does not execute database queries unless you call the ToList () method or in a foreach loop (or other way to access items in the query ). Therefore, when you call the ToList () method, the database connection must be available. For a Web project, you do not need to care about this in some cases, because the Mvc Controller method is a work unit by default, and the database connection is available throughout the request. For better understanding, see the work unit document.
Custom Return Value
Another method provides more powerful IQueryable, which can be used outside the work unit.
T Query<T>(Func<IQueryable<TEntity>, T> queryMethod);
The Query method accepts a lambda expression (or method) that receives IQueryable <T> and returns any type of object. For example:
var people = _personRepository.Query(q => q.Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).ToList());
Since the given lambda (or method) is executed in the warehousing method, it is executed when the database connection is available. After performing a query, you can return the Object List, a single object, a projection, or other objects.
Insert
The IRepository interface defines how to insert objects into the database:
TEntity Insert(TEntity entity);Task<TEntity> InsertAsync(TEntity entity);TPrimaryKey InsertAndGetId(TEntity entity);Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity);TEntity InsertOrUpdate(TEntity entity);Task<TEntity> InsertOrUpdateAsync(TEntity entity);TPrimaryKey InsertOrUpdateAndGetId(TEntity entity);Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity);
The Insert method simply inserts a new object into the database and returns the inserted object. The InsertAndGetId method returns the Id of the newly inserted object, which is useful when the Id is auto-increment. InsertOrUpdate performs the insert or update operation based on the Id value. Finally, after inserting or updating an object, InsertOrUpdateAndGetId returns its Id value.
Update
IRepository defines a method to update an object that already exists in the database. It obtains an object to be updated and returns the same object.
TEntity Update(TEntity entity);Task<TEntity> UpdateAsync(TEntity entity);
Most of the time, you do not need to explicitly call the Update method, because the work unit will call the Update method upon completion. For more information, see the work unit document.
Delete
IRepository defines how to delete an existing entity from the database.
void Delete(TEntity entity);Task DeleteAsync(TEntity entity);void Delete(TPrimaryKey id);Task DeleteAsync(TPrimaryKey id);void Delete(Expression<Func<TEntity, bool>> predicate);Task DeleteAsync(Expression<Func<TEntity, bool>> predicate);
The first method accepts an existing object, and the second method accepts the Id of the object to be deleted. The last one deletes all objects that match the given conditions. Note: all objects that match the predicates may first obtain the memory from the database and then delete the objects (see how the Warehouse is implemented ), so be careful when using it, which may cause performance problems when there are a large number of qualified entities.
Others
IRepository also provides a method to obtain the number of objects in a table.
int Count();Task<int> CountAsync();int Count(Expression<Func<TEntity, bool>> predicate);Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate);long LongCount();Task<long> LongCountAsync();long LongCount(Expression<Func<TEntity, bool>> predicate);Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate);
About asynchronous Methods
ABP supports the exception programming mode, so the warehousing method has an asynchronous version. The following example shows how to use the exception mode for an application service method:
public class PersonAppService : AbpWpfDemoAppServiceBase, IPersonAppService{ private readonly IRepository<Person> _personRepository; public PersonAppService(IRepository<Person> personRepository) { _personRepository = personRepository; } public async Task<GetPeopleOutput> GetAllPeople() { var people = await _personRepository.GetAllListAsync(); return new GetPeopleOutput { People = Mapper.Map<List<PersonDto>>(people) }; }}
The GetAllPeople method is an Asynchronous Method and uses the keyword await to call GetAllListAsync.
Not all ORM frameworks support Asynchronization. Supported EntityFramework. If not, the asynchronous method works in synchronous mode. Similarly, for example, in EntityFramework, InsertAsync and Insert work in the same way, because EF does not write the code into new entities until the work unit is completed (that is, DbContext. SaveChanges.
Manage database connections
In a warehousing method, it does not enable or disable the database connection, and the abcautomatically manages the database connection.
When you enter a warehouse method, the ABP automatically opens a database connection and starts a transaction. When the method ends and returns, all changes are saved, and the database connection is closed after the transaction is committed. If your warehousing method throws any type of exception, it automatically rolls back the transaction and closes the database connection. This applies to all public methods that implement the IRepository interface.
If one warehouse method calls another warehouse method (even a different warehouse method), they share the same connection and transaction. The first method is to manage the database connection (open/close ). For more information about database connection management, see the work unit document.
Lifecycle of a warehouse
All warehouse instances are short-lived, meaning that they are instantiated for each use. See the dependency injection document for more information.
Best warehousing practices
- As a T-type object, use IRepository <T> whenever possible. Do not create custom warehousing unless you do. The predefined warehousing method can meet the requirements in most cases.
- If you are creating a custom repository (by extending IRepository <TEntity> ):
- The warehousing class should be stateless. That is to say, you should not define an object in the warehousing level and the call of one warehousing method should not affect the call of another.
- Custom warehousing methods should not contain business logic or application logic. It should only execute data-related or ORM-related tasks.
- Although the repository can use dependency injection, the dependency on other services is minimized or not defined.