-
Notifications
You must be signed in to change notification settings - Fork 3
Base Repository Classes
(under construction) EDennis.AspNetCore.Base provides four base repository classes, which cross writeability with temporality. Two of the base repos are writeable and two are readonly. Two of the base repos have temporal support (write to history tables) and two do not have temporal support. The table below summarizes the four repo types.

Importantly, while the two writeable repo classes can be used with any supported database, including relational and in-memory databases, the two readonly repo classes can be used with relational databases only.
WriteableRepo provides methods for reading from and writing to current-record tables.
WriteableRepo has two type parameters:
| Type Parameter | Constraints |
|---|---|
TEntity |
TEntity is a class with a default constructor and implements IHasSysUser |
TContext |
TContext extends Microsoft.EntityFrameworkCore.DbContext |
The WriteableRepo constructor accepts two arguments:
- TContext -- any subclass of Entity Framework Core's DbContext
- ScopeProperties -- a class designed to communicate the user name and other data (e.g., headers) from one or more MVC filters to the repo. This class should be dependency injected with a scoped lifetime.
public WriteableRepo(TContext context, ScopeProperties scopeProperties)WriteableRepo exposes two public properties.
| Property | Description |
|---|---|
public TContext Context { get; set; } |
Provides direct access to the DbContext |
public ScopeProperties ScopeProperties { get; set; } |
An object holding data injected into the repo |
WriteableRepo provides basic CRUD methods, which automatically save data to the database when needed.
| Method | Description |
|---|---|
public virtual TEntity GetById(params object[] keyValues) |
Retrieve a record by the primary key |
public virtual async Task<TEntity> GetByIdAsync(params object[] keyValues) |
Asynchronous version of GetById |
public virtual bool Exists(params object[] keyValues) |
Determine if a record with the provided key exists |
public virtual async Task<bool> ExistsAsync(params object[] keyValues) |
Asynchronous version of Exists |
public virtual TEntity Create(TEntity entity) |
Inserts a new TEntity record into the database |
public virtual async Task<TEntity> CreateAsync(TEntity entity) |
Asynchronous version of Create |
public virtual TEntity Update(TEntity entity, params object[] keyValues) |
Replaces the TEntity record with data from the provided object |
public virtual async Task<TEntity> UpdateAsync(TEntity entity, params object[] keyValues) |
Asynchronous version of Update |
public virtual void Delete(params object[] keyValues) |
Delete the record with the provided primary key |
public virtual async Task DeleteAsync(params object[] keyValues) |
Asynchronous version of Delete |
Aside from some statements supporting unit/integration testing, WriteableRepo does not have any special requirements for the DbContext. The EDennis.AspNetCore.Base library does provide an abstract implementation of IDesignTimeDbContextFactory<TContext> that allows one to have a single constructor accepting DbContextOptions<YourDbContextClass> as a parameter (see MigrationsExtensionsDbContextDesignTimeFactory. The library also provides a MaxPlusOneValueGenerator to make it easier to use in-memory databases for testing models backed by sequences or identity specifications.
WriteableTemporalRepo extends WriteableRepo and provides some additional methods related to temporal operations.
WriteableTemporalRepo has three type parameters:
| Type Parameter | Constraints |
|---|---|
TEntity |
TEntity is a class with a default constructor and implements IEFCoreTemporalModel |
TContext |
TContext extends Microsoft.EntityFrameworkCore.DbContext |
THistoryContext |
THistoryContext extends Microsoft.EntityFrameworkCore.DbContext |
The WriteableTemporalRepo constructor accepts three arguments:
- TContext -- any subclass of Entity Framework Core's DbContext. This is the current-records context.
- THistoryContext -- any subclass of Entity Framework Core's DbContext. This is the history-records context.
- ScopeProperties -- a class designed to communicate the user name and other data (e.g., headers) from one or more MVC filters to the repo. This class should be dependency injected with a scoped lifetime.
public WriteableTemporalRepo(TContext context, THistoryContext historyContext, ScopeProperties scopeProperties)WriteableTemporalRepo exposes the two public properties inherited from WriteableRepo, as well as a temporal-only property.
| Property | Description |
|---|---|
public THistoryContext HistoryContext { get; set; } |
Provides direct access to the DbContext for the history records |
WriteableTemporalRepo provides all of the CRUD methods from WriteableRepo, as well as some temporal-related methods.
| Method | Description |
|---|---|
public virtual bool WriteUpdate(TEntity next, TEntity current) |
Allows configuration of when records are written to the history table during updates. The default is always. |
public virtual bool WriteDelete(TEntity current) |
Allows configuration of when records are written to the history table during deletes. The default is always. |
public List<TEntity> QueryAsOf(DateTime from, DateTime to, Expression<Func<TEntity, bool>> predicate, int pageNumber = 1, int pageSize = 10000, params Expression<Func<TEntity, dynamic>>[] orderSelectors) |
Executes a query, retrieving current and/or history records as of a particular date/time. NOTE: See below for a description of Order Selectors |
public List<TEntity> QueryAsOf(DateTime asOf, Expression<Func<TEntity, bool>> predicate, int pageNumber = 1, int pageSize = 10000, params Expression<Func<TEntity, dynamic>>[] orderSelectors) |
Executes a query, retrieving current and/or history records within the provided date/time range. NOTE: See below for a description of Order Selectors |
TEntity GetByIdAsOf(DateTime asOf, params object[] key) |
Retrieves a specific record by primary key as of the provided date/time. |
List<TEntity> GetByIdHistory(params object[] key) |
Retrieves the current value and entire history of a specific record by primary key. |
The temporal queries provided above allow developers to include "order selectors," which is an alternative way of representing order-by expressions in Linq. With order selectors, you use the lambda parameter name to indicate direction (d, desc, or descending = descending; a, asc, or ascending = ascending). For example to order by Name ascending and SysStart descending, the lamdba expression could look like the following:
asc => asc.Name, desc => desc.SysStart
//or
a => a.Name, d => d.SysStart
//or
ascending => ascending.Name, descending => descending.SysStartReadonlyRepo provides methods for reading from relational databases.
ReadonlyRepo has two type parameters:
| Type Parameter | Constraints |
|---|---|
TEntity |
TEntity is a class with a default constructor |
TContext |
TContext extends Microsoft.EntityFrameworkCore.DbContext |
The ReadonlyRepo constructor accepts one argument:
- TContext -- any subclass of Entity Framework Core's DbContext
public ReadonlyRepo(TContext context)ReadonlyRepo exposes two public properties.
| Property | Description |
|---|---|
public TContext Context { get; set; } |
Provides direct access to the DbContext |
public IQueryable<TEntity> Query { get => Context.Query<TEntity>(); } |
Provides direct access to the DbQuery |
By exposing the Query property, the library gives developers full freedom to compose IQueryable expressions with Linq. Of course, this means that the ReadonlyRepo class is a very thin abstraction over Entity Framework.
ReadonlyRepo provides four read methods that apply to any relational database and two read methods that work with SQL Server only.
| Method | Description |
|---|---|
public virtual List<TEntity> GetFromSql(string sql) |
Retrieves a set of records using SQL |
public virtual async Task<List<TEntity>> GetFromSqlAsync(string sql) |
Asynchronous version of GetFromSql |
public virtual T GetScalarFromSql<T>(string sql) |
Retrieves a scalar value using SQL |
public virtual async Task<T> GetScalarFromSqlAsync<T>(string sql) |
Asynchronous version of GetScalarFromSql |
public virtual string GetFromJsonSql(string fromJsonSql) |
Retrieves a JSON string using the provided FROM JSON SQL Server SELECT statement† |
public virtual async Task<string> GetFromJsonSqlAsync(string fromJsonSql) |
Asynchronous version of GetFromJsonSql† |
† The implementation of these methods work with SQL Server only. The developer is free to replace the implementation with something that works with another relational database (e.g., using Oracle json_array or json_object functions).
ReadonlyRepo assumes that its associated DbContext class will define a DbQuery for the model class associated with the repo. Generally speaking, the developer will define separate model classes for the view and the underlying table and also create mappings to the view (via modelBuilder.Query<...>.ToView(...)) and underlying table (via modelBuilder.Entity<...>.ToTable(...)). The model class and mappings for the table are used to create the table in LocalDb for testing purposes.
As mentioned above, the EDennis.AspNetCore.Base library provides an abstract implementation of IDesignTimeDbContextFactory<TContext> that allows one to have a single constructor accepting DbContextOptions<YourDbContextClass> as a parameter (see MigrationsExtensionsDbContextDesignTimeFactory.
ReadonlyTemporalRepo extends ReadonlyRepo and provides some additional methods related to temporal operations.
ReadonlyTemporalRepo shares the same three type parameters with WriteableTemporalRepo:
| Type Parameter | Constraints |
|---|---|
TEntity |
TEntity is a class with a default constructor and implements IEFCoreTemporalModel |
TContext |
TContext extends Microsoft.EntityFrameworkCore.DbContext |
THistoryContext |
THistoryContext extends Microsoft.EntityFrameworkCore.DbContext |
The ReadonlyTemporalRepo constructor accepts three arguments:
- TContext -- any subclass of Entity Framework Core's DbContext. This is the current-records context.
- THistoryContext -- any subclass of Entity Framework Core's DbContext. This is the history-records context.
public ReadonlyTemporalRepo(TContext context, THistoryContext historyContext)WriteableTemporalRepo exposes the two public properties inherited from WriteableRepo, as well as a temporal-only property.
| Property | Description |
|---|---|
public THistoryContext HistoryContext { get; set; } |
Provides direct access to the DbContext for the history records |
ReadonlyTemporalRepo inherits all of the methods from ReadonlyRepo, as well as two temporal-related methods.
| Method | Description |
|---|---|
public List<TEntity> QueryAsOf(DateTime from, DateTime to, Expression<Func<TEntity, bool>> predicate, int pageNumber = 1, int pageSize = 10000, params Expression<Func<TEntity, dynamic>>[] orderSelectors) |
Executes a query, retrieving current and/or history records as of a particular date/time. NOTE: See below for a description of Order Selectors |
public List<TEntity> QueryAsOf(DateTime asOf, Expression<Func<TEntity, bool>> predicate, int pageNumber = 1, int pageSize = 10000, params Expression<Func<TEntity, dynamic>>[] orderSelectors) |
Executes a query, retrieving current and/or history records within the provided date/time range. NOTE: See above for a description of Order Selectors |
Note that ReadonlyTemporalRepo does not provide GetByIdAsOf or GetByIdHistory because the underlying implementation uses an DbQuery, which does not expose a primary key.
Development Support
- Temporal Entities
- Base Repository Classes
- Base API Controller Classes
- ApiClient and SecureApiClient
- Security Utilities
- IServiceCollection Extension Methods
- HttpClient Extension Methods
- ScopeProperties
- MigrationsExtensionsDbContextDesignTimeFactory
Testing Support
- API Launcher
- Testing Strategy and Infrastructure
- Xunit Support Classes
- Testing Security Utilities
Related Projects