Browse Source

Merge pull request #194 from aspnetzero/repository-changes

Introduce entity and repository without primary key
pull/206/head
Halil İbrahim Kalkan 8 years ago
committed by GitHub
parent
commit
04e35fbdb1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      docs/Data-Transfer-Objects.md
  2. 169
      docs/Entities.md
  3. 14
      docs/Index.md
  4. 3
      docs/Object-To-Object-Mapping.md
  5. 117
      docs/Repositories.md
  6. 4
      src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs
  7. 4
      src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/BlogPostLister.cs
  8. 4
      src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/UserLister.cs
  9. 5
      src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoBlogModule.cs
  10. 2
      src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPost.cs
  11. 5
      src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPostComment.cs
  12. 11
      src/AbpDesk/AbpDesk.MongoBlog/Areas/Blog/Controllers/PostsController.cs
  13. 2
      src/AbpDesk/AbpDesk.Web.Mvc/Controllers/IdentityServerTestController.cs
  14. BIN
      src/AbpDesk/Web_PlugIns/AbpDesk.MongoBlog.dll
  15. 69
      src/Volo.Abp.Ddd/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs
  16. 37
      src/Volo.Abp.Ddd/Volo/Abp/Application/Dtos/EntityDto.cs
  17. 20
      src/Volo.Abp.Ddd/Volo/Abp/Application/Dtos/IEntityDto.cs
  18. 67
      src/Volo.Abp.Ddd/Volo/Abp/Application/Services/AsyncCrudAppService.cs
  19. 67
      src/Volo.Abp.Ddd/Volo/Abp/Application/Services/CrudAppService.cs
  20. 12
      src/Volo.Abp.Ddd/Volo/Abp/Application/Services/CrudAppServiceBase.cs
  21. 36
      src/Volo.Abp.Ddd/Volo/Abp/Application/Services/IAsyncCrudAppService.cs
  22. 36
      src/Volo.Abp.Ddd/Volo/Abp/Application/Services/ICrudAppService.cs
  23. 28
      src/Volo.Abp.Ddd/Volo/Abp/DependencyInjection/CommonDbContextRegistrationOptions.cs
  24. 7
      src/Volo.Abp.Ddd/Volo/Abp/DependencyInjection/ICommonDbContextRegistrationOptionsBuilder.cs
  25. 11
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/AggregateRoot.cs
  26. 49
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/Entity.cs
  27. 58
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/EntityHelper.cs
  28. 9
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/EntityNotFoundException.cs
  29. 16
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/IAggregateRoot.cs
  30. 23
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/IEntity.cs
  31. 90
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs
  32. 114
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/IBasicRepository.cs
  33. 39
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/IQueryableRepository.cs
  34. 151
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/IRepository.cs
  35. 4
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/ISupportsExplicitLoading.cs
  36. 90
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/QueryableRepositoryBase.cs
  37. 122
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/RepositoryBase.cs
  38. 28
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs
  39. 35
      src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs
  40. 16
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EfCoreRepositoryExtensions.cs
  41. 137
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
  42. 15
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreRepository.cs
  43. 2
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DbContextHelper.cs
  44. 2
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/EfCoreRepositoryRegistrar.cs
  45. 5
      src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleDto.cs
  46. 2
      src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs
  47. 6
      src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs
  48. 7
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs
  49. 16
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs
  50. 2
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRole.cs
  51. 2
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleClaim.cs
  52. 2
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs
  53. 2
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserClaim.cs
  54. 2
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLogin.cs
  55. 2
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRole.cs
  56. 2
      src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserToken.cs
  57. 2
      src/Volo.Abp.Identity.EntityFrameworkCore/Volo.Abp.Identity.EntityFrameworkCore.csproj.DotSettings
  58. 18
      src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityRoleRepository.cs
  59. 11
      src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs
  60. 4
      src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs
  61. 2
      src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj.DotSettings
  62. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs
  63. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiScope.cs
  64. 9
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs
  65. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs
  66. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientClaim.cs
  67. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientCorsOrigin.cs
  68. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientIdPRestriction.cs
  69. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUri.cs
  70. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientProperty.cs
  71. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientRedirectUri.cs
  72. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientScope.cs
  73. 5
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs
  74. 5
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs
  75. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrant.cs
  76. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs
  77. 5
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs
  78. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IdentityResource.cs
  79. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Secret.cs
  80. 2
      src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/UserClaim.cs
  81. 2
      src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo.Abp.IdentityServer.EntityFrameworkCore.csproj.DotSettings
  82. 13
      src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResourceRepository.cs
  83. 5
      src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ClientRepository.cs
  84. 2
      src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerModule.cs
  85. 5
      src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResourceRepository.cs
  86. 5
      src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/PersistedGrantRepository.cs
  87. 2
      src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabase.cs
  88. 17
      src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbRepository.cs
  89. 16
      src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/InMemoryIdGenerator.cs
  90. 4
      src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabase.cs
  91. 95
      src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
  92. 16
      src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDbCoreRepositoryExtensions.cs
  93. 2
      src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/DependencyInjection/MemoryDbRepositoryRegistrar.cs
  94. 19
      src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs
  95. 127
      src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  96. 20
      src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs
  97. 1
      src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/IMongoDbContextRegistrationOptionsBuilder.cs
  98. 4
      src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/MongoDbRepositoryRegistrar.cs
  99. 2
      src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/ITenantRepository.cs
  100. 2
      src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/Tenant.cs

3
docs/Data-Transfer-Objects.md

@ -0,0 +1,3 @@
## Data Transfer Objects
TODO

169
docs/Entities.md

@ -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.

14
docs/Index.md

@ -12,7 +12,19 @@
* <a href="Module-Development-Basics.md" target="_blank">Basics</a>
* Plug-In Modules
* Best Practices
* Domain Driven Design
* Domain Layer
* [Entities & Aggregate Roots](Entities.md)
* Value Objects
* [Repositories](Repositories.md)
* Domain Services
* Specifications
* Domain Events
* Application Layer
* Application Services
* Data Transfer Objects
* Unit Of Work
* Data Access
* [Entity Framework Core Integration](Entity-Framework-Core.md)
* Presentation (User Interface)
* Presentation / User Interface
* Localization

3
docs/Object-To-Object-Mapping.md

@ -0,0 +1,3 @@
## Data Transfer Objects
TODO

117
docs/Repositories.md

@ -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.

4
src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs

