Browse Source

Merge pull request #160 from EasyAbp/dapr-actors-inventory

Dapr Actors inventory provider plugin
pull/161/head
Super 4 years ago
committed by GitHub
parent
commit
76ae78fe78
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      Directory.Build.props
  2. 47
      EShop.sln
  3. 1
      EShop.sln.DotSettings
  4. 4
      docs/README.md
  5. 38
      docs/plugins/inventories/dapr-actors/README.md
  6. 1
      plugins/Inventories/DaprActors/README.md
  7. 18
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj
  8. 11
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp/EShop/Plugins/Inventories/DaprActors/EShopPluginsInventoriesDaprActorsAbstractionsModule.cs
  9. 13
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp/EShop/Plugins/Inventories/DaprActors/IInventoryActor.cs
  10. 8
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp/EShop/Plugins/Inventories/DaprActors/InventoryStateModel.cs
  11. 3
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/FodyWeavers.xml
  12. 30
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/FodyWeavers.xsd
  13. 19
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore/EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore.csproj
  14. 29
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore/EasyAbp/EShop/Plugins/Inventories/DaprActors/EShopPluginsInventoriesDaprActorsAspNetCoreModule.cs
  15. 18
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/EasyAbp.EShop.Plugins.Inventories.DaprActors.csproj
  16. 10
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/EasyAbp/EShop/Plugins/Inventories/DaprActors/EShopPluginsInventoriesDaprActorsModule.cs
  17. 78
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/EasyAbp/EShop/Plugins/Inventories/DaprActors/InventoryActor.cs
  18. 3
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/FodyWeavers.xml
  19. 30
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/FodyWeavers.xsd
  20. 15
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp.EShop.Products.DaprActorsInventory.Domain.csproj
  21. 113
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/DaprActorsProductInventoryProvider.cs
  22. 28
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/EShopProductsDaprActorsInventoryDomainModule.cs
  23. 11
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/IInventoryActorProvider.cs
  24. 15
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/InventoryActorProvider.cs
  25. 3
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/FodyWeavers.xml
  26. 30
      plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/FodyWeavers.xsd
  27. 48
      plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/DaprActorsProductInventoryProviderTests.cs
  28. 16
      plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/EShopProductsDaprActorsInventoryDomainTestModule.cs
  29. 24
      plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests.csproj
  30. 43
      plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/FakeInventoryActor.cs
  31. 61
      plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/ProductsDaprActorsInventoryTestBase.cs
  32. 26
      plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/TestInventoryActorProvider.cs

1
Directory.Build.props

@ -5,6 +5,7 @@
<EasyAbpAbpTreesModuleVersion>2.5.3</EasyAbpAbpTreesModuleVersion>
<EasyAbpPaymentServiceModuleVersion>2.0.11</EasyAbpPaymentServiceModuleVersion>
<EasyAbpAbpTagHelperPlusModuleVersion>0.8.2</EasyAbpAbpTagHelperPlusModuleVersion>
<DaprSdkVersion>1.7.0</DaprSdkVersion>
</PropertyGroup>
</Project>

47
EShop.sln

@ -341,6 +341,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Coupo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Orders.Plugins.Coupons", "plugins\Coupons\src\EasyAbp.EShop.Orders.Plugins.Coupons\EasyAbp.EShop.Orders.Plugins.Coupons.csproj", "{3C385657-8365-470F-9F4F-30F31F9FCA42}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Inventories", "Inventories", "{9AC27747-E175-487F-92C9-434DEE543273}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DaprActors", "DaprActors", "{6E6FE4B9-4117-4F57-B219-EE47E4046096}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.DaprActorsInventory.Domain", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Products.DaprActorsInventory.Domain\EasyAbp.EShop.Products.DaprActorsInventory.Domain.csproj", "{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj", "{CF4DE32D-9629-4C48-9BE8-5B83A1C27291}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors\EasyAbp.EShop.Plugins.Inventories.DaprActors.csproj", "{B6F3ACD5-463E-4455-A094-057A82941A94}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F468A386-5660-4888-981A-6ECF15182D32}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{485204B1-7603-4EA0-B3A4-73CB89B0D5BC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests", "plugins\Inventories\DaprActors\test\EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests\EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests.csproj", "{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore", "plugins\Inventories\DaprActors\src\EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore\EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore.csproj", "{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -895,6 +913,26 @@ Global
{3C385657-8365-470F-9F4F-30F31F9FCA42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C385657-8365-470F-9F4F-30F31F9FCA42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C385657-8365-470F-9F4F-30F31F9FCA42}.Release|Any CPU.Build.0 = Release|Any CPU
{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1}.Release|Any CPU.Build.0 = Release|Any CPU
{CF4DE32D-9629-4C48-9BE8-5B83A1C27291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF4DE32D-9629-4C48-9BE8-5B83A1C27291}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF4DE32D-9629-4C48-9BE8-5B83A1C27291}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF4DE32D-9629-4C48-9BE8-5B83A1C27291}.Release|Any CPU.Build.0 = Release|Any CPU
{B6F3ACD5-463E-4455-A094-057A82941A94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6F3ACD5-463E-4455-A094-057A82941A94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6F3ACD5-463E-4455-A094-057A82941A94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6F3ACD5-463E-4455-A094-057A82941A94}.Release|Any CPU.Build.0 = Release|Any CPU
{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB}.Release|Any CPU.Build.0 = Release|Any CPU
{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1065,6 +1103,15 @@ Global
{86CAD303-A0E5-42C9-89A5-61D9AAA4AD8F} = {4001814E-A67B-490D-9E13-2FB9A34B0A0B}
{B076C103-DF0B-464B-A9CB-4BE5CAFEE067} = {4001814E-A67B-490D-9E13-2FB9A34B0A0B}
{3C385657-8365-470F-9F4F-30F31F9FCA42} = {72F34527-9295-4F29-923E-4B075A4F31A2}
{9AC27747-E175-487F-92C9-434DEE543273} = {94CC5A11-DA0F-413C-96CA-01DB0FC426E0}
{6E6FE4B9-4117-4F57-B219-EE47E4046096} = {9AC27747-E175-487F-92C9-434DEE543273}
{F468A386-5660-4888-981A-6ECF15182D32} = {6E6FE4B9-4117-4F57-B219-EE47E4046096}
{B6F3ACD5-463E-4455-A094-057A82941A94} = {F468A386-5660-4888-981A-6ECF15182D32}
{CF4DE32D-9629-4C48-9BE8-5B83A1C27291} = {F468A386-5660-4888-981A-6ECF15182D32}
{6CD1A8B5-8AB7-4A31-8333-024A7FB602D1} = {F468A386-5660-4888-981A-6ECF15182D32}
{485204B1-7603-4EA0-B3A4-73CB89B0D5BC} = {6E6FE4B9-4117-4F57-B219-EE47E4046096}
{733C51A3-19C8-45C4-8B22-3FD40CAF4EFB} = {485204B1-7603-4EA0-B3A4-73CB89B0D5BC}
{3F0EA314-CCF4-4BB2-A8C1-79FAE4442884} = {F468A386-5660-4888-981A-6ECF15182D32}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F}

1
EShop.sln.DotSettings

@ -21,4 +21,5 @@
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Authorizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dapr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Skus/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

4
docs/README.md

@ -48,7 +48,7 @@ We have launched an online demo for this module: [https://eshop.samples.easyabp.
* Define a Product Group (optional)
* Product group is used to classify different types of products, so we can customize different behavior for them, for example, products of the "GiftCard" product group could automatically send the card number and password to the customer's mailbox.
* EShop provides a [default product group](https://github.com/EasyAbp/EShop/blob/master/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/EShopProductsDomainModule.cs#L29-L36).
* EShop provides a [default product group](https://github.com/EasyAbp/EShop/blob/master/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain/EasyAbp/EShop/Products/EShopProductsDomainModule.cs#L33-L37).
* Refer to the configuration of the default product group and define a new product group.
* Create a Product
@ -102,6 +102,8 @@ We can customize some features to use EShop in complex application scenarios.
* Plugin modules
* Baskets
* Coupons
* Inventories
* [DaprActors](https://github.com/EasyAbp/EShop/tree/dev/plugins/Inventories/DaprActors)
## Roadmap

38
docs/plugins/inventories/dapr-actors/README.md

@ -0,0 +1,38 @@
# EShop.Plugins.Inventories.DaprActors
[![ABP version](https://img.shields.io/badge/dynamic/xml?style=flat-square&color=yellow&label=abp&query=%2F%2FProject%2FPropertyGroup%2FAbpVersion&url=https%3A%2F%2Fraw.githubusercontent.com%2FEasyAbp%2FEShop%2Fmaster%2FDirectory.Build.props)](https://abp.io)
[![NuGet](https://img.shields.io/nuget/v/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.svg?style=flat-square)](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions)
[![NuGet Download](https://img.shields.io/nuget/dt/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.svg?style=flat-square)](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions)
[![Discord online](https://badgen.net/discord/online-members/S6QaezrCRq?label=Discord)](https://discord.gg/S6QaezrCRq)
[![GitHub stars](https://img.shields.io/github/stars/EasyAbp/EShop?style=social)](https://www.github.com/EasyAbp/EShop)
EShop product-inventory implementation of [Dapr Actors](https://docs.dapr.io/developing-applications/building-blocks/actors/actors-overview).
## Installation
1. Install the following NuGet packages. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-nuget-packages))
* EasyAbp.EShop.Products.DaprActorsInventory.Domain _(install at EasyAbp.EShop.Products.Domain location)_
* EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore _(install at a host project to run Actors)_
2. Add `DependsOn(typeof(EShopXxxModule))` attribute to configure the module dependencies. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-module-dependencies))
## Usage
1. Configure the DaprActors inventory provider as default.
```csharp
Configure<EShopProductsOptions>(options =>
{
// Configure as the default inventory provider
options.DefaultInventoryProviderName = "DaprActors";
// Configure as the default inventory provider for MyProductGroup
options.Groups.Configure<MyProductGroup>(group =>
{
group.DefaultInventoryProviderName = "DaprActors";
});
});
```
> Better to use `DaprActorsProductInventoryProvider.DaprActorsProductInventoryProviderName` instead of `"DaprActors"` as the provider name.
2. Create a product and set `InventoryProviderName` to `DaprActors`. Then the product is specified to use the Dapr Actors inventory provider.

1
plugins/Inventories/DaprActors/README.md

@ -0,0 +1 @@
../../../docs/plugins/inventories/dapr-actors/README.md

18
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapr.Actors" Version="$(DaprSdkVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Domain.Shared\EasyAbp.EShop.Products.Domain.Shared.csproj" />
</ItemGroup>
</Project>

11
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp/EShop/Plugins/Inventories/DaprActors/EShopPluginsInventoriesDaprActorsAbstractionsModule.cs

@ -0,0 +1,11 @@
using EasyAbp.EShop.Products;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors;
[DependsOn(
typeof(EShopProductsDomainSharedModule)
)]
public class EShopPluginsInventoriesDaprActorsAbstractionsModule : AbpModule
{
}

13
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp/EShop/Plugins/Inventories/DaprActors/IInventoryActor.cs

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Dapr.Actors;
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors;
public interface IInventoryActor : IActor
{
Task<InventoryStateModel> GetInventoryStateAsync();
Task IncreaseInventoryAsync(int quantity, bool decreaseSold);
Task ReduceInventoryAsync(int quantity, bool increaseSold);
}

8
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/EasyAbp/EShop/Plugins/Inventories/DaprActors/InventoryStateModel.cs

@ -0,0 +1,8 @@
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors;
public class InventoryStateModel
{
public int Inventory { get; set; }
public long Sold { get; set; }
}

3
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

19
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore/EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Inventories.DaprActors\EasyAbp.EShop.Plugins.Inventories.DaprActors.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore" Version="$(AbpVersion)" />
<PackageReference Include="Dapr.Actors.AspNetCore" Version="$(DaprSdkVersion)" />
</ItemGroup>
</Project>

29
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors.AspNetCore/EasyAbp/EShop/Plugins/Inventories/DaprActors/EShopPluginsInventoriesDaprActorsAspNetCoreModule.cs

@ -0,0 +1,29 @@
using EasyAbp.EShop.Products;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors;
[DependsOn(
typeof(AbpAspNetCoreModule),
typeof(EShopProductsDomainSharedModule)
)]
public class EShopPluginsInventoriesDaprActorsAspNetCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddActors(options => { options.Actors.RegisterActor<InventoryActor>(); });
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add(ctx =>
{
// Register actors handlers that interface with the Dapr runtime.
ctx.Endpoints.MapActorsHandlers();
});
});
}
}

18
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/EasyAbp.EShop.Plugins.Inventories.DaprActors.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapr.Actors" Version="$(DaprSdkVersion)" />
</ItemGroup>
</Project>

10
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/EasyAbp/EShop/Plugins/Inventories/DaprActors/EShopPluginsInventoriesDaprActorsModule.cs

@ -0,0 +1,10 @@
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors;
[DependsOn(
typeof(EShopPluginsInventoriesDaprActorsAbstractionsModule)
)]
public class EShopPluginsInventoriesDaprActorsModule : AbpModule
{
}

78
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/EasyAbp/EShop/Plugins/Inventories/DaprActors/InventoryActor.cs

@ -0,0 +1,78 @@
using System.Threading.Tasks;
using Dapr;
using Dapr.Actors.Runtime;
namespace EasyAbp.EShop.Plugins.Inventories.DaprActors;
public class InventoryActor : Actor, IInventoryActor
{
public static string InventoryStateName { get; set; } = "i";
public InventoryActor(ActorHost host) : base(host)
{
}
protected override async Task OnActivateAsync()
{
await StateManager.TryAddStateAsync(InventoryStateName, new InventoryStateModel());
}
public virtual async Task<InventoryStateModel> GetInventoryStateAsync()
{
return await StateManager.GetStateAsync<InventoryStateModel>(InventoryStateName);
}
public virtual async Task IncreaseInventoryAsync(int quantity, bool decreaseSold)
{
var state = await GetInventoryStateAsync();
InternalIncreaseInventory(state, quantity, decreaseSold);
}
public async Task ReduceInventoryAsync(int quantity, bool increaseSold)
{
var state = await GetInventoryStateAsync();
InternalReduceInventory(state, quantity, increaseSold);
}
protected virtual void InternalIncreaseInventory(InventoryStateModel stateModel, int quantity, bool decreaseSold)
{
if (quantity < 0)
{
throw new DaprException("Quantity should not be less than 0.");
}
if (decreaseSold && stateModel.Sold - quantity < 0)
{
throw new DaprException("Target Sold cannot be less than 0.");
}
stateModel.Inventory = checked(stateModel.Inventory + quantity);
if (decreaseSold)
{
stateModel.Sold -= quantity;
}
}
protected virtual void InternalReduceInventory(InventoryStateModel stateModel, int quantity, bool increaseSold)
{
if (quantity < 0)
{
throw new DaprException("Quantity should not be less than 0.");
}
if (quantity > stateModel.Inventory)
{
throw new DaprException("Insufficient inventory.");
}
if (increaseSold)
{
stateModel.Sold = checked(stateModel.Sold + quantity);
}
stateModel.Inventory -= quantity;
}
}

3
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Plugins.Inventories.DaprActors/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

15
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp.EShop.Products.DaprActorsInventory.Domain.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Domain\EasyAbp.EShop.Products.Domain.csproj" />
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions\EasyAbp.EShop.Plugins.Inventories.DaprActors.Abstractions.csproj" />
</ItemGroup>
</Project>

113
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/DaprActorsProductInventoryProvider.cs

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Dapr.Actors;
using EasyAbp.EShop.Plugins.Inventories.DaprActors;
using EasyAbp.EShop.Products.ProductInventories;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.DaprActorsInventory;
public class DaprActorsProductInventoryProvider : IProductInventoryProvider, ITransientDependency
{
public static string DaprActorsProductInventoryProviderName { get; set; } = "DaprActors";
public static string DaprActorsProductInventoryProviderDisplayName { get; set; } = "DaprActors";
public static string DaprActorsProductInventoryProviderDescription { get; set; } = "DaprActors";
public string InventoryProviderName { get; } = DaprActorsProductInventoryProviderName;
public static string ActorType { get; set; } = "InventoryActor";
private readonly ILogger<DaprActorsProductInventoryProvider> _logger;
protected IInventoryActorProvider InventoryActorProvider { get; }
public DaprActorsProductInventoryProvider(
IInventoryActorProvider inventoryActorProvider,
ILogger<DaprActorsProductInventoryProvider> logger)
{
InventoryActorProvider = inventoryActorProvider;
_logger = logger;
}
public virtual async Task<InventoryDataModel> GetInventoryDataAsync(InventoryQueryModel model)
{
var actor = await GetActorAsync(model);
var stateModel = await actor.GetInventoryStateAsync();
return new InventoryDataModel
{
Inventory = stateModel.Inventory,
Sold = stateModel.Sold
};
}
public virtual async Task<Dictionary<Guid, InventoryDataModel>> GetSkuIdInventoryDataMappingAsync(
IList<InventoryQueryModel> models)
{
var result = new Dictionary<Guid, InventoryDataModel>();
foreach (var model in models)
{
result.Add(model.ProductSkuId, await GetInventoryDataAsync(model));
}
return result;
}
public virtual async Task<bool> TryIncreaseInventoryAsync(InventoryQueryModel model, int quantity,
bool decreaseSold)
{
var actor = await GetActorAsync(model);
try
{
await actor.IncreaseInventoryAsync(quantity, decreaseSold);
}
catch (Exception e)
{
_logger.LogError("Actor threw: {Message}", e.Message);
return false;
}
return true;
}
public virtual async Task<bool> TryReduceInventoryAsync(InventoryQueryModel model, int quantity, bool increaseSold)
{
var actor = await GetActorAsync(model);
var stateModel = await actor.GetInventoryStateAsync();
if (stateModel.Inventory < quantity)
{
return false;
}
try
{
await actor.ReduceInventoryAsync(quantity, increaseSold);
}
catch (Exception e)
{
_logger.LogError("Actor threw: {Message}", e.Message);
return false;
}
return true;
}
protected virtual async Task<IInventoryActor> GetActorAsync(InventoryQueryModel model)
{
return await InventoryActorProvider.GetAsync(GetActorId(model), ActorType);
}
protected virtual ActorId GetActorId(InventoryQueryModel model)
{
return new ActorId(
$"eshop_inventory_{(model.TenantId.HasValue ? model.TenantId.Value : "host")}_{model.ProductSkuId}");
}
}

28
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/EShopProductsDaprActorsInventoryDomainModule.cs

@ -0,0 +1,28 @@
using EasyAbp.EShop.Plugins.Inventories.DaprActors;
using EasyAbp.EShop.Products.Options;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Products.DaprActorsInventory;
[DependsOn(
typeof(EShopProductsDomainModule),
typeof(EShopPluginsInventoriesDaprActorsAbstractionsModule)
)]
public class EShopProductsDaprActorsInventoryDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<EShopProductsOptions>(options =>
{
options.InventoryProviders.Configure(
DaprActorsProductInventoryProvider.DaprActorsProductInventoryProviderName, provider =>
{
provider.DisplayName =
DaprActorsProductInventoryProvider.DaprActorsProductInventoryProviderDisplayName;
provider.Description = DaprActorsProductInventoryProvider
.DaprActorsProductInventoryProviderDescription;
provider.ProviderType = typeof(DaprActorsProductInventoryProvider);
});
});
}
}

11
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/IInventoryActorProvider.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Dapr.Actors;
using EasyAbp.EShop.Plugins.Inventories.DaprActors;
using JetBrains.Annotations;
namespace EasyAbp.EShop.Products.DaprActorsInventory;
public interface IInventoryActorProvider
{
Task<IInventoryActor> GetAsync(ActorId actorId, [NotNull] string actorType);
}

15
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/EasyAbp/EShop/Products/DaprActorsInventory/InventoryActorProvider.cs

@ -0,0 +1,15 @@
using System.Threading.Tasks;
using Dapr.Actors;
using Dapr.Actors.Client;
using EasyAbp.EShop.Plugins.Inventories.DaprActors;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.DaprActorsInventory;
public class InventoryActorProvider : IInventoryActorProvider, ITransientDependency
{
public virtual Task<IInventoryActor> GetAsync(ActorId actorId, string actorType)
{
return Task.FromResult(ActorProxy.Create<IInventoryActor>(actorId, actorType));
}
}

3
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
plugins/Inventories/DaprActors/src/EasyAbp.EShop.Products.DaprActorsInventory.Domain/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

48
plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/DaprActorsProductInventoryProviderTests.cs

@ -0,0 +1,48 @@
using System.Threading.Tasks;
using EasyAbp.EShop.Products.ProductInventories;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain;
public class DaprActorsProductInventoryProviderTests : ProductsDaprActorsInventoryTestBase
{
[Fact]
public async Task Should_Get_Inventory()
{
var inventoryProvider = ServiceProvider.GetRequiredService<DaprActorsProductInventoryProvider>();
var inventoryDataModel = await inventoryProvider.GetInventoryDataAsync(new InventoryQueryModel());
inventoryDataModel.ShouldNotBeNull();
inventoryDataModel.Inventory.ShouldBe(100);
inventoryDataModel.Sold.ShouldBe(0);
}
[Fact]
public async Task Should_Change_Inventory()
{
var inventoryProvider = ServiceProvider.GetRequiredService<DaprActorsProductInventoryProvider>();
var result = await inventoryProvider.TryReduceInventoryAsync(new InventoryQueryModel(), 2, true);
result.ShouldBeTrue();
var inventoryDataModel = await inventoryProvider.GetInventoryDataAsync(new InventoryQueryModel());
inventoryDataModel.ShouldNotBeNull();
inventoryDataModel.Inventory.ShouldBe(98);
inventoryDataModel.Sold.ShouldBe(2);
result = await inventoryProvider.TryIncreaseInventoryAsync(new InventoryQueryModel(), 1, true);
result.ShouldBeTrue();
inventoryDataModel = await inventoryProvider.GetInventoryDataAsync(new InventoryQueryModel());
inventoryDataModel.ShouldNotBeNull();
inventoryDataModel.Inventory.ShouldBe(99);
inventoryDataModel.Sold.ShouldBe(1);
}
}

16
plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/EShopProductsDaprActorsInventoryDomainTestModule.cs

@ -0,0 +1,16 @@
using Volo.Abp;
using Volo.Abp.Authorization;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpTestBaseModule),
typeof(AbpAuthorizationModule),
typeof(EShopProductsDaprActorsInventoryDomainModule)
)]
public class EShopProductsDaprActorsInventoryDomainTestModule : AbpModule
{
}

24
plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests.csproj

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>EasyAbp.EShop.Products.DaprActorsInventory.Domain</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="Shouldly" Version="4.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Authorization" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.TestBase" Version="$(AbpVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\EasyAbp.EShop.Products.DaprActorsInventory.Domain\EasyAbp.EShop.Products.DaprActorsInventory.Domain.csproj" />
</ItemGroup>
</Project>

43
plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/FakeInventoryActor.cs

@ -0,0 +1,43 @@
using System.Threading.Tasks;
using EasyAbp.EShop.Plugins.Inventories.DaprActors;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain;
public class FakeInventoryActor : IInventoryActor, ITransientDependency
{
private InventoryStateModel StateModel { get; } = new()
{
Inventory = 100,
Sold = 0
};
public Task<InventoryStateModel> GetInventoryStateAsync()
{
return Task.FromResult(StateModel);
}
public Task IncreaseInventoryAsync(int quantity, bool decreaseSold)
{
StateModel.Inventory += quantity;
if (decreaseSold)
{
StateModel.Sold -= quantity;
}
return Task.CompletedTask;
}
public Task ReduceInventoryAsync(int quantity, bool increaseSold)
{
StateModel.Inventory -= quantity;
if (increaseSold)
{
StateModel.Sold += quantity;
}
return Task.CompletedTask;
}
}

61
plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/ProductsDaprActorsInventoryTestBase.cs

@ -0,0 +1,61 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Volo.Abp.Uow;
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain
{
/* All test classes are derived from this class, directly or indirectly. */
public abstract class
ProductsDaprActorsInventoryTestBase : AbpIntegratedTest<EShopProductsDaprActorsInventoryDomainTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
protected virtual Task WithUnitOfWorkAsync(Func<Task> func)
{
return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
}
protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func<Task> action)
{
using (var scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
using (var uow = uowManager.Begin(options))
{
await action();
await uow.CompleteAsync();
}
}
}
protected virtual Task<TResult> WithUnitOfWorkAsync<TResult>(Func<Task<TResult>> func)
{
return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
}
protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(AbpUnitOfWorkOptions options,
Func<Task<TResult>> func)
{
using (var scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
using (var uow = uowManager.Begin(options))
{
var result = await func();
await uow.CompleteAsync();
return result;
}
}
}
}
}

26
plugins/Inventories/DaprActors/test/EasyAbp.EShop.Products.DaprActorsInventory.Domain.Tests/TestInventoryActorProvider.cs

@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
using Dapr.Actors;
using EasyAbp.EShop.Plugins.Inventories.DaprActors;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace EasyAbp.EShop.Products.DaprActorsInventory.Domain;
[Dependency(ReplaceServices = true)]
public class TestInventoryActorProvider : IInventoryActorProvider, ITransientDependency
{
private IInventoryActor Actor { get; set; }
private readonly IServiceProvider _serviceProvider;
public TestInventoryActorProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public Task<IInventoryActor> GetAsync(ActorId actorId, string actorType)
{
return Task.FromResult(Actor ??= _serviceProvider.GetRequiredService<FakeInventoryActor>());
}
}
Loading…
Cancel
Save