diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index cf44a739b6..f838b53cf8 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -94,9 +94,7 @@ services.AddAbpDbContext(options => }); ```` -Then you can inject and use `IRepository` or `IQueryableRepository` in your services. - -Assume that you have a `Book` entity with `Guid` primary key: +Then you can inject and use `IRepository` in your services. Assume that you have a `Book` entity with `Guid` primary key: ```csharp public class Book : AggregateRoot @@ -107,7 +105,7 @@ public class Book : AggregateRoot } ``` -And you want to create a new `Book` entity in a [domain service](Domain-Services.md): +(`BookType` is a simple enum here) And you want to create a new `Book` entity in a [domain service](Domain-Services.md): ````csharp public class BookManager : DomainService @@ -185,7 +183,7 @@ If you want to replace default repository implementation with your custom reposi context.Services.AddAbpDbContext(options => { options.AddDefaultRepositories(); - options.AddRepository(); + options.AddRepository(); //Replaces IRepository }); ```` @@ -238,7 +236,7 @@ First, define your repository classes like that: ```csharp public class MyRepositoryBase : EfCoreRepository - where TEntity : class, IEntity + where TEntity : class, IEntity { public MyRepositoryBase(IDbContextProvider dbContextProvider) : base(dbContextProvider) @@ -248,7 +246,7 @@ public class MyRepositoryBase public class MyRepositoryBase : EfCoreRepository - where TEntity : class, IEntity + where TEntity : class, IEntity { public MyRepositoryBase(IDbContextProvider dbContextProvider) : base(dbContextProvider) diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index 591c4d5834..c03c6f4f2c 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -77,7 +77,7 @@ namespace MyCompany.MyProject #### Add Default Repositories -ABP can automatically create [repositories](Repositories.md) for the entities in your Db Context. Just use `AddDefaultRepositories()` option on registration: +ABP can automatically create default [generic repositories](Repositories.md) for the entities in your DbContext. Just use `AddDefaultRepositories()` option on the registration: ````C# services.AddMongoDbContext(options => @@ -86,7 +86,7 @@ services.AddMongoDbContext(options => }); ```` -This will create a repository for each aggreate root entity (classes derived from AggregateRoot) by default. If you want to create repositories for other entities too, then set `includeAllEntities` to `true`: +This will create a repository for each [aggregate root entity](Entities.md) (classes derived from `AggregateRoot`) by default. If you want to create repositories for other entities too, then set `includeAllEntities` to `true`: ```c# services.AddMongoDbContext(options => @@ -95,5 +95,234 @@ services.AddMongoDbContext(options => }); ``` -Then you can inject and use `IRepository` or `IQueryableRepository` in your services. +Then you can inject and use `IRepository` in your services. Assume that you have a `Book` entity with `Guid` primary key: +```csharp +public class Book : AggregateRoot +{ + public string Name { get; set; } + + public BookType Type { get; set; } +} +``` + +(`BookType` is a simple enum here) And you want to create a new `Book` entity in a [domain service](Domain-Services.md): + +```csharp +public class BookManager : DomainService +{ + private readonly IRepository _bookRepository; + + public BookManager(IRepository bookRepository) //inject default repository + { + _bookRepository = bookRepository; + } + + public async Task CreateBook(string name, BookType type) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + var book = new Book + { + Id = GuidGenerator.Create(), + Name = name, + Type = type + }; + + await _bookRepository.InsertAsync(book); //Use a standard repository method + + return book; + } +} +``` + +This sample uses `InsertAsync` method to insert a new entity to the database. + +#### Add Custom Repositories + +Default generic repositories are powerful enough in most cases (since they implement `IQueryable`). However, you may need to create a custom repository to add your own repository methods. + +Assume that you want to delete all books by type. It's suggested to define an interface for your custom repository: + +```csharp +public interface IBookRepository : IRepository +{ + Task DeleteBooksByType( + BookType type, + CancellationToken cancellationToken = default(CancellationToken) + ); +} +``` + +You generally want to derive from the `IRepository` to inherit standard repository methods. However, you don't have to. Repository interfaces are defined in the domain layer of a layered application. They are implemented in the data/infrastructure layer (`MongoDB` project in a [startup template](https://abp.io/Templates)). + +Example implementation of the `IBookRepository` interface: + +```csharp +public class BookRepository : + MongoDbRepository, + IBookRepository +{ + public BookRepository(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async Task DeleteBooksByType( + BookType type, + CancellationToken cancellationToken = default(CancellationToken)) + { + await Collection.DeleteManyAsync( + Builders.Filter.Eq(b => b.Type, type), + cancellationToken + ); + } +} +``` + +Now, it's possible to [inject](Dependency-Injection.md) the `IBookRepository` and use the `DeleteBooksByType` method when needed. + +##### Override Default Generic Repository + +Even if you create a custom repository, you can still inject the default generic repository (`IRepository` for this example). Default repository implementation will not use the class you have created. + +If you want to replace default repository implementation with your custom repository, do it inside `AddMongoDbContext` options: + +```csharp +context.Services.AddMongoDbContext(options => +{ + options.AddDefaultRepositories(); + options.AddRepository(); //Replaces IRepository +}); +``` + +This is especially important when you want to **override a base repository method** to customize it. For instance, you may want to override `DeleteAsync` method to delete an entity in a more efficient way: + +```csharp +public override async Task DeleteAsync( + Guid id, + bool autoSave = false, + CancellationToken cancellationToken = default) +{ + //TODO: Custom implementation of the delete method +} +``` + +#### Access to the MongoDB API + +In most cases, you want to hide MongoDB APIs behind a repository (this is the main purpose of the repository). However, if you want to access the MongoDB API over the repository, you can use `GetDatabase()` or `GetCollection()` extension methods. Example: + +```csharp +public class BookService +{ + private readonly IRepository _bookRepository; + + public BookService(IRepository bookRepository) + { + _bookRepository = bookRepository; + } + + public void Foo() + { + IMongoDatabase database = _bookRepository.GetDatabase(); + IMongoCollection books = _bookRepository.GetCollection(); + } +} +``` + +> Important: You must reference to the `Volo.Abp.MongoDB` package from the project you want to access to the MongoDB API. This breaks encapsulation, but this is what you want in that case. + +#### Advanced Topics + +##### Set Default Repository Classes + +Default generic repositories are implemented by `MongoDbRepository` class by default. You can create your own implementation and use it for default repository implementation. + +First, define your repository classes like that: + +```csharp +public class MyRepositoryBase + : MongoDbRepository + where TEntity : class, IEntity +{ + public MyRepositoryBase(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } +} + +public class MyRepositoryBase + : MongoDbRepository + where TEntity : class, IEntity +{ + public MyRepositoryBase(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } +} +``` + +First one is for [entities with composite keys](Entities.md), second one is for entities with single primary key. + +It's suggested to inherit from the `MongoDbRepository` class and override methods if needed. Otherwise, you will have to implement all standard repository methods manually. + +Now, you can use `SetDefaultRepositoryClasses` option: + +```csharp +context.Services.AddMongoDbContext(options => +{ + options.SetDefaultRepositoryClasses( + typeof(MyRepositoryBase<,>), + typeof(MyRepositoryBase<>) + ); + //... +}); +``` + +##### Set Base MongoDbContext Class or Interface for Default Repositories + +If your MongoDbContext inherits from another MongoDbContext or implements an interface, you can use that base class or interface as the MongoDbContext for default repositories. Example: + +```csharp +public interface IBookStoreMongoDbContext : IAbpMongoDbContext +{ + Collection Books { get; } +} +``` + +`IBookStoreMongoDbContext` is implemented by the `BookStoreMongoDbContext` class. Then you can use generic overload of the `AddDefaultRepositories`: + +```csharp +context.Services.AddMongoDbContext(options => +{ + options.AddDefaultRepositories(); + //... +}); +``` + +Now, your custom `BookRepository` can also use the `IBookStoreMongoDbContext` interface: + +```csharp +public class BookRepository + : MongoDbRepository, + IBookRepository +{ + //... +} +``` + +One advantage of using interface for a MongoDbContext is then it becomes replaceable by another implementation. + +##### Replace Other DbContextes + +Once you properly define and use an interface for a MongoDbContext , then any other implementation can replace it using the `ReplaceDbContext` option: + +```csharp +context.Services.AddMongoDbContext(options => +{ + //... + options.ReplaceDbContext(); +}); +``` + +In this example, `OtherMongoDbContext` implements `IBookStoreMongoDbContext`. This feature allows you to have multiple MongoDbContext (one per module) on development, but single MongoDbContext (implements all interfaces of all MongoDbContexts) on runtime. \ No newline at end of file