@ -12,11 +12,11 @@ namespace AbpDesk.Tickets
{
public class TicketAppService : ApplicationService, ITicketAppService
{
private readonly IQueryableRepository<Ticket, int> _ticketRepository;
private readonly IRepository<Ticket, int> _ticketRepository;
private readonly IAsyncQueryableExecuter _asyncQueryableExecuter;
public TicketAppService(
IQueryableRepository<Ticket, int> ticketRepository,
IRepository<Ticket, int> ticketRepository,
IAsyncQueryableExecuter asyncQueryableExecuter)
{
_ticketRepository = ticketRepository;

4
src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/BlogPostLister.cs

@ -9,11 +9,11 @@ namespace AbpDesk.ConsoleDemo
{
public class BlogPostLister : ITransientDependency
{
private readonly IQueryableRepository<BlogPost> _blogPostRepository;
private readonly IRepository<BlogPost, Guid> _blogPostRepository;
private readonly IGuidGenerator _guidGenerator;
public BlogPostLister(
IQueryableRepository<BlogPost> blogPostRepository,
IRepository<BlogPost, Guid> blogPostRepository,
IGuidGenerator guidGenerator)
{
_blogPostRepository = blogPostRepository;

4
src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/UserLister.cs

@ -11,11 +11,11 @@ namespace AbpDesk.ConsoleDemo
public class UserLister : ITransientDependency
{
private readonly IdentityUserManager _userManager;
private readonly IQueryableRepository<IdentityUser> _userRepository;
private readonly IRepository<IdentityUser, Guid> _userRepository;
public UserLister(
IdentityUserManager userManager,
IQueryableRepository<IdentityUser> userRepository)
IRepository<IdentityUser, Guid> userRepository)
{
_userManager = userManager;
_userRepository = userRepository;

5
src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoBlogModule.cs

@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp;
@ -46,7 +47,7 @@ namespace AbpDesk.Blogging
using (var uow = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>().Begin())
{
var blogPostRepository = scope.ServiceProvider.GetRequiredService<IQueryableRepository<BlogPost>>();
var blogPostRepository = scope.ServiceProvider.GetRequiredService<IRepository<BlogPost, Guid>>();
if (blogPostRepository.Any())
{
logger.LogInformation($"No need to seed database since there are already {blogPostRepository.Count()} blog posts!");

2
src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPost.cs

@ -6,7 +6,7 @@ using Volo.Abp.Domain.Entities;
namespace AbpDesk.Blogging
{
public class BlogPost : AggregateRoot
public class BlogPost : AggregateRoot<Guid>
{
public virtual string Title { get; protected set; }

5
src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPostComment.cs

@ -1,9 +1,10 @@
using JetBrains.Annotations;
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
namespace AbpDesk.Blogging
{
public class BlogPostComment : Entity
public class BlogPostComment : Entity<Guid>
{
[NotNull]
public virtual string Name { get; protected set; }

11
src/AbpDesk/AbpDesk.MongoBlog/Areas/Blog/Controllers/PostsController.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Linq;
using AbpDesk.Blogging;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
@ -9,16 +10,16 @@ namespace Areas.Blog.Controllers
[Area("Blog")]
public class PostsController : AbpController
{
private readonly IQueryableRepository<BlogPost> _blogPostRepository;
private readonly IRepository<BlogPost, Guid> _blogPostRepository;
public PostsController(IQueryableRepository<BlogPost> blogPostRepository)
public PostsController(IRepository<BlogPost, Guid> blogPostRepository)
{
_blogPostRepository = blogPostRepository;
}
public async Task<ActionResult> Index()
public ActionResult Index()
{
var posts = await _blogPostRepository.GetListAsync(HttpContext.RequestAborted);
var posts = _blogPostRepository.ToList(); //TODO: async..?
return View(posts);
}
}

2
src/AbpDesk/AbpDesk.Web.Mvc/Controllers/IdentityServerTestController.cs

@ -38,7 +38,7 @@ namespace AbpDesk.Web.Mvc.Controllers
[Route("create")]
public async Task<IActionResult> CreateClient(string clientId)
{
var apiResource = (await _apiResourceRepository.GetListAsync()).FirstOrDefault(ar => ar.Name == "api1");
var apiResource = await _apiResourceRepository.FindByNameAsync("api1");
if (apiResource == null)
{

BIN
src/AbpDesk/Web_PlugIns/AbpDesk.MongoBlog.dll

Binary file not shown.

69
src/Volo.Abp.Ddd/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs

@ -4,66 +4,49 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Reflection;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionRepositoryExtensions
{
public static void AddDefaultRepository(this IServiceCollection services, Type entityType, Type repositoryImplementationType)
public static IServiceCollection AddDefaultRepository(this IServiceCollection services, Type entityType, Type repositoryImplementationType)
{
AddDefaultRepositoryForGenericPrimaryKey(services, entityType, repositoryImplementationType);
if (BothSupportsDefaultPrimaryKey(entityType, repositoryImplementationType))
{
AddDefaultRepositoryForDefaultPrimaryKey(services, entityType, repositoryImplementationType);
}
}
private static void AddDefaultRepositoryForGenericPrimaryKey(IServiceCollection services, Type entityType, Type repositoryImplementationType)
{
var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType);
//IRepository<TEntity, TPrimaryKey>
var repositoryInterface = typeof(IRepository<,>).MakeGenericType(entityType, primaryKeyType);
if (!repositoryInterface.GetTypeInfo().IsAssignableFrom(repositoryImplementationType))
//IRepository<TEntity>
var repositoryInterfaceWithoutPk = typeof(IBasicRepository<>).MakeGenericType(entityType);
if (!repositoryInterfaceWithoutPk.IsAssignableFrom(repositoryImplementationType))
{
throw new AbpException($"Given repositoryImplementationType ({repositoryImplementationType}) must implement {repositoryInterface}");
throw new AbpException($"Given repositoryImplementationType ({repositoryImplementationType}) must implement {repositoryInterfaceWithoutPk}");
}
services.TryAddTransient(repositoryInterface, repositoryImplementationType);
//IQueryableRepository<TEntity, TPrimaryKey>
var queryableRepositoryInterface = typeof(IQueryableRepository<,>).MakeGenericType(entityType, primaryKeyType);
if (queryableRepositoryInterface.GetTypeInfo().IsAssignableFrom(repositoryImplementationType))
{
services.TryAddTransient(queryableRepositoryInterface, repositoryImplementationType);
}
}
services.TryAddTransient(repositoryInterfaceWithoutPk, repositoryImplementationType);
private static void AddDefaultRepositoryForDefaultPrimaryKey(IServiceCollection services, Type entityType, Type repositoryImplementationType)
{
//IRepository<TEntity>
var repositoryInterfaceWithDefaultPrimaryKey = typeof(IRepository<>).MakeGenericType(entityType);
if (!repositoryInterfaceWithDefaultPrimaryKey.GetTypeInfo().IsAssignableFrom(repositoryImplementationType))
//IQueryableRepository<TEntity>
var queryableRepositoryInterfaceWithPk = typeof(IRepository<>).MakeGenericType(entityType);
if (repositoryInterfaceWithoutPk.IsAssignableFrom(repositoryImplementationType))
{
throw new AbpException($"Given repositoryImplementationType ({repositoryImplementationType}) must implement {repositoryInterfaceWithDefaultPrimaryKey}");
services.TryAddTransient(queryableRepositoryInterfaceWithPk, repositoryImplementationType);
}
services.TryAddTransient(repositoryInterfaceWithDefaultPrimaryKey, repositoryImplementationType);
var primaryKeyType = EntityHelper.FindPrimaryKeyType(entityType);
//IQueryableRepository<TEntity>
var queryableRepositoryInterfaceWithDefaultPrimaryKey = typeof(IQueryableRepository<>).MakeGenericType(entityType);
if (queryableRepositoryInterfaceWithDefaultPrimaryKey.GetTypeInfo().IsAssignableFrom(repositoryImplementationType))
if (primaryKeyType != null)
{
services.TryAddTransient(queryableRepositoryInterfaceWithDefaultPrimaryKey, repositoryImplementationType);
//IRepository<TEntity, TKey>
var repositoryInterface = typeof(IBasicRepository<,>).MakeGenericType(entityType, primaryKeyType);
if (repositoryInterface.GetTypeInfo().IsAssignableFrom(repositoryImplementationType))
{
services.TryAddTransient(repositoryInterface, repositoryImplementationType);
}
//IQueryableRepository<TEntity, TKey>
var queryableRepositoryInterface = typeof(IRepository<,>).MakeGenericType(entityType, primaryKeyType);
if (queryableRepositoryInterface.GetTypeInfo().IsAssignableFrom(repositoryImplementationType))
{
services.TryAddTransient(queryableRepositoryInterface, repositoryImplementationType);
}
}
}
private static bool BothSupportsDefaultPrimaryKey(Type entityType, Type repositoryImplementationType)
{
return typeof(IEntity<Guid>).GetTypeInfo().IsAssignableFrom(entityType) &&
ReflectionHelper.IsAssignableToGenericType(repositoryImplementationType, typeof(IRepository<>));
return services;
}
}
}

37
src/Volo.Abp.Ddd/Volo/Abp/Application/Dtos/EntityDto.cs

@ -1,41 +1,22 @@
using System;
namespace Volo.Abp.Application.Dtos
{
public class EntityDto : EntityDto<Guid>, IEntityDto
public class EntityDto : IEntityDto //TODO: Consider to delete this class
{
/// <summary>
/// Creates a new <see cref="EntityDto"/> object.
/// </summary>
public EntityDto()
{
}
/// <summary>
/// Creates a new <see cref="EntityDto"/> object.
/// </summary>
/// <param name="id">Id of the entity</param>
public EntityDto(Guid id)
: base(id)
public override string ToString()
{
return $"[DTO: {GetType().Name}]";
}
}
/// <summary>
/// Implements common properties for entity based DTOs.
/// </summary>
/// <typeparam name="TPrimaryKey">Type of the primary key</typeparam>
public class EntityDto<TPrimaryKey> : IEntityDto<TPrimaryKey>
public class EntityDto<TKey> : EntityDto, IEntityDto<TKey>
{
/// <summary>
/// Id of the entity.
/// </summary>
public TPrimaryKey Id { get; set; }
public TKey Id { get; set; }
/// <summary>
/// Creates a new <see cref="EntityDto{TPrimaryKey}"/> object.
/// Creates a new <see cref="EntityDto{TKey}"/> object.
/// </summary>
public EntityDto()
{
@ -43,17 +24,17 @@ namespace Volo.Abp.Application.Dtos
}
/// <summary>
/// Creates a new <see cref="EntityDto{TPrimaryKey}"/> object.
/// Creates a new <see cref="EntityDto{TKey}"/> object.
/// </summary>
/// <param name="id">Id of the entity</param>
public EntityDto(TPrimaryKey id)
public EntityDto(TKey id)
{
Id = id;
}
public override string ToString()
{
return $"[{GetType().Name}] Id = {Id}";
return $"[DTO: {GetType().Name}] Id = {Id}";
}
}
}

20
src/Volo.Abp.Ddd/Volo/Abp/Application/Dtos/IEntityDto.cs

@ -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; }
}
}

67
src/Volo.Abp.Ddd/Volo/Abp/Application/Services/AsyncCrudAppService.cs

@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities;
@ -8,71 +7,59 @@ using Volo.Abp.Linq;
namespace Volo.Abp.Application.Services
{
public abstract class AsyncCrudAppService<TEntity, TEntityDto>
: AsyncCrudAppService<TEntity, TEntityDto, Guid>
where TEntity : class, IEntity<Guid>
where TEntityDto : IEntityDto<Guid>
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey>
: AsyncCrudAppService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected AsyncCrudAppService(IQueryableRepository<TEntity, Guid> repository)
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey>
: AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput>
: AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TEntityDto, TEntityDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected AsyncCrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput>
: AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TEntityDto, TEntityDto>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
{
protected AsyncCrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
: base(repository)
{
}
}
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput>
: AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TCreateInput>
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput>
: AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TCreateInput>
where TGetAllInput : IPagedAndSortedResultRequest
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
where TCreateInput : IEntityDto<TPrimaryKey>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
where TCreateInput : IEntityDto<TKey>
{
protected AsyncCrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput>,
IAsyncCrudAppService<TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>,
IAsyncCrudAppService<TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; }
protected AsyncCrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository)
:base(repository)
{
AsyncQueryableExecuter = DefaultAsyncQueryableExecuter.Instance;
}
public virtual async Task<TEntityDto> GetAsync(TPrimaryKey id)
public virtual async Task<TEntityDto> GetAsync(TKey id)
{
CheckGetPermission();
@ -111,7 +98,7 @@ namespace Volo.Abp.Application.Services
return MapToEntityDto(entity);
}
public virtual async Task<TEntityDto> UpdateAsync(TPrimaryKey id, TUpdateInput input)
public virtual async Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input)
{
CheckUpdatePermission();
@ -125,14 +112,14 @@ namespace Volo.Abp.Application.Services
return MapToEntityDto(entity);
}
public virtual Task DeleteAsync(TPrimaryKey id)
public virtual Task DeleteAsync(TKey id)
{
CheckDeletePermission();
return Repository.DeleteAsync(id);
}
protected virtual Task<TEntity> GetEntityByIdAsync(TPrimaryKey id)
protected virtual Task<TEntity> GetEntityByIdAsync(TKey id)
{
return Repository.GetAsync(id);
}

67
src/Volo.Abp.Ddd/Volo/Abp/Application/Services/CrudAppService.cs

@ -1,73 +1,60 @@
using System;
using System.Linq;
using System.Linq;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.Application.Services
{
public abstract class CrudAppService<TEntity, TEntityDto>
: CrudAppService<TEntity, TEntityDto, Guid>
where TEntity : class, IEntity<Guid>
where TEntityDto : IEntityDto<Guid>
public abstract class CrudAppService<TEntity, TEntityDto, TKey>
: CrudAppService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected CrudAppService(IQueryableRepository<TEntity, Guid> repository)
protected CrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class CrudAppService<TEntity, TEntityDto, TPrimaryKey>
: CrudAppService<TEntity, TEntityDto, TPrimaryKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
public abstract class CrudAppService<TEntity, TEntityDto, TKey, TGetAllInput>
: CrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TEntityDto, TEntityDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected CrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
protected CrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class CrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput>
: CrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TEntityDto, TEntityDto>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
public abstract class CrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput>
: CrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TCreateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
where TCreateInput : IEntityDto<TKey>
{
protected CrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
protected CrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class CrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput>
: CrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TCreateInput>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
where TCreateInput : IEntityDto<TPrimaryKey>
public abstract class CrudAppService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>,
ICrudAppService<TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected CrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
: base(repository)
{
}
}
public abstract class CrudAppService<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput>,
ICrudAppService<TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
{
protected CrudAppService(IQueryableRepository<TEntity, TPrimaryKey> repository)
protected CrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
public virtual TEntityDto Get(TPrimaryKey id)
public virtual TEntityDto Get(TKey id)
{
CheckGetPermission();
@ -106,7 +93,7 @@ namespace Volo.Abp.Application.Services
return MapToEntityDto(entity);
}
public virtual TEntityDto Update(TPrimaryKey id, TUpdateInput input)
public virtual TEntityDto Update(TKey id, TUpdateInput input)
{
CheckUpdatePermission();
@ -118,14 +105,14 @@ namespace Volo.Abp.Application.Services
return MapToEntityDto(entity);
}
public virtual void Delete(TPrimaryKey id)
public virtual void Delete(TKey id)
{
CheckDeletePermission();
Repository.Delete(id);
}
protected virtual TEntity GetEntityById(TPrimaryKey id)
protected virtual TEntity GetEntityById(TKey id)
{
return Repository.Get(id);
}

12
src/Volo.Abp.Ddd/Volo/Abp/Application/Services/CrudAppServiceBase.cs

@ -12,11 +12,11 @@ namespace Volo.Abp.Application.Services
/// This is a common base class for CrudAppService and AsyncCrudAppService classes.
/// Inherit either from CrudAppService or AsyncCrudAppService, not from this class.
/// </summary>
public abstract class CrudAppServiceBase<TEntity, TEntityDto, TPrimaryKey, TGetAllInput, TCreateInput, TUpdateInput> : ApplicationService
where TEntity : class, IEntity<TPrimaryKey>
where TEntityDto : IEntityDto<TPrimaryKey>
public abstract class CrudAppServiceBase<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput> : ApplicationService
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected IQueryableRepository<TEntity, TPrimaryKey> Repository { get; }
protected IRepository<TEntity, TKey> Repository { get; }
protected virtual string GetPermissionName { get; set; }
@ -28,7 +28,7 @@ namespace Volo.Abp.Application.Services
protected virtual string DeletePermissionName { get; set; }
protected CrudAppServiceBase(IQueryableRepository<TEntity, TPrimaryKey> repository)
protected CrudAppServiceBase(IRepository<TEntity, TKey> repository)
{
Repository = repository;
}
@ -120,7 +120,7 @@ namespace Volo.Abp.Application.Services
}
/// <summary>
/// Sets Id value for the entity if <see cref="TPrimaryKey"/> is <see cref="Guid"/>.
/// Sets Id value for the entity if <see cref="TKey"/> is <see cref="Guid"/>.
/// It's used while creating a new entity.
/// </summary>
protected virtual void SetIdForGuids(TEntity entity)

36
src/Volo.Abp.Ddd/Volo/Abp/Application/Services/IAsyncCrudAppService.cs

@ -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);
}
}

36
src/Volo.Abp.Ddd/Volo/Abp/Application/Services/ICrudAppService.cs

@ -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);
}
}

28
src/Volo.Abp.Ddd/Volo/Abp/DependencyInjection/CommonDbContextRegistrationOptions.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Reflection;
@ -20,7 +19,7 @@ namespace Volo.Abp.DependencyInjection
public Type DefaultRepositoryImplementationType { get; private set; }
public Type DefaultRepositoryImplementationTypeWithDefaultPrimaryKey { get; private set; }
public Type DefaultRepositoryImplementationTypeWithouTKey { get; private set; }
public bool RegisterDefaultRepositories { get; private set; }
@ -28,7 +27,7 @@ namespace Volo.Abp.DependencyInjection
public Dictionary<Type, Type> CustomRepositories { get; }
public bool SpecifiedDefaultRepositoryTypes => DefaultRepositoryImplementationType != null && DefaultRepositoryImplementationTypeWithDefaultPrimaryKey != null;
public bool SpecifiedDefaultRepositoryTypes => DefaultRepositoryImplementationType != null && DefaultRepositoryImplementationTypeWithouTKey != null;
protected CommonDbContextRegistrationOptions(Type originalDbContextType)
{
@ -80,34 +79,37 @@ namespace Volo.Abp.DependencyInjection
return this;
}
public ICommonDbContextRegistrationOptionsBuilder AddCustomRepository<TEntity, TRepository>()
public ICommonDbContextRegistrationOptionsBuilder AddRepository<TEntity, TRepository>()
{
WithCustomRepository(typeof(TEntity), typeof(TRepository));
AddCustomRepository(typeof(TEntity), typeof(TRepository));
return this;
}
public ICommonDbContextRegistrationOptionsBuilder SetDefaultRepositoryClasses([NotNull] Type repositoryImplementationType, [NotNull] Type repositoryImplementationTypeWithDefaultPrimaryKey)
public ICommonDbContextRegistrationOptionsBuilder SetDefaultRepositoryClasses(
Type repositoryImplementationType,
Type repositoryImplementationTypeWithouTKey
)
{
Check.NotNull(repositoryImplementationType, nameof(repositoryImplementationType));
Check.NotNull(repositoryImplementationTypeWithDefaultPrimaryKey, nameof(repositoryImplementationTypeWithDefaultPrimaryKey));
Check.NotNull(repositoryImplementationTypeWithouTKey, nameof(repositoryImplementationTypeWithouTKey));
DefaultRepositoryImplementationType = repositoryImplementationType;
DefaultRepositoryImplementationTypeWithDefaultPrimaryKey = repositoryImplementationTypeWithDefaultPrimaryKey;
DefaultRepositoryImplementationTypeWithouTKey = repositoryImplementationTypeWithouTKey;
return this;
}
private void WithCustomRepository(Type entityType, Type repositoryType)
private void AddCustomRepository(Type entityType, Type repositoryType)
{
if (!ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IEntity<>)))
if (!typeof(IEntity).IsAssignableFrom(entityType))
{
throw new AbpException($"Given entityType is not an entity: {entityType.AssemblyQualifiedName}. It must implement {typeof(IEntity<>).AssemblyQualifiedName}.");
}
if (!ReflectionHelper.IsAssignableToGenericType(repositoryType, typeof(IRepository<,>)))
if (!ReflectionHelper.IsAssignableToGenericType(repositoryType, typeof(IBasicRepository<>)))
{
throw new AbpException($"Given repositoryType is not a repository: {entityType.AssemblyQualifiedName}. It must implement {typeof(IRepository<,>).AssemblyQualifiedName}.");
throw new AbpException($"Given repositoryType is not a repository: {entityType.AssemblyQualifiedName}. It must implement {typeof(IBasicRepository<>).AssemblyQualifiedName}.");
}
CustomRepositories[entityType] = repositoryType;
@ -125,7 +127,7 @@ namespace Volo.Abp.DependencyInjection
return false;
}
if (!IncludeAllEntitiesForDefaultRepositories && !ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IAggregateRoot<>)))
if (!IncludeAllEntitiesForDefaultRepositories && !typeof(IAggregateRoot).IsAssignableFrom(entityType))
{
return false;
}

7
src/Volo.Abp.Ddd/Volo/Abp/DependencyInjection/ICommonDbContextRegistrationOptionsBuilder.cs

@ -1,4 +1,5 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.DependencyInjection
{
@ -41,15 +42,15 @@ namespace Volo.Abp.DependencyInjection
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TRepository">Repository type</typeparam>
ICommonDbContextRegistrationOptionsBuilder AddCustomRepository<TEntity, TRepository>(); //TODO: Rename to AddRepository!
ICommonDbContextRegistrationOptionsBuilder AddRepository<TEntity, TRepository>();
/// <summary>
/// Uses given class(es) for default repositories.
/// </summary>
/// <param name="repositoryImplementationType">Repository implementation type</param>
/// <param name="repositoryImplementationTypeWithDefaultPrimaryKey">Repository implementation type for default primary key type (<see cref="string"/>)</param>
/// <param name="repositoryImplementationTypeWithouTKey">Repository implementation type (without primary key)</param>
/// <returns></returns>
ICommonDbContextRegistrationOptionsBuilder SetDefaultRepositoryClasses(Type repositoryImplementationType, Type repositoryImplementationTypeWithDefaultPrimaryKey);
ICommonDbContextRegistrationOptionsBuilder SetDefaultRepositoryClasses([NotNull] Type repositoryImplementationType, [NotNull] Type repositoryImplementationTypeWithouTKey);
/// <summary>
/// Replaces given DbContext type with this DbContext type.

11
src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/AggregateRoot.cs

@ -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>
{
}

49
src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/Entity.cs

@ -1,37 +1,27 @@
using System;
using System.Reflection;
using System.Reflection;
namespace Volo.Abp.Domain.Entities
{
/// <summary>
/// A shortcut of <see cref="Entity{TPrimaryKey}"/> for default primary key type (<see cref="Guid"/>).
/// </summary>
public abstract class Entity : Entity<Guid>, IEntity
{
}
/// <inheritdoc/>
/// <summary>
/// Basic implementation of IEntity interface.
/// An entity can inherit this class of directly implement to IEntity interface.
/// </summary>
/// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey>
public abstract class Entity : IEntity
{
/// <inheritdoc/>
public virtual TPrimaryKey Id { get; set; }
/// <inheritdoc/>
public virtual bool IsTransient()
public override string ToString()
{
return EntityHelper.IsTransient(this);
return $"[ENTITY: {GetType().Name}]";
}
}
/// <inheritdoc cref="IEntity{TKey}" />
public abstract class Entity<TKey> : Entity, IEntity<TKey>
{
/// <inheritdoc/>
public virtual TKey Id { get; set; }
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity<TPrimaryKey>))
if (obj == null || !(obj is Entity<TKey>))
{
return false;
}
@ -43,8 +33,8 @@ namespace Volo.Abp.Domain.Entities
}
//Transient objects are not considered as equal
var other = (Entity<TPrimaryKey>)obj;
if (IsTransient() && other.IsTransient())
var other = (Entity<TKey>)obj;
if (EntityHelper.IsTransient(this) && EntityHelper.IsTransient(other))
{
return false;
}
@ -63,10 +53,15 @@ namespace Volo.Abp.Domain.Entities
/// <inheritdoc/>
public override int GetHashCode()
{
if (Id == null)
{
return 0;
}
return Id.GetHashCode();
}
public static bool operator ==(Entity<TPrimaryKey> left, Entity<TPrimaryKey> right)
public static bool operator ==(Entity<TKey> left, Entity<TKey> right)
{
if (Equals(left, null))
{
@ -76,7 +71,7 @@ namespace Volo.Abp.Domain.Entities
return left.Equals(right);
}
public static bool operator !=(Entity<TPrimaryKey> left, Entity<TPrimaryKey> right)
public static bool operator !=(Entity<TKey> left, Entity<TKey> right)
{
return !(left == right);
}
@ -84,7 +79,7 @@ namespace Volo.Abp.Domain.Entities
/// <inheritdoc/>
public override string ToString()
{
return $"[{GetType().Name} {Id}]";
return $"[ENTITY: {GetType().Name}] Id = {Id}";
}
}
}

58
src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/EntityHelper.cs

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Volo.Abp.Reflection;
namespace Volo.Abp.Domain.Entities
{
@ -13,32 +13,23 @@ namespace Volo.Abp.Domain.Entities
{
public static bool IsEntity([NotNull] Type type)
{
return ReflectionHelper.IsAssignableToGenericType(type, typeof (IEntity<>));
return typeof(IEntity).IsAssignableFrom(type);
}
/// <summary>
/// Default implementation of <see cref="Entity{TPrimaryKey}.IsTransient"/>.
///
/// This method is not used normally if given entity is derived from <see cref="Entity{TPrimaryKey}"/>.
/// Just directly call <see cref="Entity{TPrimaryKey}.IsTransient"/>.
///
/// This method is exists to help developers who want to directly implement <see cref="IEntity{TPrimaryKey}.IsTransient"/>
/// but want to use default IsTransient implementation as a shortcut.
/// </summary>
public static bool IsTransient<TPrimaryKey>(IEntity<TPrimaryKey> entity)
public static bool IsTransient<TKey>(IEntity<TKey> entity) // TODO: Completely remove IsTransient
{
if (EqualityComparer<TPrimaryKey>.Default.Equals(entity.Id, default))
if (EqualityComparer<TKey>.Default.Equals(entity.Id, default))
{
return true;
}
//Workaround for EF Core since it sets int/long to min value when attaching to dbcontext
if (typeof(TPrimaryKey) == typeof(int))
if (typeof(TKey) == typeof(int))
{
return Convert.ToInt32(entity.Id) <= 0;
}
if (typeof(TPrimaryKey) == typeof(long))
if (typeof(TKey) == typeof(long))
{
return Convert.ToInt64(entity.Id) <= 0;
}
@ -46,25 +37,50 @@ namespace Volo.Abp.Domain.Entities
return false;
}
public static Type GetPrimaryKeyType<TEntity>()
/// <summary>
/// Tries to find the primary key type of the given entity type.
/// May return null if given type does not implement <see cref="IEntity{TKey}"/>
/// </summary>
[CanBeNull]
public static Type FindPrimaryKeyType<TEntity>()
where TEntity : IEntity
{
return GetPrimaryKeyType(typeof (TEntity));
return FindPrimaryKeyType(typeof(TEntity));
}
/// <summary>
/// Gets primary key type of given entity type
/// Tries to find the primary key type of the given entity type.
/// May return null if given type does not implement <see cref="IEntity{TKey}"/>
/// </summary>
public static Type GetPrimaryKeyType([NotNull] Type entityType)
[CanBeNull]
public static Type FindPrimaryKeyType([NotNull] Type entityType)
{
if (!typeof(IEntity).IsAssignableFrom(entityType))
{
throw new AbpException($"Given {nameof(entityType)} is not an entity. It should implement {typeof(IEntity).AssemblyQualifiedName}!");
}
foreach (var interfaceType in entityType.GetTypeInfo().GetInterfaces())
{
if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof (IEntity<>))
if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>))
{
return interfaceType.GenericTypeArguments[0];
}
}
throw new AbpException("Can not find primary key type of given entity type: " + entityType + ". Be sure that this entity type implements " + typeof(IEntity<>).AssemblyQualifiedName);
return null;
}
public static Expression<Func<TEntity, bool>> CreateEqualityExpressionForId<TEntity, TKey>(TKey id)
where TEntity : IEntity<TKey>
{
var lambdaParam = Expression.Parameter(typeof(TEntity));
var lambdaBody = Expression.Equal(
Expression.PropertyOrField(lambdaParam, "Id"),
Expression.Constant(id, typeof(TKey))
);
return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
}
}
}

9
src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/EntityNotFoundException.cs

@ -25,6 +25,15 @@ namespace Volo.Abp.Domain.Entities
}
/// <summary>
/// Creates a new <see cref="EntityNotFoundException"/> object.
/// </summary>
public EntityNotFoundException(Type entityType)
: this(entityType, null, null)
{
}
/// <summary>
/// Creates a new <see cref="EntityNotFoundException"/> object.
/// </summary>

16
src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/IAggregateRoot.cs

@ -1,13 +1,19 @@
using System;
namespace Volo.Abp.Domain.Entities
namespace Volo.Abp.Domain.Entities
{
public interface IAggregateRoot : IAggregateRoot<Guid>, IEntity
/// <summary>
/// Defines an aggregate root. It's primary key may not be "Id" or it may have a composite primary key.
/// Use <see cref="IAggregateRoot{TKey}"/> where possible for better integration to repositories and other structures in the framework.
/// </summary>
public interface IAggregateRoot : IEntity
{
}
public interface IAggregateRoot<TPrimaryKey> : IEntity<TPrimaryKey>
/// <summary>
/// Defines an aggregate root with a single primary key with "Id" property.
/// </summary>
/// <typeparam name="TKey">Type of the primary key of the entity</typeparam>
public interface IAggregateRoot<TKey> : IEntity<TKey>, IAggregateRoot
{
}

23
src/Volo.Abp.Ddd/Volo/Abp/Domain/Entities/IEntity.cs

@ -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
}
}

90
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs

@ -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;
}
}
}

114
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/IBasicRepository.cs

@ -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
}
}

39
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/IQueryableRepository.cs

@ -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);
}
}

151
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/IRepository.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -8,143 +9,39 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.Domain.Repositories
{
/// <summary>
/// Just to mark a class as repository.
/// </summary>
public interface IRepository : ITransientDependency
{
}
public interface IRepository<TEntity> : IRepository<TEntity, Guid>
where TEntity : class, IEntity<Guid>
public interface IRepository<TEntity> : IBasicRepository<TEntity>, IQueryable<TEntity>
where TEntity : class, IEntity
{
}
public interface IRepository<TEntity, TPrimaryKey> : IRepository
where TEntity : class, IEntity<TPrimaryKey>
{
/// <summary>
/// Get list of all entities without any filtering.
/// </summary>
/// <returns>List of entities</returns>
List<TEntity> GetList();
/// <summary>
/// Get list of all entities without any filtering.
/// </summary>
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>List of entities</returns>
Task<List<TEntity>> GetListAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Gets an entity with given primary key.
/// Throws <see cref="EntityNotFoundException"/> if can not find an entity with given id.
/// 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="id">Primary key of the entity to get</param>
/// <returns>Entity</returns>
[NotNull]
TEntity Get(TPrimaryKey id);
/// <param name="predicate">A condition to filter entities</param>
void Delete([NotNull] Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Gets an entity with given primary key.
/// Throws <see cref="EntityNotFoundException"/> if can not find an entity with given id.
/// 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="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(TPrimaryKey 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(TPrimaryKey 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(TPrimaryKey id, CancellationToken cancellationToken = default);
/// <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
/// <summary>
/// Deletes an entity by primary key.
/// </summary>
/// <param name="id">Primary key of the entity</param>
void Delete(TPrimaryKey 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(TPrimaryKey id, CancellationToken cancellationToken = default); //TODO: Return true if deleted
/// <summary>
/// Get list of all entities without any filtering.
/// </summary>
/// <returns>List of entities</returns>
long GetCount();
/// <param name="predicate">A condition to filter entities</param>
Task DeleteAsync([NotNull] Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
}
/// <summary>
/// Get list of all entities without any filtering.
/// </summary>
/// <param name="cancellationToken">A <see cref="T:System.Threading.CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>List of entities</returns>
Task<long> GetCountAsync(CancellationToken cancellationToken = default);
public interface IRepository<TEntity, TKey> : IRepository<TEntity>, IBasicRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
}
}
}

4
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/ISupportsExplicitLoading.cs

@ -7,8 +7,8 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.Domain.Repositories
{
public interface ISupportsExplicitLoading<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
public interface ISupportsExplicitLoading<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
Task EnsureCollectionLoadedAsync<TProperty>(
TEntity entity,

90
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/QueryableRepositoryBase.cs

@ -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;
}
}
}

122
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/RepositoryBase.cs

@ -1,83 +1,104 @@
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.Threading;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Domain.Repositories
{
public abstract class RepositoryBase<TEntity> : RepositoryBase<TEntity, Guid>, IRepository<TEntity>
where TEntity : class, IEntity<Guid>
public abstract class RepositoryBase<TEntity> : BasicRepositoryBase<TEntity>, IRepository<TEntity>
where TEntity : class, IEntity
{
public IDataFilter DataFilter { get; set; }
}
public ICurrentTenant CurrentTenant { get; set; }
public abstract class RepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
public ICancellationTokenProvider CancellationTokenProvider { get; set; }
public virtual Type ElementType => GetQueryable().ElementType;
public virtual Expression Expression => GetQueryable().Expression;
public virtual IQueryProvider Provider => GetQueryable().Provider;
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
protected RepositoryBase()
public IEnumerator<TEntity> GetEnumerator()
{
CancellationTokenProvider = NullCancellationTokenProvider.Instance;
return GetQueryable().GetEnumerator();
}
public abstract List<TEntity> GetList();
protected abstract IQueryable<TEntity> GetQueryable();
public virtual Task<List<TEntity>> GetListAsync(CancellationToken cancellationToken = default)
public virtual void Delete(Expression<Func<TEntity, bool>> predicate)
{
return Task.FromResult(GetList());
foreach (var entity in GetQueryable().Where(predicate).ToList())
{
Delete(entity);
}
}
public virtual TEntity Get(TPrimaryKey id)
public virtual Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
var entity = Find(id);
Delete(predicate);
return Task.CompletedTask;
}
if (entity == null)
//TODO: Is that needed..?
protected virtual IQueryable<TEntity> ApplyDataFilters(IQueryable<TEntity> query)
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
throw new EntityNotFoundException(typeof(TEntity), id);
query = query.WhereIf(DataFilter.IsEnabled<ISoftDelete>(), e => ((ISoftDelete)e).IsDeleted == false);
}
return entity;
if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity)))
{
var tenantId = CurrentTenant.Id;
query = query.WhereIf(DataFilter.IsEnabled<IMultiTenant>(), e => ((IMultiTenant)e).TenantId == tenantId);
}
return query;
}
}
public virtual Task<TEntity> GetAsync(TPrimaryKey id, CancellationToken cancellationToken = default)
public abstract class RepositoryBase<TEntity, TKey> : RepositoryBase<TEntity>, IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
public virtual TEntity Find(TKey id)
{
return Task.FromResult(Get(id));
return GetQueryable().FirstOrDefault(EntityHelper.CreateEqualityExpressionForId<TEntity, TKey>(id));
}
public abstract TEntity Find(TPrimaryKey id);
public virtual Task<TEntity> FindAsync(TPrimaryKey id, CancellationToken cancellationToken = default)
public virtual TEntity Get(TKey id)
{
return Task.FromResult(Find(id));
}
var entity = Find(id);
public abstract TEntity Insert(TEntity entity, bool autoSave = false);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
public virtual Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
return Task.FromResult(Insert(entity, autoSave));
return entity;
}
public abstract TEntity Update(TEntity entity);
public virtual Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
public virtual Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default)
{
return Task.FromResult(Update(entity));
return Task.FromResult(Get(id));
}
public abstract void Delete(TEntity entity);
public virtual Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
public virtual Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default)
{
Delete(entity);
return Task.CompletedTask;
return Task.FromResult(Find(id));
}
public virtual void Delete(TPrimaryKey id)
public virtual void Delete(TKey id)
{
var entity = Find(id);
if (entity == null)
@ -88,29 +109,10 @@ namespace Volo.Abp.Domain.Repositories
Delete(entity);
}
public virtual Task DeleteAsync(TPrimaryKey id, CancellationToken cancellationToken = default)
public virtual Task DeleteAsync(TKey id, CancellationToken cancellationToken = default)
{
Delete(id);
return Task.CompletedTask;
}
public abstract long GetCount();
public virtual Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(GetCount());
}
protected static Expression<Func<TEntity, bool>> CreateEqualityExpressionForId(TPrimaryKey id)
{
var lambdaParam = Expression.Parameter(typeof(TEntity));
var lambdaBody = Expression.Equal(
Expression.PropertyOrField(lambdaParam, "Id"),
Expression.Constant(id, typeof(TPrimaryKey))
);
return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
}
}
}
}

28
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs

@ -11,55 +11,55 @@ namespace Volo.Abp.Domain.Repositories
{
public static class RepositoryExtensions
{
public static async Task EnsureCollectionLoadedAsync<TEntity, TPrimaryKey, TProperty>(
this IRepository<TEntity, TPrimaryKey> repository,
public static async Task EnsureCollectionLoadedAsync<TEntity, TKey, TProperty>(
this IBasicRepository<TEntity, TKey> repository,
TEntity entity,
Expression<Func<TEntity, IEnumerable<TProperty>>> propertyExpression,
CancellationToken cancellationToken = default
)
where TEntity : class, IEntity<TPrimaryKey>
where TEntity : class, IEntity<TKey>
where TProperty : class
{
var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading<TEntity, TPrimaryKey>;
var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading<TEntity, TKey>;
if (repo != null)
{
await repo.EnsureCollectionLoadedAsync(entity, propertyExpression, cancellationToken);
}
}
public static void EnsureCollectionLoaded<TEntity, TPrimaryKey, TProperty>(
this IRepository<TEntity, TPrimaryKey> repository,
public static void EnsureCollectionLoaded<TEntity, TKey, TProperty>(
this IBasicRepository<TEntity, TKey> repository,
TEntity entity,
Expression<Func<TEntity, IEnumerable<TProperty>>> propertyExpression
)
where TEntity : class, IEntity<TPrimaryKey>
where TEntity : class, IEntity<TKey>
where TProperty : class
{
AsyncHelper.RunSync(() => repository.EnsureCollectionLoadedAsync(entity, propertyExpression));
}
public static async Task EnsurePropertyLoadedAsync<TEntity, TPrimaryKey, TProperty>(
this IRepository<TEntity, TPrimaryKey> repository,
public static async Task EnsurePropertyLoadedAsync<TEntity, TKey, TProperty>(
this IBasicRepository<TEntity, TKey> repository,
TEntity entity,
Expression<Func<TEntity, TProperty>> propertyExpression,
CancellationToken cancellationToken = default
)
where TEntity : class, IEntity<TPrimaryKey>
where TEntity : class, IEntity<TKey>
where TProperty : class
{
var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading<TEntity, TPrimaryKey>;
var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading<TEntity, TKey>;
if (repo != null)
{
await repo.EnsurePropertyLoadedAsync(entity, propertyExpression, cancellationToken);
}
}
public static void EnsurePropertyLoaded<TEntity, TPrimaryKey, TProperty>(
this IRepository<TEntity, TPrimaryKey> repository,
public static void EnsurePropertyLoaded<TEntity, TKey, TProperty>(
this IBasicRepository<TEntity, TKey> repository,
TEntity entity,
Expression<Func<TEntity, TProperty>> propertyExpression
)
where TEntity : class, IEntity<TPrimaryKey>
where TEntity : class, IEntity<TKey>
where TProperty : class
{
AsyncHelper.RunSync(() => repository.EnsurePropertyLoadedAsync(entity, propertyExpression));

35
src/Volo.Abp.Ddd/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs

@ -3,7 +3,6 @@ using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Reflection;
namespace Volo.Abp.Domain.Repositories
{
@ -45,24 +44,26 @@ namespace Volo.Abp.Domain.Repositories
protected void RegisterDefaultRepository(IServiceCollection services, Type entityType)
{
var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType);
var isDefaultPrimaryKey = primaryKeyType == typeof(Guid);
services.AddDefaultRepository(
entityType,
GetDefaultRepositoryImplementationType(entityType)
);
}
Type repositoryImplementationType;
if (Options.SpecifiedDefaultRepositoryTypes)
{
repositoryImplementationType = isDefaultPrimaryKey
? Options.DefaultRepositoryImplementationTypeWithDefaultPrimaryKey.MakeGenericType(entityType)
: Options.DefaultRepositoryImplementationType.MakeGenericType(entityType, primaryKeyType);
}
else
private Type GetDefaultRepositoryImplementationType(Type entityType)
{
var primaryKeyType = EntityHelper.FindPrimaryKeyType(entityType);
if (primaryKeyType == null)
{
repositoryImplementationType = isDefaultPrimaryKey
? GetRepositoryTypeForDefaultPk(Options.DefaultRepositoryDbContextType, entityType)
: GetRepositoryType(Options.DefaultRepositoryDbContextType, entityType, primaryKeyType);
return Options.SpecifiedDefaultRepositoryTypes
? Options.DefaultRepositoryImplementationTypeWithouTKey.MakeGenericType(entityType)
: GetRepositoryType(Options.DefaultRepositoryDbContextType, entityType);
}
services.AddDefaultRepository(entityType, repositoryImplementationType);
return Options.SpecifiedDefaultRepositoryTypes
? Options.DefaultRepositoryImplementationType.MakeGenericType(entityType, primaryKeyType)
: GetRepositoryType(Options.DefaultRepositoryDbContextType, entityType, primaryKeyType);
}
public bool ShouldRegisterDefaultRepositoryFor(Type entityType)
@ -77,7 +78,7 @@ namespace Volo.Abp.Domain.Repositories
return false;
}
if (!Options.IncludeAllEntitiesForDefaultRepositories && !ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IAggregateRoot<>)))
if (!Options.IncludeAllEntitiesForDefaultRepositories && !typeof(IAggregateRoot).IsAssignableFrom(entityType))
{
return false;
}
@ -87,7 +88,7 @@ namespace Volo.Abp.Domain.Repositories
protected abstract IEnumerable<Type> GetEntityTypes(Type dbContextType);
protected abstract Type GetRepositoryTypeForDefaultPk(Type dbContextType, Type entityType);
protected abstract Type GetRepositoryType(Type dbContextType, Type entityType);
protected abstract Type GetRepositoryType(Type dbContextType, Type entityType, Type primaryKeyType);
}

16
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EfCoreRepositoryExtensions.cs

@ -7,25 +7,25 @@ namespace Volo.Abp.Domain.Repositories
{
public static class EfCoreRepositoryExtensions
{
public static DbContext GetDbContext<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static DbContext GetDbContext<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
return repository.ToEfCoreRepository().DbContext;
}
public static DbSet<TEntity> GetDbSet<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static DbSet<TEntity> GetDbSet<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
return repository.ToEfCoreRepository().DbSet;
}
public static IEfCoreRepository<TEntity, TPrimaryKey> ToEfCoreRepository<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static IEfCoreRepository<TEntity, TKey> ToEfCoreRepository<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
var efCoreRepository = repository as IEfCoreRepository<TEntity, TPrimaryKey>;
var efCoreRepository = repository as IEfCoreRepository<TEntity, TKey>;
if (efCoreRepository == null)
{
throw new ArgumentException("Given repository does not implement " + typeof(IEfCoreRepository<TEntity, TPrimaryKey>).AssemblyQualifiedName, nameof(repository));
throw new ArgumentException("Given repository does not implement " + typeof(IEfCoreRepository<TEntity, TKey>).AssemblyQualifiedName, nameof(repository));
}
return efCoreRepository;

137
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

@ -7,30 +7,16 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Threading;
namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
{
public class EfCoreRepository<TDbContext, TEntity> : EfCoreRepository<TDbContext, TEntity, Guid>, IEfCoreRepository<TEntity>
public class EfCoreRepository<TDbContext, TEntity> : RepositoryBase<TEntity>, IEfCoreRepository<TEntity>
where TDbContext : IEfCoreDbContext
where TEntity : class, IEntity<Guid>
{
public EfCoreRepository(IDbContextProvider<TDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
public class EfCoreRepository<TDbContext, TEntity, TPrimaryKey> : QueryableRepositoryBase<TEntity, TPrimaryKey>,
IEfCoreRepository<TEntity, TPrimaryKey>,
ISupportsExplicitLoading<TEntity, TPrimaryKey>
where TDbContext : IEfCoreDbContext
where TEntity : class, IEntity<TPrimaryKey>
where TEntity : class, IEntity
{
public virtual DbSet<TEntity> DbSet => DbContext.Set<TEntity>();
DbContext IEfCoreRepository<TEntity, TPrimaryKey>.DbContext => DbContext.As<DbContext>();
DbContext IEfCoreRepository<TEntity>.DbContext => DbContext.As<DbContext>();
protected virtual TDbContext DbContext => _dbContextProvider.GetDbContext();
@ -40,39 +26,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
{
_dbContextProvider = dbContextProvider;
}
protected override IQueryable<TEntity> GetQueryable()
{
return DbSet.AsQueryable();
}
public override Task<List<TEntity>> GetListAsync(CancellationToken cancellationToken = default)
{
return DbSet.ToListAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
}
public override async Task<TEntity> GetAsync(TPrimaryKey id, CancellationToken cancellationToken = default)
{
var entity = await FindAsync(id, CancellationTokenProvider.FallbackToProvider(cancellationToken));
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public override TEntity Find(TPrimaryKey id)
{
return DbSet.Find(id);
}
public override Task<TEntity> FindAsync(TPrimaryKey id, CancellationToken cancellationToken = default)
{
return DbSet.FindAsync(new object[] { id }, CancellationTokenProvider.FallbackToProvider(cancellationToken));
}
public override TEntity Insert(TEntity entity, bool autoSave = false)
{
var savedEntity = DbSet.Add(entity).Entity;
@ -91,7 +45,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
if (autoSave)
{
await DbContext.SaveChangesAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
await DbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
}
return savedEntity;
@ -108,27 +62,27 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
DbSet.Remove(entity);
}
protected override IQueryable<TEntity> GetQueryable()
{
return DbSet.AsQueryable();
}
public override async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
var entities = await GetQueryable().Where(predicate).ToListAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
var entities = await GetQueryable().Where(predicate).ToListAsync(GetCancellationToken(cancellationToken));
foreach (var entity in entities)
{
DbSet.Remove(entity);
}
}
public override Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return GetQueryable().LongCountAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
}
public virtual Task EnsureCollectionLoadedAsync<TProperty>(
TEntity entity,
Expression<Func<TEntity, IEnumerable<TProperty>>> propertyExpression,
CancellationToken cancellationToken = default)
where TProperty : class
{
return DbContext.Entry(entity).Collection(propertyExpression).LoadAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
return DbContext.Entry(entity).Collection(propertyExpression).LoadAsync(GetCancellationToken(cancellationToken));
}
public virtual Task EnsurePropertyLoadedAsync<TProperty>(
@ -137,7 +91,72 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
CancellationToken cancellationToken = default)
where TProperty : class
{
return DbContext.Entry(entity).Reference(propertyExpression).LoadAsync(CancellationTokenProvider.FallbackToProvider(cancellationToken));
return DbContext.Entry(entity).Reference(propertyExpression).LoadAsync(GetCancellationToken(cancellationToken));
}
}
public class EfCoreRepository<TDbContext, TEntity, TKey> : EfCoreRepository<TDbContext, TEntity>,
IEfCoreRepository<TEntity, TKey>,
ISupportsExplicitLoading<TEntity, TKey>
where TDbContext : IEfCoreDbContext
where TEntity : class, IEntity<TKey>
{
public EfCoreRepository(IDbContextProvider<TDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public TEntity Get(TKey id)
{
var entity = Find(id);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public virtual async Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default)
{
var entity = await FindAsync(id, GetCancellationToken(cancellationToken));
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public virtual TEntity Find(TKey id)
{
return DbSet.Find(id);
}
public virtual Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default)
{
return DbSet.FindAsync(new object[] { id }, GetCancellationToken(cancellationToken));
}
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;
}
}
}

15
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreRepository.cs

@ -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; }
}
}

2
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DbContextHelper.cs

@ -16,7 +16,7 @@ namespace Volo.Abp.EntityFrameworkCore
from property in dbContextType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance)
where
ReflectionHelper.IsAssignableToGenericType(property.PropertyType, typeof(DbSet<>)) &&
ReflectionHelper.IsAssignableToGenericType(property.PropertyType.GenericTypeArguments[0], typeof(IEntity<>))
typeof(IEntity).IsAssignableFrom(property.PropertyType.GenericTypeArguments[0])
select property.PropertyType.GenericTypeArguments[0];
}
}

