(options =>
+ {
+ //Configure authorization.
+ });
+ }
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/EasyAbp.EShop.Products.Web.csproj b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/EasyAbp.EShop.Products.Web.csproj
new file mode 100644
index 00000000..32bfdc92
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/EasyAbp.EShop.Products.Web.csproj
@@ -0,0 +1,42 @@
+
+
+
+
+
+ netcoreapp3.1
+ $(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
+ true
+ Library
+ EasyAbp.EShop.Products.Web
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/FodyWeavers.xml b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/FodyWeavers.xsd b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/Index.cshtml b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/Index.cshtml
new file mode 100644
index 00000000..ebd41f67
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/Index.cshtml
@@ -0,0 +1,7 @@
+@page
+@inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage
+@model EasyAbp.EShop.Products.Web.Pages.Products.IndexModel
+@{
+}
+Products
+A sample page for the Products module.
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/Index.cshtml.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/Index.cshtml.cs
new file mode 100644
index 00000000..b9aa30a9
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/Index.cshtml.cs
@@ -0,0 +1,9 @@
+namespace EasyAbp.EShop.Products.Web.Pages.Products
+{
+ public class IndexModel : ProductsPageModel
+ {
+ public void OnGet()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/_ViewImports.cshtml b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/_ViewImports.cshtml
new file mode 100644
index 00000000..c1da1f5f
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/Products/_ViewImports.cshtml
@@ -0,0 +1,4 @@
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI
+@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
+@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/ProductsPage.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/ProductsPage.cs
new file mode 100644
index 00000000..b10fd820
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/ProductsPage.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Mvc.Localization;
+using Microsoft.AspNetCore.Mvc.Razor.Internal;
+using EasyAbp.EShop.Products.Localization;
+using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
+
+namespace EasyAbp.EShop.Products.Web.Pages
+{
+ /* Inherit your UI Pages from this class. To do that, add this line to your Pages (.cshtml files under the Page folder):
+ * @inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage
+ */
+ public abstract class ProductsPage : AbpPage
+ {
+ [RazorInject]
+ public IHtmlLocalizer L { get; set; }
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/ProductsPageModel.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/ProductsPageModel.cs
new file mode 100644
index 00000000..17641cbd
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Pages/ProductsPageModel.cs
@@ -0,0 +1,16 @@
+using EasyAbp.EShop.Products.Localization;
+using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
+
+namespace EasyAbp.EShop.Products.Web.Pages
+{
+ /* Inherit your PageModel classes from this class.
+ */
+ public abstract class ProductsPageModel : AbpPageModel
+ {
+ protected ProductsPageModel()
+ {
+ LocalizationResourceType = typeof(ProductsResource);
+ ObjectMapperContext = typeof(EShopProductsWebModule);
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsMenuContributor.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsMenuContributor.cs
new file mode 100644
index 00000000..70b31725
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsMenuContributor.cs
@@ -0,0 +1,23 @@
+using System.Threading.Tasks;
+using Volo.Abp.UI.Navigation;
+
+namespace EasyAbp.EShop.Products.Web
+{
+ public class ProductsMenuContributor : IMenuContributor
+ {
+ public async Task ConfigureMenuAsync(MenuConfigurationContext context)
+ {
+ if (context.Menu.Name == StandardMenus.Main)
+ {
+ await ConfigureMainMenu(context);
+ }
+ }
+
+ private Task ConfigureMainMenu(MenuConfigurationContext context)
+ {
+ //Add main menu items.
+
+ return Task.CompletedTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsWebAutoMapperProfile.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsWebAutoMapperProfile.cs
new file mode 100644
index 00000000..8ebd90dd
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/ProductsWebAutoMapperProfile.cs
@@ -0,0 +1,14 @@
+using AutoMapper;
+
+namespace EasyAbp.EShop.Products.Web
+{
+ public class ProductsWebAutoMapperProfile : Profile
+ {
+ public ProductsWebAutoMapperProfile()
+ {
+ /* You can configure your AutoMapper mapping configuration here.
+ * Alternatively, you can split your mapping configurations
+ * into multiple profile classes for a better organization. */
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Properties/launchSettings.json b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Properties/launchSettings.json
new file mode 100644
index 00000000..66f9ad25
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Web/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:56993/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "EasyAbp.EShop.Products.Web": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:56994/"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/EShopProductsApplicationTestModule.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/EShopProductsApplicationTestModule.cs
new file mode 100644
index 00000000..3bb8063a
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/EShopProductsApplicationTestModule.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Products
+{
+ [DependsOn(
+ typeof(EShopProductsApplicationModule),
+ typeof(EShopProductsDomainTestModule)
+ )]
+ public class EShopProductsApplicationTestModule : AbpModule
+ {
+
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/EasyAbp.EShop.Products.Application.Tests.csproj b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/EasyAbp.EShop.Products.Application.Tests.csproj
new file mode 100644
index 00000000..44b48023
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/EasyAbp.EShop.Products.Application.Tests.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+ netcoreapp3.1
+ EasyAbp.EShop.Products
+
+
+
+
+
+
+
+
+
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/FodyWeavers.xml b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/FodyWeavers.xsd b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/ProductsApplicationTestBase.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/ProductsApplicationTestBase.cs
new file mode 100644
index 00000000..2c773c37
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Application.Tests/ProductsApplicationTestBase.cs
@@ -0,0 +1,10 @@
+namespace EasyAbp.EShop.Products
+{
+ /* Inherit from this class for your application layer tests.
+ * See SampleAppService_Tests for example.
+ */
+ public abstract class ProductsApplicationTestBase : ProductsTestBase
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/EShopProductsDomainTestModule.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/EShopProductsDomainTestModule.cs
new file mode 100644
index 00000000..b8049a9d
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/EShopProductsDomainTestModule.cs
@@ -0,0 +1,17 @@
+using EasyAbp.EShop.Products.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Products
+{
+ /* Domain tests are configured to use the EF Core provider.
+ * You can switch to MongoDB, however your domain tests should be
+ * database independent anyway.
+ */
+ [DependsOn(
+ typeof(EShopProductsEntityFrameworkCoreTestModule)
+ )]
+ public class EShopProductsDomainTestModule : AbpModule
+ {
+
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/EasyAbp.EShop.Products.Domain.Tests.csproj b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/EasyAbp.EShop.Products.Domain.Tests.csproj
new file mode 100644
index 00000000..b6fe4d37
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/EasyAbp.EShop.Products.Domain.Tests.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+
+ netcoreapp3.1
+ EasyAbp.EShop.Products
+
+
+
+
+
+
+
+
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/FodyWeavers.xml b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/FodyWeavers.xsd b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/ProductsDomainTestBase.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/ProductsDomainTestBase.cs
new file mode 100644
index 00000000..d767e166
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.Domain.Tests/ProductsDomainTestBase.cs
@@ -0,0 +1,10 @@
+namespace EasyAbp.EShop.Products
+{
+ /* Inherit from this class for your domain layer tests.
+ * See SampleManager_Tests for example.
+ */
+ public abstract class ProductsDomainTestBase : ProductsTestBase
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EasyAbp.EShop.Products.EntityFrameworkCore.Tests.csproj b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EasyAbp.EShop.Products.EntityFrameworkCore.Tests.csproj
new file mode 100644
index 00000000..817db344
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EasyAbp.EShop.Products.EntityFrameworkCore.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ netcoreapp3.1
+ EasyAbp.EShop.Products
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EntityFrameworkCore/EShopProductsEntityFrameworkCoreTestModule.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EntityFrameworkCore/EShopProductsEntityFrameworkCoreTestModule.cs
new file mode 100644
index 00000000..6ad07173
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EntityFrameworkCore/EShopProductsEntityFrameworkCoreTestModule.cs
@@ -0,0 +1,41 @@
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Products.EntityFrameworkCore
+{
+ [DependsOn(
+ typeof(ProductsTestBaseModule),
+ typeof(EShopProductsEntityFrameworkCoreModule)
+ )]
+ public class EShopProductsEntityFrameworkCoreTestModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var sqliteConnection = CreateDatabaseAndGetConnection();
+
+ Configure(options =>
+ {
+ options.Configure(abpDbContextConfigurationContext =>
+ {
+ abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection);
+ });
+ });
+ }
+
+ private static SqliteConnection CreateDatabaseAndGetConnection()
+ {
+ var connection = new SqliteConnection("Data Source=:memory:");
+ connection.Open();
+
+ new ProductsDbContext(
+ new DbContextOptionsBuilder().UseSqlite(connection).Options
+ ).GetService().CreateTables();
+
+ return connection;
+ }
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EntityFrameworkCore/ProductsEntityFrameworkCoreTestBase.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EntityFrameworkCore/ProductsEntityFrameworkCoreTestBase.cs
new file mode 100644
index 00000000..08d7e96c
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/EntityFrameworkCore/ProductsEntityFrameworkCoreTestBase.cs
@@ -0,0 +1,10 @@
+namespace EasyAbp.EShop.Products.EntityFrameworkCore
+{
+ /* This class can be used as a base class for EF Core integration tests,
+ * while SampleRepository_Tests uses a different approach.
+ */
+ public abstract class ProductsEntityFrameworkCoreTestBase : ProductsTestBase
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/FodyWeavers.xml b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/FodyWeavers.xsd b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.EntityFrameworkCore.Tests/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/ClientDemoService.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/ClientDemoService.cs
new file mode 100644
index 00000000..584acb65
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/ClientDemoService.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Net.Http;
+using System.Threading.Tasks;
+using IdentityModel.Client;
+using Microsoft.Extensions.Configuration;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.IdentityModel;
+
+namespace EasyAbp.EShop.Products
+{
+ public class ClientDemoService : ITransientDependency
+ {
+ private readonly IIdentityModelAuthenticationService _authenticationService;
+ private readonly IConfiguration _configuration;
+
+ public ClientDemoService(
+ IIdentityModelAuthenticationService authenticationService,
+ IConfiguration configuration)
+ {
+ _authenticationService = authenticationService;
+ _configuration = configuration;
+ }
+
+ public async Task RunAsync()
+ {
+ await TestWithDynamicProxiesAsync();
+ await TestWithHttpClientAndIdentityModelAuthenticationServiceAsync();
+ await TestAllManuallyAsync();
+ }
+
+ /* Shows how to perform an HTTP request to the API using ABP's dynamic c# proxy
+ * feature. It is just simple as calling a local service method.
+ * Authorization and HTTP request details are handled by the ABP framework.
+ */
+ private async Task TestWithDynamicProxiesAsync()
+ {
+ Console.WriteLine();
+ Console.WriteLine($"***** {nameof(TestWithDynamicProxiesAsync)} *****");
+ }
+
+ /* Shows how to use HttpClient to perform a request to the HTTP API.
+ * It uses ABP's IIdentityModelAuthenticationService to simplify obtaining access tokens.
+ */
+ private async Task TestWithHttpClientAndIdentityModelAuthenticationServiceAsync()
+ {
+ Console.WriteLine();
+ Console.WriteLine($"***** {nameof(TestWithHttpClientAndIdentityModelAuthenticationServiceAsync)} *****");
+
+ //Get access token using ABP's IIdentityModelAuthenticationService
+
+ var accessToken = await _authenticationService.GetAccessTokenAsync(
+ new IdentityClientConfiguration(
+ _configuration["IdentityClients:Default:Authority"],
+ _configuration["IdentityClients:Default:Scope"],
+ _configuration["IdentityClients:Default:ClientId"],
+ _configuration["IdentityClients:Default:ClientSecret"],
+ _configuration["IdentityClients:Default:GrantType"],
+ _configuration["IdentityClients:Default:UserName"],
+ _configuration["IdentityClients:Default:UserPassword"]
+ )
+ );
+
+ //Perform the actual HTTP request
+
+ using (var httpClient = new HttpClient())
+ {
+ httpClient.SetBearerToken(accessToken);
+
+ var url = _configuration["RemoteServices:Products:BaseUrl"] +
+ "api/Products/sample/authorized";
+
+ var responseMessage = await httpClient.GetAsync(url);
+ if (responseMessage.IsSuccessStatusCode)
+ {
+ var responseString = await responseMessage.Content.ReadAsStringAsync();
+ Console.WriteLine("Result: " + responseString);
+ }
+ else
+ {
+ throw new Exception("Remote server returns error code: " + responseMessage.StatusCode);
+ }
+ }
+ }
+
+ /* Shows how to use HttpClient to perform a request to the HTTP API.
+ * It obtains access token using IdentityServer's API. See its documentation:
+ * https://identityserver4.readthedocs.io/en/latest/quickstarts/2_resource_owner_passwords.html
+ */
+ private async Task TestAllManuallyAsync()
+ {
+ Console.WriteLine();
+ Console.WriteLine($"***** {nameof(TestAllManuallyAsync)} *****");
+
+ //Obtain access token from the IDS4 server
+
+ // discover endpoints from metadata
+ var client = new HttpClient();
+ var disco = await client.GetDiscoveryDocumentAsync(_configuration["IdentityClients:Default:Authority"]);
+ if (disco.IsError)
+ {
+ Console.WriteLine(disco.Error);
+ return;
+ }
+
+ // request token
+ var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
+ {
+ Address = disco.TokenEndpoint,
+ ClientId = _configuration["IdentityClients:Default:ClientId"],
+ ClientSecret = _configuration["IdentityClients:Default:ClientSecret"],
+ UserName = _configuration["IdentityClients:Default:UserName"],
+ Password = _configuration["IdentityClients:Default:UserPassword"],
+ Scope = _configuration["IdentityClients:Default:Scope"]
+ });
+
+ if (tokenResponse.IsError)
+ {
+ Console.WriteLine(tokenResponse.Error);
+ return;
+ }
+
+ Console.WriteLine(tokenResponse.Json);
+
+ //Perform the actual HTTP request
+
+ using (var httpClient = new HttpClient())
+ {
+ httpClient.SetBearerToken(tokenResponse.AccessToken);
+
+ var url = _configuration["RemoteServices:Products:BaseUrl"] +
+ "api/Products/sample/authorized";
+
+ var responseMessage = await httpClient.GetAsync(url);
+ if (responseMessage.IsSuccessStatusCode)
+ {
+ var responseString = await responseMessage.Content.ReadAsStringAsync();
+ Console.WriteLine("Result: " + responseString);
+ }
+ else
+ {
+ throw new Exception("Remote server returns error code: " + responseMessage.StatusCode);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/ConsoleTestAppHostedService.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/ConsoleTestAppHostedService.cs
new file mode 100644
index 00000000..ccf7ac31
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/ConsoleTestAppHostedService.cs
@@ -0,0 +1,26 @@
+using Microsoft.Extensions.Hosting;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp;
+
+namespace EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp
+{
+ public class ConsoleTestAppHostedService : IHostedService
+ {
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ using (var application = AbpApplicationFactory.Create())
+ {
+ application.Initialize();
+
+ var demo = application.ServiceProvider.GetRequiredService();
+ await demo.RunAsync();
+
+ application.Shutdown();
+ }
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/EShopProductsConsoleApiClientModule.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/EShopProductsConsoleApiClientModule.cs
new file mode 100644
index 00000000..3a841839
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/EShopProductsConsoleApiClientModule.cs
@@ -0,0 +1,14 @@
+using Volo.Abp.Http.Client.IdentityModel;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Products
+{
+ [DependsOn(
+ typeof(EShopProductsHttpApiClientModule),
+ typeof(AbpHttpClientIdentityModelModule)
+ )]
+ public class EShopProductsConsoleApiClientModule : AbpModule
+ {
+
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp.csproj b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp.csproj
new file mode 100644
index 00000000..656770db
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp.csproj
@@ -0,0 +1,26 @@
+
+
+
+ Exe
+ netcoreapp3.1
+ EasyAbp.EShop.Products
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+ Always
+
+
+
+
+
+
+
+
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/Program.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/Program.cs
new file mode 100644
index 00000000..6532bd2d
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/Program.cs
@@ -0,0 +1,21 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp
+{
+ class Program
+ {
+ static async Task Main(string[] args)
+ {
+ await CreateHostBuilder(args).RunConsoleAsync();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddHostedService();
+ });
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/appsettings.json b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/appsettings.json
new file mode 100644
index 00000000..c77e4b00
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.HttpApi.Client.ConsoleTestApp/appsettings.json
@@ -0,0 +1,21 @@
+{
+ "RemoteServices": {
+ "Default": {
+ "BaseUrl": "https://localhost:44312/"
+ },
+ "Products": {
+ "BaseUrl": "https://localhost:44359/"
+ }
+ },
+ "IdentityClients": {
+ "Default": {
+ "GrantType": "password",
+ "ClientId": "Products_ConsoleTestApp",
+ "ClientSecret": "1q2w3e*",
+ "UserName": "admin",
+ "UserPassword": "1q2w3E*",
+ "Authority": "https://localhost:44312/",
+ "Scope": "Products"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/EasyAbp.EShop.Products.MongoDB.Tests.csproj b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/EasyAbp.EShop.Products.MongoDB.Tests.csproj
new file mode 100644
index 00000000..5a9c51fc
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/EasyAbp.EShop.Products.MongoDB.Tests.csproj
@@ -0,0 +1,17 @@
+
+
+
+
+
+ netcoreapp3.1
+ EasyAbp.EShop.Products
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/FodyWeavers.xml b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/FodyWeavers.xsd b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/MongoDB/EShopProductsMongoDbTestModule.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/MongoDB/EShopProductsMongoDbTestModule.cs
new file mode 100644
index 00000000..ea0faadb
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/MongoDB/EShopProductsMongoDbTestModule.cs
@@ -0,0 +1,29 @@
+using System;
+using Mongo2Go;
+using Volo.Abp;
+using Volo.Abp.Data;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Products.MongoDB
+{
+ [DependsOn(
+ typeof(ProductsTestBaseModule),
+ typeof(EShopProductsMongoDbModule)
+ )]
+ public class EShopProductsMongoDbTestModule : AbpModule
+ {
+ private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start();
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') +
+ "Db_" +
+ Guid.NewGuid().ToString("N");
+
+ Configure(options =>
+ {
+ options.ConnectionStrings.Default = connectionString;
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/MongoDB/ProductsMongoDbTestBase.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/MongoDB/ProductsMongoDbTestBase.cs
new file mode 100644
index 00000000..37901010
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.MongoDB.Tests/MongoDB/ProductsMongoDbTestBase.cs
@@ -0,0 +1,10 @@
+namespace EasyAbp.EShop.Products.MongoDB
+{
+ /* This class can be used as a base class for MongoDB integration tests,
+ * while SampleRepository_Tests uses a different approach.
+ */
+ public abstract class ProductsMongoDbTestBase : ProductsTestBase
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/EShopProductsTestBaseModule.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/EShopProductsTestBaseModule.cs
new file mode 100644
index 00000000..7bfbba49
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/EShopProductsTestBaseModule.cs
@@ -0,0 +1,42 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp;
+using Volo.Abp.Authorization;
+using Volo.Abp.Autofac;
+using Volo.Abp.Data;
+using Volo.Abp.Modularity;
+using Volo.Abp.Threading;
+
+namespace EasyAbp.EShop.Products
+{
+ [DependsOn(
+ typeof(AbpAutofacModule),
+ typeof(AbpTestBaseModule),
+ typeof(AbpAuthorizationModule),
+ typeof(EShopProductsDomainModule)
+ )]
+ public class ProductsTestBaseModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAlwaysAllowAuthorization();
+ }
+
+ public override void OnApplicationInitialization(ApplicationInitializationContext context)
+ {
+ SeedTestData(context);
+ }
+
+ private static void SeedTestData(ApplicationInitializationContext context)
+ {
+ AsyncHelper.RunSync(async () =>
+ {
+ using (var scope = context.ServiceProvider.CreateScope())
+ {
+ await scope.ServiceProvider
+ .GetRequiredService()
+ .SeedAsync();
+ }
+ });
+ }
+ }
+}
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/EasyAbp.EShop.Products.TestBase.csproj b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/EasyAbp.EShop.Products.TestBase.csproj
new file mode 100644
index 00000000..b928bfce
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/EasyAbp.EShop.Products.TestBase.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+ netcoreapp3.1
+ EasyAbp.EShop.Products
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/FodyWeavers.xml b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/FodyWeavers.xsd b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsDataSeedContributor.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsDataSeedContributor.cs
new file mode 100644
index 00000000..d9b7843e
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsDataSeedContributor.cs
@@ -0,0 +1,27 @@
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Guids;
+
+namespace EasyAbp.EShop.Products
+{
+ public class ProductsDataSeedContributor : IDataSeedContributor, ITransientDependency
+ {
+ private readonly IGuidGenerator _guidGenerator;
+
+ public ProductsDataSeedContributor(
+ IGuidGenerator guidGenerator)
+ {
+ _guidGenerator = guidGenerator;
+ }
+
+ public Task SeedAsync(DataSeedContext context)
+ {
+ /* Instead of returning the Task.CompletedTask, you can insert your test data
+ * at this point!
+ */
+
+ return Task.CompletedTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestBase.cs b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestBase.cs
new file mode 100644
index 00000000..40a1741e
--- /dev/null
+++ b/modules/EasyAbp.EShop.Products/test/EasyAbp.EShop.Products.TestBase/ProductsTestBase.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp;
+using Volo.Abp.Modularity;
+using Volo.Abp.Uow;
+using Volo.Abp.Testing;
+
+namespace EasyAbp.EShop.Products
+{
+ /* All test classes are derived from this class, directly or indirectly. */
+ public abstract class ProductsTestBase : AbpIntegratedTest
+ where TStartupModule : IAbpModule
+ {
+ protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
+ {
+ options.UseAutofac();
+ }
+
+ protected virtual Task WithUnitOfWorkAsync(Func func)
+ {
+ return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
+ }
+
+ protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func action)
+ {
+ using (var scope = ServiceProvider.CreateScope())
+ {
+ var uowManager = scope.ServiceProvider.GetRequiredService();
+
+ using (var uow = uowManager.Begin(options))
+ {
+ await action();
+
+ await uow.CompleteAsync();
+ }
+ }
+ }
+
+ protected virtual Task WithUnitOfWorkAsync(Func> func)
+ {
+ return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
+ }
+
+ protected virtual async Task WithUnitOfWorkAsync