From 0eeffb405f6150e4729b7d01d61638cffc437fa1 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 20 Nov 2020 12:42:03 +0300 Subject: [PATCH 1/8] feat: create SUPPRESS_UNSAVED_CHANGES_WARNING token #4444 --- npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts | 1 + .../src/lib/tokens/suppress-unsaved-changes-warning.token.ts | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts index a923c8fc23..18a2aac2f6 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/index.ts @@ -1,3 +1,4 @@ export * from './append-content.token'; export * from './http-error.token'; export * from './lazy-styles.token'; +export * from './suppress-unsaved-changes-warning.token'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts new file mode 100644 index 0000000000..b76a18706c --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts @@ -0,0 +1,5 @@ +import { InjectionToken } from '@angular/core'; + +export const SUPPRESS_UNSAVED_CHANGES_WARNING = new InjectionToken( + 'SUPPRESS_UNSAVED_CHANGES_WARNING', +); From b5f8cae0b87066e75015cff5643d84c670021a2f Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 20 Nov 2020 12:42:18 +0300 Subject: [PATCH 2/8] feat: add suppressUnsavedChangesWarning input to modal component #4444 --- .../lib/components/modal/modal.component.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index b16b2bb14c..c476bd8dd3 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -4,8 +4,11 @@ import { ContentChild, ElementRef, EventEmitter, + Inject, Input, OnDestroy, + OnInit, + Optional, Output, Renderer2, TemplateRef, @@ -18,6 +21,7 @@ import { fadeAnimation } from '../../animations/modal.animations'; import { Confirmation } from '../../models/confirmation'; import { ConfirmationService } from '../../services/confirmation.service'; import { ModalService } from '../../services/modal.service'; +import { SUPPRESS_UNSAVED_CHANGES_WARNING } from '../../tokens/suppress-unsaved-changes-warning.token'; import { ButtonComponent } from '../button/button.component'; export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; @@ -29,7 +33,7 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; styleUrls: ['./modal.component.scss'], providers: [ModalService, SubscriptionService], }) -export class ModalComponent implements OnDestroy { +export class ModalComponent implements OnDestroy, OnInit { @Input() get visible(): boolean { return this._visible; @@ -57,6 +61,8 @@ export class ModalComponent implements OnDestroy { @Input() size: ModalSize = 'lg'; + @Input() suppressUnsavedChangesWarning: boolean; + @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) abpSubmit: ButtonComponent; @@ -104,6 +110,9 @@ export class ModalComponent implements OnDestroy { private confirmationService: ConfirmationService, private modalService: ModalService, private subscription: SubscriptionService, + @Optional() + @Inject(SUPPRESS_UNSAVED_CHANGES_WARNING) + private suppressUnsavedChangesWarningToken: boolean, ) { this.initToggleStream(); } @@ -132,6 +141,12 @@ export class ModalComponent implements OnDestroy { } } + ngOnInit() { + if (typeof this.suppressUnsavedChangesWarning === 'undefined') { + this.suppressUnsavedChangesWarning = this.suppressUnsavedChangesWarningToken; + } + } + ngOnDestroy(): void { this.destroy$.next(); } @@ -139,7 +154,7 @@ export class ModalComponent implements OnDestroy { close() { if (this.busy) return; - if (this.isFormDirty) { + if (this.isFormDirty && !this.suppressUnsavedChangesWarning) { if (this.isConfirmationOpen) return; this.isConfirmationOpen = true; From 55ed8732ecdc2b1a6d61083c6452092b3023d419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 20 Nov 2020 13:09:56 +0300 Subject: [PATCH 3/8] Added "Loading related entities" section to the EF Core document. --- docs/en/Entity-Framework-Core.md | 211 ++++++++++++++++++++++++++++++- 1 file changed, 207 insertions(+), 4 deletions(-) diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index e536cfe5cb..d0cf3a26be 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -174,7 +174,7 @@ public class Book : AggregateRoot } ``` -(`BookType` is a simple enum here) And you want to create a new `Book` entity in a [domain service](Domain-Services.md): +(`BookType` is a simple `enum` here and not important) And you want to create a new `Book` entity in a [domain service](Domain-Services.md): ````csharp public class BookManager : DomainService @@ -221,12 +221,13 @@ public interface IBookRepository : IRepository } ```` -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 (`EntityFrameworkCore` project in a [startup template](https://abp.io/Templates)). +You generally want to derive from the `IRepository` to inherit standard repository methods (while, you don't have to do). Repository interfaces are defined in the domain layer of a layered application. They are implemented in the data/infrastructure layer (`EntityFrameworkCore` project in a [startup template](https://abp.io/Templates)). Example implementation of the `IBookRepository` interface: ````csharp -public class BookRepository : EfCoreRepository, IBookRepository +public class BookRepository + : EfCoreRepository, IBookRepository { public BookRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) @@ -272,6 +273,208 @@ public async override Task DeleteAsync( } ```` +## Loading Related Entities + +Assume that you've an `Order` with a collection of `OrderLine`s and the `OrderLine` has a navigation property to the `Order`: + +````csharp +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; + +namespace MyCrm +{ + public class Order : AggregateRoot, IHasCreationTime + { + public Guid CustomerId { get; set; } + public DateTime CreationTime { get; set; } + + public ICollection Lines { get; set; } //Sub collection + + public Order() + { + Lines = new Collection(); + } + } + + public class OrderLine : Entity + { + public Order Order { get; set; } //Navigation property + public Guid OrderId { get; set; } + + public Guid ProductId { get; set; } + public int Count { get; set; } + public double UnitPrice { get; set; } + } +} + +```` + +And defined the database mapping as shown below: + +````csharp +builder.Entity(b => +{ + b.ToTable("Orders"); + b.ConfigureByConvention(); + + //Define the relation + b.HasMany(x => x.Lines) + .WithOne(x => x.Order) + .HasForeignKey(x => x.OrderId) + .IsRequired(); +}); + +builder.Entity(b => +{ + b.ToTable("OrderLines"); + b.ConfigureByConvention(); +}); +```` + +When you query an `Order`, you may want to **include** all the `OrderLine`s in a single query or you may want to **load them later** on demand. + +> Actually these are not directly related to the ABP Framework. You can follow the EF Core documentation to learn all the details. This section will cover some topics related to the ABP Framework. + +### Load With Details + +You have different options when you want to load the related entities while querying an entity. + +#### Repository.WithDetails + +`IRepository.WithDetails(...)` can be used to include one relation collection/property to the query. + +**Example: Get an order with lines** + +````csharp +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Domain.Services; + +namespace MyCrm +{ + public class OrderManager : DomainService + { + private readonly IRepository _orderRepository; + + public OrderManager(IRepository orderRepository) + { + _orderRepository = orderRepository; + } + + public async Task TestWithDetails(Guid id) + { + var query = _orderRepository + .WithDetails(x => x.Lines) + .Where(x => x.Id == id); + + var order = await AsyncExecuter.FirstOrDefaultAsync(query); + } + } +} +```` + +> `AsyncExecuter` is used to execute async LINQ extensions without depending on the EF Core. If you add EF Core NuGet package reference to your project, then you can directly use `await _orderRepository.WithDetails(x => x.Lines).FirstOrDefaultAsync()`. But, this time you depend on the EF Core in your domain layer. See the [repository document](Repositories.md) to learn more. + +**Example: Get a list of orders with their lines** + +````csharp +public async Task TestWithDetails() +{ + var query = _orderRepository + .WithDetails(x => x.Lines); + + var orders = await AsyncExecuter.ToListAsync(query); +} +```` + +> `WithDetails` method can get more than one expression parameter if you need to include more than one navigation property or collection. + +#### DefaultWithDetailsFunc + +If you don't pass any expression to the `WithDetails` method, then it includes all the details using the `DefaultWithDetailsFunc` option you provide. + +You can configure `DefaultWithDetailsFunc` for an entity in the `ConfigureServices` method of your [module](Module-Development-Basics.md) in your `EntityFrameworkCore` project. + +**Example: Include `Lines` while querying an `Order`** + +````csharp +Configure(options => +{ + options.Entity(orderOptions => + { + orderOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Lines); + }); +}); +```` + +> You can fully use the EF Core API here since this is located in the EF Core integration project. + +Then you can use the `WithDetails` without any parameter: + +````csharp +public async Task TestWithDetails() +{ + var query = _orderRepository.WithDetails(); + var orders = await AsyncExecuter.ToListAsync(query); +} +```` + +`WithDetails()` executes the expression you've setup as the `DefaultWithDetailsFunc`. + +#### Repository Get/Find Methods + +Some of the standard [Repository](Repositories.md) methods have optional `includeDetails` parameters; + +* `GetAsync` and `FindAsync` gets `includeDetails` with default value is `true`. +* `GetListAsync` and `GetPagedListAsync` gets `includeDetails` with default value is `false`. + +That means, the methods return a **single entity includes details** by default while list returning methods don't include details by default. You can explicitly pass `includeDetails` to change the behavior. + +> These methods use the `DefaultWithDetailsFunc` option that is explained above. + +**Example: Get an order with details** + +````csharp +public async Task TestWithDetails(Guid id) +{ + var order = await _orderRepository.GetAsync(id); +} +```` + +**Example: Get an order without details** + +````csharp +public async Task TestWithoutDetails(Guid id) +{ + var order = await _orderRepository.GetAsync(id, includeDetails: false); +} +```` + +**Example: Get list of entities with details** + +````csharp +public async Task TestWithDetails(Guid id) +{ + var orders = await _orderRepository.GetListAsync(includeDetails: true); +} +```` + +#### Alternatives + +The repository patters tries to encapsulate the EF Core, so your options are limited. If you need an advanced scenario, you can follow one of the options; + +* Create a custom repository method and use the complete EF Core API. +* Reference to the `Volo.Abp.EntityFrameworkCore` package from your project. In this way, you can directly use `Include` and `ThenInclude` in your code. + +### Lazy Loading + +TODO + ## Access to the EF Core API In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository pattern). However, if you want to access the `DbContext` instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example: @@ -296,7 +499,7 @@ public class BookService * `GetDbContext` returns a `DbContext` reference instead of `BookStoreDbContext`. You can cast it, however in most cases you don't need it. -> Important: You must reference to the `Volo.Abp.EntityFrameworkCore` package from the project you want to access to the DbContext. This breaks encapsulation, but this is what you want in that case. +> Important: You must reference to the `Volo.Abp.EntityFrameworkCore` package from the project you want to access to the `DbContext`. This breaks encapsulation, but this is what you want in that case. ## Extra Properties & Object Extension Manager From 82c87b1ed961d0cf615eaafb3eb49014a142cbf6 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 20 Nov 2020 13:48:29 +0300 Subject: [PATCH 4/8] refactor: improve code quality --- .../src/lib/components/modal/modal.component.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index c476bd8dd3..b1fbef79be 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -7,7 +7,6 @@ import { Inject, Input, OnDestroy, - OnInit, Optional, Output, Renderer2, @@ -33,7 +32,7 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; styleUrls: ['./modal.component.scss'], providers: [ModalService, SubscriptionService], }) -export class ModalComponent implements OnDestroy, OnInit { +export class ModalComponent implements OnDestroy { @Input() get visible(): boolean { return this._visible; @@ -61,7 +60,7 @@ export class ModalComponent implements OnDestroy, OnInit { @Input() size: ModalSize = 'lg'; - @Input() suppressUnsavedChangesWarning: boolean; + @Input() suppressUnsavedChangesWarning = this.suppressUnsavedChangesWarningToken; @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) abpSubmit: ButtonComponent; @@ -141,12 +140,6 @@ export class ModalComponent implements OnDestroy, OnInit { } } - ngOnInit() { - if (typeof this.suppressUnsavedChangesWarning === 'undefined') { - this.suppressUnsavedChangesWarning = this.suppressUnsavedChangesWarningToken; - } - } - ngOnDestroy(): void { this.destroy$.next(); } From 161b989abcbae90e11af0a37dd40abe315e20809 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 20 Nov 2020 13:48:39 +0300 Subject: [PATCH 5/8] chore: add a todo comment --- .../src/lib/tokens/suppress-unsaved-changes-warning.token.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts index b76a18706c..af68c8130c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/suppress-unsaved-changes-warning.token.ts @@ -1,5 +1,6 @@ import { InjectionToken } from '@angular/core'; +// TODO: Should be documented export const SUPPRESS_UNSAVED_CHANGES_WARNING = new InjectionToken( 'SUPPRESS_UNSAVED_CHANGES_WARNING', ); From 7331a8d22c2e06380803a70f1ba11060f67b4927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 20 Nov 2020 14:34:40 +0300 Subject: [PATCH 6/8] Complete the EF Core eagar/lazy loading document. --- docs/en/Entity-Framework-Core.md | 87 ++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index d0cf3a26be..4585831633 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -336,9 +336,9 @@ builder.Entity(b => When you query an `Order`, you may want to **include** all the `OrderLine`s in a single query or you may want to **load them later** on demand. -> Actually these are not directly related to the ABP Framework. You can follow the EF Core documentation to learn all the details. This section will cover some topics related to the ABP Framework. +> Actually these are not directly related to the ABP Framework. You can follow the [EF Core documentation](https://docs.microsoft.com/en-us/ef/core/querying/related-data/) to learn all the details. This section will cover some topics related to the ABP Framework. -### Load With Details +### Eager Loading / Load With Details You have different options when you want to load the related entities while querying an entity. @@ -471,9 +471,79 @@ The repository patters tries to encapsulate the EF Core, so your options are lim * Create a custom repository method and use the complete EF Core API. * Reference to the `Volo.Abp.EntityFrameworkCore` package from your project. In this way, you can directly use `Include` and `ThenInclude` in your code. -### Lazy Loading +See also [eager loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/eager) of the EF Core. -TODO +### Explicit / Lazy Loading + +If you don't include relations while querying an entity and later need to access to a navigation property or collection, you have different options. + +#### EnsurePropertyLoadedAsync / EnsureCollectionLoadedAsync + +Repositories provide `EnsurePropertyLoadedAsync` and `EnsureCollectionLoadedAsync` extension methods to **explicitly load** a navigation property or sub collection. + +**Example: Load Lines of an Order when needed** + +````csharp +public async Task TestWithDetails(Guid id) +{ + var order = await _orderRepository.GetAsync(id, includeDetails: false); + //order.Lines is empty on this stage + + await _orderRepository.EnsureCollectionLoadedAsync(order, x => x.Lines); + //order.Lines is filled now +} +```` + +`EnsurePropertyLoadedAsync` and `EnsureCollectionLoadedAsync` methods do nothing if the property or collection was already loaded. So, calling multiple times has no problem. + +See also [explicit loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/explicit) of the EF Core. + +#### Lazy Loading with Proxies + +Explicit loading may not be possible in some cases, especially when you don't have a reference to the `Repository` or `DbContext`. Lazy Loading is a feature of the EF Core that loads the related properties / collections when you first access to it. + +To enable lazy loading; + +1. Install the [Microsoft.EntityFrameworkCore.Proxies](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Proxies/) package into your project (typically to the EF Core integration project) +2. Configure `UseLazyLoadingProxies` for your `DbContext` (in the `ConfigureServices` method of your module in your EF Core project). Example: + +````csharp +Configure(options => +{ + options.PreConfigure(opts => + { + opts.DbContextOptions.UseLazyLoadingProxies(); //Enable lazy loading + }); + + options.UseSqlServer(); +}); +```` + +3. Make your navigation properties and collections `virtual`. Examples: + +````csharp +public virtual ICollection Lines { get; set; } //virtual collection +public virtual Order Order { get; set; } //virtual navigation property +```` + +Once you enable lazy loading and arrange your entities, you can freely access to the navigation properties and collections: + +````csharp +public async Task TestWithDetails(Guid id) +{ + var order = await _orderRepository.GetAsync(id); + //order.Lines is empty on this stage + + var lines = order.Lines; + //order.Lines is filled (lazy loaded) +} +```` + +Whenever you access to a property/collection, EF Core automatically performs an additional query to load the property/collection from the database. + +> Lazy loading should be carefully used since it may cause performance problems in some specific cases. + +See also [lazy loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/lazy) of the EF Core. ## Access to the EF Core API @@ -649,23 +719,22 @@ context.Services.AddAbpDbContext(options => In this example, `OtherDbContext` implements `IBookStoreDbContext`. This feature allows you to have multiple DbContext (one per module) on development, but single DbContext (implements all interfaces of all DbContexts) on runtime. -### Enabling split queries globally by default +### Split Queries -Abp enables split queries globally by default for better performance. You can also change it as needed, such as: +ABP enables [split queries](https://docs.microsoft.com/en-us/ef/core/querying/single-split-queries) globally by default for better performance. You can change it as needed. + +**Example** ````csharp Configure(options => { options.UseSqlServer(optionsBuilder => { - //Change QuerySplittingBehavior optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery); }); }); ```` -For more information, please refer to https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.querysplittingbehavior?view=efcore-5.0 - ## See Also * [Entities](Entities.md) From da551d72797a389501284740d4ace042d8a83626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 20 Nov 2020 14:44:17 +0300 Subject: [PATCH 7/8] Document the HardDeleteAsync method. --- docs/en/Data-Filtering.md | 2 ++ docs/en/Repositories.md | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/docs/en/Data-Filtering.md b/docs/en/Data-Filtering.md index 626719b2fc..6df738cf18 100644 --- a/docs/en/Data-Filtering.md +++ b/docs/en/Data-Filtering.md @@ -32,6 +32,8 @@ namespace Acme.BookStore > `ISoftDelete` filter is enabled by default and you can not get deleted entities from database unless you explicitly disable it. See the `IDataFilter` service below. +> Soft-delete entities can be hard-deleted when you use `HardDeleteAsync` method on the repositories. + ### IMultiTenant [Multi-tenancy](Multi-Tenancy.md) is an efficient way of creating SaaS applications. Once you create a multi-tenant application, you typically want to isolate data between tenants. Implement `IMultiTenant` interface to make your entity "multi-tenant aware". diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index 0fc80984e7..5ff8372af8 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -79,6 +79,14 @@ If your entity does not have an Id primary key (it may have a composite primary > `IRepository` 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` features to query entities by standard LINQ methods. +### Soft / Hard Delete + +`DeleteAsync` method of the repository doesn't delete the entity if the entity is a **soft-delete** entity (that implements `ISoftDelete`). Soft-delete entities are marked as "deleted" in the database. Data Filter system ensures that the soft deleted entities are not retrieved from database normally. + +If your entity is a soft-delete entity, you can use the `HardDeleteAsync` method to really delete the entity from database in case of you need it. + +See the [Data Filtering](Data-Filtering.md) documentation for more about soft-delete. + ## Custom Repositories Default generic repositories will be sufficient for most cases. However, you may need to create a custom repository class for your entity. From 33f6a58186cf0b6e7c6f40c09a24383f506ef9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 20 Nov 2020 14:52:32 +0300 Subject: [PATCH 8/8] Update Entity-Framework-Core.md --- docs/en/Entity-Framework-Core.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index 4585831633..8c0a1c9cc9 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -458,7 +458,7 @@ public async Task TestWithoutDetails(Guid id) **Example: Get list of entities with details** ````csharp -public async Task TestWithDetails(Guid id) +public async Task TestWithDetails() { var orders = await _orderRepository.GetListAsync(includeDetails: true); }