2
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/EfCoreRepositoryRegistrar.cs

@ -17,7 +17,7 @@ namespace Volo.Abp.EntityFrameworkCore.DependencyInjection
return DbContextHelper.GetEntityTypes(dbContextType);
}
protected override Type GetRepositoryTypeForDefaultPk(Type dbContextType, Type entityType)
protected override Type GetRepositoryType(Type dbContextType, Type entityType)
{
return typeof(EfCoreRepository<,>).MakeGenericType(dbContextType, entityType);
}

5
src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleDto.cs

@ -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; }
}

2
src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs

@ -3,7 +3,7 @@ using Volo.Abp.Application.Dtos;
namespace Volo.Abp.Identity
{
public class IdentityUserDto : EntityDto
public class IdentityUserDto : EntityDto<Guid>
{
public string UserName { get; set; }

6
src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityRoleAppService.cs

@ -26,10 +26,10 @@ namespace Volo.Abp.Identity
);
}
public async Task<PagedResultDto<IdentityRoleDto>> GetListAsync(GetIdentityRolesInput input)
public async Task<PagedResultDto<IdentityRoleDto>> GetListAsync(GetIdentityRolesInput input) //TODO: Remove input
{
var count = (int)await _roleRepository.GetCountAsync();
var list = await _roleRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, input.Filter);
var count = (int) await _roleRepository.GetCountAsync();
var list = await _roleRepository.GetListAsync();
return new PagedResultDto<IdentityRoleDto>(
count,

7
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -5,10 +6,12 @@ using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.Identity
{
public interface IIdentityRoleRepository : IRepository<IdentityRole>
public interface IIdentityRoleRepository : IBasicRepository<IdentityRole, Guid>
{
Task<IdentityRole> FindByNormalizedNameAsync(string normalizedRoleName, CancellationToken cancellationToken);
Task<List<IdentityRole>> GetListAsync(string sorting, int maxResultCount, int skipCount, string filter);
Task<List<IdentityRole>> GetListAsync(string sorting = null, int maxResultCount = int.MaxValue, int skipCount = 0);
Task<long> GetCountAsync(CancellationToken cancellationToken = default);
}
}

16
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs

@ -8,25 +8,27 @@ using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.Identity
{
public interface IIdentityUserRepository : IRepository<IdentityUser>
public interface IIdentityUserRepository : IBasicRepository<IdentityUser, Guid>
{
Task<IdentityUser> FindByNormalizedUserNameAsync([NotNull] string normalizedUserName, CancellationToken cancellationToken);
Task<IdentityUser> FindByNormalizedUserNameAsync([NotNull] string normalizedUserName, CancellationToken cancellationToken = default);
Task<List<string>> GetRoleNamesAsync(Guid userId);
Task<IdentityUser> FindByLoginAsync([NotNull] string loginProvider, [NotNull] string providerKey, CancellationToken cancellationToken);
Task<IdentityUser> FindByLoginAsync([NotNull] string loginProvider, [NotNull] string providerKey, CancellationToken cancellationToken = default);
Task<IdentityUser> FindByNormalizedEmailAsync([NotNull] string normalizedEmail, CancellationToken cancellationToken);
Task<IdentityUser> FindByNormalizedEmailAsync([NotNull] string normalizedEmail, CancellationToken cancellationToken = default);
//TODO: Why not return List instead of IList
Task<IList<IdentityUser>> GetListByClaimAsync(Claim claim, CancellationToken cancellationToken);
Task<IList<IdentityUser>> GetListByClaimAsync(Claim claim, CancellationToken cancellationToken = default);
//TODO: Why not return List instead of IList
Task<IList<IdentityUser>> GetListByNormalizedRoleNameAsync(string normalizedRoleName, CancellationToken cancellationToken);
Task<IList<IdentityUser>> GetListByNormalizedRoleNameAsync(string normalizedRoleName, CancellationToken cancellationToken = default);
//TODO: DTO can be used instead of parameters
Task<List<IdentityUser>> GetListAsync(string sorting, int maxResultCount, int skipCount, string filter);
Task<List<IdentityUser>> GetListAsync(string sorting, int maxResultCount, int skipCount, string filter, CancellationToken cancellationToken = default);
Task<List<IdentityRole>> GetRolesAsync(Guid userId);
Task<long> GetCountAsync(CancellationToken cancellationToken = default);
}
}

2
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRole.cs

@ -11,7 +11,7 @@ namespace Volo.Abp.Identity
/// <summary>
/// Represents a role in the identity system
/// </summary>
public class IdentityRole : AggregateRoot, IHasConcurrencyStamp
public class IdentityRole : AggregateRoot<Guid>, IHasConcurrencyStamp
{
/// <summary>
/// Gets or sets the name for this role.

2
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleClaim.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.Identity
/// <summary>
/// Represents a claim that is granted to all users within a role.
/// </summary>
public class IdentityRoleClaim : Entity
public class IdentityRoleClaim : Entity<Guid>
{
/// <summary>
/// Gets or sets the of the primary key of the role associated with this claim.

2
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs

@ -12,7 +12,7 @@ namespace Volo.Abp.Identity
{
//Add Name and Surname properties?
public class IdentityUser : AggregateRoot, IHasConcurrencyStamp
public class IdentityUser : AggregateRoot<Guid>, IHasConcurrencyStamp
{
/// <summary>
/// Gets or sets the user name for this user.

2
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserClaim.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.Identity
/// <summary>
/// Represents a claim that a user possesses.
/// </summary>
public class IdentityUserClaim : Entity
public class IdentityUserClaim : Entity<Guid>
{
/// <summary>
/// Gets or sets the primary key of the user associated with this claim.

2
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLogin.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.Identity
/// <summary>
/// Represents a login and its associated provider for a user.
/// </summary>
public class IdentityUserLogin : Entity
public class IdentityUserLogin : Entity<Guid>
{
/// <summary>
/// Gets or sets the of the primary key of the user associated with this login.

2
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRole.cs

@ -6,7 +6,7 @@ namespace Volo.Abp.Identity
/// <summary>
/// Represents the link between a user and a role.
/// </summary>
public class IdentityUserRole : Entity
public class IdentityUserRole : Entity<Guid>
{
/// <summary>
/// Gets or sets the primary key of the user that is linked to a role.

2
src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserToken.cs

@ -7,7 +7,7 @@ namespace Volo.Abp.Identity
/// <summary>
/// Represents an authentication token for a user.
/// </summary>
public class IdentityUserToken : Entity
public class IdentityUserToken : Entity<Guid>
{
/// <summary>
/// Gets or sets the primary key of the user that the token belongs to.

2
src/Volo.Abp.Identity.EntityFrameworkCore/Volo.Abp.Identity.EntityFrameworkCore.csproj.DotSettings

@ -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>

18
src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityRoleRepository.cs

@ -11,7 +11,7 @@ using System;
namespace Volo.Abp.Identity
{
public class EfCoreIdentityRoleRepository : EfCoreRepository<IdentityDbContext, IdentityRole>, IIdentityRoleRepository
public class EfCoreIdentityRoleRepository : EfCoreRepository<IdentityDbContext, IdentityRole, Guid>, IIdentityRoleRepository
{
public EfCoreIdentityRoleRepository(IDbContextProvider<IdentityDbContext> dbContextProvider)
: base(dbContextProvider)
@ -23,13 +23,17 @@ namespace Volo.Abp.Identity
return DbSet.FirstOrDefaultAsync(r => r.NormalizedName == normalizedRoleName, cancellationToken);
}
public async Task<List<IdentityRole>> GetListAsync(string sorting, int maxResultCount, int skipCount, string filter)
public async Task<List<IdentityRole>> GetListAsync(string sorting = null, int maxResultCount = int.MaxValue, int skipCount = 0)
{
return await this.WhereIf(
!filter.IsNullOrWhiteSpace(),
r => r.Name.Contains(filter)
).OrderBy(sorting ?? nameof(IdentityRole.Name))
.PageBy(skipCount, maxResultCount).ToListAsync();
return await this
.OrderBy(sorting ?? nameof(IdentityRole.Name))
.PageBy(skipCount, maxResultCount)
.ToListAsync();
}
public async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return await this.LongCountAsync(cancellationToken);
}
}
}

11
src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs

@ -12,7 +12,7 @@ using Volo.Abp.Identity.EntityFrameworkCore;
namespace Volo.Abp.Identity
{
public class EfCoreIdentityUserRepository : EfCoreRepository<IdentityDbContext, IdentityUser>, IIdentityUserRepository
public class EfCoreIdentityUserRepository : EfCoreRepository<IdentityDbContext, IdentityUser, Guid>, IIdentityUserRepository
{
public EfCoreIdentityUserRepository(IDbContextProvider<IdentityDbContext> dbContextProvider)
: base(dbContextProvider)
@ -81,7 +81,7 @@ namespace Volo.Abp.Identity
return await query.ToListAsync(cancellationToken);
}
public async Task<List<IdentityUser>> GetListAsync(string sorting, int maxResultCount, int skipCount, string filter)
public async Task<List<IdentityUser>> GetListAsync(string sorting, int maxResultCount, int skipCount, string filter, CancellationToken cancellationToken = default)
{
return await this.WhereIf(
!filter.IsNullOrWhiteSpace(),
@ -90,7 +90,7 @@ namespace Volo.Abp.Identity
u.Email.Contains(filter)
)
.OrderBy(sorting ?? nameof(IdentityUser.UserName))
.PageBy(skipCount, maxResultCount).ToListAsync();
.PageBy(skipCount, maxResultCount).ToListAsync(cancellationToken);
}
public async Task<List<IdentityUser>> GetListAsync(string sorting, int maxResultCount, int skipCount)
@ -107,5 +107,10 @@ namespace Volo.Abp.Identity
return await query.ToListAsync();
}
public async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return await this.LongCountAsync(cancellationToken);
}
}
}

4
src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs

@ -12,8 +12,8 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
services.AddAbpDbContext<IdentityDbContext>(options =>
{
options.AddDefaultRepositories();
options.AddCustomRepository<IdentityUser, EfCoreIdentityUserRepository>();
options.AddCustomRepository<IdentityRole, EfCoreIdentityRoleRepository>();
options.AddRepository<IdentityUser, EfCoreIdentityUserRepository>();
options.AddRepository<IdentityRole, EfCoreIdentityRoleRepository>();
});
services.AddAssemblyOf<AbpIdentityEntityFrameworkCoreModule>();

2
src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj.DotSettings

@ -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>

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs

@ -5,7 +5,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.ApiResources
{
public class ApiResource : AggregateRoot
public class ApiResource : AggregateRoot<Guid>
{
public virtual bool Enabled { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiScope.cs

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.ApiResources
{
public class ApiScope : Entity
public class ApiScope : Entity<Guid>
{
public virtual string Name { get; set; }

9
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs

@ -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);
}
}

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs

@ -8,7 +8,7 @@ using Volo.Abp.Guids;
namespace Volo.Abp.IdentityServer.Clients
{
public class Client : AggregateRoot
public class Client : AggregateRoot<Guid>
{
public virtual string ClientId { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientClaim.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Clients
{
public class ClientClaim : Entity
public class ClientClaim : Entity<Guid>
{
public virtual string Type { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientCorsOrigin.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Clients
{
public class ClientCorsOrigin : Entity
public class ClientCorsOrigin : Entity<Guid>
{
public virtual string Origin { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientIdPRestriction.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Clients
{
public class ClientIdPRestriction : Entity
public class ClientIdPRestriction : Entity<Guid>
{
public virtual string Provider { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUri.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Clients
{
public class ClientPostLogoutRedirectUri : Entity
public class ClientPostLogoutRedirectUri : Entity<Guid>
{
public virtual string PostLogoutRedirectUri { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientProperty.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Clients
{
public class ClientProperty : Entity
public class ClientProperty : Entity<Guid>
{
public virtual string Key { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientRedirectUri.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Clients
{
public class ClientRedirectUri : Entity
public class ClientRedirectUri : Entity<Guid>
{
public virtual string RedirectUri { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientScope.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Clients
{
public class ClientScope : Entity
public class ClientScope : Entity<Guid>
{
public virtual string Scope { get; protected set; }

5
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs

@ -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);
}

5
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs

@ -1,10 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.IdentityServer.Grants
{
public interface IPersistentGrantRepository : IRepository<PersistedGrant>
public interface IPersistentGrantRepository : IBasicRepository<PersistedGrant, Guid>
{
Task<PersistedGrant> FindByKeyAsync(string key);

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrant.cs

@ -3,7 +3,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.Grants
{
public class PersistedGrant : AggregateRoot
public class PersistedGrant : AggregateRoot<Guid>
{
public virtual string Key { get; set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs

@ -1,10 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using IdentityServer4.Stores;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.ObjectMapping;
namespace Volo.Abp.IdentityServer.Grants

5
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs

@ -1,11 +1,12 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource;
namespace Volo.Abp.IdentityServer.IdentityResources
{
public interface IIdentityResourceRepository : IRepository<IdentityResource>
public interface IIdentityResourceRepository : IBasicRepository<IdentityResource, Guid>
{
Task<List<IdentityResource>> FindIdentityResourcesByScopeAsync(string[] scopeNames);

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IdentityResource.cs

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.IdentityResources
{
public class IdentityResource : AggregateRoot
public class IdentityResource : AggregateRoot<Guid>
{
public virtual bool Enabled { get; set; } = true;

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Secret.cs

@ -6,7 +6,7 @@ namespace Volo.Abp.IdentityServer
{
//TODO: Eleminate Secret class for simplicity.
public abstract class Secret : Entity
public abstract class Secret : Entity<Guid>
{
public virtual string Description { get; protected set; }

2
src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/UserClaim.cs

@ -5,7 +5,7 @@ namespace Volo.Abp.IdentityServer
{
//TODO: Eleminate UserClaim class for simplicity.
public abstract class UserClaim : Entity
public abstract class UserClaim : Entity<Guid>
{
public virtual string Type { get; set; }

2
src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo.Abp.IdentityServer.EntityFrameworkCore.csproj.DotSettings

@ -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>

13
src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResourceRepository.cs

@ -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);
}
}
}

5
src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ClientRepository.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
@ -7,7 +8,7 @@ using Volo.Abp.IdentityServer.EntityFrameworkCore;
namespace Volo.Abp.IdentityServer
{
public class ClientRepository : EfCoreRepository<IdentityServerDbContext, Client>, IClientRepository
public class ClientRepository : EfCoreRepository<IdentityServerDbContext, Client, Guid>, IClientRepository
{
public ClientRepository(IDbContextProvider<IdentityServerDbContext> dbContextProvider) : base(dbContextProvider)
{

2
src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerModule.cs

@ -14,7 +14,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore
services.AddAbpDbContext<IdentityServerDbContext>(options =>
{
options.AddDefaultRepositories();
options.AddCustomRepository<Client, ClientRepository>();
options.AddRepository<Client, ClientRepository>();
});
services.AddAssemblyOf<AbpIdentityServerEntityFrameworkCoreModule>();

5
src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResourceRepository.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
@ -13,7 +14,7 @@ namespace Volo.Abp.IdentityServer
{
//TODO: This is not true implementation! This repository works for 2 different aggregate root!
public class IdentityResourceRepository : EfCoreRepository<IdentityServerDbContext, IdentityResource>, IIdentityResourceRepository
public class IdentityResourceRepository : EfCoreRepository<IdentityServerDbContext, IdentityResource, Guid>, IIdentityResourceRepository
{
public IdentityResourceRepository(IDbContextProvider<IdentityServerDbContext> dbContextProvider)
: base(dbContextProvider)

5
src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/PersistedGrantRepository.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
@ -9,7 +10,7 @@ using Volo.Abp.IdentityServer.Grants;
namespace Volo.Abp.IdentityServer
{
public class PersistentGrantRepository : EfCoreRepository<IdentityServerDbContext, PersistedGrant>, IPersistentGrantRepository
public class PersistentGrantRepository : EfCoreRepository<IdentityServerDbContext, PersistedGrant, Guid>, IPersistentGrantRepository
{
public PersistentGrantRepository(IDbContextProvider<IdentityServerDbContext> dbContextProvider) : base(dbContextProvider)
{

2
src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabase.cs

@ -6,6 +6,6 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
{
List<TEntity> Collection<TEntity>();
TPrimaryKey GenerateNextId<TEntity, TPrimaryKey>();
TKey GenerateNextId<TEntity, TKey>();
}
}

17
src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbRepository.cs

@ -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; }
}
}

16
src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/InMemoryIdGenerator.cs

@ -8,24 +8,24 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
private int _lastInt;
private long _lastLong;
public TPrimaryKey GenerateNext<TPrimaryKey>()
public TKey GenerateNext<TKey>()
{
if (typeof(TPrimaryKey) == typeof(Guid))
if (typeof(TKey) == typeof(Guid))
{
return (TPrimaryKey)(object)Guid.NewGuid();
return (TKey)(object)Guid.NewGuid();
}
if (typeof(TPrimaryKey) == typeof(int))
if (typeof(TKey) == typeof(int))
{
return (TPrimaryKey)(object)Interlocked.Increment(ref _lastInt);
return (TKey)(object)Interlocked.Increment(ref _lastInt);
}
if (typeof(TPrimaryKey) == typeof(long))
if (typeof(TKey) == typeof(long))
{
return (TPrimaryKey)(object)Interlocked.Increment(ref _lastLong);
return (TKey)(object)Interlocked.Increment(ref _lastLong);
}
throw new AbpException("Not supported PrimaryKey type: " + typeof(TPrimaryKey).FullName);
throw new AbpException("Not supported PrimaryKey type: " + typeof(TKey).FullName);
}
}
}

4
src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabase.cs

@ -21,11 +21,11 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
return _sets.GetOrAdd(typeof(TEntity), _ => new List<TEntity>()) as List<TEntity>;
}
public TPrimaryKey GenerateNextId<TEntity, TPrimaryKey>()
public TKey GenerateNextId<TEntity, TKey>()
{
return _idGenerators
.GetOrAdd(typeof(TEntity), () => new InMemoryIdGenerator())
.GenerateNext<TPrimaryKey>();
.GenerateNext<TKey>();
}
}
}

95
src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs

@ -1,24 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MemoryDb;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public class MemoryDbRepository<TMemoryDbContext, TEntity> : MemoryDbRepository<TMemoryDbContext, TEntity, Guid>, IMemoryDbRepository<TEntity>
public class MemoryDbRepository<TMemoryDbContext, TEntity> : RepositoryBase<TEntity>, IMemoryDbRepository<TEntity>
where TMemoryDbContext : MemoryDbContext
where TEntity : class, IEntity<Guid>
{
public MemoryDbRepository(IMemoryDatabaseProvider<TMemoryDbContext> databaseProvider)
: base(databaseProvider)
{
}
}
public class MemoryDbRepository<TMemoryDbContext, TEntity, TPrimaryKey> : QueryableRepositoryBase<TEntity, TPrimaryKey>, IMemoryDbRepository<TEntity, TPrimaryKey>
where TMemoryDbContext : MemoryDbContext
where TEntity : class, IEntity<TPrimaryKey>
where TEntity : class, IEntity
{
public virtual List<TEntity> Collection => Database.Collection<TEntity>();
@ -33,35 +25,94 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
public override TEntity Insert(TEntity entity, bool autoSave = false)
{
SetIdIfNeeded(entity);
Collection.Add(entity);
return entity;
}
public override TEntity Update(TEntity entity)
{
return entity;
}
public override void Delete(TEntity entity)
{
Collection.Remove(entity);
}
protected override IQueryable<TEntity> GetQueryable()
{
return ApplyDataFilters(Collection.AsQueryable());
}
}
public class MemoryDbRepository<TMemoryDbContext, TEntity, TKey> : MemoryDbRepository<TMemoryDbContext, TEntity>, IMemoryDbRepository<TEntity, TKey>
where TMemoryDbContext : MemoryDbContext
where TEntity : class, IEntity<TKey>
{
public MemoryDbRepository(IMemoryDatabaseProvider<TMemoryDbContext> databaseProvider)
: base(databaseProvider)
{
}
public override TEntity Insert(TEntity entity, bool autoSave = false)
{
SetIdIfNeeded(entity);
return base.Insert(entity, autoSave);
}
private void SetIdIfNeeded(TEntity entity)
{
if (typeof(TPrimaryKey) == typeof(int) || typeof(TPrimaryKey) == typeof(long) || typeof(TPrimaryKey) == typeof(Guid))
if (typeof(TKey) == typeof(int) || typeof(TKey) == typeof(long) || typeof(TKey) == typeof(Guid))
{
if (entity.IsTransient())
if (EntityHelper.IsTransient(entity))
{
entity.Id = Database.GenerateNextId<TEntity, TPrimaryKey>();
entity.Id = Database.GenerateNextId<TEntity, TKey>();
}
}
}
public override TEntity Update(TEntity entity)
public virtual TEntity Find(TKey id)
{
return GetQueryable().FirstOrDefault(EntityHelper.CreateEqualityExpressionForId<TEntity, TKey>(id));
}
public virtual TEntity Get(TKey id)
{
var entity = Find(id);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public override void Delete(TEntity entity)
public virtual Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default)
{
Collection.Remove(entity);
return Task.FromResult(Get(id));
}
protected override IQueryable<TEntity> GetQueryable()
public virtual Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default)
{
return ApplyDataFilters(Collection.AsQueryable());
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;
}
}
}

16
src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDbCoreRepositoryExtensions.cs

@ -7,25 +7,25 @@ namespace Volo.Abp.Domain.Repositories
{
public static class MemoryDbCoreRepositoryExtensions
{
public static IMemoryDatabase GetDatabase<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static IMemoryDatabase GetDatabase<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
return repository.ToMemoryDbRepository().Database;
}
public static List<TEntity> GetCollection<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static List<TEntity> GetCollection<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
return repository.ToMemoryDbRepository().Collection;
}
public static IMemoryDbRepository<TEntity, TPrimaryKey> ToMemoryDbRepository<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static IMemoryDbRepository<TEntity, TKey> ToMemoryDbRepository<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
var memoryDbRepository = repository as IMemoryDbRepository<TEntity, TPrimaryKey>;
var memoryDbRepository = repository as IMemoryDbRepository<TEntity, TKey>;
if (memoryDbRepository == null)
{
throw new ArgumentException("Given repository does not implement " + typeof(IMemoryDbRepository<TEntity, TPrimaryKey>).AssemblyQualifiedName, nameof(repository));
throw new ArgumentException("Given repository does not implement " + typeof(IMemoryDbRepository<TEntity, TKey>).AssemblyQualifiedName, nameof(repository));
}
return memoryDbRepository;

2
src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/DependencyInjection/MemoryDbRepositoryRegistrar.cs

@ -18,7 +18,7 @@ namespace Volo.Abp.MemoryDb.DependencyInjection
return memoryDbContext.GetEntityTypes();
}
protected override Type GetRepositoryTypeForDefaultPk(Type dbContextType, Type entityType)
protected override Type GetRepositoryType(Type dbContextType, Type entityType)
{
return typeof(MemoryDbRepository<,>).MakeGenericType(dbContextType, entityType);
}

19
src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs

@ -1,17 +1,10 @@
using System;
using MongoDB.Driver;
using MongoDB.Driver;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.Domain.Repositories.MongoDB
{
public interface IMongoDbRepository<TEntity> : IMongoDbRepository<TEntity, Guid>, IQueryableRepository<TEntity>
where TEntity : class, IEntity<Guid>
{
}
public interface IMongoDbRepository<TEntity, TPrimaryKey> : IQueryableRepository<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
public interface IMongoDbRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
IMongoDatabase Database { get; }
@ -19,4 +12,10 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
string CollectionName { get; }
}
public interface IMongoDbRepository<TEntity, TKey> : IMongoDbRepository<TEntity>, IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
}
}

127
src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
@ -10,19 +9,9 @@ using Volo.Abp.MongoDB;
namespace Volo.Abp.Domain.Repositories.MongoDB
{
public class MongoDbRepository<TMongoDbContext, TEntity> : MongoDbRepository<TMongoDbContext, TEntity, Guid>, IMongoDbRepository<TEntity>
public class MongoDbRepository<TMongoDbContext, TEntity> : RepositoryBase<TEntity>, IMongoDbRepository<TEntity>
where TMongoDbContext : AbpMongoDbContext
where TEntity : class, IEntity<Guid>
{
public MongoDbRepository(IMongoDatabaseProvider<TMongoDbContext> databaseProvider)
: base(databaseProvider)
{
}
}
public class MongoDbRepository<TMongoDbContext, TEntity, TPrimaryKey> : QueryableRepositoryBase<TEntity, TPrimaryKey>, IMongoDbRepository<TEntity, TPrimaryKey>
where TMongoDbContext : AbpMongoDbContext
where TEntity : class, IEntity<TPrimaryKey>
where TEntity : class, IEntity
{
public virtual string CollectionName => DatabaseProvider.DbContext.GetCollectionName<TEntity>();
@ -37,25 +26,6 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
DatabaseProvider = databaseProvider;
}
//TODO: Override other methods?
public override async Task<List<TEntity>> GetListAsync(CancellationToken cancellationToken = new CancellationToken())
{
return await (await Collection.FindAsync(Builders<TEntity>.Filter.Empty, cancellationToken: cancellationToken)).ToListAsync(cancellationToken);
}
public override async Task<TEntity> GetAsync(TPrimaryKey id, CancellationToken cancellationToken = default)
{
var entity = await FindAsync(id, cancellationToken);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public override TEntity Insert(TEntity entity, bool autoSave = false)
{
Collection.InsertOne(entity);
@ -70,60 +40,109 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
public override TEntity Update(TEntity entity)
{
var filter = Builders<TEntity>.Filter.Eq(e => e.Id, entity.Id);
Collection.ReplaceOne(filter, entity);
Collection.ReplaceOne(CreateEntityFilter(entity), entity);
return entity;
}
public override async Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
{
var filter = Builders<TEntity>.Filter.Eq(e => e.Id, entity.Id);
await Collection.ReplaceOneAsync(filter, entity, cancellationToken: cancellationToken);
await Collection.ReplaceOneAsync(CreateEntityFilter(entity), entity, cancellationToken: cancellationToken);
return entity;
}
public override void Delete(TEntity entity)
{
Delete(entity.Id);
Collection.DeleteOne(CreateEntityFilter(entity));
}
public override void Delete(TPrimaryKey id)
public override async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
{
var filter = Builders<TEntity>.Filter.Eq(e => e.Id, id);
Collection.DeleteOne(filter);
await Collection.DeleteOneAsync(CreateEntityFilter(entity), cancellationToken);
}
public override void Delete(Expression<Func<TEntity, bool>> predicate)
{
var filter = Builders<TEntity>.Filter.Where(predicate);
Collection.DeleteOne(filter);
Collection.DeleteMany(Builders<TEntity>.Filter.Where(predicate));
}
public override Task DeleteAsync(TPrimaryKey id, CancellationToken cancellationToken = default)
public override async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
var filter = Builders<TEntity>.Filter.Eq(e => e.Id, id);
return Collection.DeleteOneAsync(filter, cancellationToken: cancellationToken);
await Collection.DeleteManyAsync(Builders<TEntity>.Filter.Where(predicate), cancellationToken);
}
public override Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
protected override IQueryable<TEntity> GetQueryable()
{
return DeleteAsync(entity.Id, cancellationToken);
return Collection.AsQueryable();
}
public override Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
protected virtual FilterDefinition<TEntity> CreateEntityFilter(TEntity entity)
{
var filter = Builders<TEntity>.Filter.Where(predicate);
return Collection.DeleteOneAsync(filter, cancellationToken: cancellationToken);
throw new NotImplementedException("CreateEntityFilter is not implemented for MongoDb by default. It should be overrided and implemented by deriving classes!");
}
}
protected override IQueryable<TEntity> GetQueryable()
public class MongoDbRepository<TMongoDbContext, TEntity, TKey> : MongoDbRepository<TMongoDbContext, TEntity>, IMongoDbRepository<TEntity, TKey>
where TMongoDbContext : AbpMongoDbContext
where TEntity : class, IEntity<TKey>
{
public MongoDbRepository(IMongoDatabaseProvider<TMongoDbContext> databaseProvider)
: base(databaseProvider)
{
return Collection.AsQueryable();
}
public virtual TEntity Get(TKey id)
{
var entity = Find(id);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public virtual async Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default)
{
var entity = await FindAsync(id, cancellationToken);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public virtual void Delete(TKey id)
{
Collection.DeleteOne(CreateEntityFilter(id));
}
public virtual Task DeleteAsync(TKey id, CancellationToken cancellationToken = default)
{
return Collection.DeleteOneAsync(CreateEntityFilter(id), cancellationToken);
}
public virtual async Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default)
{
return await Collection.Find(CreateEntityFilter(id)).FirstOrDefaultAsync(cancellationToken);
}
public virtual TEntity Find(TKey id)
{
return Collection.Find(CreateEntityFilter(id)).FirstOrDefault();
}
protected override FilterDefinition<TEntity> CreateEntityFilter(TEntity entity)
{
return Builders<TEntity>.Filter.Eq(e => e.Id, entity.Id);
}
public override Task<long> GetCountAsync(CancellationToken cancellationToken = default)
private static FilterDefinition<TEntity> CreateEntityFilter(TKey id)
{
return Collection.CountAsync(Builders<TEntity>.Filter.Empty, cancellationToken: cancellationToken);
return Builders<TEntity>.Filter.Eq(e => e.Id, id);
}
}
}

20
src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs

@ -7,31 +7,31 @@ namespace Volo.Abp.Domain.Repositories
{
public static class MongoDbCoreRepositoryExtensions
{
public static IMongoDatabase GetDatabase<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static IMongoDatabase GetDatabase<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
return repository.ToMongoDbRepository().Database;
}
public static IMongoCollection<TEntity> GetCollection<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static IMongoCollection<TEntity> GetCollection<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
return repository.ToMongoDbRepository().Collection;
}
public static string GetCollectionName<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static string GetCollectionName<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
return repository.ToMongoDbRepository().CollectionName;
}
public static IMongoDbRepository<TEntity, TPrimaryKey> ToMongoDbRepository<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
public static IMongoDbRepository<TEntity, TKey> ToMongoDbRepository<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository)
where TEntity : class, IEntity<TKey>
{
var mongoDbRepository = repository as IMongoDbRepository<TEntity, TPrimaryKey>;
var mongoDbRepository = repository as IMongoDbRepository<TEntity, TKey>;
if (mongoDbRepository == null)
{
throw new ArgumentException("Given repository does not implement " + typeof(IMongoDbRepository<TEntity, TPrimaryKey>).AssemblyQualifiedName, nameof(repository));
throw new ArgumentException("Given repository does not implement " + typeof(IMongoDbRepository<TEntity, TKey>).AssemblyQualifiedName, nameof(repository));
}
return mongoDbRepository;

1
src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/IMongoDbContextRegistrationOptionsBuilder.cs

@ -1,4 +1,3 @@
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MongoDB.DependencyInjection

4
src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/MongoDbRepositoryRegistrar.cs

@ -19,9 +19,9 @@ namespace Volo.Abp.MongoDB.DependencyInjection
return mongoDbContext.GetMappings().Select(m => m.EntityType);
}
protected override Type GetRepositoryTypeForDefaultPk(Type dbContextType, Type entityType)
protected override Type GetRepositoryType(Type dbContextType, Type entityType)
{
return typeof(MongoDbRepository<,>).MakeGenericType(dbContextType, entityType);
return typeof(MongoDbRepository<,,>).MakeGenericType(dbContextType, entityType);
}
protected override Type GetRepositoryType(Type dbContextType, Type entityType, Type primaryKeyType)

2
src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/ITenantRepository.cs

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.MultiTenancy
{
public interface ITenantRepository : IRepository<Tenant>
public interface ITenantRepository : IBasicRepository<Tenant, Guid>
{
Task<Tenant> FindByNameIncludeDetailsAsync(string name);

2
src/Volo.Abp.MultiTenancy.Domain/Volo/Abp/MultiTenancy/Tenant.cs

@ -6,7 +6,7 @@ using Volo.Abp.Domain.Entities;
namespace Volo.Abp.MultiTenancy
{
public class Tenant : AggregateRoot
public class Tenant : AggregateRoot<Guid>
{
public virtual string Name { get; protected set; }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save