mirror of https://github.com/abpframework/abp.git
committed by
GitHub
138 changed files with 1690 additions and 1189 deletions
@ -0,0 +1,3 @@ |
|||
## Data Transfer Objects |
|||
|
|||
TODO |
|||
@ -0,0 +1,169 @@ |
|||
## Entities |
|||
|
|||
Entities are one of the core concepts of DDD (Domain Driven Design). Eric Evans describe it as "*An object that is not fundamentally defined by its attributes, but rather by a thread of continuity and identity*". |
|||
|
|||
An entity is generally mapped to a table in a relational database. |
|||
|
|||
### Entity Class |
|||
|
|||
Entities are derived from `Entity<TKey>` class as shown below: |
|||
|
|||
```C# |
|||
public class Person : Entity<int> |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public DateTime CreationTime { get; set; } |
|||
|
|||
public Person() |
|||
{ |
|||
CreationTime = DateTime.Now; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> If you do not want derive your entity from the base `Entity<TKey>` class, you can directly implement `IEntity<TKey>` interface. |
|||
|
|||
`Entity<TKey>` class just defines an `Id` property with the given primary **key type**, which is `int` in the sample above. It can be other types like `string`, `Guid`, `long` or whatever you need. |
|||
|
|||
Entity class also overrides the **equality** operator (==) to easily check if two entities are equal (they are equals if they are same entity type and their Ids are equals). |
|||
|
|||
#### Entities with Composite Keys |
|||
|
|||
Some entities may need to have **composite keys**. In that case, you can derive your entity from the non-generic `Entity` class. Example: |
|||
|
|||
````C# |
|||
public class UserRole : Entity |
|||
{ |
|||
public Guid UserId { get; set; } |
|||
|
|||
public Guid RoleId { get; set; } |
|||
|
|||
public DateTime CreationTime { get; set; } |
|||
|
|||
public Phone() |
|||
{ |
|||
|
|||
} |
|||
} |
|||
```` |
|||
|
|||
For the example above, the composite key is composed of `UserId` and `RoleId`. For a relational database, it is the composite primary key of the related table. |
|||
|
|||
Notice that you also need to define keys of the entity in your **object-to-relational mapping** (ORM) configuration. |
|||
|
|||
> Composite primary keys has a restriction with repositories. Since it has not known Id property, you can not use `IRepository<TEntity, TKey>` for these entities. However, you can always use `IRepository<TEntity>`. See repository documentation (TODO: link) for more. |
|||
|
|||
### AggregateRoot Class |
|||
|
|||
"*Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it's useful to treat the order (together with its line items) as a single aggregate.*" (see the [full description](http://martinfowler.com/bliki/DDD_Aggregate.html)) |
|||
|
|||
`AggregateRoot` class extends the `Entity` class. So, it also has an `Id` property by default. |
|||
|
|||
> Notice that ABP creates default repositories only for aggregate roots by default. However, it's possible to include all entities. See repository documentation (TODO: link) for more. |
|||
|
|||
ABP does not force you to use aggregate roots, you can only use the `Entity` class as defined before. However, if you want to implement DDD and want to create aggregate root classes, there are some best practices you may want to consider: |
|||
|
|||
* An aggregate root is responsible to preserve it's own integrity. This is also true for all entities, but aggregate root has responsibility for it's sub entities too. So, the aggregate root always be in a valid state. |
|||
* An aggregate root can be referenced by it's Id. Do not reference it by navigation property. |
|||
* An aggregate root is treated as a single unit. It's retrieved and updated as a single unit. It's generally considered as a transaction boundary. |
|||
* Work with sub-entities over the aggregate root, do not modify them independently. |
|||
|
|||
#### Aggregate Example |
|||
|
|||
This is a full sample of an aggregate root with a related sub-entity collection: |
|||
|
|||
````C# |
|||
public class Order : AggregateRoot<Guid> |
|||
{ |
|||
public virtual string ReferenceNo { get; protected set; } |
|||
|
|||
public virtual int TotalItemCount { get; protected set; } |
|||
|
|||
public virtual DateTime CreationTime { get; protected set; } |
|||
|
|||
public virtual List<OrderLine> OrderLines { get; protected set; } |
|||
|
|||
protected Order() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public Order(Guid id, string referenceNo) |
|||
{ |
|||
Check.NotNull(referenceNo, nameof(referenceNo)); |
|||
|
|||
Id = id; |
|||
ReferenceNo = referenceNo; |
|||
|
|||
OrderLines = new List<OrderLine>(); |
|||
} |
|||
|
|||
public void AddProduct(Guid productId, int count) |
|||
{ |
|||
if (count <= 0) |
|||
{ |
|||
throw new ArgumentException( |
|||
"You can not add zero or negative count of products!", |
|||
nameof(count) |
|||
); |
|||
} |
|||
|
|||
var existingLine = OrderLines.FirstOrDefault(ol => ol.ProductId == productId); |
|||
|
|||
if (existingLine == null) |
|||
{ |
|||
OrderLines.Add(new OrderLine(this.Id, productId, count)); |
|||
} |
|||
else |
|||
{ |
|||
existingLine.ChangeCount(existingLine.Count + count); |
|||
} |
|||
|
|||
TotalItemCount += count; |
|||
} |
|||
} |
|||
|
|||
public class OrderLine : Entity |
|||
{ |
|||
public virtual Guid OrderId { get; protected set; } |
|||
|
|||
public virtual Guid ProductId { get; protected set; } |
|||
|
|||
public virtual int Count { get; protected set; } |
|||
|
|||
protected OrderLine() |
|||
{ |
|||
|
|||
} |
|||
|
|||
internal OrderLine(Guid orderId, Guid productId, int count) |
|||
{ |
|||
OrderId = orderId; |
|||
ProductId = productId; |
|||
Count = count; |
|||
} |
|||
|
|||
internal void ChangeCount(int newCount) |
|||
{ |
|||
Count = newCount; |
|||
} |
|||
} |
|||
```` |
|||
|
|||
> If you do not want derive your aggregate root from the base `AggregateRoot<TKey>` class, you can directly implement `IAggregateRoot<TKey>` interface. |
|||
|
|||
`Order` is an **aggregate root** with `Guid` type `Id` property. It has a collection of `OrderLine` entities. `OrderLine` is another entity with a composite primary key (`OrderLine` and ` ProductId`). |
|||
|
|||
While this example may not implement all best practices of an aggregate root, it follows some good practices: |
|||
|
|||
* `Order` has a public constructor that takes **minimal requirements** to construct an `Order` instance. So, it's not possible to create an order without an id and reference number. The **protected/private** constructor is only necessary to **deserialize** object while reading from a data source. |
|||
* `OrderLine` constructor is internal, so it only allows to be created by the domain layer. It's used inside of `Order.AddProduct` method. |
|||
* `Order.AddProduct` implements the business rule to add a product to an order. |
|||
* All properties have `protected` setters. This is to prevent entity from arbitrary changes from outside of the entity. For instance, it would be dangerous to set `TotalItemCount` without adding a new product to the order. It's value is maintained by the `AddProduct` method. |
|||
|
|||
ABP does not force you to apply any DDD rule or pattern. However, it tries to make it possible and easier when you want to apply. The documentation also follows this principle. |
|||
|
|||
#### Aggregate Roots with Composite Keys |
|||
|
|||
While it's not common (and not suggested) for aggregate roots, it's possible to define composite keys just as defined for entities above. Use non-generic `AggregateRoot` base class in that case. |
|||
@ -0,0 +1,3 @@ |
|||
## Data Transfer Objects |
|||
|
|||
TODO |
|||
@ -0,0 +1,117 @@ |
|||
## Repositories |
|||
|
|||
"*Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects*" (Martin Fowler). |
|||
|
|||
Repositories, in practice, are used to perform database operations for domain objects (see [Entities](Entities.md)). Generally, a separated repository is used for each **aggregate root** or entity. |
|||
|
|||
### Generic Repositories |
|||
|
|||
ABP can provide a **default generic repository** for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository<TEntity, TKey>` into your service and perform standard **CRUD** operations. Example usage: |
|||
|
|||
````C# |
|||
public class PersonAppService : ApplicationService |
|||
{ |
|||
private readonly IRepository<Person, Guid> _personRepository; |
|||
|
|||
public PersonAppService(IRepository<Person, Guid> 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<PersonDto> 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<Person, Guid>` 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<TEntity>`, 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<TEntity, TKey>` defined above. In that case, you can inject and use `IRepository<TEntity>` for your entity. |
|||
|
|||
> `IRepository<TEntity>` 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<TEntity>` features to query entities by standard LINQ methods. |
|||
|
|||
### Basic Repositories |
|||
|
|||
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 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<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 tightly depends on the data access tool you are using. In this example, we will use Entity Framework Core: |
|||
|
|||
````C# |
|||
public class PersonRepository : EfCoreRepository<MyDbContext, Person, Guid>, IPersonRepository |
|||
{ |
|||
public PersonRepository(IDbContextProvider<TestAppDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public async Task<Person> FindByNameAsync(string name) |
|||
{ |
|||
return await DbContext.Set<Person>() |
|||
.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. |
|||
|
|||
Binary file not shown.
@ -1,24 +1,12 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Application.Dtos |
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// A shortcut of <see cref="IEntityDto{TPrimaryKey}"/> for default primary key type (<see cref="Guid"/>).
|
|||
/// </summary>
|
|||
public interface IEntityDto : IEntityDto<Guid> |
|||
public interface IEntityDto |
|||
{ |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Defines common properties for entity based DTOs.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey"></typeparam>
|
|||
public interface IEntityDto<TPrimaryKey> |
|||
public interface IEntityDto<TKey> : IEntityDto |
|||
{ |
|||
/// <summary>
|
|||
/// Id of the entity.
|
|||
/// </summary>
|
|||
TPrimaryKey Id { get; set; } |
|||
TKey Id { get; set; } |
|||
} |
|||
} |
|||
@ -1,49 +1,41 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Volo.Abp.Application.Services |
|||
{ |
|||
public interface IAsyncCrudAppService<TEntityDto> |
|||
: IAsyncCrudAppService<TEntityDto, Guid> |
|||
where TEntityDto : IEntityDto<Guid> |
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey> |
|||
: IAsyncCrudAppService<TEntityDto, TKey, PagedAndSortedResultRequestDto> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TEntityDto, in TPrimaryKey> |
|||
: IAsyncCrudAppService<TEntityDto, TPrimaryKey, PagedAndSortedResultRequestDto> |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey, in TGetListInput> |
|||
: IAsyncCrudAppService<TEntityDto, TKey, TGetListInput, TEntityDto, TEntityDto> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TEntityDto, in TPrimaryKey, in TGetListInput> |
|||
: IAsyncCrudAppService<TEntityDto, TPrimaryKey, TGetListInput, TEntityDto, TEntityDto> |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput> |
|||
: IAsyncCrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TEntityDto, in TPrimaryKey, in TGetListInput, in TCreateInput> |
|||
: IAsyncCrudAppService<TEntityDto, TPrimaryKey, TGetListInput, TCreateInput, TCreateInput> |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TEntityDto, in TPrimaryKey, in TGetListInput, in TCreateInput, in TUpdateInput> |
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput> |
|||
: IApplicationService |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
Task<TEntityDto> GetAsync(TPrimaryKey id); |
|||
Task<TEntityDto> GetAsync(TKey id); |
|||
|
|||
Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input); |
|||
|
|||
Task<TEntityDto> CreateAsync(TCreateInput input); |
|||
|
|||
Task<TEntityDto> UpdateAsync(TPrimaryKey id, TUpdateInput input); |
|||
Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input); |
|||
|
|||
Task DeleteAsync(TPrimaryKey id); |
|||
Task DeleteAsync(TKey id); |
|||
} |
|||
} |
|||
|
|||
@ -1,48 +1,40 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Volo.Abp.Application.Services |
|||
{ |
|||
public interface ICrudAppService<TEntityDto> |
|||
: ICrudAppService<TEntityDto, Guid> |
|||
where TEntityDto : IEntityDto<Guid> |
|||
public interface ICrudAppService<TEntityDto, in TKey> |
|||
: ICrudAppService<TEntityDto, TKey, PagedAndSortedResultRequestDto> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface ICrudAppService<TEntityDto, in TPrimaryKey> |
|||
: ICrudAppService<TEntityDto, TPrimaryKey, PagedAndSortedResultRequestDto> |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
public interface ICrudAppService<TEntityDto, in TKey, in TGetListInput> |
|||
: ICrudAppService<TEntityDto, TKey, TGetListInput, TEntityDto, TEntityDto> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface ICrudAppService<TEntityDto, in TPrimaryKey, in TGetListInput> |
|||
: ICrudAppService<TEntityDto, TPrimaryKey, TGetListInput, TEntityDto, TEntityDto> |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
public interface ICrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput> |
|||
: ICrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface ICrudAppService<TEntityDto, in TPrimaryKey, in TGetListInput, in TCreateInput> |
|||
: ICrudAppService<TEntityDto, TPrimaryKey, TGetListInput, TCreateInput, TCreateInput> |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface ICrudAppService<TEntityDto, in TPrimaryKey, in TGetListInput, in TCreateInput, in TUpdateInput> |
|||
public interface ICrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput> |
|||
: IApplicationService |
|||
where TEntityDto : IEntityDto<TPrimaryKey> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
TEntityDto Get(TPrimaryKey id); |
|||
TEntityDto Get(TKey id); |
|||
|
|||
PagedResultDto<TEntityDto> GetAll(TGetListInput input); |
|||
|
|||
TEntityDto Create(TCreateInput input); |
|||
|
|||
TEntityDto Update(TPrimaryKey id, TUpdateInput input); |
|||
TEntityDto Update(TKey id, TUpdateInput input); |
|||
|
|||
void Delete(TPrimaryKey id); |
|||
void Delete(TKey id); |
|||
} |
|||
} |
|||
|
|||
@ -1,13 +1,14 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Domain.Entities |
|||
namespace Volo.Abp.Domain.Entities |
|||
{ |
|||
public abstract class AggregateRoot : AggregateRoot<Guid>, IAggregateRoot |
|||
|
|||
/// <inheritdoc cref="IAggregateRoot" />
|
|||
public abstract class AggregateRoot : IAggregateRoot |
|||
{ |
|||
|
|||
} |
|||
|
|||
public abstract class AggregateRoot<TPrimaryKey> : Entity<TPrimaryKey>, IAggregateRoot<TPrimaryKey> |
|||
/// <inheritdoc cref="IAggregateRoot{TKey}" />
|
|||
public abstract class AggregateRoot<TKey> : Entity<TKey>, IAggregateRoot<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
@ -1,30 +1,23 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Domain.Entities |
|||
namespace Volo.Abp.Domain.Entities |
|||
{ |
|||
/// <summary>
|
|||
/// A shortcut of <see cref="IEntity{TPrimaryKey}"/> for default primary key type (<see cref="string"/>).
|
|||
/// Defines an entity. It's primary key may not be "Id" or it mah have a composite primary key.
|
|||
/// Use <see cref="IEntity{TKey}"/> where possible for better integration to repositories and other structures in the framework.
|
|||
/// </summary>
|
|||
public interface IEntity : IEntity<Guid> |
|||
public interface IEntity |
|||
{ |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Defines interface for base entity type. All entities in the system must implement this interface.
|
|||
/// Defines an entity with a single primary key with "Id" property.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
|
|||
public interface IEntity<TPrimaryKey> |
|||
/// <typeparam name="TKey">Type of the primary key of the entity</typeparam>
|
|||
public interface IEntity<TKey> : IEntity |
|||
{ |
|||
/// <summary>
|
|||
/// Unique identifier for this entity.
|
|||
/// </summary>
|
|||
TPrimaryKey Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Checks if this entity is transient (not persisted to database and it has not an <see cref="Id"/>).
|
|||
/// </summary>
|
|||
/// <returns>True, if this entity is transient</returns>
|
|||
bool IsTransient(); |
|||
TKey Id { get; set; } //TODO: Consider to remove setter and make it protected in Entity class
|
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,90 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.Domain.Repositories |
|||
{ |
|||
public abstract class BasicRepositoryBase<TEntity> : IBasicRepository<TEntity> |
|||
where TEntity : class, IEntity |
|||
{ |
|||
public ICancellationTokenProvider CancellationTokenProvider { get; set; } |
|||
|
|||
protected BasicRepositoryBase() |
|||
{ |
|||
CancellationTokenProvider = NullCancellationTokenProvider.Instance; |
|||
} |
|||
|
|||
public abstract TEntity Insert(TEntity entity, bool autoSave = false); |
|||
|
|||
public virtual Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) |
|||
{ |
|||
return Task.FromResult(Insert(entity, autoSave)); |
|||
} |
|||
|
|||
public abstract TEntity Update(TEntity entity); |
|||
|
|||
public virtual Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) |
|||
{ |
|||
return Task.FromResult(Update(entity)); |
|||
} |
|||
|
|||
public abstract void Delete(TEntity entity); |
|||
|
|||
public virtual Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) |
|||
{ |
|||
Delete(entity); |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected virtual CancellationToken GetCancellationToken(CancellationToken prefferedValue = default) |
|||
{ |
|||
return CancellationTokenProvider.FallbackToProvider(prefferedValue); |
|||
} |
|||
} |
|||
|
|||
public abstract class BasicRepositoryBase<TEntity, TKey> : BasicRepositoryBase<TEntity>, IBasicRepository<TEntity, TKey> |
|||
where TEntity : class, IEntity<TKey> |
|||
{ |
|||
public virtual TEntity Get(TKey id) |
|||
{ |
|||
var entity = Find(id); |
|||
|
|||
if (entity == null) |
|||
{ |
|||
throw new EntityNotFoundException(typeof(TEntity), id); |
|||
} |
|||
|
|||
return entity; |
|||
} |
|||
|
|||
public virtual Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default) |
|||
{ |
|||
return Task.FromResult(Get(id)); |
|||
} |
|||
|
|||
public abstract TEntity Find(TKey id); |
|||
|
|||
public virtual Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default) |
|||
{ |
|||
return Task.FromResult(Find(id)); |
|||
} |
|||
|
|||
public virtual void Delete(TKey id) |
|||
{ |
|||
var entity = Find(id); |
|||
if (entity == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Delete(entity); |
|||
} |
|||
|
|||
public virtual Task DeleteAsync(TKey id, CancellationToken cancellationToken = default) |
|||
{ |
|||
Delete(id); |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Volo.Abp.Domain.Repositories |
|||
{ |
|||
public interface IBasicRepository<TEntity> : IRepository |
|||
where TEntity : class, IEntity |
|||
{ |
|||
/// <summary>
|
|||
/// Inserts a new entity.
|
|||
/// </summary>
|
|||
/// <param name="entity">Inserted entity</param>
|
|||
/// <param name="autoSave">
|
|||
/// Set true to automatically save changes to database.
|
|||
/// This can be used to set database generated Id of an entity for some ORMs (like Entity Framework).
|
|||
/// </param>
|
|||
[NotNull] |
|||
TEntity Insert([NotNull] TEntity entity, bool autoSave = false); |
|||
|
|||
/// <summary>
|
|||
/// Inserts a new entity.
|
|||
/// </summary>
|
|||
/// <param name="autoSave">
|
|||
/// Set true to automatically save changes to database.
|
|||
/// This can be used to set database generated Id of an entity for some ORMs (like Entity Framework).
|
|||
/// </param>
|
|||
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
|
|||
/// <param name="entity">Inserted entity</param>
|
|||
[NotNull] |
|||
Task<TEntity> InsertAsync([NotNull] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); |
|||
|
|||
/// <summary>
|
|||
/// Updates an existing entity.
|
|||
/// </summary>
|
|||
/// <param name="entity">Entity</param>
|
|||
[NotNull] |
|||
TEntity Update([NotNull] TEntity entity); |
|||
|
|||
/// <summary>
|
|||
/// Updates an existing entity.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
|
|||
/// <param name="entity">Entity</param>
|
|||
[NotNull] |
|||
Task<TEntity> UpdateAsync([NotNull] TEntity entity, CancellationToken cancellationToken = default); |
|||
|
|||
/// <summary>
|
|||
/// Deletes an entity.
|
|||
/// </summary>
|
|||
/// <param name="entity">Entity to be deleted</param>
|
|||
void Delete([NotNull] TEntity entity); //TODO: Return true if deleted
|
|||
|
|||
/// <summary>
|
|||
/// Deletes an entity.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
|
|||
/// <param name="entity">Entity to be deleted</param>
|
|||
Task DeleteAsync([NotNull] TEntity entity, CancellationToken cancellationToken = default); //TODO: Return true if deleted
|
|||
} |
|||
|
|||
public interface IBasicRepository<TEntity, TKey> : IBasicRepository<TEntity> |
|||
where TEntity : class, IEntity<TKey> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets an entity with given primary key.
|
|||
/// Throws <see cref="EntityNotFoundException"/> if can not find an entity with given id.
|
|||
/// </summary>
|
|||
/// <param name="id">Primary key of the entity to get</param>
|
|||
/// <returns>Entity</returns>
|
|||
[NotNull] |
|||
TEntity Get(TKey id); |
|||
|
|||
/// <summary>
|
|||
/// Gets an entity with given primary key.
|
|||
/// Throws <see cref="EntityNotFoundException"/> if can not find an entity with given id.
|
|||
/// </summary>
|
|||
/// <param name="id">Primary key of the entity to get</param>
|
|||
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
|
|||
/// <returns>Entity</returns>
|
|||
[NotNull] |
|||
Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default); |
|||
|
|||
/// <summary>
|
|||
/// Gets an entity with given primary key or null if not found.
|
|||
/// </summary>
|
|||
/// <param name="id">Primary key of the entity to get</param>
|
|||
/// <returns>Entity or null</returns>
|
|||
[CanBeNull] |
|||
TEntity Find(TKey id); |
|||
|
|||
/// <summary>
|
|||
/// Gets an entity with given primary key or null if not found.
|
|||
/// </summary>
|
|||
/// <param name="id">Primary key of the entity to get</param>
|
|||
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
|
|||
/// <returns>Entity or null</returns>
|
|||
Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default); |
|||
|
|||
/// <summary>
|
|||
/// Deletes an entity by primary key.
|
|||
/// </summary>
|
|||
/// <param name="id">Primary key of the entity</param>
|
|||
void Delete(TKey id); //TODO: Return true if deleted
|
|||
|
|||
/// <summary>
|
|||
/// Deletes an entity by primary key.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
|
|||
/// <param name="id">Primary key of the entity</param>
|
|||
Task DeleteAsync(TKey id, CancellationToken cancellationToken = default); //TODO: Return true if deleted
|
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Volo.Abp.Domain.Repositories |
|||
{ |
|||
public interface IQueryableRepository<TEntity> : IQueryableRepository<TEntity, Guid>, IRepository<TEntity> |
|||
where TEntity : class, IEntity<Guid> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IQueryableRepository<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>, IQueryable<TEntity> |
|||
where TEntity : class, IEntity<TPrimaryKey> |
|||
{ |
|||
/// <summary>
|
|||
/// Deletes many entities by function.
|
|||
/// Notice that: All entities fits to given predicate are retrieved and deleted.
|
|||
/// This may cause major performance problems if there are too many entities with
|
|||
/// given predicate.
|
|||
/// </summary>
|
|||
/// <param name="predicate">A condition to filter entities</param>
|
|||
void Delete([NotNull] Expression<Func<TEntity, bool>> predicate); |
|||
|
|||
/// <summary>
|
|||
/// Deletes many entities by function.
|
|||
/// Notice that: All entities fits to given predicate are retrieved and deleted.
|
|||
/// This may cause major performance problems if there are too many entities with
|
|||
/// given predicate.
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
|
|||
/// <param name="predicate">A condition to filter entities</param>
|
|||
Task DeleteAsync([NotNull] Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -1,90 +0,0 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.Domain.Repositories |
|||
{ |
|||
public abstract class QueryableRepositoryBase<TEntity> : QueryableRepositoryBase<TEntity, Guid>, IQueryableRepository<TEntity> |
|||
where TEntity : class, IEntity<Guid> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public abstract class QueryableRepositoryBase<TEntity, TPrimaryKey> : RepositoryBase<TEntity, TPrimaryKey>, IQueryableRepository<TEntity, TPrimaryKey> |
|||
where TEntity : class, IEntity<TPrimaryKey> |
|||
{ |
|||
public virtual Type ElementType => GetQueryable().ElementType; |
|||
|
|||
public virtual Expression Expression => GetQueryable().Expression; |
|||
|
|||
public virtual IQueryProvider Provider => GetQueryable().Provider; |
|||
|
|||
public IDataFilter DataFilter { get; set; } |
|||
|
|||
public ICurrentTenant CurrentTenant { get; set; } |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return GetEnumerator(); |
|||
} |
|||
|
|||
public IEnumerator<TEntity> GetEnumerator() |
|||
{ |
|||
return GetQueryable().GetEnumerator(); |
|||
} |
|||
|
|||
protected abstract IQueryable<TEntity> GetQueryable(); |
|||
|
|||
public override List<TEntity> GetList() |
|||
{ |
|||
return GetQueryable().ToList(); |
|||
} |
|||
|
|||
public override TEntity Find(TPrimaryKey id) |
|||
{ |
|||
return GetQueryable().FirstOrDefault(CreateEqualityExpressionForId(id)); |
|||
} |
|||
|
|||
public virtual void Delete(Expression<Func<TEntity, bool>> predicate) |
|||
{ |
|||
foreach (var entity in GetQueryable().Where(predicate).ToList()) |
|||
{ |
|||
Delete(entity); |
|||
} |
|||
} |
|||
|
|||
public virtual Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) |
|||
{ |
|||
Delete(predicate); |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public override long GetCount() |
|||
{ |
|||
return GetQueryable().LongCount(); |
|||
} |
|||
|
|||
protected virtual IQueryable<TEntity> ApplyDataFilters(IQueryable<TEntity> query) |
|||
{ |
|||
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) |
|||
{ |
|||
query = query.WhereIf(DataFilter.IsEnabled<ISoftDelete>(), e => ((ISoftDelete)e).IsDeleted == false); |
|||
} |
|||
|
|||
if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) |
|||
{ |
|||
var tenantId = CurrentTenant.Id; |
|||
query = query.WhereIf(DataFilter.IsEnabled<IMultiTenant>(), e => ((IMultiTenant)e).TenantId == tenantId); |
|||
} |
|||
|
|||
return query; |
|||
} |
|||
} |
|||
} |
|||
@ -1,20 +1,19 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore |
|||
{ |
|||
public interface IEfCoreRepository<TEntity> : IEfCoreRepository<TEntity, Guid>, IQueryableRepository<TEntity> |
|||
where TEntity : class, IEntity<Guid> |
|||
public interface IEfCoreRepository<TEntity> : IRepository<TEntity> |
|||
where TEntity : class, IEntity |
|||
{ |
|||
|
|||
DbContext DbContext { get; } |
|||
|
|||
DbSet<TEntity> DbSet { get; } |
|||
} |
|||
|
|||
public interface IEfCoreRepository<TEntity, TPrimaryKey> : IQueryableRepository<TEntity, TPrimaryKey> |
|||
where TEntity : class, IEntity<TPrimaryKey> |
|||
public interface IEfCoreRepository<TEntity, TKey> : IEfCoreRepository<TEntity>, IRepository<TEntity, TKey> |
|||
where TEntity : class, IEntity<TKey> |
|||
{ |
|||
DbContext DbContext { get; } |
|||
|
|||
DbSet<TEntity> DbSet { get; } |
|||
} |
|||
} |
|||
@ -1,8 +1,9 @@ |
|||
using Volo.Abp.Application.Dtos; |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public class IdentityRoleDto : EntityDto |
|||
public class IdentityRoleDto : EntityDto<Guid> |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
|
|||
@ -0,0 +1,2 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary> |
|||
@ -0,0 +1,2 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary> |
|||
@ -1,9 +1,12 @@ |
|||
using Volo.Abp.Domain.Repositories; |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace Volo.Abp.IdentityServer.ApiResources |
|||
{ |
|||
public interface IApiResourceRepository : IRepository<ApiResource> |
|||
public interface IApiResourceRepository : IBasicRepository<ApiResource, Guid> |
|||
{ |
|||
|
|||
Task<ApiResource> FindByNameAsync(string name, CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -1,10 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Clients |
|||
{ |
|||
public interface IClientRepository : IRepository<Client> |
|||
public interface IClientRepository : IBasicRepository<Client, Guid> |
|||
{ |
|||
Task<Client> FindByCliendIdIncludingAllAsync([NotNull] string clientId); |
|||
} |
|||
|
|||
@ -0,0 +1,2 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary> |
|||
@ -1,15 +1,24 @@ |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.IdentityServer.ApiResources; |
|||
using Volo.Abp.IdentityServer.EntityFrameworkCore; |
|||
|
|||
namespace Volo.Abp.IdentityServer |
|||
{ |
|||
public class ApiResourceRepository : EfCoreRepository<IdentityServerDbContext, ApiResource>, IApiResourceRepository |
|||
public class ApiResourceRepository : EfCoreRepository<IdentityServerDbContext, ApiResource, Guid>, IApiResourceRepository |
|||
{ |
|||
public ApiResourceRepository(IDbContextProvider<IdentityServerDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public async Task<ApiResource> FindByNameAsync(string name, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await this.FirstOrDefaultAsync(ar => ar.Name == name, cancellationToken); |
|||
} |
|||
} |
|||
} |
|||
@ -1,20 +1,19 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Volo.Abp.Domain.Repositories.MemoryDb |
|||
{ |
|||
public interface IMemoryDbRepository<TEntity> : IMemoryDbRepository<TEntity, Guid>, IQueryableRepository<TEntity> |
|||
where TEntity : class, IEntity<Guid> |
|||
public interface IMemoryDbRepository<TEntity> : IRepository<TEntity> |
|||
where TEntity : class, IEntity |
|||
{ |
|||
|
|||
IMemoryDatabase Database { get; } |
|||
|
|||
List<TEntity> Collection { get; } |
|||
} |
|||
|
|||
public interface IMemoryDbRepository<TEntity, TPrimaryKey> : IQueryableRepository<TEntity, TPrimaryKey> |
|||
where TEntity : class, IEntity<TPrimaryKey> |
|||
public interface IMemoryDbRepository<TEntity, TKey> : IMemoryDbRepository<TEntity>, IRepository<TEntity, TKey> |
|||
where TEntity : class, IEntity<TKey> |
|||
{ |
|||
IMemoryDatabase Database { get; } |
|||
|
|||
List<TEntity> Collection { get; } |
|||
} |
|||
} |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue