In many applications, the business logic accesses data from data stores such as databases and web services. Without solid design pattern, your apps can quickly result in the following:
- Duplicated code
- A higher potential for programming errors
- Weak typing of the business data
- Difficulty in centralizing data-related policies such as caching
- An inability to easily test the business logic in isolation from external dependencies
The Repository Pattern implemented in Intersoft Application Framework is designed to overcome these challenges.
Repository Pattern Overview
Repository in repository pattern must encapsulate the mechanism of storage, query and data access logic along. Intersoft Application Framework provides several repository base class that you can use to perform data access to REST service and SQLite database. Furthermore it also provides the interfaces that are used within the application framework, so you can create your own repository if needed.
The following diagram overviews the repository classes available in Intersoft Application Framework.
In general, these repositories can be categorized into two base repository, RestRepository and LocalEntityRepository. Most repositories share common methods such as GetAll, GetSingle, Create, Delete, Insert, Update, SaveChanges and RejectChanges. Some repositories are extended with their own characteristics, for instance, asynchronous repositories contain async version of the GetAll and SaveChanges methods.
The following sections discuss the specific data repository supported in Intersoft App Framework.
Using Rest Repository
The RestRepository class encapsulate the process of accessing data to a REST service. It is a generic repository with sole purpose to handle an IRestRequest passed to the ExecuteAsync method and returns an IRestResponse object.
To give you insights how repository works, the following code shows an example of the ExecuteAsync implemented in the data repository.
To learn more how to access data using RestClient, see Using Crosslight RestClient.
Initializing Rest Client
At the heart of the RestRepository class is the RestClient instance which is created in the InitializeRestClient method of the repository. In most cases, you do not need to modify with the RestRepository class directly since it has delegated the factory process to the IActivatorService which you can easily modify and maintain in the AppService.cs typically located in the shared core project.
The following code example shows how to register the IRestClient to the activator service which will be automatically picked up by RestRepository when performing data operations.
Another purpose using Repository Pattern is to have centralized process to perform data access, data manipulation and other related processes. Handling error is one of the process that you can streamline. RestRepository provides an OnError method that is called whenever there are error during data access to RestService. By default, it only throws the error back to the caller. You can alter this process if needed to that comply with your application specification.
Using Entity Repository
Entity Repository encapsulates the process of accessing data to an enterprise data service. To learn more how to implement enterprise data service, see Creating Entity Model and Services with Crosslight Entity Designer Extensions.
Defining Resource Target
This repository is intended to perform data access to a single entity type defined by TEntity, with the target url defined by ResourceName.
If you are targeting http://192.168.1.111:11011/data/CrossTask/Tasks where the http://192.168.1.111:11011/data/CrossTask/ represents its base url, then the ResourceName should be set to Tasks.
To query data, you can use one of the available methods as follows.
- Task<TEntity> GetSingleAsync(TKey key)
Create and execute RestRequest that returns a single entity using the specified key.
- Task<ISelectResult<TEntity>> GetAllAsync()
Create and execute RestRequest that returns all entities
- Task<ISelectResult<TEntity>> GetAllAsync(ISelectParameter selectParameter)
Create and execute RestRequest based on the specified select parameter. The select parameter has a QueryDescriptor object that is used to perform data manipulation using OData query.
Note that whenever you retrieve data, the entity will be stored in an EntityContainer in the repository. This allows the EntityContainer to track any changes that happens to your entity so that you can easily send the changes back to the enterprise data service.
Creating, Inserting Updating and Deleting Entity
Create, update, and delete operation is common data operations when dealing with an entity. The EntityRepository fully supports these data operations as well.
The following are methods that available in EntityRepository that handles those data operation.
- TEntity Create()
- void Delete(TEntity entity)
- void Delete(IEnumerable<TEntity> entities)
- void Insert(TEntity entity)
- void Update(TEntity entity)
By default, the entity repository covers only simple data operations. You can overwrite the above methods and add custom logic whenever necessary.
Saving The Changes
Any changes made in the entity are recorded in the EntityContainer. You can save the changes made to the entities using SaveChangesAsync() method. It will prepare the data changes to be sent back to enterprise data service. Upon completion, it will finalize the changes back to original entity. This finalization covers replacing temporary entity key for new objects, and updating additional changes to the entity made by the enterprise data service.
Local Entity Repository
Local Entity Repository is a repository targeting SQLite data storage. It has the same interfaces uses by Entity Repository to ensure standard usage of defining an entity repository.
Defining Target SQLite Storage
LocalEntityRepository uses ISQLiteService that is implemented within LocalDataRepository class to perform all data operations related to SQLite data storage. To use the local entity repository, you need to define the target SQLite storage at AppService.cs in the shared core project as follows.
Querying data from LocalEntityRepository is similar with EntityRepository, you can use the same methods that you used in entity repository to query the data from SQLite data storage. However, depending on the scenario, you can enable data caching in local entity repository by setting the LoadFromContainerWhenAvailable property to true. Consequently, the results of the queries will be cached and further identical queries will return the cached data for the best performance.