recreate init
This commit is contained in:
parent
1550c640d1
commit
9d939e2f2c
94
Dockerfile
94
Dockerfile
|
|
@ -1,22 +1,60 @@
|
|||
# create the build instance
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
|
||||
WORKDIR /src
|
||||
COPY ./src ./
|
||||
|
||||
# restore solution
|
||||
RUN dotnet restore NopCommerce.sln
|
||||
|
||||
WORKDIR /src/Presentation/Nop.Web
|
||||
|
||||
# build project
|
||||
RUN dotnet build Nop.Web.csproj -c Release
|
||||
|
||||
# build plugins
|
||||
WORKDIR /src/Plugins
|
||||
RUN set -eux; \
|
||||
for dir in *; do \
|
||||
if [ -d "$dir" ]; then \
|
||||
dotnet build "$dir/$dir.csproj" -c Release; \
|
||||
fi; \
|
||||
done
|
||||
WORKDIR /src/Plugins/Nop.Plugin.DiscountRules.CustomerRoles
|
||||
RUN dotnet build Nop.Plugin.DiscountRules.CustomerRoles.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.ExchangeRate.EcbExchange
|
||||
RUN dotnet build Nop.Plugin.ExchangeRate.EcbExchange.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.ExternalAuth.Facebook
|
||||
RUN dotnet build Nop.Plugin.ExternalAuth.Facebook.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Misc.Sendinblue
|
||||
RUN dotnet build Nop.Plugin.Misc.Sendinblue.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Misc.WebApi.Frontend
|
||||
RUN dotnet build Nop.Plugin.Misc.WebApi.Frontend.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.MultiFactorAuth.GoogleAuthenticator
|
||||
RUN dotnet build Nop.Plugin.MultiFactorAuth.GoogleAuthenticator.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Payments.CheckMoneyOrder
|
||||
RUN dotnet build Nop.Plugin.Payments.CheckMoneyOrder.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Payments.Manual
|
||||
RUN dotnet build Nop.Plugin.Payments.Manual.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Payments.PayPalCommerce
|
||||
RUN dotnet build Nop.Plugin.Payments.PayPalCommerce.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Payments.PayPalStandard
|
||||
RUN dotnet build Nop.Plugin.Payments.PayPalStandard.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Pickup.PickupInStore
|
||||
RUN dotnet build Nop.Plugin.Pickup.PickupInStore.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Shipping.EasyPost
|
||||
RUN dotnet build Nop.Plugin.Shipping.EasyPost.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Shipping.FixedByWeightByTotal
|
||||
RUN dotnet build Nop.Plugin.Shipping.FixedByWeightByTotal.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Shipping.UPS
|
||||
RUN dotnet build Nop.Plugin.Shipping.UPS.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Tax.Avalara
|
||||
RUN dotnet build Nop.Plugin.Tax.Avalara.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Tax.FixedOrByCountryStateZip
|
||||
RUN dotnet build Nop.Plugin.Tax.FixedOrByCountryStateZip.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Widgets.AccessiBe
|
||||
RUN dotnet build Nop.Plugin.Widgets.AccessiBe.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Widgets.FacebookPixel
|
||||
RUN dotnet build Nop.Plugin.Widgets.FacebookPixel.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Widgets.GoogleAnalytics
|
||||
RUN dotnet build Nop.Plugin.Widgets.GoogleAnalytics.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Widgets.NivoSlider
|
||||
RUN dotnet build Nop.Plugin.Widgets.NivoSlider.csproj -c Release
|
||||
WORKDIR /src/Plugins/Nop.Plugin.Widgets.What3words
|
||||
RUN dotnet build Nop.Plugin.Widgets.What3words.csproj -c Release
|
||||
|
||||
# publish project
|
||||
WORKDIR /src/Presentation/Nop.Web
|
||||
|
|
@ -24,33 +62,33 @@ RUN dotnet publish Nop.Web.csproj -c Release -o /app/published
|
|||
|
||||
WORKDIR /app/published
|
||||
|
||||
RUN mkdir logs bin
|
||||
RUN mkdir logs
|
||||
RUN mkdir bin
|
||||
|
||||
RUN chmod 775 App_Data \
|
||||
App_Data/DataProtectionKeys \
|
||||
bin \
|
||||
logs \
|
||||
Plugins \
|
||||
wwwroot/bundles \
|
||||
wwwroot/db_backups \
|
||||
wwwroot/files/exportimport \
|
||||
wwwroot/icons \
|
||||
wwwroot/images \
|
||||
wwwroot/images/thumbs \
|
||||
wwwroot/images/uploaded \
|
||||
wwwroot/sitemaps
|
||||
RUN chmod 775 App_Data/
|
||||
RUN chmod 775 App_Data/DataProtectionKeys
|
||||
RUN chmod 775 bin
|
||||
RUN chmod 775 logs
|
||||
RUN chmod 775 Plugins
|
||||
RUN chmod 775 wwwroot/bundles
|
||||
RUN chmod 775 wwwroot/db_backups
|
||||
RUN chmod 775 wwwroot/files/exportimport
|
||||
RUN chmod 775 wwwroot/icons
|
||||
RUN chmod 775 wwwroot/images
|
||||
RUN chmod 775 wwwroot/images/thumbs
|
||||
RUN chmod 775 wwwroot/images/uploaded
|
||||
|
||||
# create the runtime instance
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS runtime
|
||||
|
||||
# add globalization support
|
||||
RUN apk add --no-cache icu-libs icu-data-full
|
||||
RUN apk add --no-cache icu-libs
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
|
||||
|
||||
# installs required packages
|
||||
RUN apk add tiff --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/main/ --allow-untrusted
|
||||
RUN apk add libgdiplus --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/community/ --allow-untrusted
|
||||
RUN apk add libc-dev tzdata --no-cache
|
||||
RUN apk add libgdiplus --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted
|
||||
RUN apk add libc-dev --no-cache
|
||||
RUN apk add tzdata --no-cache
|
||||
|
||||
# copy entrypoint script
|
||||
COPY ./entrypoint.sh /entrypoint.sh
|
||||
|
|
@ -60,4 +98,4 @@ WORKDIR /app
|
|||
|
||||
COPY --from=build /app/published .
|
||||
|
||||
ENTRYPOINT "/entrypoint.sh"
|
||||
ENTRYPOINT "/entrypoint.sh"
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
pipeline {
|
||||
agent any
|
||||
|
||||
environment {
|
||||
DOCKER_IMAGE = 'docker.cloud.pokash.pl/szkolenia.riskoff.pl'
|
||||
DOCKER_REGISTRY = 'docker.cloud.pokash.pl'
|
||||
GIT_REPO = 'https://repo.pokash.pl/POKASH.PL/SzkoleniaRiskoff.git'
|
||||
REGISTRY_CREDENTIALS_ID = '2753fc17-5ad1-4c78-b86a-a3e54c543adc' // ID poświadczeń do lokalnego rejestru
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
git url: "${GIT_REPO}", branch: 'main'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Docker Image') {
|
||||
steps {
|
||||
script {
|
||||
docker.build("${DOCKER_IMAGE}:latest")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Push Docker Image') {
|
||||
steps {
|
||||
script {
|
||||
// Logowanie do lokalnego rejestru
|
||||
docker.withRegistry("http://${DOCKER_REGISTRY}", "${REGISTRY_CREDENTIALS_ID}") {
|
||||
docker.image("${DOCKER_IMAGE}:latest").push('latest')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
cleanWs() // Czyści workspace po zakończeniu builda
|
||||
}
|
||||
}
|
||||
}
|
||||
14
LICENSE.md
14
LICENSE.md
|
|
@ -1,15 +1,11 @@
|
|||
# nopCommerce Public License Version 4.0 ("NPL")
|
||||
nopCommerce Public License Version 3.0 ("NPL")
|
||||
|
||||
nopCommerce open-source edition is licensed under nopCommerce Public License. It's basically a GNU Affero General Public License version 3 (GNU AGPL v3.0) plus the "powered by nopCommerce" text requirement on every single page. The nopCommerce Public License Version 4.0 ("NPL") consists of the GNU AGPL v3.0 License with the Additional Terms below. The original GNU AGPL v3.0 License can be found at: http://opensource.org/licenses/GPL-3.0
|
||||
|
||||
**Additional nopCommerce terms:**
|
||||
nopCommerce open source edition is licensed under nopCommerce Public License. It's basically a GPLv3 License plus the "powered by nopCommerce" text requirement on every single page. The nopCommerce Public License Version 3.0 ("NPL") consists of the GPL3 License with the Additional Terms below. The original GPLv3 License can be found at: http://opensource.org/licenses/GPL-3.0
|
||||
|
||||
However, in addition to the other notice obligations, (1) all copies of the Program in Executable and Source Code form must, as a form of attribution of the original author, include on each user interface screen (i) the "powered by nopCommerce" text; and (2) all derivative works and copies of derivative works of the Covered Code in Executable and Source Code form must include on each user interface screen (i) the "powered by nopCommerce" text. In addition, the "powered by nopCommerce" text, as appropriate, must be visible to all users, must appear in each user interface screen, and must be in the same position. When users click on the "powered by nopCommerce" text it must direct them to https://www.nopcommerce.com. This obligation shall also apply to any copies or derivative works. Find more info at https://www.nopcommerce.com/nopcommerce-copyright-removal-key
|
||||
Additional nopCommerce terms:
|
||||
|
||||
License page: https://www.nopcommerce.com/license
|
||||
However, in addition to the other notice obligations, (1) all copies of the Program in Executable and Source Code form must, as a form of attribution of the original author, include on each user interface screen (i) the "powered by nopCommerce" text; and (2) all derivative works and copies of derivative works of the Covered Code in Executable and Source Code form must include on each user interface screen (i) the "powered by nopCommerce" text. In addition, the "powered by nopCommerce" text, as appropriate, must be visible to all users, must appear in each user interface screen, and must be in the same position. When users click on the "powered by nopCommerce" text it must direct them to https://www.nopCommerce.com. This obligation shall also apply to any copies or derivative works. Find more info at https://www.nopcommerce.com/p/1/nopcommerce-copyright-removal-key.aspx
|
||||
|
||||
# Commercial License
|
||||
|
||||
Independent Software Vendors that want the benefits of embedding nopCommerce software in their commercial applications but do not want to be subject to the nopCommerce Public License ("NPL") and do not want to release the source code for their proprietary applications must purchase a commercial license from the nopCommerce team. Purchasing a commercial license means that the nopCommerce Public License ("NPL") does not apply, and a commercial license includes the assurances that distributors typically find in commercial distribution agreements. If use of nopCommerce under the NPL does not satisfy your organization's legal department you should also enter into a commercial license agreement with the nopCommerce team.
|
||||
|
||||
Feel free to contact us for more details - https://www.nopcommerce.com/contact-us
|
||||
License page: https://www.nopcommerce.com/licensev3.aspx
|
||||
44
Readme.md
44
Readme.md
|
|
@ -1,23 +1,29 @@
|
|||
nopCommerce: free and open-source eCommerce solution
|
||||
nopCommerce: free and open-source eCommerce solution[](https://travis-ci.com/nopSolutions/nopCommerce)
|
||||
===========
|
||||
|
||||
[nopCommerce](https://www.nopcommerce.com/?utm_source=github&utm_medium=content&utm_campaign=homepage) is the best open-source eCommerce platform. nopCommerce is free, and it is the most popular ASP.NET Core shopping cart.
|
||||
[nopCommerce](https://www.nopcommerce.com/?utm_source=github&utm_medium=content&utm_campaign=homepage) is the best open-source eCommerce shopping cart solution. nopCommerce is free, and it is the most popular ASP.NET eCommerce platform.
|
||||
|
||||

|
||||
|
||||
### Key features ###
|
||||
The product is being developed and supported by the professional team since 2008.
|
||||
|
||||
* The product is being developed and supported by the professional team since 2008.
|
||||
* nopCommerce has been downloaded more than 3,000,000 times.
|
||||
* The active developer community has more than 250,000 members.
|
||||
* nopCommerce runs on .NET 8 with an MS SQL 2012 (or higher) backend database.
|
||||
* nopCommerce is cross-platform, and you can run it on Windows, Linux, or Mac.
|
||||
* nopCommerce supports Docker out of the box, so you can easily run nopCommerce on a Linux machine.
|
||||
* nopCommerce supports PostgreSQL and MySQL databases.
|
||||
* nopCommerce fully supports web farms. You can read more about it [here](https://docs.nopcommerce.com/en/developer/tutorials/web-farms.html?utm_source=github&utm_medium=referral&utm_campaign=documentation&utm_content=text).
|
||||
* All methods in nopCommerce are async.
|
||||
* nopCommerce supports multi-factor authentication out of the box.
|
||||
* Start our [online course for developers](https://nopcommerce.com/training?utm_source=github&utm_medium=referral&utm_campaign=course&utm_content=text) and get the practical and technical skills you need to run and customize nopCommerce websites.
|
||||
nopCommerce has been downloaded more than 3,000,000 times.
|
||||
|
||||
The active developer community has more than 250,000 members.
|
||||
|
||||
nopCommerce runs on ASP.NET Core 5 with an MS SQL 2012 (or higher) backend database.
|
||||
|
||||
nopCommerce is cross-platform, and you can run it on Windows, Linux, or Mac.
|
||||
|
||||
nopCommerce supports Docker and MySQL out of the box, so you can easily run nopCommerce on a Linux machine.
|
||||
|
||||
nopCommerce supports PostgreSQL database.
|
||||
|
||||
nopCommerce fully supports web farms. You can read more about it [here](https://docs.nopcommerce.com/en/developer/tutorials/web-farms.html?utm_source=github&utm_medium=referral&utm_campaign=documentation&utm_content=text).
|
||||
|
||||
All methods in nopCommerce are async.
|
||||
|
||||
nopCommerce supports multi-factor authentication out of the box.
|
||||
|
||||

|
||||
|
||||
|
|
@ -25,7 +31,7 @@ nopCommerce architecture follows well-known software patterns and the best secur
|
|||
|
||||
Using the latest Microsoft technologies, nopCommerce provides high performance, stability, and security. nopCommerce is also fully compatible with Azure and web farms.
|
||||
|
||||
Our clear and detailed [documentation](https://docs.nopcommerce.com/developer/index.html?utm_source=github&utm_medium=referral&utm_campaign=documentation&utm_content=text) and [online course](https://nopcommerce.com/training?utm_source=github&utm_medium=referral&utm_campaign=course&utm_content=text) for developers will help you start with nopCommerce easily.
|
||||
Clear and detailed [documentation for developers](https://docs.nopcommerce.com/developer/index.html?utm_source=github&utm_medium=referral&utm_campaign=documentation&utm_content=text) will help you start with nopCommerce easily.
|
||||
|
||||
|
||||
### The advantages of working with nopCommerce ###
|
||||
|
|
@ -34,8 +40,6 @@ nopCommerce offers powerful [out-of-the-box features](https://www.nopcommerce.co
|
|||
|
||||
nopCommerce is integrated with all the popular third-party services. You can find thousands of integrations on nopCommerce [Marketplace](https://www.nopcommerce.com/marketplace?utm_source=github&utm_medium=referral&utm_campaign=marketplace&utm_content=text).
|
||||
|
||||
The [Web API plugin](https://www.nopcommerce.com/web-api?utm_source=github&utm_medium=referral&utm_campaign=WebAPI&utm_content=text) by the nopCommerce team lets you build integrations with third-party services or mobile applications using REST. The Web API plugin is available with source code and covers all methods of nopCommerce: backend and frontend. You can read more about it [here](https://www.nopcommerce.com/web-api?utm_source=github&utm_medium=referral&utm_campaign=WebAPI&utm_content=text).
|
||||
|
||||
Friendly members of the [nopCommerce community](https://www.nopcommerce.com/boards?utm_source=github&utm_medium=referral&utm_campaign=forum&utm_content=text) will always help with advice and share their experiences. nopCommerce core development team provides [professional support](https://www.nopcommerce.com/nopcommerce-premium-support-services?utm_source=github&utm_medium=referral&utm_campaign=premium_support&utm_content=text) within 24 hours.
|
||||
|
||||
|
||||
|
|
@ -45,7 +49,7 @@ Evaluate the functionality and convenience of nopCommerce as a customer and stor
|
|||
|
||||
Front End | Admin area
|
||||
----|------
|
||||
[](https://demo.nopcommerce.com?utm_source=github&utm_medium=referral&utm_campaign=demo_store&utm_content=button) | [](https://admin-demo.nopcommerce.com/admin?utm_source=github&utm_medium=referral&utm_campaign=demo_store&utm_content=button)
|
||||
[](https://frontend.nopcommerce.com?utm_source=github&utm_medium=referral&utm_campaign=demo_store&utm_content=button) | [](https://admin-demo.nopcommerce.com/admin?utm_source=github&utm_medium=referral&utm_campaign=demo_store&utm_content=button)
|
||||
|
||||
|
||||
### nopCommerce resources ###
|
||||
|
|
@ -54,9 +58,7 @@ nopCommerce official site: [https://www.nopcommerce.com](https://www.nopcommerce
|
|||
|
||||
* [Demo store](https://www.nopcommerce.com/demo?utm_source=github&utm_medium=referral&utm_campaign=demo_store&utm_content=links)
|
||||
* [Download nopCommerce](https://www.nopcommerce.com/download-nopcommerce?utm_source=github&utm_medium=referral&utm_campaign=download_nop&utm_content=links)
|
||||
* [Online course for developers](https://nopcommerce.com/training?utm_source=github&utm_medium=referral&utm_campaign=course&utm_content=links)
|
||||
* [Feature list](https://www.nopcommerce.com/features?utm_source=github&utm_medium=referral&utm_campaign=features&utm_content=links)
|
||||
* [Web API plugin](https://www.nopcommerce.com/web-api?utm_source=github&utm_medium=referral&utm_campaign=WebAPI&utm_content=links)
|
||||
* [nopCommerce documentation](https://docs.nopcommerce.com?utm_source=github&utm_medium=referral&utm_campaign=documentation&utm_content=links)
|
||||
* [Community forums](https://www.nopcommerce.com/boards?utm_source=github&utm_medium=referral&utm_campaign=forum&utm_content=links)
|
||||
* [Premium support services](https://www.nopcommerce.com/nopcommerce-premium-support-services?utm_source=github&utm_medium=referral&utm_campaign=premium_support&utm_content=links)
|
||||
|
|
@ -77,4 +79,4 @@ Create a new graphical theme or develop a new plugin or integration and sell it
|
|||
|
||||
### Contribute ###
|
||||
|
||||
As a free and open-source project, we are very grateful to everyone who helps us to develop nopCommerce. Please find more details about the options and bonuses for contributors at [contribute page](https://www.nopcommerce.com/contribute?utm_source=github&utm_medium=referral&utm_campaign=contribute&utm_content=text).
|
||||
As a free and open-source project, we are very grateful to everyone who helps us to develop nopCommerce. Please find more details about the options and bonuses for contributors at [сontribute page](https://www.nopcommerce.com/contribute?utm_source=github&utm_medium=referral&utm_campaign=contribute&utm_content=text).
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ services:
|
|||
build: .
|
||||
container_name: nopcommerce
|
||||
ports:
|
||||
- "8010:80"
|
||||
- "80:80"
|
||||
depends_on:
|
||||
- nopcommerce_database
|
||||
nopcommerce_database:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"sdk": {
|
||||
"version": "8.0.204",
|
||||
"version": "6.0.101",
|
||||
"rollForward": "latestFeature",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,11 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Copyright>Copyright © Nop Solutions, Ltd</Copyright>
|
||||
<Company>Nop Solutions, Ltd</Company>
|
||||
<Authors>Nop Solutions, Ltd</Authors>
|
||||
<PackageLicenseUrl>https://www.nopcommerce.com/license</PackageLicenseUrl>
|
||||
<PackageLicenseUrl>http://www.nopcommerce.com/licensev3.aspx</PackageLicenseUrl>
|
||||
<PackageProjectUrl>http://www.nopcommerce.com/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/nopSolutions/nopCommerce</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net8.0",
|
||||
"tfm": "net6.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "8.0.0"
|
||||
"version": "6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
namespace ClearPluginAssemblies
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace ClearPluginAssemblies
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
|
|
@ -38,7 +42,7 @@
|
|||
File.Delete(pdbfilePath);
|
||||
}
|
||||
|
||||
if (directoryInfo.GetFiles().Length == 0 && directoryInfo.GetDirectories().Length == 0 && !saveLocalesFolders)
|
||||
if (!directoryInfo.GetFiles().Any() && !directoryInfo.GetDirectories().Any() && !saveLocalesFolders)
|
||||
directoryInfo.Delete(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +59,7 @@
|
|||
var pluginPaths = string.Empty;
|
||||
var saveLocalesFolders = true;
|
||||
|
||||
var settings = args.FirstOrDefault(a => a.Contains('|')) ?? string.Empty;
|
||||
var settings = args.FirstOrDefault(a => a.Contains("|")) ?? string.Empty;
|
||||
if(string.IsNullOrEmpty(settings))
|
||||
return;
|
||||
|
||||
|
|
@ -75,7 +79,7 @@
|
|||
pluginPaths = value;
|
||||
break;
|
||||
case "SaveLocalesFolders":
|
||||
_ = bool.TryParse(value, out saveLocalesFolders);
|
||||
bool.TryParse(value, out saveLocalesFolders);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -84,13 +88,11 @@
|
|||
return;
|
||||
|
||||
var di = new DirectoryInfo(outputPath);
|
||||
var separator = Path.DirectorySeparatorChar;
|
||||
var folderToIgnore = string.Concat(separator, "Plugins", separator);
|
||||
var fileNames = di.GetFiles("*.dll", SearchOption.AllDirectories)
|
||||
.Where(fi => !fi.FullName.Contains(folderToIgnore))
|
||||
.Where(fi => !fi.FullName.Contains(@"\Plugins\"))
|
||||
.Select(fi => fi.Name.Replace(fi.Extension, "")).ToList();
|
||||
|
||||
if (string.IsNullOrEmpty(pluginPaths) || fileNames.Count == 0)
|
||||
if (string.IsNullOrEmpty(pluginPaths) || !fileNames.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
namespace Nop.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the base class for entities
|
||||
/// </summary>
|
||||
public abstract partial class BaseEntity
|
||||
namespace Nop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the entity identifier
|
||||
/// Represents the base class for entities
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
public abstract partial class BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the entity identifier
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +1,73 @@
|
|||
using Nop.Core.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Infrastructure;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents key for caching objects
|
||||
/// </summary>
|
||||
public partial class CacheKey
|
||||
namespace Nop.Core.Caching
|
||||
{
|
||||
#region Ctor
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance with key and prefixes
|
||||
/// Represents key for caching objects
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
/// <param name="prefixes">Prefixes for remove by prefix functionality</param>
|
||||
public CacheKey(string key, params string[] prefixes)
|
||||
public partial class CacheKey
|
||||
{
|
||||
Key = key;
|
||||
Prefixes.AddRange(prefixes.Where(prefix => !string.IsNullOrEmpty(prefix)));
|
||||
}
|
||||
#region Ctor
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Initialize a new instance with key and prefixes
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
/// <param name="prefixes">Prefixes for remove by prefix functionality</param>
|
||||
public CacheKey(string key, params string[] prefixes)
|
||||
{
|
||||
Key = key;
|
||||
Prefixes.AddRange(prefixes.Where(prefix => !string.IsNullOrEmpty(prefix)));
|
||||
}
|
||||
|
||||
#region Methods
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance from the current one and fill it with passed parameters
|
||||
/// </summary>
|
||||
/// <param name="createCacheKeyParameters">Function to create parameters</param>
|
||||
/// <param name="keyObjects">Objects to create parameters</param>
|
||||
/// <returns>Cache key</returns>
|
||||
public virtual CacheKey Create(Func<object, object> createCacheKeyParameters, params object[] keyObjects)
|
||||
{
|
||||
var cacheKey = new CacheKey(Key, Prefixes.ToArray());
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance from the current one and fill it with passed parameters
|
||||
/// </summary>
|
||||
/// <param name="createCacheKeyParameters">Function to create parameters</param>
|
||||
/// <param name="keyObjects">Objects to create parameters</param>
|
||||
/// <returns>Cache key</returns>
|
||||
public virtual CacheKey Create(Func<object, object> createCacheKeyParameters, params object[] keyObjects)
|
||||
{
|
||||
var cacheKey = new CacheKey(Key, Prefixes.ToArray());
|
||||
|
||||
if (!keyObjects.Any())
|
||||
return cacheKey;
|
||||
|
||||
cacheKey.Key = string.Format(cacheKey.Key, keyObjects.Select(createCacheKeyParameters).ToArray());
|
||||
|
||||
for (var i = 0; i < cacheKey.Prefixes.Count; i++)
|
||||
cacheKey.Prefixes[i] = string.Format(cacheKey.Prefixes[i], keyObjects.Select(createCacheKeyParameters).ToArray());
|
||||
|
||||
if (!keyObjects.Any())
|
||||
return cacheKey;
|
||||
}
|
||||
|
||||
cacheKey.Key = string.Format(cacheKey.Key, keyObjects.Select(createCacheKeyParameters).ToArray());
|
||||
#endregion
|
||||
|
||||
for (var i = 0; i < cacheKey.Prefixes.Count; i++)
|
||||
cacheKey.Prefixes[i] = string.Format(cacheKey.Prefixes[i], keyObjects.Select(createCacheKeyParameters).ToArray());
|
||||
#region Properties
|
||||
|
||||
return cacheKey;
|
||||
/// <summary>
|
||||
/// Gets or sets a cache key
|
||||
/// </summary>
|
||||
public string Key { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets prefixes for remove by prefix functionality
|
||||
/// </summary>
|
||||
public List<string> Prefixes { get; protected set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a cache time in minutes
|
||||
/// </summary>
|
||||
public int CacheTime { get; set; } = Singleton<AppSettings>.Instance.Get<CacheConfig>().DefaultCacheTime;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a cache key
|
||||
/// </summary>
|
||||
public string Key { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets prefixes for remove by prefix functionality
|
||||
/// </summary>
|
||||
public List<string> Prefixes { get; protected set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a cache time in minutes
|
||||
/// </summary>
|
||||
public int CacheTime { get; set; } = Singleton<AppSettings>.Instance.Get<CacheConfig>().DefaultCacheTime;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
using Nop.Core.Infrastructure;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Cache key manager
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class should be registered on IoC as singleton instance
|
||||
/// </remarks>
|
||||
public partial class CacheKeyManager : ICacheKeyManager
|
||||
{
|
||||
protected readonly IConcurrentCollection<byte> _keys;
|
||||
|
||||
public CacheKeyManager(IConcurrentCollection<byte> keys)
|
||||
{
|
||||
_keys = keys;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the key
|
||||
/// </summary>
|
||||
/// <param name="key">The key to add</param>
|
||||
public void AddKey(string key)
|
||||
{
|
||||
_keys.Add(key, default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the key
|
||||
/// </summary>
|
||||
/// <param name="key">The key to remove</param>
|
||||
public void RemoveKey(string key)
|
||||
{
|
||||
_keys.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all keys
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_keys.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove keys by prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Prefix to delete keys</param>
|
||||
/// <returns>The list of removed keys</returns>
|
||||
public IEnumerable<string> RemoveByPrefix(string prefix)
|
||||
{
|
||||
if (!_keys.Prune(prefix, out var subtree) || subtree?.Keys == null)
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
return subtree.Keys;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of keys
|
||||
/// </summary>
|
||||
public IEnumerable<string> Keys => _keys.Keys;
|
||||
}
|
||||
|
|
@ -1,115 +1,133 @@
|
|||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Nop.Core.Configuration;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the default cache key service implementation
|
||||
/// </summary>
|
||||
public abstract partial class CacheKeyService : ICacheKeyService
|
||||
namespace Nop.Core.Caching
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected readonly AppSettings _appSettings;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
protected CacheKeyService(AppSettings appSettings)
|
||||
{
|
||||
_appSettings = appSettings;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Prepare the cache key prefix
|
||||
/// Represents the default cache key service implementation
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
protected virtual string PrepareKeyPrefix(string prefix, params object[] prefixParameters)
|
||||
public abstract partial class CacheKeyService
|
||||
{
|
||||
return prefixParameters?.Any() ?? false
|
||||
? string.Format(prefix, prefixParameters.Select(CreateCacheKeyParameters).ToArray())
|
||||
: prefix;
|
||||
}
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Create the hash value of the passed identifiers
|
||||
/// </summary>
|
||||
/// <param name="ids">Collection of identifiers</param>
|
||||
/// <returns>String hash value</returns>
|
||||
protected virtual string CreateIdsHash(IEnumerable<int> ids)
|
||||
{
|
||||
var identifiers = ids.ToList();
|
||||
/// <summary>
|
||||
/// Gets an algorithm used to create the hash value of identifiers need to cache
|
||||
/// </summary>
|
||||
private string HashAlgorithm => "SHA1";
|
||||
|
||||
if (!identifiers.Any())
|
||||
return string.Empty;
|
||||
#endregion
|
||||
|
||||
var identifiersString = string.Join(", ", identifiers.OrderBy(id => id));
|
||||
return HashHelper.CreateHash(Encoding.UTF8.GetBytes(identifiersString), HashAlgorithm);
|
||||
}
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Converts an object to cache parameter
|
||||
/// </summary>
|
||||
/// <param name="parameter">Object to convert</param>
|
||||
/// <returns>Cache parameter</returns>
|
||||
protected virtual object CreateCacheKeyParameters(object parameter)
|
||||
{
|
||||
return parameter switch
|
||||
protected readonly AppSettings _appSettings;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
protected CacheKeyService(AppSettings appSettings)
|
||||
{
|
||||
null => "null",
|
||||
IEnumerable<int> ids => CreateIdsHash(ids),
|
||||
IEnumerable<BaseEntity> entities => CreateIdsHash(entities.Select(entity => entity.Id)),
|
||||
BaseEntity entity => entity.Id,
|
||||
decimal param => param.ToString(CultureInfo.InvariantCulture),
|
||||
_ => parameter
|
||||
};
|
||||
_appSettings = appSettings;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Prepare the cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
protected virtual string PrepareKeyPrefix(string prefix, params object[] prefixParameters)
|
||||
{
|
||||
return prefixParameters?.Any() ?? false
|
||||
? string.Format(prefix, prefixParameters.Select(CreateCacheKeyParameters).ToArray())
|
||||
: prefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the hash value of the passed identifiers
|
||||
/// </summary>
|
||||
/// <param name="ids">Collection of identifiers</param>
|
||||
/// <returns>String hash value</returns>
|
||||
protected virtual string CreateIdsHash(IEnumerable<int> ids)
|
||||
{
|
||||
var identifiers = ids.ToList();
|
||||
|
||||
if (!identifiers.Any())
|
||||
return string.Empty;
|
||||
|
||||
var identifiersString = string.Join(", ", identifiers.OrderBy(id => id));
|
||||
return HashHelper.CreateHash(Encoding.UTF8.GetBytes(identifiersString), HashAlgorithm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an object to cache parameter
|
||||
/// </summary>
|
||||
/// <param name="parameter">Object to convert</param>
|
||||
/// <returns>Cache parameter</returns>
|
||||
protected virtual object CreateCacheKeyParameters(object parameter)
|
||||
{
|
||||
return parameter switch
|
||||
{
|
||||
null => "null",
|
||||
IEnumerable<int> ids => CreateIdsHash(ids),
|
||||
IEnumerable<BaseEntity> entities => CreateIdsHash(entities.Select(entity => entity.Id)),
|
||||
BaseEntity entity => entity.Id,
|
||||
decimal param => param.ToString(CultureInfo.InvariantCulture),
|
||||
_ => parameter
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
public virtual CacheKey PrepareKey(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
return cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key using the default cache time and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
public virtual CacheKey PrepareKeyForDefaultCache(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
var key = cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters);
|
||||
|
||||
key.CacheTime = _appSettings.Get<CacheConfig>().DefaultCacheTime;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key using the short cache time and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
public virtual CacheKey PrepareKeyForShortTermCache(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
var key = cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters);
|
||||
|
||||
key.CacheTime = _appSettings.Get<CacheConfig>().ShortTermCacheTime;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
public virtual CacheKey PrepareKey(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
return cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key using the default cache time and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
public virtual CacheKey PrepareKeyForDefaultCache(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
var key = cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters);
|
||||
|
||||
key.CacheTime = _appSettings.Get<CacheConfig>().DefaultCacheTime;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets an algorithm used to create the hash value of identifiers need to cache
|
||||
/// </summary>
|
||||
protected string HashAlgorithm => "SHA1";
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
namespace Nop.Core.Caching;
|
||||
|
||||
public static class CachingExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it.
|
||||
/// NOTE: this method is only kept for backwards compatibility: the async overload is preferred!
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="cacheManager">Cache manager</param>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>The cached value associated with the specified key</returns>
|
||||
public static T Get<T>(this IStaticCacheManager cacheManager, CacheKey key, Func<T> acquire)
|
||||
{
|
||||
return cacheManager.GetAsync(key, acquire).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="cacheManager">Cache manager</param>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
public static void RemoveByPrefix(this IStaticCacheManager cacheManager, string prefix, params object[] prefixParameters)
|
||||
{
|
||||
cacheManager.RemoveByPrefixAsync(prefix, prefixParameters).Wait();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
public partial class DistributedCacheLocker : ILocker
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected static readonly string _running = JsonConvert.SerializeObject(TaskStatus.Running);
|
||||
protected readonly IDistributedCache _distributedCache;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public DistributedCacheLocker(IDistributedCache distributedCache)
|
||||
{
|
||||
_distributedCache = distributedCache;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Performs some asynchronous task with exclusive lock
|
||||
/// </summary>
|
||||
/// <param name="resource">The key we are locking on</param>
|
||||
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||
/// <param name="action">Asynchronous task to be performed with locking</param>
|
||||
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||
public async Task<bool> PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func<Task> action)
|
||||
{
|
||||
//ensure that lock is acquired
|
||||
if (!string.IsNullOrEmpty(await _distributedCache.GetStringAsync(resource)))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
await _distributedCache.SetStringAsync(resource, resource, new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = expirationTime
|
||||
});
|
||||
|
||||
await action();
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//release lock even if action fails
|
||||
await _distributedCache.RemoveAsync(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
|
||||
/// others that the task is running and stop them from starting the same task.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the background task</param>
|
||||
/// <param name="expirationTime">The time after which the heartbeat key will automatically be expired. Should be longer than <paramref name="heartbeatInterval"/></param>
|
||||
/// <param name="heartbeatInterval">The interval at which to update the heartbeat, if required by the implementation</param>
|
||||
/// <param name="action">Asynchronous background task to be performed</param>
|
||||
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||
public async Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(await _distributedCache.GetStringAsync(key)))
|
||||
return;
|
||||
|
||||
var tokenSource = cancellationTokenSource ?? new CancellationTokenSource();
|
||||
|
||||
try
|
||||
{
|
||||
// run heartbeat early to minimize risk of multiple execution
|
||||
await _distributedCache.SetStringAsync(
|
||||
key,
|
||||
_running,
|
||||
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime },
|
||||
token: tokenSource.Token);
|
||||
|
||||
await using var timer = new Timer(
|
||||
callback: _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
tokenSource.Token.ThrowIfCancellationRequested();
|
||||
var status = _distributedCache.GetString(key);
|
||||
if (!string.IsNullOrEmpty(status) && JsonConvert.DeserializeObject<TaskStatus>(status) ==
|
||||
TaskStatus.Canceled)
|
||||
{
|
||||
tokenSource.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
_distributedCache.SetString(
|
||||
key,
|
||||
_running,
|
||||
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime });
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
},
|
||||
state: null,
|
||||
dueTime: 0,
|
||||
period: (int)heartbeatInterval.TotalMilliseconds);
|
||||
|
||||
await action(tokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
finally
|
||||
{
|
||||
await _distributedCache.RemoveAsync(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
|
||||
/// </summary>
|
||||
/// <param name="key">The task's key</param>
|
||||
/// <param name="expirationTime">The time after which the task will be considered stopped due to system shutdown or other causes,
|
||||
/// even if not explicitly canceled.</param>
|
||||
/// <returns>A task that represents requesting cancellation of the task. Note that the completion of this task does not
|
||||
/// necessarily imply that the task has been canceled, only that cancellation has been requested.</returns>
|
||||
public async Task CancelTaskAsync(string key, TimeSpan expirationTime)
|
||||
{
|
||||
var status = await _distributedCache.GetStringAsync(key);
|
||||
if (!string.IsNullOrEmpty(status) &&
|
||||
JsonConvert.DeserializeObject<TaskStatus>(status) != TaskStatus.Canceled)
|
||||
await _distributedCache.SetStringAsync(
|
||||
key,
|
||||
JsonConvert.SerializeObject(TaskStatus.Canceled),
|
||||
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a background task is running.
|
||||
/// </summary>
|
||||
/// <param name="key">The task's key</param>
|
||||
/// <returns>A task that resolves to true if the background task is running; otherwise false</returns>
|
||||
public async Task<bool> IsTaskRunningAsync(string key)
|
||||
{
|
||||
return !string.IsNullOrEmpty(await _distributedCache.GetStringAsync(key));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,283 +1,499 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Newtonsoft.Json;
|
||||
using Nito.AsyncEx;
|
||||
using Nop.Core.ComponentModel;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Infrastructure;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a base distributed cache
|
||||
/// </summary>
|
||||
public abstract class DistributedCacheManager : CacheKeyService, IStaticCacheManager
|
||||
namespace Nop.Core.Caching
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Holds the keys known by this nopCommerce instance
|
||||
/// Represents a distributed cache
|
||||
/// </summary>
|
||||
protected readonly ICacheKeyManager _localKeyManager;
|
||||
protected readonly IDistributedCache _distributedCache;
|
||||
protected readonly IConcurrentCollection<object> _concurrentCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Holds ongoing acquisition tasks, used to avoid duplicating work
|
||||
/// </summary>
|
||||
protected readonly ConcurrentDictionary<string, Lazy<Task<object>>> _ongoing = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
protected DistributedCacheManager(AppSettings appSettings,
|
||||
IDistributedCache distributedCache,
|
||||
ICacheKeyManager cacheKeyManager,
|
||||
IConcurrentCollection<object> concurrentCollection)
|
||||
: base(appSettings)
|
||||
public partial class DistributedCacheManager: CacheKeyService, ILocker, IStaticCacheManager
|
||||
{
|
||||
_distributedCache = distributedCache;
|
||||
_localKeyManager = cacheKeyManager;
|
||||
_concurrentCollection = concurrentCollection;
|
||||
}
|
||||
#region Fields
|
||||
|
||||
#endregion
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private readonly PerRequestCache _perRequestCache;
|
||||
private static readonly List<string> _keys;
|
||||
private static readonly AsyncLock _locker;
|
||||
|
||||
#region Utilities
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Clear all data on this instance
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
protected virtual void ClearInstanceData()
|
||||
{
|
||||
_concurrentCollection.Clear();
|
||||
_localKeyManager.Clear();
|
||||
}
|
||||
#region Ctor
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
/// <returns>The removed keys</returns>
|
||||
protected virtual IEnumerable<string> RemoveByPrefixInstanceData(string prefix, params object[] prefixParameters)
|
||||
{
|
||||
var keyPrefix = PrepareKeyPrefix(prefix, prefixParameters);
|
||||
_concurrentCollection.Prune(keyPrefix, out _);
|
||||
|
||||
return _localKeyManager.RemoveByPrefix(keyPrefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepare cache entry options for the passed key
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>Cache entry options</returns>
|
||||
protected virtual DistributedCacheEntryOptions PrepareEntryOptions(CacheKey key)
|
||||
{
|
||||
//set expiration time for the passed cache key
|
||||
return new DistributedCacheEntryOptions
|
||||
static DistributedCacheManager()
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(key.CacheTime)
|
||||
};
|
||||
}
|
||||
_locker = new AsyncLock();
|
||||
_keys = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the local cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="value">Value for caching</param>
|
||||
protected virtual void SetLocal(string key, object value)
|
||||
{
|
||||
_concurrentCollection.Add(key, value);
|
||||
_localKeyManager.AddKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
protected virtual void RemoveLocal(string key)
|
||||
{
|
||||
_concurrentCollection.Remove(key);
|
||||
_localKeyManager.RemoveKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try get a cached item. If it's not in the cache yet, then return default object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
protected virtual async Task<(bool isSet, T item)> TryGetItemAsync<T>(string key)
|
||||
{
|
||||
var json = await _distributedCache.GetStringAsync(key);
|
||||
|
||||
return string.IsNullOrEmpty(json)
|
||||
? (false, default)
|
||||
: (true, item: JsonConvert.DeserializeObject<T>(json));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="removeFromInstance">Remove from instance</param>
|
||||
protected virtual async Task RemoveAsync(string key, bool removeFromInstance = true)
|
||||
{
|
||||
_ongoing.TryRemove(key, out _);
|
||||
await _distributedCache.RemoveAsync(key);
|
||||
|
||||
if (!removeFromInstance)
|
||||
return;
|
||||
|
||||
RemoveLocal(key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
await RemoveAsync(PrepareKey(cacheKey, cacheKeyParameters).Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire)
|
||||
{
|
||||
if (_concurrentCollection.TryGetValue(key.Key, out var data))
|
||||
return (T)data;
|
||||
|
||||
var lazy = _ongoing.GetOrAdd(key.Key, _ => new(async () => await acquire(), true));
|
||||
var setTask = Task.CompletedTask;
|
||||
|
||||
try
|
||||
public DistributedCacheManager(AppSettings appSettings, IDistributedCache distributedCache, IHttpContextAccessor httpContextAccessor) :base(appSettings)
|
||||
{
|
||||
if (lazy.IsValueCreated)
|
||||
return (T)await lazy.Value;
|
||||
_distributedCache = distributedCache;
|
||||
_perRequestCache = new PerRequestCache(httpContextAccessor);
|
||||
}
|
||||
|
||||
var (isSet, item) = await TryGetItemAsync<T>(key.Key);
|
||||
if (!isSet)
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Prepare cache entry options for the passed key
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>Cache entry options</returns>
|
||||
private DistributedCacheEntryOptions PrepareEntryOptions(CacheKey key)
|
||||
{
|
||||
//set expiration time for the passed cache key
|
||||
var options = new DistributedCacheEntryOptions
|
||||
{
|
||||
item = (T)await lazy.Value;
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(key.CacheTime)
|
||||
};
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
if (key.CacheTime == 0 || item == null)
|
||||
return item;
|
||||
/// <summary>
|
||||
/// Try to get the cached item
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the flag which indicate is the key exists in the cache, cached item or default value
|
||||
/// </returns>
|
||||
private async Task<(bool isSet, T item)> TryGetItemAsync<T>(CacheKey key)
|
||||
{
|
||||
var json = await _distributedCache.GetStringAsync(key.Key);
|
||||
|
||||
setTask = _distributedCache.SetStringAsync(
|
||||
key.Key,
|
||||
JsonConvert.SerializeObject(item),
|
||||
PrepareEntryOptions(key));
|
||||
if (string.IsNullOrEmpty(json))
|
||||
return (false, default);
|
||||
|
||||
var item = JsonConvert.DeserializeObject<T>(json);
|
||||
_perRequestCache.Set(key.Key, item);
|
||||
|
||||
using var _ = await _locker.LockAsync();
|
||||
_keys.Add(key.Key);
|
||||
|
||||
return (true, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the cached item
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>Flag which indicate is the key exists in the cache, cached item or default value</returns>
|
||||
private (bool isSet, T item) TryGetItem<T>(CacheKey key)
|
||||
{
|
||||
var json = _distributedCache.GetString(key.Key);
|
||||
|
||||
if (string.IsNullOrEmpty(json))
|
||||
return (false, default);
|
||||
|
||||
var item = JsonConvert.DeserializeObject<T>(json);
|
||||
_perRequestCache.Set(key.Key, item);
|
||||
|
||||
using var _ = _locker.Lock();
|
||||
_keys.Add(key.Key);
|
||||
|
||||
return (true, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
private void Set(CacheKey key, object data)
|
||||
{
|
||||
if ((key?.CacheTime ?? 0) <= 0 || data == null)
|
||||
return;
|
||||
|
||||
_distributedCache.SetString(key.Key, JsonConvert.SerializeObject(data), PrepareEntryOptions(key));
|
||||
_perRequestCache.Set(key.Key, data);
|
||||
|
||||
using var _ = _locker.Lock();
|
||||
_keys.Add(key.Key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing,
|
||||
/// releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire)
|
||||
{
|
||||
//little performance workaround here:
|
||||
//we use "PerRequestCache" to cache a loaded object in memory for the current HTTP request.
|
||||
//this way we won't connect to Redis server many times per HTTP request (e.g. each time to load a locale or setting)
|
||||
if (_perRequestCache.IsSet(key.Key))
|
||||
return _perRequestCache.Get(key.Key, () => default(T));
|
||||
|
||||
if (key.CacheTime <= 0)
|
||||
return await acquire();
|
||||
|
||||
var (isSet, item) = await TryGetItemAsync<T>(key);
|
||||
|
||||
if (isSet)
|
||||
return item;
|
||||
|
||||
var result = await acquire();
|
||||
|
||||
if (result != null)
|
||||
await SetAsync(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, Func<T> acquire)
|
||||
{
|
||||
//little performance workaround here:
|
||||
//we use "PerRequestCache" to cache a loaded object in memory for the current HTTP request.
|
||||
//this way we won't connect to Redis server many times per HTTP request (e.g. each time to load a locale or setting)
|
||||
if (_perRequestCache.IsSet(key.Key))
|
||||
return _perRequestCache.Get(key.Key, () => default(T));
|
||||
|
||||
if (key.CacheTime <= 0)
|
||||
return acquire();
|
||||
|
||||
var (isSet, item) = await TryGetItemAsync<T>(key);
|
||||
|
||||
if (isSet)
|
||||
return item;
|
||||
|
||||
var result = acquire();
|
||||
|
||||
if (result != null)
|
||||
await SetAsync(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>The cached value associated with the specified key</returns>
|
||||
public T Get<T>(CacheKey key, Func<T> acquire)
|
||||
{
|
||||
//little performance workaround here:
|
||||
//we use "PerRequestCache" to cache a loaded object in memory for the current HTTP request.
|
||||
//this way we won't connect to Redis server many times per HTTP request (e.g. each time to load a locale or setting)
|
||||
if (_perRequestCache.IsSet(key.Key))
|
||||
return _perRequestCache.Get(key.Key, () => default(T));
|
||||
|
||||
if (key.CacheTime <= 0)
|
||||
return acquire();
|
||||
|
||||
var (isSet, item) = TryGetItem<T>(key);
|
||||
|
||||
if (isSet)
|
||||
return item;
|
||||
|
||||
var result = acquire();
|
||||
|
||||
if (result != null)
|
||||
Set(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
cacheKey = PrepareKey(cacheKey, cacheKeyParameters);
|
||||
|
||||
await _distributedCache.RemoveAsync(cacheKey.Key);
|
||||
_perRequestCache.Remove(cacheKey.Key);
|
||||
|
||||
using var _ = await _locker.LockAsync();
|
||||
_keys.Remove(cacheKey.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task SetAsync(CacheKey key, object data)
|
||||
{
|
||||
if ((key?.CacheTime ?? 0) <= 0 || data == null)
|
||||
return;
|
||||
|
||||
await _distributedCache.SetStringAsync(key.Key, JsonConvert.SerializeObject(data), PrepareEntryOptions(key));
|
||||
_perRequestCache.Set(key.Key, data);
|
||||
|
||||
using var _ = await _locker.LockAsync();
|
||||
_keys.Add(key.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters)
|
||||
{
|
||||
prefix = PrepareKeyPrefix(prefix, prefixParameters);
|
||||
_perRequestCache.RemoveByPrefix(prefix);
|
||||
|
||||
using var _ = await _locker.LockAsync();
|
||||
|
||||
foreach (var key in _keys.Where(key => key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase)).ToList())
|
||||
{
|
||||
await _distributedCache.RemoveAsync(key);
|
||||
_keys.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all cache data
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task ClearAsync()
|
||||
{
|
||||
//we can't use _perRequestCache.Clear(),
|
||||
//because HttpContext stores some server data that we should not delete
|
||||
foreach (var redisKey in _keys)
|
||||
_perRequestCache.Remove(redisKey);
|
||||
|
||||
using var _ = await _locker.LockAsync();
|
||||
|
||||
foreach (var key in _keys)
|
||||
await _distributedCache.RemoveAsync(key);
|
||||
|
||||
_keys.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform some action with exclusive lock
|
||||
/// </summary>
|
||||
/// <param name="resource">The key we are locking on</param>
|
||||
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||
/// <param name="action">Action to be performed with locking</param>
|
||||
/// <returns>True if lock was acquired and action was performed; otherwise false</returns>
|
||||
public bool PerformActionWithLock(string resource, TimeSpan expirationTime, Action action)
|
||||
{
|
||||
//ensure that lock is acquired
|
||||
if (!string.IsNullOrEmpty(_distributedCache.GetString(resource)))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
_distributedCache.SetString(resource, resource, new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = expirationTime
|
||||
});
|
||||
|
||||
//perform action
|
||||
action();
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//release lock even if action fails
|
||||
_distributedCache.Remove(resource);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested class
|
||||
|
||||
/// <summary>
|
||||
/// Represents a manager for caching during an HTTP request (short term caching)
|
||||
/// </summary>
|
||||
protected class PerRequestCache
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ReaderWriterLockSlim _lockSlim;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public PerRequestCache(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
|
||||
_lockSlim = new ReaderWriterLockSlim();
|
||||
}
|
||||
|
||||
SetLocal(key.Key, item);
|
||||
#endregion
|
||||
|
||||
return item;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = setTask.ContinueWith(_ => _ongoing.TryRemove(new KeyValuePair<string, Lazy<Task<object>>>(key.Key, lazy)));
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Get a key/value collection that can be used to share data within the scope of this request
|
||||
/// </summary>
|
||||
protected virtual IDictionary<object, object> GetItems()
|
||||
{
|
||||
return _httpContextAccessor.HttpContext?.Items;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>The cached value associated with the specified key</returns>
|
||||
public virtual T Get<T>(string key, Func<T> acquire)
|
||||
{
|
||||
IDictionary<object, object> items;
|
||||
|
||||
using (new ReaderWriteLockDisposable(_lockSlim, ReaderWriteLockType.Read))
|
||||
{
|
||||
items = GetItems();
|
||||
if (items == null)
|
||||
return acquire();
|
||||
|
||||
//item already is in cache, so return it
|
||||
if (items[key] != null)
|
||||
return (T)items[key];
|
||||
}
|
||||
|
||||
//or create it using passed function
|
||||
var result = acquire();
|
||||
|
||||
//and set in cache (if cache time is defined)
|
||||
using (new ReaderWriteLockDisposable(_lockSlim))
|
||||
items[key] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
public virtual void Set(string key, object data)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
using (new ReaderWriteLockDisposable(_lockSlim))
|
||||
{
|
||||
var items = GetItems();
|
||||
if (items == null)
|
||||
return;
|
||||
|
||||
items[key] = data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a value indicating whether the value associated with the specified key is cached
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <returns>True if item already is in cache; otherwise false</returns>
|
||||
public virtual bool IsSet(string key)
|
||||
{
|
||||
using (new ReaderWriteLockDisposable(_lockSlim, ReaderWriteLockType.Read))
|
||||
{
|
||||
var items = GetItems();
|
||||
return items?[key] != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
public virtual void Remove(string key)
|
||||
{
|
||||
using (new ReaderWriteLockDisposable(_lockSlim))
|
||||
{
|
||||
var items = GetItems();
|
||||
items?.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">String key prefix</param>
|
||||
public virtual void RemoveByPrefix(string prefix)
|
||||
{
|
||||
using (new ReaderWriteLockDisposable(_lockSlim, ReaderWriteLockType.UpgradeableRead))
|
||||
{
|
||||
var items = GetItems();
|
||||
if (items == null)
|
||||
return;
|
||||
|
||||
//get cache keys that matches pattern
|
||||
var regex = new Regex(prefix,
|
||||
RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
var matchesKeys = items.Keys.Select(p => p.ToString())
|
||||
.Where(key => regex.IsMatch(key ?? string.Empty)).ToList();
|
||||
|
||||
if (!matchesKeys.Any())
|
||||
return;
|
||||
|
||||
using (new ReaderWriteLockDisposable(_lockSlim))
|
||||
//remove matching values
|
||||
foreach (var key in matchesKeys)
|
||||
items.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public Task<T> GetAsync<T>(CacheKey key, Func<T> acquire)
|
||||
{
|
||||
return GetAsync(key, () => Task.FromResult(acquire()));
|
||||
}
|
||||
|
||||
public async Task<T> GetAsync<T>(CacheKey key, T defaultValue = default)
|
||||
{
|
||||
var value = await _distributedCache.GetStringAsync(key.Key);
|
||||
|
||||
return value != null
|
||||
? JsonConvert.DeserializeObject<T>(value)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item as an <see cref="object"/> instance, or null on a cache miss.
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key, or null if none was found
|
||||
/// </returns>
|
||||
public async Task<object> GetAsync(CacheKey key)
|
||||
{
|
||||
return await GetAsync<object>(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public async Task SetAsync<T>(CacheKey key, T data)
|
||||
{
|
||||
if (data == null || (key?.CacheTime ?? 0) <= 0)
|
||||
return;
|
||||
|
||||
var lazy = new Lazy<Task<object>>(() => Task.FromResult(data as object), true);
|
||||
|
||||
try
|
||||
{
|
||||
_ongoing.TryAdd(key.Key, lazy);
|
||||
// await the lazy task in order to force value creation instead of directly setting data
|
||||
// this way, other cache manager instances can access it while it is being set
|
||||
SetLocal(key.Key, await lazy.Value);
|
||||
await _distributedCache.SetStringAsync(key.Key, JsonConvert.SerializeObject(data), PrepareEntryOptions(key));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ongoing.TryRemove(new KeyValuePair<string, Lazy<Task<object>>>(key.Key, lazy));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public abstract Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Clear all cache data
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public abstract Task ClearAsync();
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a cache key manager
|
||||
/// </summary>
|
||||
public partial interface ICacheKeyManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Add the key
|
||||
/// </summary>
|
||||
/// <param name="key">The key to add</param>
|
||||
void AddKey(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Remove the key
|
||||
/// </summary>
|
||||
/// <param name="key">The key to remove</param>
|
||||
void RemoveKey(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Remove all keys
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Remove keys by prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Prefix to delete keys</param>
|
||||
/// <returns>The list of removed keys</returns>
|
||||
IEnumerable<string> RemoveByPrefix(string prefix);
|
||||
|
||||
/// <summary>
|
||||
/// The list of keys
|
||||
/// </summary>
|
||||
IEnumerable<string> Keys { get; }
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Cache key service interface
|
||||
/// </summary>
|
||||
public partial interface ICacheKeyService
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a copy of cache key and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
CacheKey PrepareKey(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key using the default cache time and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
CacheKey PrepareKeyForDefaultCache(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
}
|
||||
|
|
@ -1,43 +1,16 @@
|
|||
namespace Nop.Core.Caching;
|
||||
using System;
|
||||
|
||||
public partial interface ILocker
|
||||
namespace Nop.Core.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs some asynchronous task with exclusive lock
|
||||
/// </summary>
|
||||
/// <param name="resource">The key we are locking on</param>
|
||||
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||
/// <param name="action">Asynchronous task to be performed with locking</param>
|
||||
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||
Task<bool> PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func<Task> action);
|
||||
|
||||
/// <summary>
|
||||
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
|
||||
/// others that the task is running and stop them from starting the same task.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the background task</param>
|
||||
/// <param name="expirationTime">The time after which the heartbeat key will automatically be expired. Should be longer than <paramref name="heartbeatInterval"/></param>
|
||||
/// <param name="heartbeatInterval">The interval at which to update the heartbeat, if required by the implementation</param>
|
||||
/// <param name="action">Asynchronous background task to be performed</param>
|
||||
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||
Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
|
||||
/// </summary>
|
||||
/// <param name="key">The task's key</param>
|
||||
/// <param name="expirationTime">The time after which the task will be considered stopped due to system shutdown or other causes,
|
||||
/// even if not explicitly canceled.</param>
|
||||
/// <returns>A task that represents requesting cancellation of the task. Note that the completion of this task does not
|
||||
/// necessarily imply that the task has been canceled, only that cancellation has been requested.</returns>
|
||||
|
||||
Task CancelTaskAsync(string key, TimeSpan expirationTime);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a background task is running.
|
||||
/// </summary>
|
||||
/// <param name="key">The task's key</param>
|
||||
/// <returns>A task that resolves to true if the background task is running; otherwise false</returns>
|
||||
Task<bool> IsTaskRunningAsync(string key);
|
||||
}
|
||||
public interface ILocker
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform some action with exclusive lock
|
||||
/// </summary>
|
||||
/// <param name="resource">The key we are locking on</param>
|
||||
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||
/// <param name="action">Action to be performed with locking</param>
|
||||
/// <returns>True if lock was acquired and action was performed; otherwise false</returns>
|
||||
bool PerformActionWithLock(string resource, TimeSpan expirationTime, Action action);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a manager for caching during an HTTP request (short term caching)
|
||||
/// </summary>
|
||||
public partial interface IShortTermCacheManager : ICacheKeyService
|
||||
{
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
void RemoveByPrefix(string prefix, params object[] prefixParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
void Remove(string cacheKey, params object[] cacheKeyParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// /// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
Task<T> GetAsync<T>(Func<Task<T>> acquire, CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
}
|
||||
|
|
@ -1,83 +1,102 @@
|
|||
namespace Nop.Core.Caching;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a manager for caching between HTTP requests (long term caching)
|
||||
/// </summary>
|
||||
public partial interface IStaticCacheManager : IDisposable, ICacheKeyService
|
||||
namespace Nop.Core.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// Represents a manager for caching between HTTP requests (long term caching)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire);
|
||||
public interface IStaticCacheManager : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire);
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
Task<T> GetAsync<T>(CacheKey key, Func<T> acquire);
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
Task<T> GetAsync<T>(CacheKey key, Func<T> acquire);
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, return a default value
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="defaultValue">A default value to return if the key is not present in the cache</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key, or the default value if none was found
|
||||
/// </returns>
|
||||
Task<T> GetAsync<T>(CacheKey key, T defaultValue = default);
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>The cached value associated with the specified key</returns>
|
||||
T Get<T>(CacheKey key, Func<T> acquire);
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item as an <see cref="object"/> instance, or null on a cache miss.
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key, or null if none was found
|
||||
/// </returns>
|
||||
Task<object> GetAsync(CacheKey key);
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task SetAsync(CacheKey key, object data);
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task SetAsync<T>(CacheKey key, T data);
|
||||
/// <summary>
|
||||
/// Clear all cache data
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task ClearAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters);
|
||||
#region Cache key
|
||||
|
||||
/// <summary>
|
||||
/// Clear all cache data
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
Task ClearAsync();
|
||||
/// <summary>
|
||||
/// Create a copy of cache key and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
CacheKey PrepareKey(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key using the default cache time and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
CacheKey PrepareKeyForDefaultCache(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of cache key using the short cache time and fills it by passed parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>Cache key</returns>
|
||||
CacheKey PrepareKeyForShortTermCache(CacheKey cacheKey, params object[] cacheKeyParameters);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a local in-memory cache with distributed synchronization
|
||||
/// </summary>
|
||||
public partial interface ISynchronizedMemoryCache : IMemoryCache
|
||||
{
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// A distributed cache manager that locks the acquisition task
|
||||
/// </summary>
|
||||
public partial class MemoryCacheLocker : ILocker
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected readonly IMemoryCache _memoryCache;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public MemoryCacheLocker(IMemoryCache memoryCache)
|
||||
{
|
||||
_memoryCache = memoryCache;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Run action
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the background task</param>
|
||||
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||
/// <param name="action">The action to perform</param>
|
||||
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||
/// <returns></returns>
|
||||
protected virtual async Task<bool> RunAsync(string key, TimeSpan? expirationTime, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default)
|
||||
{
|
||||
var started = false;
|
||||
|
||||
try
|
||||
{
|
||||
var tokenSource = _memoryCache.GetOrCreate(key, entry => new Lazy<CancellationTokenSource>(() =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = expirationTime;
|
||||
entry.SetPriority(CacheItemPriority.NeverRemove);
|
||||
started = true;
|
||||
return cancellationTokenSource ?? new CancellationTokenSource();
|
||||
}, true))?.Value;
|
||||
|
||||
if (tokenSource != null && started)
|
||||
await action(tokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
finally
|
||||
{
|
||||
if (started)
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
|
||||
return started;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Performs some asynchronous task with exclusive lock
|
||||
/// </summary>
|
||||
/// <param name="resource">The key we are locking on</param>
|
||||
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||
/// <param name="action">Asynchronous task to be performed with locking</param>
|
||||
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||
public async Task<bool> PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func<Task> action)
|
||||
{
|
||||
return await RunAsync(resource, expirationTime, _ => action());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
|
||||
/// others that the task is running and stop them from starting the same task.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the background task</param>
|
||||
/// <param name="expirationTime">The time after which the heartbeat key will automatically be expired. Should be longer than <paramref name="heartbeatInterval"/></param>
|
||||
/// <param name="heartbeatInterval">The interval at which to update the heartbeat, if required by the implementation</param>
|
||||
/// <param name="action">Asynchronous background task to be performed</param>
|
||||
/// <param name="cancellationTokenSource">A CancellationTokenSource for manually canceling the task</param>
|
||||
/// <returns>A task that resolves true if lock was acquired and action was performed; otherwise false</returns>
|
||||
public async Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func<CancellationToken, Task> action, CancellationTokenSource cancellationTokenSource = default)
|
||||
{
|
||||
// We ignore expirationTime and heartbeatInterval here, as the cache is not shared with other instances,
|
||||
// and will be cleared on system failure anyway. The task is guaranteed to still be running as long as it is in the cache.
|
||||
await RunAsync(key, null, action, cancellationTokenSource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
|
||||
/// </summary>
|
||||
/// <param name="key">The task's key</param>
|
||||
/// <param name="expirationTime">The time after which the task will be considered stopped due to system shutdown or other causes,
|
||||
/// even if not explicitly canceled.</param>
|
||||
/// <returns>A task that represents requesting cancellation of the task. Note that the completion of this task does not
|
||||
/// necessarily imply that the task has been canceled, only that cancellation has been requested.</returns>
|
||||
public Task CancelTaskAsync(string key, TimeSpan expirationTime)
|
||||
{
|
||||
if (_memoryCache.TryGetValue(key, out Lazy<CancellationTokenSource> tokenSource))
|
||||
tokenSource.Value.Cancel();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a background task is running.
|
||||
/// </summary>
|
||||
/// <param name="key">The task's key</param>
|
||||
/// <returns>A task that resolves to true if the background task is running; otherwise false</returns>
|
||||
public Task<bool> IsTaskRunningAsync(string key)
|
||||
{
|
||||
return Task.FromResult(_memoryCache.TryGetValue(key, out _));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,290 +1,285 @@
|
|||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Nop.Core.Configuration;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a memory cache manager
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class should be registered on IoC as singleton instance
|
||||
/// </remarks>
|
||||
public partial class MemoryCacheManager : CacheKeyService, IStaticCacheManager
|
||||
namespace Nop.Core.Caching
|
||||
{
|
||||
#region Fields
|
||||
|
||||
// Flag: Has Dispose already been called?
|
||||
protected bool _disposed;
|
||||
|
||||
protected readonly IMemoryCache _memoryCache;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the keys known by this nopCommerce instance
|
||||
/// Represents a memory cache manager
|
||||
/// </summary>
|
||||
protected readonly ICacheKeyManager _keyManager;
|
||||
|
||||
protected static CancellationTokenSource _clearToken = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public MemoryCacheManager(AppSettings appSettings, IMemoryCache memoryCache, ICacheKeyManager cacheKeyManager)
|
||||
: base(appSettings)
|
||||
public partial class MemoryCacheManager : CacheKeyService, ILocker, IStaticCacheManager
|
||||
{
|
||||
_memoryCache = memoryCache;
|
||||
_keyManager = cacheKeyManager;
|
||||
}
|
||||
#region Fields
|
||||
|
||||
#endregion
|
||||
// Flag: Has Dispose already been called?
|
||||
private bool _disposed;
|
||||
|
||||
#region Utilities
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
|
||||
/// <summary>
|
||||
/// Prepare cache entry options for the passed key
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>Cache entry options</returns>
|
||||
protected virtual MemoryCacheEntryOptions PrepareEntryOptions(CacheKey key)
|
||||
{
|
||||
//set expiration time for the passed cache key
|
||||
var options = new MemoryCacheEntryOptions
|
||||
private static readonly ConcurrentDictionary<string, CancellationTokenSource> _prefixes = new();
|
||||
private static CancellationTokenSource _clearToken = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public MemoryCacheManager(AppSettings appSettings, IMemoryCache memoryCache) : base(appSettings)
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(key.CacheTime)
|
||||
};
|
||||
|
||||
//add token to clear cache entries
|
||||
options.AddExpirationToken(new CancellationChangeToken(_clearToken.Token));
|
||||
options.RegisterPostEvictionCallback(OnEviction);
|
||||
_keyManager.AddKey(key.Key);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The callback method which gets called when a cache entry expires.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the entry being evicted.</param>
|
||||
/// <param name="value">The value of the entry being evicted.</param>
|
||||
/// <param name="reason">The <see cref="EvictionReason"/>.</param>
|
||||
/// <param name="state">The information that was passed when registering the callback.</param>
|
||||
protected virtual void OnEviction(object key, object value, EvictionReason reason, object state)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
// we clean up after ourselves elsewhere
|
||||
case EvictionReason.Removed:
|
||||
case EvictionReason.Replaced:
|
||||
case EvictionReason.TokenExpired:
|
||||
break;
|
||||
// if the entry was evicted by the cache itself, we remove the key
|
||||
default:
|
||||
_keyManager.RemoveKey(key as string);
|
||||
break;
|
||||
_memoryCache = memoryCache;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Prepare cache entry options for the passed key
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>Cache entry options</returns>
|
||||
private MemoryCacheEntryOptions PrepareEntryOptions(CacheKey key)
|
||||
{
|
||||
//set expiration time for the passed cache key
|
||||
var options = new MemoryCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(key.CacheTime)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
var key = PrepareKey(cacheKey, cacheKeyParameters).Key;
|
||||
_memoryCache.Remove(key);
|
||||
_keyManager.RemoveKey(key);
|
||||
//add tokens to clear cache entries
|
||||
options.AddExpirationToken(new CancellationChangeToken(_clearToken.Token));
|
||||
foreach (var keyPrefix in key.Prefixes.ToList())
|
||||
{
|
||||
var tokenSource = _prefixes.GetOrAdd(keyPrefix, new CancellationTokenSource());
|
||||
options.AddExpirationToken(new CancellationChangeToken(tokenSource.Token));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire)
|
||||
{
|
||||
if ((key?.CacheTime ?? 0) <= 0)
|
||||
return await acquire();
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
private void Remove(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
cacheKey = PrepareKey(cacheKey, cacheKeyParameters);
|
||||
_memoryCache.Remove(cacheKey.Key);
|
||||
}
|
||||
|
||||
var task = _memoryCache.GetOrCreate(
|
||||
key.Key,
|
||||
entry =>
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
private void Set(CacheKey key, object data)
|
||||
{
|
||||
if ((key?.CacheTime ?? 0) <= 0 || data == null)
|
||||
return;
|
||||
|
||||
_memoryCache.Set(key.Key, data, PrepareEntryOptions(key));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
Remove(cacheKey, cacheKeyParameters);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, Func<Task<T>> acquire)
|
||||
{
|
||||
if ((key?.CacheTime ?? 0) <= 0)
|
||||
return await acquire();
|
||||
|
||||
if (_memoryCache.TryGetValue(key.Key, out T result))
|
||||
return result;
|
||||
|
||||
result = await acquire();
|
||||
|
||||
if(result != null)
|
||||
await SetAsync(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, Func<T> acquire)
|
||||
{
|
||||
if ((key?.CacheTime ?? 0) <= 0)
|
||||
return acquire();
|
||||
|
||||
var result = _memoryCache.GetOrCreate(key.Key, entry =>
|
||||
{
|
||||
entry.SetOptions(PrepareEntryOptions(key));
|
||||
return new Lazy<Task<T>>(acquire, true);
|
||||
|
||||
return acquire();
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var data = await task!.Value;
|
||||
|
||||
//if a cached function return null, remove it from the cache
|
||||
if (data == null)
|
||||
//do not cache null value
|
||||
if (result == null)
|
||||
await RemoveAsync(key);
|
||||
|
||||
return data;
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>The cached value associated with the specified key</returns>
|
||||
public T Get<T>(CacheKey key, Func<T> acquire)
|
||||
{
|
||||
//if a cached function throws an exception, remove it from the cache
|
||||
await RemoveAsync(key);
|
||||
if ((key?.CacheTime ?? 0) <= 0)
|
||||
return acquire();
|
||||
|
||||
if (ex is NullReferenceException)
|
||||
return default;
|
||||
if (_memoryCache.TryGetValue(key.Key, out T result))
|
||||
return result;
|
||||
|
||||
throw;
|
||||
result = acquire();
|
||||
|
||||
if (result != null)
|
||||
Set(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, return a default value
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="defaultValue">A default value to return if the key is not present in the cache</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key, or the default value if none was found
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, T defaultValue = default)
|
||||
{
|
||||
var value = _memoryCache.Get<Lazy<Task<T>>>(key.Key)?.Value;
|
||||
|
||||
try
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task SetAsync(CacheKey key, object data)
|
||||
{
|
||||
return value != null ? await value : defaultValue;
|
||||
Set(key, data);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
|
||||
/// <summary>
|
||||
/// Perform some action with exclusive in-memory lock
|
||||
/// </summary>
|
||||
/// <param name="key">The key we are locking on</param>
|
||||
/// <param name="expirationTime">The time after which the lock will automatically be expired</param>
|
||||
/// <param name="action">Action to be performed with locking</param>
|
||||
/// <returns>True if lock was acquired and action was performed; otherwise false</returns>
|
||||
public bool PerformActionWithLock(string key, TimeSpan expirationTime, Action action)
|
||||
{
|
||||
//if a cached function throws an exception, remove it from the cache
|
||||
await RemoveAsync(key);
|
||||
//ensure that lock is acquired
|
||||
if (_memoryCache.TryGetValue(key, out _))
|
||||
return false;
|
||||
|
||||
throw;
|
||||
try
|
||||
{
|
||||
_memoryCache.Set(key, key, expirationTime);
|
||||
|
||||
//perform action
|
||||
action();
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//release lock even if action fails
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(CacheKey key, Func<T> acquire)
|
||||
{
|
||||
return await GetAsync(key, () => Task.FromResult(acquire()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item as an <see cref="object"/> instance, or null on a cache miss.
|
||||
/// </summary>
|
||||
/// <param name="key">Cache key</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key, or null if none was found
|
||||
/// </returns>
|
||||
public async Task<object> GetAsync(CacheKey key)
|
||||
{
|
||||
var entry = _memoryCache.Get(key.Key);
|
||||
if (entry == null)
|
||||
return null;
|
||||
try
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters)
|
||||
{
|
||||
if (entry.GetType().GetProperty("Value")?.GetValue(entry) is not Task task)
|
||||
return null;
|
||||
prefix = PrepareKeyPrefix(prefix, prefixParameters);
|
||||
|
||||
await task;
|
||||
_prefixes.TryRemove(prefix, out var tokenSource);
|
||||
tokenSource?.Cancel();
|
||||
tokenSource?.Dispose();
|
||||
|
||||
return task.GetType().GetProperty("Result")!.GetValue(task);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
|
||||
/// <summary>
|
||||
/// Clear all cache data
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task ClearAsync()
|
||||
{
|
||||
//if a cached function throws an exception, remove it from the cache
|
||||
await RemoveAsync(key);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the specified key and object to the cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key of cached item</param>
|
||||
/// <param name="data">Value for caching</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task SetAsync<T>(CacheKey key, T data)
|
||||
{
|
||||
if (data != null && (key?.CacheTime ?? 0) > 0)
|
||||
_memoryCache.Set(
|
||||
key.Key,
|
||||
new Lazy<Task<T>>(() => Task.FromResult(data), true),
|
||||
PrepareEntryOptions(key));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters)
|
||||
{
|
||||
foreach (var key in _keyManager.RemoveByPrefix(PrepareKeyPrefix(prefix, prefixParameters)))
|
||||
_memoryCache.Remove(key);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all cache data
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation</returns>
|
||||
public Task ClearAsync()
|
||||
{
|
||||
_clearToken.Cancel();
|
||||
_clearToken.Dispose();
|
||||
_clearToken = new CancellationTokenSource();
|
||||
_keyManager.Clear();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Protected implementation of Dispose pattern.
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
// don't dispose of the MemoryCache, as it is injected
|
||||
_clearToken.Cancel();
|
||||
_clearToken.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
_clearToken = new CancellationTokenSource();
|
||||
|
||||
#endregion
|
||||
foreach (var prefix in _prefixes.Keys.ToList())
|
||||
{
|
||||
_prefixes.TryRemove(prefix, out var tokenSource);
|
||||
tokenSource?.Dispose();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose cache manager
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Protected implementation of Dispose pattern.
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
_memoryCache.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +1,54 @@
|
|||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents default values related to caching entities
|
||||
/// </summary>
|
||||
public static partial class NopEntityCacheDefaults<TEntity> where TEntity : BaseEntity
|
||||
namespace Nop.Core.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an entity type name used in cache keys
|
||||
/// Represents default values related to caching entities
|
||||
/// </summary>
|
||||
public static string EntityTypeName => typeof(TEntity).Name.ToLowerInvariant();
|
||||
public static partial class NopEntityCacheDefaults<TEntity> where TEntity : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an entity type name used in cache keys
|
||||
/// </summary>
|
||||
public static string EntityTypeName => typeof(TEntity).Name.ToLowerInvariant();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key for caching entity by identifier
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// {0} : entity id
|
||||
/// </remarks>
|
||||
public static CacheKey ByIdCacheKey => new($"Nop.{EntityTypeName}.byid.{{0}}", ByIdPrefix, Prefix);
|
||||
/// <summary>
|
||||
/// Gets a key for caching entity by identifier
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// {0} : entity id
|
||||
/// </remarks>
|
||||
public static CacheKey ByIdCacheKey => new($"Nop.{EntityTypeName}.byid.{{0}}", ByIdPrefix, Prefix);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key for caching entities by identifiers
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// {0} : entity ids
|
||||
/// </remarks>
|
||||
public static CacheKey ByIdsCacheKey => new($"Nop.{EntityTypeName}.byids.{{0}}", ByIdsPrefix, Prefix);
|
||||
/// <summary>
|
||||
/// Gets a key for caching entities by identifiers
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// {0} : entity ids
|
||||
/// </remarks>
|
||||
public static CacheKey ByIdsCacheKey => new($"Nop.{EntityTypeName}.byids.{{0}}", ByIdsPrefix, Prefix);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key for caching all entities
|
||||
/// </summary>
|
||||
public static CacheKey AllCacheKey => new($"Nop.{EntityTypeName}.all.", AllPrefix, Prefix);
|
||||
/// <summary>
|
||||
/// Gets a key for caching all entities
|
||||
/// </summary>
|
||||
public static CacheKey AllCacheKey => new($"Nop.{EntityTypeName}.all.", AllPrefix, Prefix);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string Prefix => $"Nop.{EntityTypeName}.";
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string Prefix => $"Nop.{EntityTypeName}.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string ByIdPrefix => $"Nop.{EntityTypeName}.byid.";
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string ByIdPrefix => $"Nop.{EntityTypeName}.byid.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string ByIdsPrefix => $"Nop.{EntityTypeName}.byids.";
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string ByIdsPrefix => $"Nop.{EntityTypeName}.byids.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string AllPrefix => $"Nop.{EntityTypeName}.all.";
|
||||
/// <summary>
|
||||
/// Gets a key pattern to clear cache
|
||||
/// </summary>
|
||||
public static string AllPrefix => $"Nop.{EntityTypeName}.all.";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Infrastructure;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a per request cache manager
|
||||
/// </summary>
|
||||
public partial class PerRequestCacheManager : CacheKeyService, IShortTermCacheManager
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected readonly ConcurrentTrie<object> _concurrentCollection;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public PerRequestCacheManager(AppSettings appSettings) : base(appSettings)
|
||||
{
|
||||
_concurrentCollection = new ConcurrentTrie<object>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a cached item. If it's not in the cache yet, then load and cache it
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of cached item</typeparam>
|
||||
/// /// <param name="acquire">Function to load item if it's not in the cache yet</param>
|
||||
/// <param name="cacheKey">Initial cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation
|
||||
/// The task result contains the cached value associated with the specified key
|
||||
/// </returns>
|
||||
public async Task<T> GetAsync<T>(Func<Task<T>> acquire, CacheKey cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
var key = cacheKey.Create(CreateCacheKeyParameters, cacheKeyParameters).Key;
|
||||
|
||||
if (_concurrentCollection.TryGetValue(key, out var data))
|
||||
return (T)data;
|
||||
|
||||
var result = await acquire();
|
||||
|
||||
if (result != null)
|
||||
_concurrentCollection.Add(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove items by cache key prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">Cache key prefix</param>
|
||||
/// <param name="prefixParameters">Parameters to create cache key prefix</param>
|
||||
public virtual void RemoveByPrefix(string prefix, params object[] prefixParameters)
|
||||
{
|
||||
var keyPrefix = PrepareKeyPrefix(prefix, prefixParameters);
|
||||
_concurrentCollection.Prune(keyPrefix, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the value with the specified key from the cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key</param>
|
||||
/// <param name="cacheKeyParameters">Parameters to create cache key</param>
|
||||
public virtual void Remove(string cacheKey, params object[] cacheKeyParameters)
|
||||
{
|
||||
_concurrentCollection.Remove(PrepareKey(new CacheKey(cacheKey), cacheKeyParameters).Key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
using Nop.Core.Configuration;
|
||||
|
||||
namespace Nop.Core.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a memory cache manager with distributed synchronization
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class should be registered on IoC as singleton instance
|
||||
/// </remarks>
|
||||
public partial class SynchronizedMemoryCacheManager : MemoryCacheManager
|
||||
{
|
||||
public SynchronizedMemoryCacheManager(AppSettings appSettings,
|
||||
ISynchronizedMemoryCache memoryCache,
|
||||
ICacheKeyManager cacheKeyManager) : base(appSettings, memoryCache, cacheKeyManager)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,322 +1,333 @@
|
|||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using Nop.Core.Infrastructure;
|
||||
|
||||
namespace Nop.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a common helper
|
||||
/// </summary>
|
||||
public partial class CommonHelper
|
||||
namespace Nop.Core
|
||||
{
|
||||
#region Fields
|
||||
|
||||
//we use regular expression based on RFC 5322 Official Standard (see https://emailregex.com/)
|
||||
private const string EMAIL_EXPRESSION = @"^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|""(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*"")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get email validation regex
|
||||
/// Represents a common helper
|
||||
/// </summary>
|
||||
/// <returns>Regular expression</returns>
|
||||
[GeneratedRegex(EMAIL_EXPRESSION, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture, "en-US")]
|
||||
public static partial Regex GetEmailRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the subscriber email or throw.
|
||||
/// </summary>
|
||||
/// <param name="email">The email.</param>
|
||||
/// <returns></returns>
|
||||
public static string EnsureSubscriberEmailOrThrow(string email)
|
||||
public partial class CommonHelper
|
||||
{
|
||||
var output = EnsureNotNull(email);
|
||||
output = output.Trim();
|
||||
output = EnsureMaximumLength(output, 255);
|
||||
#region Fields
|
||||
|
||||
if (!IsValidEmail(output))
|
||||
//we use EmailValidator from FluentValidation. So let's keep them sync - https://github.com/JeremySkinner/FluentValidation/blob/master/src/FluentValidation/Validators/EmailValidator.cs
|
||||
private const string EMAIL_EXPRESSION = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-||_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+([a-z]+|\d|-|\.{0,1}|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$";
|
||||
|
||||
private static readonly Regex _emailRegex;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
static CommonHelper()
|
||||
{
|
||||
throw new NopException("Email is not valid.");
|
||||
_emailRegex = new Regex(EMAIL_EXPRESSION, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a string is in valid e-mail format
|
||||
/// </summary>
|
||||
/// <param name="email">Email to verify</param>
|
||||
/// <returns>true if the string is a valid e-mail address and false if it's not</returns>
|
||||
public static bool IsValidEmail(string email)
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
return false;
|
||||
#region Methods
|
||||
|
||||
email = email.Trim();
|
||||
/// <summary>
|
||||
/// Ensures the subscriber email or throw.
|
||||
/// </summary>
|
||||
/// <param name="email">The email.</param>
|
||||
/// <returns></returns>
|
||||
public static string EnsureSubscriberEmailOrThrow(string email)
|
||||
{
|
||||
var output = EnsureNotNull(email);
|
||||
output = output.Trim();
|
||||
output = EnsureMaximumLength(output, 255);
|
||||
|
||||
return GetEmailRegex().IsMatch(email);
|
||||
}
|
||||
if (!IsValidEmail(output))
|
||||
{
|
||||
throw new NopException("Email is not valid.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that string is an valid IP-Address
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IPAddress to verify</param>
|
||||
/// <returns>true if the string is a valid IpAddress and false if it's not</returns>
|
||||
public static bool IsValidIpAddress(string ipAddress)
|
||||
{
|
||||
return IPAddress.TryParse(ipAddress, out var _);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate random digit code
|
||||
/// </summary>
|
||||
/// <param name="length">Length</param>
|
||||
/// <returns>Result string</returns>
|
||||
public static string GenerateRandomDigitCode(int length)
|
||||
{
|
||||
using var random = new SecureRandomNumberGenerator();
|
||||
var str = string.Empty;
|
||||
for (var i = 0; i < length; i++)
|
||||
str = string.Concat(str, random.Next(10).ToString());
|
||||
return str;
|
||||
}
|
||||
/// <summary>
|
||||
/// Verifies that a string is in valid e-mail format
|
||||
/// </summary>
|
||||
/// <param name="email">Email to verify</param>
|
||||
/// <returns>true if the string is a valid e-mail address and false if it's not</returns>
|
||||
public static bool IsValidEmail(string email)
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
return false;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an random integer number within a specified rage
|
||||
/// </summary>
|
||||
/// <param name="min">Minimum number</param>
|
||||
/// <param name="max">Maximum number</param>
|
||||
/// <returns>Result</returns>
|
||||
public static int GenerateRandomInteger(int min = 0, int max = int.MaxValue)
|
||||
{
|
||||
using var random = new SecureRandomNumberGenerator();
|
||||
return random.Next(min, max);
|
||||
}
|
||||
email = email.Trim();
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that a string doesn't exceed maximum allowed length
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <param name="maxLength">Maximum length</param>
|
||||
/// <param name="postfix">A string to add to the end if the original string was shorten</param>
|
||||
/// <returns>Input string if its length is OK; otherwise, truncated input string</returns>
|
||||
public static string EnsureMaximumLength(string str, int maxLength, string postfix = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return _emailRegex.IsMatch(email);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that string is an valid IP-Address
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IPAddress to verify</param>
|
||||
/// <returns>true if the string is a valid IpAddress and false if it's not</returns>
|
||||
public static bool IsValidIpAddress(string ipAddress)
|
||||
{
|
||||
return IPAddress.TryParse(ipAddress, out var _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate random digit code
|
||||
/// </summary>
|
||||
/// <param name="length">Length</param>
|
||||
/// <returns>Result string</returns>
|
||||
public static string GenerateRandomDigitCode(int length)
|
||||
{
|
||||
using var random = new SecureRandomNumberGenerator();
|
||||
var str = string.Empty;
|
||||
for (var i = 0; i < length; i++)
|
||||
str = string.Concat(str, random.Next(10).ToString());
|
||||
return str;
|
||||
|
||||
if (str.Length <= maxLength)
|
||||
return str;
|
||||
|
||||
var pLen = postfix?.Length ?? 0;
|
||||
|
||||
var result = str[0..(maxLength - pLen)];
|
||||
if (!string.IsNullOrEmpty(postfix))
|
||||
{
|
||||
result += postfix;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a string only contains numeric values
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Input string with only numeric values, empty string if input is null/empty</returns>
|
||||
public static string EnsureNumericOnly(string str)
|
||||
{
|
||||
return string.IsNullOrEmpty(str) ? string.Empty : new string(str.Where(char.IsDigit).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that a string is not null
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Result</returns>
|
||||
public static string EnsureNotNull(string str)
|
||||
{
|
||||
return str ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the specified strings are null or empty strings
|
||||
/// </summary>
|
||||
/// <param name="stringsToValidate">Array of strings to validate</param>
|
||||
/// <returns>Boolean</returns>
|
||||
public static bool AreNullOrEmpty(params string[] stringsToValidate)
|
||||
{
|
||||
return stringsToValidate.Any(string.IsNullOrEmpty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two arrays
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="a1">Array 1</param>
|
||||
/// <param name="a2">Array 2</param>
|
||||
/// <returns>Result</returns>
|
||||
public static bool ArraysEqual<T>(T[] a1, T[] a2)
|
||||
{
|
||||
//also see Enumerable.SequenceEqual(a1, a2);
|
||||
if (ReferenceEquals(a1, a2))
|
||||
return true;
|
||||
|
||||
if (a1 == null || a2 == null)
|
||||
return false;
|
||||
|
||||
if (a1.Length != a2.Length)
|
||||
return false;
|
||||
|
||||
var comparer = EqualityComparer<T>.Default;
|
||||
return !a1.Where((t, i) => !comparer.Equals(t, a2[i])).Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a property on an object to a value.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object whose property to set.</param>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set the property to.</param>
|
||||
public static void SetProperty(object instance, string propertyName, object value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(instance);
|
||||
ArgumentNullException.ThrowIfNull(propertyName);
|
||||
|
||||
var instanceType = instance.GetType();
|
||||
var pi = instanceType.GetProperty(propertyName)
|
||||
?? throw new NopException("No property '{0}' found on the instance of type '{1}'.", propertyName, instanceType);
|
||||
|
||||
if (!pi.CanWrite)
|
||||
throw new NopException("The property '{0}' on the instance of type '{1}' does not have a setter.", propertyName, instanceType);
|
||||
if (value != null && !value.GetType().IsAssignableFrom(pi.PropertyType))
|
||||
value = To(value, pi.PropertyType);
|
||||
pi.SetValue(instance, value, Array.Empty<object>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="destinationType">The type to convert the value to.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static object To(object value, Type destinationType)
|
||||
{
|
||||
return To(value, destinationType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="destinationType">The type to convert the value to.</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static object To(object value, Type destinationType, CultureInfo culture)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
var sourceType = value.GetType();
|
||||
|
||||
var destinationConverter = TypeDescriptor.GetConverter(destinationType);
|
||||
if (destinationConverter.CanConvertFrom(value.GetType()))
|
||||
return destinationConverter.ConvertFrom(null, culture, value);
|
||||
|
||||
var sourceConverter = TypeDescriptor.GetConverter(sourceType);
|
||||
if (sourceConverter.CanConvertTo(destinationType))
|
||||
return sourceConverter.ConvertTo(null, culture, value, destinationType);
|
||||
|
||||
if (destinationType.IsEnum && value is int)
|
||||
return Enum.ToObject(destinationType, (int)value);
|
||||
|
||||
if (!destinationType.IsInstanceOfType(value))
|
||||
return Convert.ChangeType(value, destinationType, culture);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <typeparam name="T">The type to convert the value to.</typeparam>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static T To<T>(object value)
|
||||
{
|
||||
//return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
|
||||
return (T)To(value, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the camel-case word into separate one
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Splitted string</returns>
|
||||
public static string SplitCamelCaseWord(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return string.Empty;
|
||||
|
||||
var result = str.ToCharArray()
|
||||
.Select(p => p.ToString())
|
||||
.Aggregate(string.Empty, (current, c) => current + (c == c.ToUpperInvariant() ? $" {c}" : c));
|
||||
|
||||
//ensure no spaces (e.g. when the first letter is upper case)
|
||||
result = result.TrimStart();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get difference in years
|
||||
/// </summary>
|
||||
/// <param name="startDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetDifferenceInYears(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
//source: http://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c
|
||||
//this assumes you are looking for the western idea of age and not using East Asian reckoning.
|
||||
var age = endDate.Year - startDate.Year;
|
||||
if (startDate > endDate.AddYears(-age))
|
||||
age--;
|
||||
return age;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get DateTime to the specified year, month, and day using the conventions of the current thread culture
|
||||
/// </summary>
|
||||
/// <param name="year">The year</param>
|
||||
/// <param name="month">The month</param>
|
||||
/// <param name="day">The day</param>
|
||||
/// <returns>An instance of the Nullable<System.DateTime></returns>
|
||||
public static DateTime? ParseDate(int? year, int? month, int? day)
|
||||
{
|
||||
if (!year.HasValue || !month.HasValue || !day.HasValue)
|
||||
return null;
|
||||
|
||||
DateTime? date = null;
|
||||
try
|
||||
/// <summary>
|
||||
/// Returns an random integer number within a specified rage
|
||||
/// </summary>
|
||||
/// <param name="min">Minimum number</param>
|
||||
/// <param name="max">Maximum number</param>
|
||||
/// <returns>Result</returns>
|
||||
public static int GenerateRandomInteger(int min = 0, int max = int.MaxValue)
|
||||
{
|
||||
date = new DateTime(year.Value, month.Value, day.Value, CultureInfo.CurrentCulture.Calendar);
|
||||
using var random = new SecureRandomNumberGenerator();
|
||||
return random.Next(min, max);
|
||||
}
|
||||
catch { }
|
||||
return date;
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that a string doesn't exceed maximum allowed length
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <param name="maxLength">Maximum length</param>
|
||||
/// <param name="postfix">A string to add to the end if the original string was shorten</param>
|
||||
/// <returns>Input string if its length is OK; otherwise, truncated input string</returns>
|
||||
public static string EnsureMaximumLength(string str, int maxLength, string postfix = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return str;
|
||||
|
||||
if (str.Length <= maxLength)
|
||||
return str;
|
||||
|
||||
var pLen = postfix?.Length ?? 0;
|
||||
|
||||
var result = str[0..(maxLength - pLen)];
|
||||
if (!string.IsNullOrEmpty(postfix))
|
||||
{
|
||||
result += postfix;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a string only contains numeric values
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Input string with only numeric values, empty string if input is null/empty</returns>
|
||||
public static string EnsureNumericOnly(string str)
|
||||
{
|
||||
return string.IsNullOrEmpty(str) ? string.Empty : new string(str.Where(char.IsDigit).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that a string is not null
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Result</returns>
|
||||
public static string EnsureNotNull(string str)
|
||||
{
|
||||
return str ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the specified strings are null or empty strings
|
||||
/// </summary>
|
||||
/// <param name="stringsToValidate">Array of strings to validate</param>
|
||||
/// <returns>Boolean</returns>
|
||||
public static bool AreNullOrEmpty(params string[] stringsToValidate)
|
||||
{
|
||||
return stringsToValidate.Any(string.IsNullOrEmpty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two arrays
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="a1">Array 1</param>
|
||||
/// <param name="a2">Array 2</param>
|
||||
/// <returns>Result</returns>
|
||||
public static bool ArraysEqual<T>(T[] a1, T[] a2)
|
||||
{
|
||||
//also see Enumerable.SequenceEqual(a1, a2);
|
||||
if (ReferenceEquals(a1, a2))
|
||||
return true;
|
||||
|
||||
if (a1 == null || a2 == null)
|
||||
return false;
|
||||
|
||||
if (a1.Length != a2.Length)
|
||||
return false;
|
||||
|
||||
var comparer = EqualityComparer<T>.Default;
|
||||
return !a1.Where((t, i) => !comparer.Equals(t, a2[i])).Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a property on an object to a value.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object whose property to set.</param>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set the property to.</param>
|
||||
public static void SetProperty(object instance, string propertyName, object value)
|
||||
{
|
||||
if (instance == null)
|
||||
throw new ArgumentNullException(nameof(instance));
|
||||
if (propertyName == null)
|
||||
throw new ArgumentNullException(nameof(propertyName));
|
||||
|
||||
var instanceType = instance.GetType();
|
||||
var pi = instanceType.GetProperty(propertyName);
|
||||
if (pi == null)
|
||||
throw new NopException("No property '{0}' found on the instance of type '{1}'.", propertyName, instanceType);
|
||||
if (!pi.CanWrite)
|
||||
throw new NopException("The property '{0}' on the instance of type '{1}' does not have a setter.", propertyName, instanceType);
|
||||
if (value != null && !value.GetType().IsAssignableFrom(pi.PropertyType))
|
||||
value = To(value, pi.PropertyType);
|
||||
pi.SetValue(instance, value, Array.Empty<object>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="destinationType">The type to convert the value to.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static object To(object value, Type destinationType)
|
||||
{
|
||||
return To(value, destinationType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="destinationType">The type to convert the value to.</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static object To(object value, Type destinationType, CultureInfo culture)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
var sourceType = value.GetType();
|
||||
|
||||
var destinationConverter = TypeDescriptor.GetConverter(destinationType);
|
||||
if (destinationConverter.CanConvertFrom(value.GetType()))
|
||||
return destinationConverter.ConvertFrom(null, culture, value);
|
||||
|
||||
var sourceConverter = TypeDescriptor.GetConverter(sourceType);
|
||||
if (sourceConverter.CanConvertTo(destinationType))
|
||||
return sourceConverter.ConvertTo(null, culture, value, destinationType);
|
||||
|
||||
if (destinationType.IsEnum && value is int)
|
||||
return Enum.ToObject(destinationType, (int)value);
|
||||
|
||||
if (!destinationType.IsInstanceOfType(value))
|
||||
return Convert.ChangeType(value, destinationType, culture);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <typeparam name="T">The type to convert the value to.</typeparam>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static T To<T>(object value)
|
||||
{
|
||||
//return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
|
||||
return (T)To(value, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert enum for front-end
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Converted string</returns>
|
||||
public static string ConvertEnum(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return string.Empty;
|
||||
var result = string.Empty;
|
||||
foreach (var c in str)
|
||||
if (c.ToString() != c.ToString().ToLowerInvariant())
|
||||
result += " " + c.ToString();
|
||||
else
|
||||
result += c.ToString();
|
||||
|
||||
//ensure no spaces (e.g. when the first letter is upper case)
|
||||
result = result.TrimStart();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get difference in years
|
||||
/// </summary>
|
||||
/// <param name="startDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetDifferenceInYears(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
//source: http://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c
|
||||
//this assumes you are looking for the western idea of age and not using East Asian reckoning.
|
||||
var age = endDate.Year - startDate.Year;
|
||||
if (startDate > endDate.AddYears(-age))
|
||||
age--;
|
||||
return age;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get DateTime to the specified year, month, and day using the conventions of the current thread culture
|
||||
/// </summary>
|
||||
/// <param name="year">The year</param>
|
||||
/// <param name="month">The month</param>
|
||||
/// <param name="day">The day</param>
|
||||
/// <returns>An instance of the Nullable<System.DateTime></returns>
|
||||
public static DateTime? ParseDate(int? year, int? month, int? day)
|
||||
{
|
||||
if (!year.HasValue || !month.HasValue || !day.HasValue)
|
||||
return null;
|
||||
|
||||
DateTime? date = null;
|
||||
try
|
||||
{
|
||||
date = new DateTime(year.Value, month.Value, day.Value, CultureInfo.CurrentCulture.Calendar);
|
||||
}
|
||||
catch { }
|
||||
return date;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default file provider
|
||||
/// </summary>
|
||||
public static INopFileProvider DefaultFileProvider { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default file provider
|
||||
/// </summary>
|
||||
public static INopFileProvider DefaultFileProvider { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +1,118 @@
|
|||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nop.Core.ComponentModel;
|
||||
|
||||
/// <summary>
|
||||
/// Generic Dictionary type converted
|
||||
/// </summary>
|
||||
/// <typeparam name="K">Key type (simple)</typeparam>
|
||||
/// <typeparam name="V">Value type (simple)</typeparam>
|
||||
public partial class GenericDictionaryTypeConverter<K, V> : TypeConverter
|
||||
namespace Nop.Core.ComponentModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Type converter
|
||||
/// Generic Dictionary type converted
|
||||
/// </summary>
|
||||
protected readonly TypeConverter _typeConverterKey;
|
||||
|
||||
/// <summary>
|
||||
/// Type converter
|
||||
/// </summary>
|
||||
protected readonly TypeConverter _typeConverterValue;
|
||||
|
||||
public GenericDictionaryTypeConverter()
|
||||
/// <typeparam name="K">Key type (simple)</typeparam>
|
||||
/// <typeparam name="V">Value type (simple)</typeparam>
|
||||
public class GenericDictionaryTypeConverter<K, V> : TypeConverter
|
||||
{
|
||||
_typeConverterKey = TypeDescriptor.GetConverter(typeof(K));
|
||||
if (_typeConverterKey == null)
|
||||
throw new InvalidOperationException("No type converter exists for type " + typeof(K).FullName);
|
||||
_typeConverterValue = TypeDescriptor.GetConverter(typeof(V));
|
||||
if (_typeConverterValue == null)
|
||||
throw new InvalidOperationException("No type converter exists for type " + typeof(V).FullName);
|
||||
}
|
||||
/// <summary>
|
||||
/// Type converter
|
||||
/// </summary>
|
||||
protected readonly TypeConverter typeConverterKey;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this converter can
|
||||
/// convert an object in the given source type to the native type of the converter
|
||||
/// using the context.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="sourceType">Source type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
return true;
|
||||
/// <summary>
|
||||
/// Type converter
|
||||
/// </summary>
|
||||
protected readonly TypeConverter typeConverterValue;
|
||||
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given object to the converter's native type.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is not string)
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
var input = (string)value;
|
||||
var items = string.IsNullOrEmpty(input) ? Array.Empty<string>() : input.Split(';').Select(x => x.Trim()).ToArray();
|
||||
|
||||
var result = new Dictionary<K, V>();
|
||||
foreach (var item in items)
|
||||
public GenericDictionaryTypeConverter()
|
||||
{
|
||||
var keyValueStr = string.IsNullOrEmpty(item) ? Array.Empty<string>() : item.Split(',').Select(x => x.Trim()).ToArray();
|
||||
if (keyValueStr.Length != 2)
|
||||
continue;
|
||||
|
||||
object dictionaryKey = (K)_typeConverterKey.ConvertFromInvariantString(keyValueStr[0]);
|
||||
object dictionaryValue = (V)_typeConverterValue.ConvertFromInvariantString(keyValueStr[1]);
|
||||
if (dictionaryKey == null || dictionaryValue == null)
|
||||
continue;
|
||||
|
||||
if (!result.ContainsKey((K)dictionaryKey))
|
||||
result.Add((K)dictionaryKey, (V)dictionaryValue);
|
||||
typeConverterKey = TypeDescriptor.GetConverter(typeof(K));
|
||||
if (typeConverterKey == null)
|
||||
throw new InvalidOperationException("No type converter exists for type " + typeof(K).FullName);
|
||||
typeConverterValue = TypeDescriptor.GetConverter(typeof(V));
|
||||
if (typeConverterValue == null)
|
||||
throw new InvalidOperationException("No type converter exists for type " + typeof(V).FullName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this converter can
|
||||
/// convert an object in the given source type to the native type of the converter
|
||||
/// using the context.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="sourceType">Source type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
return true;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given value object to the specified destination type using the specified context and arguments
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <param name="destinationType">Destination type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType != typeof(string))
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given object to the converter's native type.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is not string)
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
var input = (string)value;
|
||||
var items = string.IsNullOrEmpty(input) ? Array.Empty<string>() : input.Split(';').Select(x => x.Trim()).ToArray();
|
||||
|
||||
var result = new Dictionary<K, V>();
|
||||
Array.ForEach(items, s =>
|
||||
{
|
||||
var keyValueStr = string.IsNullOrEmpty(s) ? Array.Empty<string>() : s.Split(',').Select(x => x.Trim()).ToArray();
|
||||
if (keyValueStr.Length != 2)
|
||||
return;
|
||||
|
||||
object dictionaryKey = (K)typeConverterKey.ConvertFromInvariantString(keyValueStr[0]);
|
||||
object dictionaryValue = (V)typeConverterValue.ConvertFromInvariantString(keyValueStr[1]);
|
||||
if (dictionaryKey == null || dictionaryValue == null)
|
||||
return;
|
||||
|
||||
if (!result.ContainsKey((K)dictionaryKey))
|
||||
result.Add((K)dictionaryKey, (V)dictionaryValue);
|
||||
});
|
||||
|
||||
var result = string.Empty;
|
||||
if (value == null)
|
||||
return result;
|
||||
|
||||
//we don't use string.Join() because it doesn't support invariant culture
|
||||
var counter = 0;
|
||||
var dictionary = (IDictionary<K, V>)value;
|
||||
foreach (var keyValue in dictionary)
|
||||
{
|
||||
result += $"{Convert.ToString(keyValue.Key, CultureInfo.InvariantCulture)}, {Convert.ToString(keyValue.Value, CultureInfo.InvariantCulture)}";
|
||||
//don't add ; after the last element
|
||||
if (counter != dictionary.Count - 1)
|
||||
result += ";";
|
||||
counter++;
|
||||
}
|
||||
|
||||
return result;
|
||||
/// <summary>
|
||||
/// Converts the given value object to the specified destination type using the specified context and arguments
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <param name="destinationType">Destination type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType != typeof(string))
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
|
||||
var result = string.Empty;
|
||||
if (value == null)
|
||||
return result;
|
||||
|
||||
//we don't use string.Join() because it doesn't support invariant culture
|
||||
var counter = 0;
|
||||
var dictionary = (IDictionary<K, V>)value;
|
||||
foreach (var keyValue in dictionary)
|
||||
{
|
||||
result += $"{Convert.ToString(keyValue.Key, CultureInfo.InvariantCulture)}, {Convert.ToString(keyValue.Value, CultureInfo.InvariantCulture)}";
|
||||
//don't add ; after the last element
|
||||
if (counter != dictionary.Count - 1)
|
||||
result += ";";
|
||||
counter++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +1,110 @@
|
|||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nop.Core.ComponentModel;
|
||||
|
||||
/// <summary>
|
||||
/// Generic List type converted
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
public partial class GenericListTypeConverter<T> : TypeConverter
|
||||
namespace Nop.Core.ComponentModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Type converter
|
||||
/// Generic List type converted
|
||||
/// </summary>
|
||||
protected readonly TypeConverter typeConverter;
|
||||
|
||||
public GenericListTypeConverter()
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
public class GenericListTypeConverter<T> : TypeConverter
|
||||
{
|
||||
typeConverter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (typeConverter == null)
|
||||
throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName);
|
||||
}
|
||||
/// <summary>
|
||||
/// Type converter
|
||||
/// </summary>
|
||||
protected readonly TypeConverter typeConverter;
|
||||
|
||||
/// <summary>
|
||||
/// Get string array from a comma-separate string
|
||||
/// </summary>
|
||||
/// <param name="input">Input</param>
|
||||
/// <returns>Array</returns>
|
||||
protected virtual string[] GetStringArray(string input)
|
||||
{
|
||||
return string.IsNullOrEmpty(input) ? Array.Empty<string>() : input.Split(',').Select(x => x.Trim()).ToArray();
|
||||
}
|
||||
public GenericListTypeConverter()
|
||||
{
|
||||
typeConverter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (typeConverter == null)
|
||||
throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this converter can
|
||||
/// convert an object in the given source type to the native type of the converter
|
||||
/// using the context.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="sourceType">Source type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType != typeof(string))
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
/// <summary>
|
||||
/// Get string array from a comma-separate string
|
||||
/// </summary>
|
||||
/// <param name="input">Input</param>
|
||||
/// <returns>Array</returns>
|
||||
protected virtual string[] GetStringArray(string input)
|
||||
{
|
||||
return string.IsNullOrEmpty(input) ? Array.Empty<string>() : input.Split(',').Select(x => x.Trim()).ToArray();
|
||||
}
|
||||
|
||||
var items = GetStringArray(sourceType.ToString());
|
||||
return items.Any();
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this converter can
|
||||
/// convert an object in the given source type to the native type of the converter
|
||||
/// using the context.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="sourceType">Source type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType != typeof(string))
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given object to the converter's native type.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is not string && value != null)
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
var items = GetStringArray(sourceType.ToString());
|
||||
return items.Any();
|
||||
}
|
||||
|
||||
var items = GetStringArray((string)value);
|
||||
/// <summary>
|
||||
/// Converts the given object to the converter's native type.
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is not string && value != null)
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
return items.Select(typeConverter.ConvertFromInvariantString)
|
||||
.Where(item => item != null)
|
||||
.Cast<T>()
|
||||
.ToList();
|
||||
}
|
||||
var items = GetStringArray((string)value);
|
||||
var result = new List<T>();
|
||||
Array.ForEach(items, s =>
|
||||
{
|
||||
var item = typeConverter.ConvertFromInvariantString(s);
|
||||
if (item != null)
|
||||
{
|
||||
result.Add((T)item);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given value object to the specified destination type using the specified context and arguments
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <param name="destinationType">Destination type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType != typeof(string))
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
|
||||
var result = string.Empty;
|
||||
|
||||
if (value == null)
|
||||
return result;
|
||||
}
|
||||
|
||||
var cultureInvariantStrings = ((IList<T>)value)
|
||||
.Select(o => Convert.ToString(o, CultureInfo.InvariantCulture));
|
||||
/// <summary>
|
||||
/// Converts the given value object to the specified destination type using the specified context and arguments
|
||||
/// </summary>
|
||||
/// <param name="context">Context</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <param name="destinationType">Destination type</param>
|
||||
/// <returns>Result</returns>
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType != typeof(string))
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
|
||||
result = string.Join(',', cultureInvariantStrings);
|
||||
var result = string.Empty;
|
||||
if (value == null)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
//we don't use string.Join() because it doesn't support invariant culture
|
||||
for (var i = 0; i < ((IList<T>)value).Count; i++)
|
||||
{
|
||||
var str1 = Convert.ToString(((IList<T>)value)[i], CultureInfo.InvariantCulture);
|
||||
result += str1;
|
||||
//don't add comma after the last element
|
||||
if (i != ((IList<T>)value).Count - 1)
|
||||
result += ",";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +1,73 @@
|
|||
namespace Nop.Core.ComponentModel;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a convenience methodology for implementing locked access to resources.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended as an infrastructure class.
|
||||
/// </remarks>
|
||||
public partial class ReaderWriteLockDisposable : IDisposable
|
||||
namespace Nop.Core.ComponentModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected bool _disposed;
|
||||
protected readonly ReaderWriterLockSlim _rwLock;
|
||||
protected readonly ReaderWriteLockType _readerWriteLockType;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReaderWriteLockDisposable"/> class.
|
||||
/// Provides a convenience methodology for implementing locked access to resources.
|
||||
/// </summary>
|
||||
/// <param name="rwLock">The readers–writer lock</param>
|
||||
/// <param name="readerWriteLockType">Lock type</param>
|
||||
public ReaderWriteLockDisposable(ReaderWriterLockSlim rwLock, ReaderWriteLockType readerWriteLockType = ReaderWriteLockType.Write)
|
||||
/// <remarks>
|
||||
/// Intended as an infrastructure class.
|
||||
/// </remarks>
|
||||
public class ReaderWriteLockDisposable : IDisposable
|
||||
{
|
||||
_rwLock = rwLock;
|
||||
_readerWriteLockType = readerWriteLockType;
|
||||
private bool _disposed = false;
|
||||
private readonly ReaderWriterLockSlim _rwLock;
|
||||
private readonly ReaderWriteLockType _readerWriteLockType;
|
||||
|
||||
switch (_readerWriteLockType)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReaderWriteLockDisposable"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rwLock">The readers–writer lock</param>
|
||||
/// <param name="readerWriteLockType">Lock type</param>
|
||||
public ReaderWriteLockDisposable(ReaderWriterLockSlim rwLock, ReaderWriteLockType readerWriteLockType = ReaderWriteLockType.Write)
|
||||
{
|
||||
case ReaderWriteLockType.Read:
|
||||
_rwLock.EnterReadLock();
|
||||
break;
|
||||
case ReaderWriteLockType.Write:
|
||||
_rwLock.EnterWriteLock();
|
||||
break;
|
||||
case ReaderWriteLockType.UpgradeableRead:
|
||||
_rwLock.EnterUpgradeableReadLock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
_rwLock = rwLock;
|
||||
_readerWriteLockType = readerWriteLockType;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Protected implementation of Dispose pattern.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Specifies whether to disposing resources</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
switch (_readerWriteLockType)
|
||||
{
|
||||
case ReaderWriteLockType.Read:
|
||||
_rwLock.ExitReadLock();
|
||||
_rwLock.EnterReadLock();
|
||||
break;
|
||||
case ReaderWriteLockType.Write:
|
||||
_rwLock.ExitWriteLock();
|
||||
_rwLock.EnterWriteLock();
|
||||
break;
|
||||
case ReaderWriteLockType.UpgradeableRead:
|
||||
_rwLock.ExitUpgradeableReadLock();
|
||||
_rwLock.EnterUpgradeableReadLock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
// Public implementation of Dispose pattern callable by consumers.
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Protected implementation of Dispose pattern.
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
switch (_readerWriteLockType)
|
||||
{
|
||||
case ReaderWriteLockType.Read:
|
||||
_rwLock.ExitReadLock();
|
||||
break;
|
||||
case ReaderWriteLockType.Write:
|
||||
_rwLock.ExitWriteLock();
|
||||
break;
|
||||
case ReaderWriteLockType.UpgradeableRead:
|
||||
_rwLock.ExitUpgradeableReadLock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Public implementation of Dispose pattern callable by consumers.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
namespace Nop.Core.ComponentModel;
|
||||
|
||||
/// <summary>
|
||||
/// Reader/Write locker type
|
||||
/// </summary>
|
||||
public enum ReaderWriteLockType
|
||||
namespace Nop.Core.ComponentModel
|
||||
{
|
||||
Read,
|
||||
Write,
|
||||
UpgradeableRead
|
||||
}
|
||||
/// <summary>
|
||||
/// Reader/Write locker type
|
||||
/// </summary>
|
||||
public enum ReaderWriteLockType
|
||||
{
|
||||
Read,
|
||||
Write,
|
||||
UpgradeableRead
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +1,71 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the app settings
|
||||
/// </summary>
|
||||
public partial class AppSettings
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected readonly Dictionary<Type, IConfig> _configurations;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public AppSettings(IList<IConfig> configurations = null)
|
||||
{
|
||||
_configurations = configurations
|
||||
?.OrderBy(config => config.GetOrder())
|
||||
?.ToDictionary(config => config.GetType(), config => config)
|
||||
?? new Dictionary<Type, IConfig>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get configuration parameters by type
|
||||
/// Represents the app settings
|
||||
/// </summary>
|
||||
/// <typeparam name="TConfig">Configuration type</typeparam>
|
||||
/// <returns>Configuration parameters</returns>
|
||||
public TConfig Get<TConfig>() where TConfig : class, IConfig
|
||||
public partial class AppSettings
|
||||
{
|
||||
if (_configurations[typeof(TConfig)] is not TConfig config)
|
||||
throw new NopException($"No configuration with type '{typeof(TConfig)}' found");
|
||||
#region Fields
|
||||
|
||||
return config;
|
||||
}
|
||||
private readonly Dictionary<Type, IConfig> _configurations = new();
|
||||
|
||||
/// <summary>
|
||||
/// Update app settings
|
||||
/// </summary>
|
||||
/// <param name="configurations">Configurations to update</param>
|
||||
public void Update(IList<IConfig> configurations)
|
||||
{
|
||||
foreach (var config in configurations)
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
public AppSettings(IList<IConfig> configurations = null)
|
||||
{
|
||||
_configurations[config.GetType()] = config;
|
||||
_configurations = configurations
|
||||
?.OrderBy(config => config.GetOrder())
|
||||
?.ToDictionary(config => config.GetType(), config => config)
|
||||
?? new Dictionary<Type, IConfig>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets raw configuration parameters
|
||||
/// </summary>
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, JToken> Configuration { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get configuration parameters by type
|
||||
/// </summary>
|
||||
/// <typeparam name="TConfig">Configuration type</typeparam>
|
||||
/// <returns>Configuration parameters</returns>
|
||||
public TConfig Get<TConfig>() where TConfig : class, IConfig
|
||||
{
|
||||
if (_configurations[typeof(TConfig)] is not TConfig config)
|
||||
throw new NopException($"No configuration with type '{typeof(TConfig)}' found");
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update app settings
|
||||
/// </summary>
|
||||
/// <param name="configurations">Configurations to update</param>
|
||||
public void Update(IList<IConfig> configurations)
|
||||
{
|
||||
foreach (var config in configurations)
|
||||
{
|
||||
_configurations[config.GetType()] = config;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets raw configuration parameters
|
||||
/// </summary>
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, JToken> Configuration { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,72 +1,78 @@
|
|||
using System.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Nop.Core.Infrastructure;
|
||||
|
||||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the app settings helper
|
||||
/// </summary>
|
||||
public partial class AppSettingsHelper
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
#region Fields
|
||||
|
||||
protected static Dictionary<string, int> _configurationOrder;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Create app settings with the passed configurations and save it to the file
|
||||
/// Represents the app settings helper
|
||||
/// </summary>
|
||||
/// <param name="configurations">Configurations to save</param>
|
||||
/// <param name="fileProvider">File provider</param>
|
||||
/// <param name="overwrite">Whether to overwrite appsettings file</param>
|
||||
/// <returns>App settings</returns>
|
||||
public static AppSettings SaveAppSettings(IList<IConfig> configurations, INopFileProvider fileProvider, bool overwrite = true)
|
||||
public partial class AppSettingsHelper
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(configurations);
|
||||
#region Fields
|
||||
|
||||
_configurationOrder = configurations.ToDictionary(config => config.Name, config => config.GetOrder());
|
||||
private static Dictionary<string, int> _configurationOrder;
|
||||
|
||||
//create app settings
|
||||
var appSettings = Singleton<AppSettings>.Instance ?? new AppSettings();
|
||||
appSettings.Update(configurations);
|
||||
Singleton<AppSettings>.Instance = appSettings;
|
||||
#endregion
|
||||
|
||||
//create file if not exists
|
||||
var filePath = fileProvider.MapPath(NopConfigurationDefaults.AppSettingsFilePath);
|
||||
var fileExists = fileProvider.FileExists(filePath);
|
||||
fileProvider.CreateFile(filePath);
|
||||
#region Methods
|
||||
|
||||
//get raw configuration parameters
|
||||
var configuration = JsonConvert.DeserializeObject<AppSettings>(fileProvider.ReadAllText(filePath, Encoding.UTF8))
|
||||
?.Configuration
|
||||
?? new();
|
||||
foreach (var config in configurations)
|
||||
/// <summary>
|
||||
/// Create app settings with the passed configurations and save it to the file
|
||||
/// </summary>
|
||||
/// <param name="configurations">Configurations to save</param>
|
||||
/// <param name="fileProvider">File provider</param>
|
||||
/// <param name="overwrite">Whether to overwrite appsettings file</param>
|
||||
/// <returns>App settings</returns>
|
||||
public static AppSettings SaveAppSettings(IList<IConfig> configurations, INopFileProvider fileProvider, bool overwrite = true)
|
||||
{
|
||||
configuration[config.Name] = JToken.FromObject(config);
|
||||
if (configurations is null)
|
||||
throw new ArgumentNullException(nameof(configurations));
|
||||
|
||||
if (_configurationOrder is null)
|
||||
_configurationOrder = configurations.ToDictionary(config => config.Name, config => config.GetOrder());
|
||||
|
||||
//create app settings
|
||||
var appSettings = Singleton<AppSettings>.Instance ?? new AppSettings();
|
||||
appSettings.Update(configurations);
|
||||
Singleton<AppSettings>.Instance = appSettings;
|
||||
|
||||
//create file if not exists
|
||||
var filePath = fileProvider.MapPath(NopConfigurationDefaults.AppSettingsFilePath);
|
||||
var fileExists = fileProvider.FileExists(filePath);
|
||||
fileProvider.CreateFile(filePath);
|
||||
|
||||
//get raw configuration parameters
|
||||
var configuration = JsonConvert.DeserializeObject<AppSettings>(fileProvider.ReadAllText(filePath, Encoding.UTF8))
|
||||
?.Configuration
|
||||
?? new();
|
||||
foreach (var config in configurations)
|
||||
{
|
||||
configuration[config.Name] = JToken.FromObject(config);
|
||||
}
|
||||
|
||||
//sort configurations for display by order (e.g. data configuration with 0 will be the first)
|
||||
appSettings.Configuration = configuration
|
||||
.SelectMany(outConfig => _configurationOrder.Where(inConfig => inConfig.Key == outConfig.Key).DefaultIfEmpty(),
|
||||
(outConfig, inConfig) => new { OutConfig = outConfig, InConfig = inConfig })
|
||||
.OrderBy(config => config.InConfig.Value)
|
||||
.Select(config => config.OutConfig)
|
||||
.ToDictionary(config => config.Key, config => config.Value);
|
||||
|
||||
//save app settings to the file
|
||||
if (!fileExists || overwrite)
|
||||
{
|
||||
var text = JsonConvert.SerializeObject(appSettings, Formatting.Indented);
|
||||
fileProvider.WriteAllText(filePath, text, Encoding.UTF8);
|
||||
}
|
||||
|
||||
return appSettings;
|
||||
}
|
||||
|
||||
//sort configurations for display by order (e.g. data configuration with 0 will be the first)
|
||||
appSettings.Configuration = configuration
|
||||
.SelectMany(outConfig => _configurationOrder.Where(inConfig => inConfig.Key == outConfig.Key).DefaultIfEmpty(),
|
||||
(outConfig, inConfig) => new { OutConfig = outConfig, InConfig = inConfig })
|
||||
.OrderBy(config => config.InConfig.Value)
|
||||
.Select(config => config.OutConfig)
|
||||
.ToDictionary(config => config.Key, config => config.Value);
|
||||
|
||||
//save app settings to the file
|
||||
if (!fileExists || overwrite)
|
||||
{
|
||||
var text = JsonConvert.SerializeObject(appSettings, Formatting.Indented);
|
||||
fileProvider.WriteAllText(filePath, text, Encoding.UTF8);
|
||||
}
|
||||
|
||||
return appSettings;
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,41 +1,45 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the event that is raised when App Settings are saving
|
||||
/// </summary>
|
||||
public partial class AppSettingsSavingEvent
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
#region Ctor
|
||||
|
||||
public AppSettingsSavingEvent(IList<IConfig> configurations)
|
||||
{
|
||||
Configurations = configurations;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Add configuration to save
|
||||
/// Represents the event that is raised when App Settings are saving
|
||||
/// </summary>
|
||||
/// <param name="config">Configuration to save</param>
|
||||
public void AddConfig<TConfig>(TConfig config) where TConfig : class, IConfig
|
||||
public class AppSettingsSavingEvent
|
||||
{
|
||||
if (Configurations.OfType<TConfig>().FirstOrDefault() is TConfig currentConfig)
|
||||
Configurations[Configurations.IndexOf(currentConfig)] = config;
|
||||
else
|
||||
Configurations.Add(config);
|
||||
#region Ctor
|
||||
|
||||
public AppSettingsSavingEvent(IList<IConfig> configurations)
|
||||
{
|
||||
Configurations = configurations;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets configurations to save
|
||||
/// </summary>
|
||||
public IList<IConfig> Configurations { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Add configuration to save
|
||||
/// </summary>
|
||||
/// <param name="config">Configuration to save</param>
|
||||
public void AddConfig<TConfig>(TConfig config) where TConfig : class, IConfig
|
||||
{
|
||||
if (Configurations.OfType<TConfig>().FirstOrDefault() is TConfig currentConfig)
|
||||
Configurations[Configurations.IndexOf(currentConfig)] = config;
|
||||
else
|
||||
Configurations.Add(config);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets configurations to save
|
||||
/// </summary>
|
||||
public IList<IConfig> Configurations { get; protected set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,56 +1,57 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents Azure Blob storage configuration parameters
|
||||
/// </summary>
|
||||
public partial class AzureBlobConfig : IConfig
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets connection string for Azure Blob storage
|
||||
/// Represents Azure Blob storage configuration parameters
|
||||
/// </summary>
|
||||
public string ConnectionString { get; protected set; } = string.Empty;
|
||||
public partial class AzureBlobConfig : IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets connection string for Azure Blob storage
|
||||
/// </summary>
|
||||
public string ConnectionString { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets container name for Azure Blob storage
|
||||
/// </summary>
|
||||
public string ContainerName { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets container name for Azure Blob storage
|
||||
/// </summary>
|
||||
public string ContainerName { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets end point for Azure Blob storage
|
||||
/// </summary>
|
||||
public string EndPoint { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets end point for Azure Blob storage
|
||||
/// </summary>
|
||||
public string EndPoint { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or the Container Name is appended to the AzureBlobStorageEndPoint when constructing the url
|
||||
/// </summary>
|
||||
public bool AppendContainerName { get; protected set; } = true;
|
||||
/// <summary>
|
||||
/// Gets or sets whether or the Container Name is appended to the AzureBlobStorageEndPoint when constructing the url
|
||||
/// </summary>
|
||||
public bool AppendContainerName { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to store Data Protection Keys in Azure Blob Storage
|
||||
/// </summary>
|
||||
public bool StoreDataProtectionKeys { get; protected set; } = false;
|
||||
/// <summary>
|
||||
/// Gets or sets whether to store Data Protection Keys in Azure Blob Storage
|
||||
/// </summary>
|
||||
public bool StoreDataProtectionKeys { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Azure container name for storing Data Prtection Keys (this container should be separate from the container used for media and should be Private)
|
||||
/// </summary>
|
||||
public string DataProtectionKeysContainerName { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the Azure container name for storing Data Prtection Keys (this container should be separate from the container used for media and should be Private)
|
||||
/// </summary>
|
||||
public string DataProtectionKeysContainerName { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Azure key vault ID used to encrypt the Data Protection Keys. (this is optional)
|
||||
/// </summary>
|
||||
public string DataProtectionKeysVaultId { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the Azure key vault ID used to encrypt the Data Protection Keys. (this is optional)
|
||||
/// </summary>
|
||||
public string DataProtectionKeysVaultId { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether we should use Azure Blob storage
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public bool Enabled => !string.IsNullOrEmpty(ConnectionString);
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether we should use Azure Blob storage
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public bool Enabled => !string.IsNullOrEmpty(ConnectionString);
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use an Azure Key Vault to encrypt the Data Protection Keys
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public bool DataProtectionKeysEncryptWithVault => !string.IsNullOrEmpty(DataProtectionKeysVaultId);
|
||||
/// <summary>
|
||||
/// Whether to use an Azure Key Vault to encrypt the Data Protection Keys
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public bool DataProtectionKeysEncryptWithVault => !string.IsNullOrEmpty(DataProtectionKeysVaultId);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,23 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents cache configuration parameters
|
||||
/// </summary>
|
||||
public partial class CacheConfig : IConfig
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the default cache time in minutes
|
||||
/// Represents cache configuration parameters
|
||||
/// </summary>
|
||||
public int DefaultCacheTime { get; protected set; } = 60;
|
||||
public partial class CacheConfig : IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the default cache time in minutes
|
||||
/// </summary>
|
||||
public int DefaultCacheTime { get; private set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to disable linq2db query cache
|
||||
/// </summary>
|
||||
public bool LinqDisableQueryCache { get; protected set; } = false;
|
||||
/// <summary>
|
||||
/// Gets or sets the short term cache time in minutes
|
||||
/// </summary>
|
||||
public int ShortTermCacheTime { get; private set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bundled files cache time in minutes
|
||||
/// </summary>
|
||||
public int BundledFilesCacheTime { get; private set; } = 120;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +1,59 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents common configuration parameters
|
||||
/// </summary>
|
||||
public partial class CommonConfig : IConfig
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to display the full error in production environment. It's ignored (always enabled) in development environment
|
||||
/// Represents common configuration parameters
|
||||
/// </summary>
|
||||
public bool DisplayFullErrorStack { get; protected set; } = false;
|
||||
public partial class CommonConfig : IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to display the full error in production environment. It's ignored (always enabled) in development environment
|
||||
/// </summary>
|
||||
public bool DisplayFullErrorStack { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path to database with user agent strings
|
||||
/// </summary>
|
||||
public string UserAgentStringsPath { get; protected set; } = "~/App_Data/browscap.xml";
|
||||
/// <summary>
|
||||
/// Gets or sets path to database with user agent strings
|
||||
/// </summary>
|
||||
public string UserAgentStringsPath { get; private set; } = "~/App_Data/browscap.xml";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path to database with crawler only user agent strings
|
||||
/// </summary>
|
||||
public string CrawlerOnlyUserAgentStringsPath { get; protected set; } = "~/App_Data/browscap.crawlersonly.xml";
|
||||
/// <summary>
|
||||
/// Gets or sets path to database with crawler only user agent strings
|
||||
/// </summary>
|
||||
public string CrawlerOnlyUserAgentStringsPath { get; private set; } = "~/App_Data/browscap.crawlersonly.xml";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path to additional database with crawler only user agent strings
|
||||
/// </summary>
|
||||
public string CrawlerOnlyAdditionalUserAgentStringsPath { get; protected set; } = "~/App_Data/additional.crawlers.xml";
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to store TempData in the session state. By default the cookie-based TempData provider is used to store TempData in cookies.
|
||||
/// </summary>
|
||||
public bool UseSessionStateTempDataProvider { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to store TempData in the session state. By default the cookie-based TempData provider is used to store TempData in cookies.
|
||||
/// </summary>
|
||||
public bool UseSessionStateTempDataProvider { get; protected set; } = false;
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether to use MiniProfiler services
|
||||
/// </summary>
|
||||
public bool MiniProfilerEnabled { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The length of time, in milliseconds, before the running schedule task times out. Set null to use default value
|
||||
/// </summary>
|
||||
public int? ScheduleTaskRunTimeout { get; protected set; } = null;
|
||||
/// <summary>
|
||||
/// The length of time, in milliseconds, before the running schedule task times out. Set null to use default value
|
||||
/// </summary>
|
||||
public int? ScheduleTaskRunTimeout { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value of "Cache-Control" header value for static content (in seconds)
|
||||
/// </summary>
|
||||
public string StaticFilesCacheControl { get; protected set; } = "public,max-age=31536000";
|
||||
/// <summary>
|
||||
/// Gets or sets a value of "Cache-Control" header value for static content (in seconds)
|
||||
/// </summary>
|
||||
public string StaticFilesCacheControl { get; private set; } = "public,max-age=31536000";
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the blacklist of static file extension for plugin directories
|
||||
/// </summary>
|
||||
public string PluginStaticFileExtensionsBlacklist { get; protected set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether we should support previous nopCommerce versions (it can slightly improve performance)
|
||||
/// </summary>
|
||||
public bool SupportPreviousNopcommerceVersions { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set a value indicating whether to serve files that don't have a recognized content-type
|
||||
/// </summary>
|
||||
public bool ServeUnknownFileTypes { get; protected set; } = false;
|
||||
/// <summary>
|
||||
/// Get or set the blacklist of static file extension for plugin directories
|
||||
/// </summary>
|
||||
public string PluginStaticFileExtensionsBlacklist { get; private set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Get or set a value indicating whether to use Autofac IoC container
|
||||
///
|
||||
/// If value is set to false then the default .Net IoC container will be use
|
||||
/// </summary>
|
||||
public bool UseAutofac { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of permit counters that can be allowed in a window (1 minute).
|
||||
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
|
||||
/// If set to 0 than limitation is off
|
||||
/// </summary>
|
||||
public int PermitLimit { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum cumulative permit count of queued acquisition requests.
|
||||
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
|
||||
/// If set to 0 than Queue is off
|
||||
/// </summary>
|
||||
public int QueueCount { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Default status code to set on the response when a request is rejected.
|
||||
/// </summary>
|
||||
public int RejectionStatusCode { get; set; } = 503;
|
||||
/// <summary>
|
||||
/// Get or set a value indicating whether to serve files that don't have a recognized content-type
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool ServeUnknownFileTypes { get; private set; } = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +1,37 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents distributed cache configuration parameters
|
||||
/// </summary>
|
||||
public partial class DistributedCacheConfig : IConfig
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a distributed cache type
|
||||
/// Represents distributed cache configuration parameters
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public DistributedCacheType DistributedCacheType { get; protected set; } = DistributedCacheType.RedisSynchronizedMemory;
|
||||
public partial class DistributedCacheConfig : IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a distributed cache type
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public DistributedCacheType DistributedCacheType { get; private set; } = DistributedCacheType.Redis;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether we should use distributed cache
|
||||
/// </summary>
|
||||
public bool Enabled { get; protected set; } = false;
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether we should use distributed cache
|
||||
/// </summary>
|
||||
public bool Enabled { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets connection string. Used when distributed cache is enabled
|
||||
/// </summary>
|
||||
public string ConnectionString { get; protected set; } = "127.0.0.1:6379,ssl=False";
|
||||
/// <summary>
|
||||
/// Gets or sets connection string. Used when distributed cache is enabled
|
||||
/// </summary>
|
||||
public string ConnectionString { get; private set; } = "127.0.0.1:6379,ssl=False";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets schema name. Used when distributed cache is enabled and DistributedCacheType property is set as SqlServer
|
||||
/// </summary>
|
||||
public string SchemaName { get; protected set; } = "dbo";
|
||||
/// <summary>
|
||||
/// Gets or sets schema name. Used when distributed cache is enabled and DistributedCacheType property is set as SqlServer
|
||||
/// </summary>
|
||||
public string SchemaName { get; private set; } = "dbo";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets table name. Used when distributed cache is enabled and DistributedCacheType property is set as SqlServer
|
||||
/// </summary>
|
||||
public string TableName { get; protected set; } = "DistributedCache";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets instance name. Used when distributed cache is enabled and DistributedCacheType property is set as Redis or RedisSynchronizedMemory.
|
||||
/// Useful when one wants to partition a single Redis server for use with multiple apps, e.g. by setting InstanceName to "development" and "production".
|
||||
/// </summary>
|
||||
public string InstanceName { get; protected set; } = "nopCommerce";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Redis event publish interval in milliseconds.
|
||||
/// Used when distributed cache is enabled and DistributedCacheType property is set as RedisSynchronizedMemory.
|
||||
/// If greater than zero, events will be buffered for this long before being published in batch, in order to reduce server load.
|
||||
/// If zero, events are published when they are raised, without buffering.
|
||||
/// </summary>
|
||||
public int PublishIntervalMs { get; protected set; } = 500;
|
||||
/// <summary>
|
||||
/// Gets or sets table name. Used when distributed cache is enabled and DistributedCacheType property is set as SqlServer
|
||||
/// </summary>
|
||||
public string TableName { get; private set; } = "DistributedCache";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents distributed cache types enumeration
|
||||
/// </summary>
|
||||
public enum DistributedCacheType
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
[EnumMember(Value = "memory")]
|
||||
Memory,
|
||||
[EnumMember(Value = "sqlserver")]
|
||||
SqlServer,
|
||||
[EnumMember(Value = "redis")]
|
||||
Redis,
|
||||
[EnumMember(Value = "redissynchronizedmemory")]
|
||||
RedisSynchronizedMemory
|
||||
/// <summary>
|
||||
/// Represents distributed cache types enumeration
|
||||
/// </summary>
|
||||
public enum DistributedCacheType
|
||||
{
|
||||
[EnumMember(Value = "memory")]
|
||||
Memory,
|
||||
[EnumMember(Value = "sqlserver")]
|
||||
SqlServer,
|
||||
[EnumMember(Value = "redis")]
|
||||
Redis
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +1,29 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents hosting configuration parameters
|
||||
/// </summary>
|
||||
public partial class HostingConfig : IConfig
|
||||
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use proxy servers and load balancers
|
||||
/// Represents hosting configuration parameters
|
||||
/// </summary>
|
||||
public bool UseProxy { get; protected set; }
|
||||
public partial class HostingConfig : IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use proxy servers and load balancers
|
||||
/// </summary>
|
||||
public bool UseProxy { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header used to retrieve the value for the originating scheme (HTTP/HTTPS)
|
||||
/// </summary>
|
||||
public string ForwardedProtoHeaderName { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the header used to retrieve the value for the originating scheme (HTTP/HTTPS)
|
||||
/// </summary>
|
||||
public string ForwardedProtoHeaderName { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header used to retrieve the originating client IP
|
||||
/// </summary>
|
||||
public string ForwardedForHeaderName { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the header used to retrieve the originating client IP
|
||||
/// </summary>
|
||||
public string ForwardedForHeaderName { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets addresses of known proxies to accept forwarded headers from
|
||||
/// </summary>
|
||||
public string KnownProxies { get; protected set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets addresses of known networks to accept forwarded headers from
|
||||
/// </summary>
|
||||
public string KnownNetworks { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets addresses of known proxies to accept forwarded headers from
|
||||
/// </summary>
|
||||
public string KnownProxies { get; private set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,22 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a configuration from app settings
|
||||
/// </summary>
|
||||
public partial interface IConfig
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a section name to load configuration
|
||||
/// Represents a configuration from app settings
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
string Name => GetType().Name;
|
||||
public partial interface IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a section name to load configuration
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
string Name => GetType().Name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an order of configuration
|
||||
/// </summary>
|
||||
/// <returns>Order</returns>
|
||||
public int GetOrder() => 1;
|
||||
/// <summary>
|
||||
/// Gets an order of configuration
|
||||
/// </summary>
|
||||
/// <returns>Order</returns>
|
||||
public int GetOrder() => 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Setting interface
|
||||
/// </summary>
|
||||
public partial interface ISettings
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Setting interface
|
||||
/// </summary>
|
||||
public interface ISettings
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents installation configuration parameters
|
||||
/// </summary>
|
||||
public partial class InstallationConfig : IConfig
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a store owner can install sample data during installation
|
||||
/// Represents installation configuration parameters
|
||||
/// </summary>
|
||||
public bool DisableSampleData { get; protected set; } = false;
|
||||
public partial class InstallationConfig : IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a store owner can install sample data during installation
|
||||
/// </summary>
|
||||
public bool DisableSampleData { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of plugins ignored during nopCommerce installation
|
||||
/// </summary>
|
||||
public string DisabledPlugins { get; protected set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets a list of plugins ignored during nopCommerce installation
|
||||
/// </summary>
|
||||
public string DisabledPlugins { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to download and setup the regional language pack during installation
|
||||
/// </summary>
|
||||
public bool InstallRegionalResources { get; protected set; } = true;
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to download and setup the regional language pack during installation
|
||||
/// </summary>
|
||||
public bool InstallRegionalResources { get; private set; } = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,19 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents default values related to configuration services
|
||||
/// </summary>
|
||||
public static partial class NopConfigurationDefaults
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the path to file that contains app settings
|
||||
/// Represents default values related to configuration services
|
||||
/// </summary>
|
||||
public static string AppSettingsFilePath => "App_Data/appsettings.json";
|
||||
public static partial class NopConfigurationDefaults
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the path to file that contains app settings
|
||||
/// </summary>
|
||||
public static string AppSettingsFilePath => "App_Data/appsettings.json";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to file that contains app settings for specific hosting environment
|
||||
/// </summary>
|
||||
/// <remarks>0 - Environment name</remarks>
|
||||
public static string AppSettingsEnvironmentFilePath => "App_Data/appsettings.{0}.json";
|
||||
/// <summary>
|
||||
/// Gets the path to file that contains app settings for specific hosting environment
|
||||
/// </summary>
|
||||
/// <remarks>0 - Environment name</remarks>
|
||||
public static string AppSettingsEnvironmentFilePath => "App_Data/appsettings.{0}.json";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,28 @@
|
|||
namespace Nop.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents plugin configuration parameters
|
||||
/// </summary>
|
||||
public partial class PluginConfig : IConfig
|
||||
namespace Nop.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to load an assembly into the load-from context, bypassing some security checks.
|
||||
/// Represents plugin configuration parameters
|
||||
/// </summary>
|
||||
public bool UseUnsafeLoadAssembly { get; set; } = true;
|
||||
public partial class PluginConfig : IConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to clear /Plugins/bin directory on application startup
|
||||
/// </summary>
|
||||
public bool ClearPluginShadowDirectoryOnStartup { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to copy "locked" assemblies from /Plugins/bin directory to temporary subdirectories on application startup
|
||||
/// </summary>
|
||||
public bool CopyLockedPluginAssembilesToSubdirectoriesOnStartup { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to load an assembly into the load-from context, bypassing some security checks.
|
||||
/// </summary>
|
||||
public bool UseUnsafeLoadAssembly { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to copy plugins library to the /Plugins/bin directory on application startup
|
||||
/// </summary>
|
||||
public bool UsePluginsShadowCopy { get; private set; } = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +1,35 @@
|
|||
using Nop.Core.Domain.Common;
|
||||
|
||||
namespace Nop.Core.Domain.Affiliates;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an affiliate
|
||||
/// </summary>
|
||||
public partial class Affiliate : BaseEntity, ISoftDeletedEntity
|
||||
namespace Nop.Core.Domain.Affiliates
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the address identifier
|
||||
/// Represents an affiliate
|
||||
/// </summary>
|
||||
public int AddressId { get; set; }
|
||||
public partial class Affiliate : BaseEntity, ISoftDeletedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the address identifier
|
||||
/// </summary>
|
||||
public int AddressId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the admin comment
|
||||
/// </summary>
|
||||
public string AdminComment { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the admin comment
|
||||
/// </summary>
|
||||
public string AdminComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the friendly name for generated affiliate URL (by default affiliate ID is used)
|
||||
/// </summary>
|
||||
public string FriendlyUrlName { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the friendly name for generated affiliate URL (by default affiliate ID is used)
|
||||
/// </summary>
|
||||
public string FriendlyUrlName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is active
|
||||
/// </summary>
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is active
|
||||
/// </summary>
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the base class for attributes
|
||||
/// </summary>
|
||||
public abstract partial class BaseAttribute : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the attribute is required
|
||||
/// </summary>
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute control type identifier
|
||||
/// </summary>
|
||||
public int AttributeControlTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute control type
|
||||
/// </summary>
|
||||
public AttributeControlType AttributeControlType
|
||||
{
|
||||
get => (AttributeControlType)AttributeControlTypeId;
|
||||
set => AttributeControlTypeId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether this attribute should have values
|
||||
/// </summary>
|
||||
public bool ShouldHaveValues
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AttributeControlType == AttributeControlType.TextBox ||
|
||||
AttributeControlType == AttributeControlType.MultilineTextbox ||
|
||||
AttributeControlType == AttributeControlType.Datepicker ||
|
||||
AttributeControlType == AttributeControlType.FileUpload)
|
||||
return false;
|
||||
|
||||
//other attribute control types support values
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether this attribute can be used as condition for some other attribute
|
||||
/// </summary>
|
||||
public bool CanBeUsedAsCondition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AttributeControlType == AttributeControlType.ReadonlyCheckboxes ||
|
||||
AttributeControlType == AttributeControlType.TextBox ||
|
||||
AttributeControlType == AttributeControlType.MultilineTextbox ||
|
||||
AttributeControlType == AttributeControlType.Datepicker ||
|
||||
AttributeControlType == AttributeControlType.FileUpload)
|
||||
return false;
|
||||
|
||||
//other attribute control types support it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the base class for attribute values
|
||||
/// </summary>
|
||||
public abstract partial class BaseAttributeValue : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the value is pre-selected
|
||||
/// </summary>
|
||||
public bool IsPreSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute identifier
|
||||
/// </summary>
|
||||
public int AttributeId { get; set; }
|
||||
}
|
||||
|
|
@ -1,37 +1,40 @@
|
|||
namespace Nop.Core.Domain.Blogs;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a blog comment
|
||||
/// </summary>
|
||||
public partial class BlogComment : BaseEntity
|
||||
namespace Nop.Core.Domain.Blogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// Represents a blog comment
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
public partial class BlogComment : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment text
|
||||
/// </summary>
|
||||
public string CommentText { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the comment text
|
||||
/// </summary>
|
||||
public string CommentText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the comment is approved
|
||||
/// </summary>
|
||||
public bool IsApproved { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the comment is approved
|
||||
/// </summary>
|
||||
public bool IsApproved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post identifier
|
||||
/// </summary>
|
||||
public int BlogPostId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post identifier
|
||||
/// </summary>
|
||||
public int BlogPostId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +1,82 @@
|
|||
using Nop.Core.Domain.Seo;
|
||||
using System;
|
||||
using Nop.Core.Domain.Seo;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Core.Domain.Blogs;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a blog post
|
||||
/// </summary>
|
||||
public partial class BlogPost : BaseEntity, ISlugSupported, IStoreMappingSupported
|
||||
namespace Nop.Core.Domain.Blogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the language identifier
|
||||
/// Represents a blog post
|
||||
/// </summary>
|
||||
public int LanguageId { get; set; }
|
||||
public partial class BlogPost : BaseEntity, ISlugSupported, IStoreMappingSupported
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the language identifier
|
||||
/// </summary>
|
||||
public int LanguageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value indicating whether this blog post should be included in sitemap
|
||||
/// </summary>
|
||||
public bool IncludeInSitemap { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the value indicating whether this blog post should be included in sitemap
|
||||
/// </summary>
|
||||
public bool IncludeInSitemap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post title
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post title
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post body
|
||||
/// </summary>
|
||||
public string Body { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post body
|
||||
/// </summary>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post overview. If specified, then it's used on the blog page instead of the "Body"
|
||||
/// </summary>
|
||||
public string BodyOverview { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post overview. If specified, then it's used on the blog page instead of the "Body"
|
||||
/// </summary>
|
||||
public string BodyOverview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the blog post comments are allowed
|
||||
/// </summary>
|
||||
public bool AllowComments { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the blog post comments are allowed
|
||||
/// </summary>
|
||||
public bool AllowComments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blog tags
|
||||
/// </summary>
|
||||
public string Tags { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the blog tags
|
||||
/// </summary>
|
||||
public string Tags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post start date and time
|
||||
/// </summary>
|
||||
public DateTime? StartDateUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post start date and time
|
||||
/// </summary>
|
||||
public DateTime? StartDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post end date and time
|
||||
/// </summary>
|
||||
public DateTime? EndDateUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the blog post end date and time
|
||||
/// </summary>
|
||||
public DateTime? EndDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta keywords
|
||||
/// </summary>
|
||||
public string MetaKeywords { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta keywords
|
||||
/// </summary>
|
||||
public string MetaKeywords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta description
|
||||
/// </summary>
|
||||
public string MetaDescription { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta description
|
||||
/// </summary>
|
||||
public string MetaDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta title
|
||||
/// </summary>
|
||||
public string MetaTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
public virtual bool LimitedToStores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of entity creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta title
|
||||
/// </summary>
|
||||
public string MetaTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
public virtual bool LimitedToStores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of entity creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Blogs;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a blog post tag
|
||||
/// </summary>
|
||||
public partial class BlogPostTag
|
||||
namespace Nop.Core.Domain.Blogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// Represents a blog post tag
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class BlogPostTag
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tagged product count
|
||||
/// </summary>
|
||||
public int BlogPostCount { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the tagged product count
|
||||
/// </summary>
|
||||
public int BlogPostCount { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +1,50 @@
|
|||
using Nop.Core.Configuration;
|
||||
|
||||
namespace Nop.Core.Domain.Blogs;
|
||||
|
||||
/// <summary>
|
||||
/// Blog settings
|
||||
/// </summary>
|
||||
public partial class BlogSettings : ISettings
|
||||
namespace Nop.Core.Domain.Blogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether blog is enabled
|
||||
/// Blog settings
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
public class BlogSettings : ISettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether blog is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the page size for posts
|
||||
/// </summary>
|
||||
public int PostsPageSize { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the page size for posts
|
||||
/// </summary>
|
||||
public int PostsPageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether not registered user can leave comments
|
||||
/// </summary>
|
||||
public bool AllowNotRegisteredUsersToLeaveComments { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether not registered user can leave comments
|
||||
/// </summary>
|
||||
public bool AllowNotRegisteredUsersToLeaveComments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to notify about new blog comments
|
||||
/// </summary>
|
||||
public bool NotifyAboutNewBlogComments { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to notify about new blog comments
|
||||
/// </summary>
|
||||
public bool NotifyAboutNewBlogComments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a number of blog tags that appear in the tag cloud
|
||||
/// </summary>
|
||||
public int NumberOfTags { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a number of blog tags that appear in the tag cloud
|
||||
/// </summary>
|
||||
public int NumberOfTags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable the blog RSS feed link in customers browser address bar
|
||||
/// </summary>
|
||||
public bool ShowHeaderRssUrl { get; set; }
|
||||
/// <summary>
|
||||
/// Enable the blog RSS feed link in customers browser address bar
|
||||
/// </summary>
|
||||
public bool ShowHeaderRssUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether blog comments must be approved
|
||||
/// </summary>
|
||||
public bool BlogCommentsMustBeApproved { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether blog comments must be approved
|
||||
/// </summary>
|
||||
public bool BlogCommentsMustBeApproved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether blog comments will be filtered per store
|
||||
/// </summary>
|
||||
public bool ShowBlogCommentsPerStore { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether blog comments will be filtered per store
|
||||
/// </summary>
|
||||
public bool ShowBlogCommentsPerStore { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,22 @@
|
|||
namespace Nop.Core.Domain.Blogs;
|
||||
|
||||
/// <summary>
|
||||
/// Blog post comment approved event
|
||||
/// </summary>
|
||||
public partial class BlogCommentApprovedEvent
|
||||
namespace Nop.Core.Domain.Blogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// Blog post comment approved event
|
||||
/// </summary>
|
||||
/// <param name="blogComment">Blog comment</param>
|
||||
public BlogCommentApprovedEvent(BlogComment blogComment)
|
||||
public class BlogCommentApprovedEvent
|
||||
{
|
||||
BlogComment = blogComment;
|
||||
}
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
/// <param name="blogComment">Blog comment</param>
|
||||
public BlogCommentApprovedEvent(BlogComment blogComment)
|
||||
{
|
||||
BlogComment = blogComment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blog post comment
|
||||
/// </summary>
|
||||
public BlogComment BlogComment { get; }
|
||||
/// <summary>
|
||||
/// Blog post comment
|
||||
/// </summary>
|
||||
public BlogComment BlogComment { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +1,58 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an attribute control type
|
||||
/// </summary>
|
||||
public enum AttributeControlType
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Dropdown list
|
||||
/// Represents an attribute control type
|
||||
/// </summary>
|
||||
DropdownList = 1,
|
||||
public enum AttributeControlType
|
||||
{
|
||||
/// <summary>
|
||||
/// Dropdown list
|
||||
/// </summary>
|
||||
DropdownList = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Radio list
|
||||
/// </summary>
|
||||
RadioList = 2,
|
||||
/// <summary>
|
||||
/// Radio list
|
||||
/// </summary>
|
||||
RadioList = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Checkboxes
|
||||
/// </summary>
|
||||
Checkboxes = 3,
|
||||
/// <summary>
|
||||
/// Checkboxes
|
||||
/// </summary>
|
||||
Checkboxes = 3,
|
||||
|
||||
/// <summary>
|
||||
/// TextBox
|
||||
/// </summary>
|
||||
TextBox = 4,
|
||||
/// <summary>
|
||||
/// TextBox
|
||||
/// </summary>
|
||||
TextBox = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Multiline textbox
|
||||
/// </summary>
|
||||
MultilineTextbox = 10,
|
||||
/// <summary>
|
||||
/// Multiline textbox
|
||||
/// </summary>
|
||||
MultilineTextbox = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Datepicker
|
||||
/// </summary>
|
||||
Datepicker = 20,
|
||||
/// <summary>
|
||||
/// Datepicker
|
||||
/// </summary>
|
||||
Datepicker = 20,
|
||||
|
||||
/// <summary>
|
||||
/// File upload control
|
||||
/// </summary>
|
||||
FileUpload = 30,
|
||||
/// <summary>
|
||||
/// File upload control
|
||||
/// </summary>
|
||||
FileUpload = 30,
|
||||
|
||||
/// <summary>
|
||||
/// Color squares
|
||||
/// </summary>
|
||||
ColorSquares = 40,
|
||||
/// <summary>
|
||||
/// Color squares
|
||||
/// </summary>
|
||||
ColorSquares = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Image squares
|
||||
/// </summary>
|
||||
ImageSquares = 45,
|
||||
/// <summary>
|
||||
/// Image squares
|
||||
/// </summary>
|
||||
ImageSquares = 45,
|
||||
|
||||
/// <summary>
|
||||
/// Read-only checkboxes
|
||||
/// </summary>
|
||||
ReadonlyCheckboxes = 50
|
||||
/// <summary>
|
||||
/// Read-only checkboxes
|
||||
/// </summary>
|
||||
ReadonlyCheckboxes = 50
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an attribute value display type when out of stock
|
||||
/// </summary>
|
||||
public enum AttributeValueOutOfStockDisplayType
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute value is visible, but cannot be interacted
|
||||
/// Represents an attribute value display type when out of stock
|
||||
/// </summary>
|
||||
Disable,
|
||||
public enum AttributeValueOutOfStockDisplayType
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute value is visible, but cannot be interacted
|
||||
/// </summary>
|
||||
Disable,
|
||||
|
||||
/// <summary>
|
||||
/// Attribute value is display always
|
||||
/// </summary>
|
||||
AlwaysDisplay
|
||||
}
|
||||
/// <summary>
|
||||
/// Attribute value is display always
|
||||
/// </summary>
|
||||
AlwaysDisplay
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an attribute value type
|
||||
/// </summary>
|
||||
public enum AttributeValueType
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple attribute value
|
||||
/// Represents an attribute value type
|
||||
/// </summary>
|
||||
Simple = 0,
|
||||
public enum AttributeValueType
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple attribute value
|
||||
/// </summary>
|
||||
Simple = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Associated to a product (used when configuring bundled products)
|
||||
/// </summary>
|
||||
AssociatedToProduct = 10,
|
||||
}
|
||||
/// <summary>
|
||||
/// Associated to a product (used when configuring bundled products)
|
||||
/// </summary>
|
||||
AssociatedToProduct = 10,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,30 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a back in stock subscription
|
||||
/// </summary>
|
||||
public partial class BackInStockSubscription : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// Represents a back in stock subscription
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
public partial class BackInStockSubscription : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a backorder mode
|
||||
/// </summary>
|
||||
public enum BackorderMode
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// No backorders
|
||||
/// Represents a backorder mode
|
||||
/// </summary>
|
||||
NoBackorders = 0,
|
||||
public enum BackorderMode
|
||||
{
|
||||
/// <summary>
|
||||
/// No backorders
|
||||
/// </summary>
|
||||
NoBackorders = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Allow qty below 0
|
||||
/// </summary>
|
||||
AllowQtyBelow0 = 1,
|
||||
/// <summary>
|
||||
/// Allow qty below 0
|
||||
/// </summary>
|
||||
AllowQtyBelow0 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Allow qty below 0 and notify customer
|
||||
/// </summary>
|
||||
AllowQtyBelow0AndNotifyCustomer = 2,
|
||||
}
|
||||
/// <summary>
|
||||
/// Allow qty below 0 and notify customer
|
||||
/// </summary>
|
||||
AllowQtyBelow0AndNotifyCustomer = 2,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,134 +1,136 @@
|
|||
using Nop.Core.Domain.Common;
|
||||
using System;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Discounts;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Security;
|
||||
using Nop.Core.Domain.Seo;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a category
|
||||
/// </summary>
|
||||
public partial class Category : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountCategoryMapping>, ISoftDeletedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// Represents a category
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class Category : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountCategoryMapping>, ISoftDeletedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the description
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value of used category template identifier
|
||||
/// </summary>
|
||||
public int CategoryTemplateId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value of used category template identifier
|
||||
/// </summary>
|
||||
public int CategoryTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta keywords
|
||||
/// </summary>
|
||||
public string MetaKeywords { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta keywords
|
||||
/// </summary>
|
||||
public string MetaKeywords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta description
|
||||
/// </summary>
|
||||
public string MetaDescription { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta description
|
||||
/// </summary>
|
||||
public string MetaDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta title
|
||||
/// </summary>
|
||||
public string MetaTitle { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta title
|
||||
/// </summary>
|
||||
public string MetaTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent category identifier
|
||||
/// </summary>
|
||||
public int ParentCategoryId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the parent category identifier
|
||||
/// </summary>
|
||||
public int ParentCategoryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the picture identifier
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the picture identifier
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the page size
|
||||
/// </summary>
|
||||
public int PageSize { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the page size
|
||||
/// </summary>
|
||||
public int PageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether customers can select the page size
|
||||
/// </summary>
|
||||
public bool AllowCustomersToSelectPageSize { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether customers can select the page size
|
||||
/// </summary>
|
||||
public bool AllowCustomersToSelectPageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the available customer selectable page size options
|
||||
/// </summary>
|
||||
public string PageSizeOptions { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the available customer selectable page size options
|
||||
/// </summary>
|
||||
public string PageSizeOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show the category on home page
|
||||
/// </summary>
|
||||
public bool ShowOnHomepage { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show the category on home page
|
||||
/// </summary>
|
||||
public bool ShowOnHomepage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to include this category in the top menu
|
||||
/// </summary>
|
||||
public bool IncludeInTopMenu { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to include this category in the top menu
|
||||
/// </summary>
|
||||
public bool IncludeInTopMenu { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||
/// </summary>
|
||||
public bool SubjectToAcl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||
/// </summary>
|
||||
public bool SubjectToAcl { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
public bool LimitedToStores { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is published
|
||||
/// </summary>
|
||||
public bool Published { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is published
|
||||
/// </summary>
|
||||
public bool Published { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance update
|
||||
/// </summary>
|
||||
public DateTime UpdatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance update
|
||||
/// </summary>
|
||||
public DateTime UpdatedOnUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range filtering is enabled
|
||||
/// </summary>
|
||||
public bool PriceRangeFiltering { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range filtering is enabled
|
||||
/// </summary>
|
||||
public bool PriceRangeFiltering { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the "from" price
|
||||
/// </summary>
|
||||
public decimal PriceFrom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the "from" price
|
||||
/// </summary>
|
||||
public decimal PriceFrom { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the "to" price
|
||||
/// </summary>
|
||||
public decimal PriceTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the "to" price
|
||||
/// </summary>
|
||||
public decimal PriceTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range should be entered manually
|
||||
/// </summary>
|
||||
public bool ManuallyPriceRange { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range should be entered manually
|
||||
/// </summary>
|
||||
public bool ManuallyPriceRange { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a category template
|
||||
/// </summary>
|
||||
public partial class CategoryTemplate : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the template name
|
||||
/// Represents a category template
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class CategoryTemplate : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the template name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the view path
|
||||
/// </summary>
|
||||
public string ViewPath { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the view path
|
||||
/// </summary>
|
||||
public string ViewPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a cross-sell product
|
||||
/// </summary>
|
||||
public partial class CrossSellProduct : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the first product identifier
|
||||
/// Represents a cross-sell product
|
||||
/// </summary>
|
||||
public int ProductId1 { get; set; }
|
||||
public partial class CrossSellProduct : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the first product identifier
|
||||
/// </summary>
|
||||
public int ProductId1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the second product identifier
|
||||
/// </summary>
|
||||
public int ProductId2 { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the second product identifier
|
||||
/// </summary>
|
||||
public int ProductId2 { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a download activation type
|
||||
/// </summary>
|
||||
public enum DownloadActivationType
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// When order is paid
|
||||
/// Represents a download activation type
|
||||
/// </summary>
|
||||
WhenOrderIsPaid = 0,
|
||||
public enum DownloadActivationType
|
||||
{
|
||||
/// <summary>
|
||||
/// When order is paid
|
||||
/// </summary>
|
||||
WhenOrderIsPaid = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Manually
|
||||
/// </summary>
|
||||
Manually = 10,
|
||||
}
|
||||
/// <summary>
|
||||
/// Manually
|
||||
/// </summary>
|
||||
Manually = 10,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Product review approved event
|
||||
/// </summary>
|
||||
public partial class ProductReviewApprovedEvent
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// Product review approved event
|
||||
/// </summary>
|
||||
/// <param name="productReview">Product review</param>
|
||||
public ProductReviewApprovedEvent(ProductReview productReview)
|
||||
public class ProductReviewApprovedEvent
|
||||
{
|
||||
ProductReview = productReview;
|
||||
}
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
/// <param name="productReview">Product review</param>
|
||||
public ProductReviewApprovedEvent(ProductReview productReview)
|
||||
{
|
||||
ProductReview = productReview;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Product review
|
||||
/// </summary>
|
||||
public ProductReview ProductReview { get; }
|
||||
/// <summary>
|
||||
/// Product review
|
||||
/// </summary>
|
||||
public ProductReview ProductReview { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a gift card type
|
||||
/// </summary>
|
||||
public enum GiftCardType
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Virtual
|
||||
/// Represents a gift card type
|
||||
/// </summary>
|
||||
Virtual = 0,
|
||||
public enum GiftCardType
|
||||
{
|
||||
/// <summary>
|
||||
/// Virtual
|
||||
/// </summary>
|
||||
Virtual = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Physical
|
||||
/// </summary>
|
||||
Physical = 1,
|
||||
}
|
||||
/// <summary>
|
||||
/// Physical
|
||||
/// </summary>
|
||||
Physical = 1,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a low stock activity
|
||||
/// </summary>
|
||||
public enum LowStockActivity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing
|
||||
/// Represents a low stock activity
|
||||
/// </summary>
|
||||
Nothing = 0,
|
||||
public enum LowStockActivity
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing
|
||||
/// </summary>
|
||||
Nothing = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Disable buy button
|
||||
/// </summary>
|
||||
DisableBuyButton = 1,
|
||||
/// <summary>
|
||||
/// Disable buy button
|
||||
/// </summary>
|
||||
DisableBuyButton = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unpublish
|
||||
/// </summary>
|
||||
Unpublish = 2,
|
||||
}
|
||||
/// <summary>
|
||||
/// Unpublish
|
||||
/// </summary>
|
||||
Unpublish = 2,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a method of inventory management
|
||||
/// </summary>
|
||||
public enum ManageInventoryMethod
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't track inventory for product
|
||||
/// Represents a method of inventory management
|
||||
/// </summary>
|
||||
DontManageStock = 0,
|
||||
public enum ManageInventoryMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't track inventory for product
|
||||
/// </summary>
|
||||
DontManageStock = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Track inventory for product
|
||||
/// </summary>
|
||||
ManageStock = 1,
|
||||
/// <summary>
|
||||
/// Track inventory for product
|
||||
/// </summary>
|
||||
ManageStock = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Track inventory for product by product attributes
|
||||
/// </summary>
|
||||
ManageStockByAttributes = 2,
|
||||
}
|
||||
/// <summary>
|
||||
/// Track inventory for product by product attributes
|
||||
/// </summary>
|
||||
ManageStockByAttributes = 2,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,119 +1,121 @@
|
|||
using Nop.Core.Domain.Common;
|
||||
using System;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Discounts;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Security;
|
||||
using Nop.Core.Domain.Seo;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a manufacturer
|
||||
/// </summary>
|
||||
public partial class Manufacturer : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountManufacturerMapping>, ISoftDeletedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// Represents a manufacturer
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class Manufacturer : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountManufacturerMapping>, ISoftDeletedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the description
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value of used manufacturer template identifier
|
||||
/// </summary>
|
||||
public int ManufacturerTemplateId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value of used manufacturer template identifier
|
||||
/// </summary>
|
||||
public int ManufacturerTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta keywords
|
||||
/// </summary>
|
||||
public string MetaKeywords { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta keywords
|
||||
/// </summary>
|
||||
public string MetaKeywords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta description
|
||||
/// </summary>
|
||||
public string MetaDescription { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta description
|
||||
/// </summary>
|
||||
public string MetaDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta title
|
||||
/// </summary>
|
||||
public string MetaTitle { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the meta title
|
||||
/// </summary>
|
||||
public string MetaTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent picture identifier
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the parent picture identifier
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the page size
|
||||
/// </summary>
|
||||
public int PageSize { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the page size
|
||||
/// </summary>
|
||||
public int PageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether customers can select the page size
|
||||
/// </summary>
|
||||
public bool AllowCustomersToSelectPageSize { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether customers can select the page size
|
||||
/// </summary>
|
||||
public bool AllowCustomersToSelectPageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the available customer selectable page size options
|
||||
/// </summary>
|
||||
public string PageSizeOptions { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the available customer selectable page size options
|
||||
/// </summary>
|
||||
public string PageSizeOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||
/// </summary>
|
||||
public bool SubjectToAcl { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||
/// </summary>
|
||||
public bool SubjectToAcl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
public bool LimitedToStores { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is published
|
||||
/// </summary>
|
||||
public bool Published { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is published
|
||||
/// </summary>
|
||||
public bool Published { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance update
|
||||
/// </summary>
|
||||
public DateTime UpdatedOnUtc { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance update
|
||||
/// </summary>
|
||||
public DateTime UpdatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range filtering is enabled
|
||||
/// </summary>
|
||||
public bool PriceRangeFiltering { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range filtering is enabled
|
||||
/// </summary>
|
||||
public bool PriceRangeFiltering { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the "from" price
|
||||
/// </summary>
|
||||
public decimal PriceFrom { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the "from" price
|
||||
/// </summary>
|
||||
public decimal PriceFrom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the "to" price
|
||||
/// </summary>
|
||||
public decimal PriceTo { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the "to" price
|
||||
/// </summary>
|
||||
public decimal PriceTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range should be entered manually
|
||||
/// </summary>
|
||||
public bool ManuallyPriceRange { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the price range should be entered manually
|
||||
/// </summary>
|
||||
public bool ManuallyPriceRange { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a manufacturer template
|
||||
/// </summary>
|
||||
public partial class ManufacturerTemplate : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the template name
|
||||
/// Represents a manufacturer template
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class ManufacturerTemplate : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the template name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the view path
|
||||
/// </summary>
|
||||
public string ViewPath { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the view path
|
||||
/// </summary>
|
||||
public string ViewPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,50 @@
|
|||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a predefined (default) product attribute value
|
||||
/// </summary>
|
||||
public partial class PredefinedProductAttributeValue : BaseEntity, ILocalizedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute identifier
|
||||
/// Represents a predefined (default) product attribute value
|
||||
/// </summary>
|
||||
public int ProductAttributeId { get; set; }
|
||||
public partial class PredefinedProductAttributeValue : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute identifier
|
||||
/// </summary>
|
||||
public int ProductAttributeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the price adjustment
|
||||
/// </summary>
|
||||
public decimal PriceAdjustment { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the price adjustment
|
||||
/// </summary>
|
||||
public decimal PriceAdjustment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether "price adjustment" is specified as percentage
|
||||
/// </summary>
|
||||
public bool PriceAdjustmentUsePercentage { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether "price adjustment" is specified as percentage
|
||||
/// </summary>
|
||||
public bool PriceAdjustmentUsePercentage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weight adjustment
|
||||
/// </summary>
|
||||
public decimal WeightAdjustment { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the weight adjustment
|
||||
/// </summary>
|
||||
public decimal WeightAdjustment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value cost
|
||||
/// </summary>
|
||||
public decimal Cost { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value cost
|
||||
/// </summary>
|
||||
public decimal Cost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the value is pre-selected
|
||||
/// </summary>
|
||||
public bool IsPreSelected { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the value is pre-selected
|
||||
/// </summary>
|
||||
public bool IsPreSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,19 +1,20 @@
|
|||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product attribute
|
||||
/// </summary>
|
||||
public partial class ProductAttribute : BaseEntity, ILocalizedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// Represents a product attribute
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class ProductAttribute : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the description
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,63 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product attribute combination
|
||||
/// </summary>
|
||||
public partial class ProductAttributeCombination : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a product attribute combination
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
public partial class ProductAttributeCombination : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attributes
|
||||
/// </summary>
|
||||
public string AttributesXml { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the attributes
|
||||
/// </summary>
|
||||
public string AttributesXml { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stock quantity
|
||||
/// </summary>
|
||||
public int StockQuantity { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the stock quantity
|
||||
/// </summary>
|
||||
public int StockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to allow orders when out of stock
|
||||
/// </summary>
|
||||
public bool AllowOutOfStockOrders { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to allow orders when out of stock
|
||||
/// </summary>
|
||||
public bool AllowOutOfStockOrders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SKU
|
||||
/// </summary>
|
||||
public string Sku { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SKU
|
||||
/// </summary>
|
||||
public string Sku { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the manufacturer part number
|
||||
/// </summary>
|
||||
public string ManufacturerPartNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the manufacturer part number
|
||||
/// </summary>
|
||||
public string ManufacturerPartNumber { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books).
|
||||
/// </summary>
|
||||
public string Gtin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books).
|
||||
/// </summary>
|
||||
public string Gtin { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute combination price. This way a store owner can override the default product price when this attribute combination is added to the cart. For example, you can give a discount this way.
|
||||
/// </summary>
|
||||
public decimal? OverriddenPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute combination price. This way a store owner can override the default product price when this attribute combination is added to the cart. For example, you can give a discount this way.
|
||||
/// </summary>
|
||||
public decimal? OverriddenPrice { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity when admin should be notified
|
||||
/// </summary>
|
||||
public int NotifyAdminForQuantityBelow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity when admin should be notified
|
||||
/// </summary>
|
||||
public int NotifyAdminForQuantityBelow { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of picture associated with this combination
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum stock quantity
|
||||
/// </summary>
|
||||
public int MinStockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The field is not used since 4.70 and is left only for the update process
|
||||
/// use the <see cref="ProductAttributeCombinationPicture"/> instead
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Browsable(false)]
|
||||
[Obsolete("The field is not used since 4.70 and is left only for the update process use the ProductAttributeCombinationPicture instead")]
|
||||
public int? PictureId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum stock quantity
|
||||
/// </summary>
|
||||
public int MinStockQuantity { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product attribute combination picture
|
||||
/// </summary>
|
||||
public partial class ProductAttributeCombinationPicture : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute combination id
|
||||
/// </summary>
|
||||
public int ProductAttributeCombinationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of picture associated with this combination
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
}
|
||||
|
|
@ -1,83 +1,84 @@
|
|||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product attribute mapping
|
||||
/// </summary>
|
||||
public partial class ProductAttributeMapping : BaseEntity, ILocalizedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a product attribute mapping
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute identifier
|
||||
/// </summary>
|
||||
public int ProductAttributeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value a text prompt
|
||||
/// </summary>
|
||||
public string TextPrompt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is required
|
||||
/// </summary>
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute control type identifier
|
||||
/// </summary>
|
||||
public int AttributeControlTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
//validation fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for minimum length (for textbox and multiline textbox)
|
||||
/// </summary>
|
||||
public int? ValidationMinLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for maximum length (for textbox and multiline textbox)
|
||||
/// </summary>
|
||||
public int? ValidationMaxLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for file allowed extensions (for file upload)
|
||||
/// </summary>
|
||||
public string ValidationFileAllowedExtensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for file maximum size in kilobytes (for file upload)
|
||||
/// </summary>
|
||||
public int? ValidationFileMaximumSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default value (for textbox and multiline textbox)
|
||||
/// </summary>
|
||||
public string DefaultValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a condition (depending on other attribute) when this attribute should be enabled (visible).
|
||||
/// Leave empty (or null) to enable this attribute.
|
||||
/// Conditional attributes that only appear if a previous attribute is selected, such as having an option
|
||||
/// for personalizing clothing with a name and only providing the text input box if the "Personalize" radio button is checked.
|
||||
/// </summary>
|
||||
public string ConditionAttributeXml { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute control type
|
||||
/// </summary>
|
||||
public AttributeControlType AttributeControlType
|
||||
public partial class ProductAttributeMapping : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
get => (AttributeControlType)AttributeControlTypeId;
|
||||
set => AttributeControlTypeId = (int)value;
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute identifier
|
||||
/// </summary>
|
||||
public int ProductAttributeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value a text prompt
|
||||
/// </summary>
|
||||
public string TextPrompt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is required
|
||||
/// </summary>
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute control type identifier
|
||||
/// </summary>
|
||||
public int AttributeControlTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
//validation fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for minimum length (for textbox and multiline textbox)
|
||||
/// </summary>
|
||||
public int? ValidationMinLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for maximum length (for textbox and multiline textbox)
|
||||
/// </summary>
|
||||
public int? ValidationMaxLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for file allowed extensions (for file upload)
|
||||
/// </summary>
|
||||
public string ValidationFileAllowedExtensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validation rule for file maximum size in kilobytes (for file upload)
|
||||
/// </summary>
|
||||
public int? ValidationFileMaximumSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default value (for textbox and multiline textbox)
|
||||
/// </summary>
|
||||
public string DefaultValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a condition (depending on other attribute) when this attribute should be enabled (visible).
|
||||
/// Leave empty (or null) to enable this attribute.
|
||||
/// Conditional attributes that only appear if a previous attribute is selected, such as having an option
|
||||
/// for personalizing clothing with a name and only providing the text input box if the "Personalize" radio button is checked.
|
||||
/// </summary>
|
||||
public string ConditionAttributeXml { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute control type
|
||||
/// </summary>
|
||||
public AttributeControlType AttributeControlType
|
||||
{
|
||||
get => (AttributeControlType)AttributeControlTypeId;
|
||||
set => AttributeControlTypeId = (int)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +1,94 @@
|
|||
using System.ComponentModel;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product attribute value
|
||||
/// </summary>
|
||||
public partial class ProductAttributeValue : BaseEntity, ILocalizedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute mapping identifier
|
||||
/// Represents a product attribute value
|
||||
/// </summary>
|
||||
public int ProductAttributeMappingId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value type identifier
|
||||
/// </summary>
|
||||
public int AttributeValueTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the associated product identifier (used only with AttributeValueType.AssociatedToProduct)
|
||||
/// </summary>
|
||||
public int AssociatedProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color RGB value (used with "Color squares" attribute type)
|
||||
/// </summary>
|
||||
public string ColorSquaresRgb { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the picture ID for image square (used with "Image squares" attribute type)
|
||||
/// </summary>
|
||||
public int ImageSquaresPictureId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the price adjustment (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public decimal PriceAdjustment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether "price adjustment" is specified as percentage (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public bool PriceAdjustmentUsePercentage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weight adjustment (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public decimal WeightAdjustment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value cost (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public decimal Cost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer can enter the quantity of associated product (used only with AttributeValueType.AssociatedToProduct)
|
||||
/// </summary>
|
||||
public bool CustomerEntersQty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity of associated product (used only with AttributeValueType.AssociatedToProduct)
|
||||
/// </summary>
|
||||
public int Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the value is pre-selected
|
||||
/// </summary>
|
||||
public bool IsPreSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value type
|
||||
/// </summary>
|
||||
public AttributeValueType AttributeValueType
|
||||
public partial class ProductAttributeValue : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
get => (AttributeValueType)AttributeValueTypeId;
|
||||
set => AttributeValueTypeId = (int)value;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute mapping identifier
|
||||
/// </summary>
|
||||
public int ProductAttributeMappingId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The field is not used since 4.70 and is left only for the update process
|
||||
/// use the <see cref="ProductAttributeValuePicture"/> instead
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Browsable(false)]
|
||||
[Obsolete("The field is not used since 4.70 and is left only for the update process use the ProductAttributeValuePicture instead")]
|
||||
public int? PictureId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value type identifier
|
||||
/// </summary>
|
||||
public int AttributeValueTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the associated product identifier (used only with AttributeValueType.AssociatedToProduct)
|
||||
/// </summary>
|
||||
public int AssociatedProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color RGB value (used with "Color squares" attribute type)
|
||||
/// </summary>
|
||||
public string ColorSquaresRgb { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the picture ID for image square (used with "Image squares" attribute type)
|
||||
/// </summary>
|
||||
public int ImageSquaresPictureId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the price adjustment (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public decimal PriceAdjustment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether "price adjustment" is specified as percentage (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public bool PriceAdjustmentUsePercentage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weight adjustment (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public decimal WeightAdjustment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value cost (used only with AttributeValueType.Simple)
|
||||
/// </summary>
|
||||
public decimal Cost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer can enter the quantity of associated product (used only with AttributeValueType.AssociatedToProduct)
|
||||
/// </summary>
|
||||
public bool CustomerEntersQty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity of associated product (used only with AttributeValueType.AssociatedToProduct)
|
||||
/// </summary>
|
||||
public int Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the value is pre-selected
|
||||
/// </summary>
|
||||
public bool IsPreSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the picture (identifier) associated with this value. This picture should replace a product main picture once clicked (selected).
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value type
|
||||
/// </summary>
|
||||
public AttributeValueType AttributeValueType
|
||||
{
|
||||
get => (AttributeValueType)AttributeValueTypeId;
|
||||
set => AttributeValueTypeId = (int)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product attribute value picture
|
||||
/// </summary>
|
||||
public partial class ProductAttributeValuePicture : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute value id
|
||||
/// </summary>
|
||||
public int ProductAttributeValueId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the picture (identifier) associated with this value. This picture should replace a product main picture once clicked (selected).
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
}
|
||||
|
|
@ -1,27 +1,28 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product category mapping
|
||||
/// </summary>
|
||||
public partial class ProductCategory : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a product category mapping
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
public partial class ProductCategory : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the category identifier
|
||||
/// </summary>
|
||||
public int CategoryId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the category identifier
|
||||
/// </summary>
|
||||
public int CategoryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is featured
|
||||
/// </summary>
|
||||
public bool IsFeaturedProduct { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is featured
|
||||
/// </summary>
|
||||
public bool IsFeaturedProduct { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,304 +1,310 @@
|
|||
using Nop.Core.Configuration;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Product editor settings
|
||||
/// </summary>
|
||||
public partial class ProductEditorSettings : ISettings
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product type' field is shown
|
||||
/// Product editor settings
|
||||
/// </summary>
|
||||
public bool ProductType { get; set; }
|
||||
public class ProductEditorSettings : ISettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product type' field is shown
|
||||
/// </summary>
|
||||
public bool ProductType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Visible individually' field is shown
|
||||
/// </summary>
|
||||
public bool VisibleIndividually { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Visible individually' field is shown
|
||||
/// </summary>
|
||||
public bool VisibleIndividually { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product template' field is shown
|
||||
/// </summary>
|
||||
public bool ProductTemplate { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product template' field is shown
|
||||
/// </summary>
|
||||
public bool ProductTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Admin comment' field is shown
|
||||
/// </summary>
|
||||
public bool AdminComment { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Admin comment' field is shown
|
||||
/// </summary>
|
||||
public bool AdminComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Vendor' field is shown
|
||||
/// </summary>
|
||||
public bool Vendor { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Vendor' field is shown
|
||||
/// </summary>
|
||||
public bool Vendor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Stores' field is shown
|
||||
/// </summary>
|
||||
public bool Stores { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Stores' field is shown
|
||||
/// </summary>
|
||||
public bool Stores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'ACL' field is shown
|
||||
/// </summary>
|
||||
public bool ACL { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'ACL' field is shown
|
||||
/// </summary>
|
||||
public bool ACL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Show on home page' field is shown
|
||||
/// </summary>
|
||||
public bool ShowOnHomepage { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Show on home page' field is shown
|
||||
/// </summary>
|
||||
public bool ShowOnHomepage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allow customer reviews' field is shown
|
||||
/// </summary>
|
||||
public bool AllowCustomerReviews { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allow customer reviews' field is shown
|
||||
/// </summary>
|
||||
public bool AllowCustomerReviews { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product tags' field is shown
|
||||
/// </summary>
|
||||
public bool ProductTags { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product tags' field is shown
|
||||
/// </summary>
|
||||
public bool ProductTags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Manufacturer part number' field is shown
|
||||
/// </summary>
|
||||
public bool ManufacturerPartNumber { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Manufacturer part number' field is shown
|
||||
/// </summary>
|
||||
public bool ManufacturerPartNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'GTIN' field is shown
|
||||
/// </summary>
|
||||
public bool GTIN { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'GTIN' field is shown
|
||||
/// </summary>
|
||||
public bool GTIN { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product cost' field is shown
|
||||
/// </summary>
|
||||
public bool ProductCost { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product cost' field is shown
|
||||
/// </summary>
|
||||
public bool ProductCost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Tier prices' field is shown
|
||||
/// </summary>
|
||||
public bool TierPrices { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Tier prices' field is shown
|
||||
/// </summary>
|
||||
public bool TierPrices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Discounts' field is shown
|
||||
/// </summary>
|
||||
public bool Discounts { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Discounts' field is shown
|
||||
/// </summary>
|
||||
public bool Discounts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Disable buy button' field is shown
|
||||
/// </summary>
|
||||
public bool DisableBuyButton { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Disable buy button' field is shown
|
||||
/// </summary>
|
||||
public bool DisableBuyButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Disable wishlist button' field is shown
|
||||
/// </summary>
|
||||
public bool DisableWishlistButton { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Disable wishlist button' field is shown
|
||||
/// </summary>
|
||||
public bool DisableWishlistButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Available for pre-order' field is shown
|
||||
/// </summary>
|
||||
public bool AvailableForPreOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Available for pre-order' field is shown
|
||||
/// </summary>
|
||||
public bool AvailableForPreOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Call for price' field is shown
|
||||
/// </summary>
|
||||
public bool CallForPrice { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Call for price' field is shown
|
||||
/// </summary>
|
||||
public bool CallForPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Old price' field is shown
|
||||
/// </summary>
|
||||
public bool OldPrice { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Old price' field is shown
|
||||
/// </summary>
|
||||
public bool OldPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Customer enters price' field is shown
|
||||
/// </summary>
|
||||
public bool CustomerEntersPrice { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Customer enters price' field is shown
|
||||
/// </summary>
|
||||
public bool CustomerEntersPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'PAngV' field is shown
|
||||
/// </summary>
|
||||
public bool PAngV { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'PAngV' field is shown
|
||||
/// </summary>
|
||||
public bool PAngV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Require other products added to the cart' field is shown
|
||||
/// </summary>
|
||||
public bool RequireOtherProductsAddedToCart { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Require other products added to the cart' field is shown
|
||||
/// </summary>
|
||||
public bool RequireOtherProductsAddedToCart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Is gift card' field is shown
|
||||
/// </summary>
|
||||
public bool IsGiftCard { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Is gift card' field is shown
|
||||
/// </summary>
|
||||
public bool IsGiftCard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Downloadable product' field is shown
|
||||
/// </summary>
|
||||
public bool DownloadableProduct { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Downloadable product' field is shown
|
||||
/// </summary>
|
||||
public bool DownloadableProduct { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Recurring product' field is shown
|
||||
/// </summary>
|
||||
public bool RecurringProduct { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Recurring product' field is shown
|
||||
/// </summary>
|
||||
public bool RecurringProduct { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Is rental' field is shown
|
||||
/// </summary>
|
||||
public bool IsRental { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Is rental' field is shown
|
||||
/// </summary>
|
||||
public bool IsRental { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Free shipping' field is shown
|
||||
/// </summary>
|
||||
public bool FreeShipping { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Free shipping' field is shown
|
||||
/// </summary>
|
||||
public bool FreeShipping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Ship separately' field is shown
|
||||
/// </summary>
|
||||
public bool ShipSeparately { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Ship separately' field is shown
|
||||
/// </summary>
|
||||
public bool ShipSeparately { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Additional shipping charge' field is shown
|
||||
/// </summary>
|
||||
public bool AdditionalShippingCharge { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Additional shipping charge' field is shown
|
||||
/// </summary>
|
||||
public bool AdditionalShippingCharge { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Delivery date' field is shown
|
||||
/// </summary>
|
||||
public bool DeliveryDate { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Delivery date' field is shown
|
||||
/// </summary>
|
||||
public bool DeliveryDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product availability range' field is shown
|
||||
/// </summary>
|
||||
public bool ProductAvailabilityRange { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Telecommunications, broadcasting and electronic services' field is shown
|
||||
/// </summary>
|
||||
public bool TelecommunicationsBroadcastingElectronicServices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Use multiple warehouses' field is shown
|
||||
/// </summary>
|
||||
public bool UseMultipleWarehouses { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product availability range' field is shown
|
||||
/// </summary>
|
||||
public bool ProductAvailabilityRange { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Warehouse' field is shown
|
||||
/// </summary>
|
||||
public bool Warehouse { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Use multiple warehouses' field is shown
|
||||
/// </summary>
|
||||
public bool UseMultipleWarehouses { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Display stock availability' field is shown
|
||||
/// </summary>
|
||||
public bool DisplayStockAvailability { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Warehouse' field is shown
|
||||
/// </summary>
|
||||
public bool Warehouse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Minimum stock quantity' field is shown
|
||||
/// </summary>
|
||||
public bool MinimumStockQuantity { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Display stock availability' field is shown
|
||||
/// </summary>
|
||||
public bool DisplayStockAvailability { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Low stock activity' field is shown
|
||||
/// </summary>
|
||||
public bool LowStockActivity { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Minimum stock quantity' field is shown
|
||||
/// </summary>
|
||||
public bool MinimumStockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Notify admin for quantity below' field is shown
|
||||
/// </summary>
|
||||
public bool NotifyAdminForQuantityBelow { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Low stock activity' field is shown
|
||||
/// </summary>
|
||||
public bool LowStockActivity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Backorders' field is shown
|
||||
/// </summary>
|
||||
public bool Backorders { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Notify admin for quantity below' field is shown
|
||||
/// </summary>
|
||||
public bool NotifyAdminForQuantityBelow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allow back in stock subscriptions' field is shown
|
||||
/// </summary>
|
||||
public bool AllowBackInStockSubscriptions { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Backorders' field is shown
|
||||
/// </summary>
|
||||
public bool Backorders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Minimum cart quantity' field is shown
|
||||
/// </summary>
|
||||
public bool MinimumCartQuantity { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allow back in stock subscriptions' field is shown
|
||||
/// </summary>
|
||||
public bool AllowBackInStockSubscriptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Maximum cart quantity' field is shown
|
||||
/// </summary>
|
||||
public bool MaximumCartQuantity { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Minimum cart quantity' field is shown
|
||||
/// </summary>
|
||||
public bool MinimumCartQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allowed quantities' field is shown
|
||||
/// </summary>
|
||||
public bool AllowedQuantities { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Maximum cart quantity' field is shown
|
||||
/// </summary>
|
||||
public bool MaximumCartQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allow only existing attribute combinations' field is shown
|
||||
/// </summary>
|
||||
public bool AllowAddingOnlyExistingAttributeCombinations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Not returnable' field is shown
|
||||
/// </summary>
|
||||
public bool NotReturnable { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allowed quantities' field is shown
|
||||
/// </summary>
|
||||
public bool AllowedQuantities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Weight' field is shown
|
||||
/// </summary>
|
||||
public bool Weight { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Allow only existing attribute combinations' field is shown
|
||||
/// </summary>
|
||||
public bool AllowAddingOnlyExistingAttributeCombinations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Dimension' fields (height, length, width) are shown
|
||||
/// </summary>
|
||||
public bool Dimensions { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Not returnable' field is shown
|
||||
/// </summary>
|
||||
public bool NotReturnable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Available start date' field is shown
|
||||
/// </summary>
|
||||
public bool AvailableStartDate { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Weight' field is shown
|
||||
/// </summary>
|
||||
public bool Weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Available end date' field is shown
|
||||
/// </summary>
|
||||
public bool AvailableEndDate { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Dimension' fields (height, length, width) are shown
|
||||
/// </summary>
|
||||
public bool Dimensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Mark as new' field is shown
|
||||
/// </summary>
|
||||
public bool MarkAsNew { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Available start date' field is shown
|
||||
/// </summary>
|
||||
public bool AvailableStartDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Published' field is shown
|
||||
/// </summary>
|
||||
public bool Published { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Available end date' field is shown
|
||||
/// </summary>
|
||||
public bool AvailableEndDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Related products' block is shown
|
||||
/// </summary>
|
||||
public bool RelatedProducts { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Mark as new' field is shown
|
||||
/// </summary>
|
||||
public bool MarkAsNew { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Cross-sells products' block is shown
|
||||
/// </summary>
|
||||
public bool CrossSellsProducts { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Published' field is shown
|
||||
/// </summary>
|
||||
public bool Published { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Related products' block is shown
|
||||
/// </summary>
|
||||
public bool RelatedProducts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'SEO' tab is shown
|
||||
/// </summary>
|
||||
public bool Seo { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Cross-sells products' block is shown
|
||||
/// </summary>
|
||||
public bool CrossSellsProducts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Purchased with orders' tab is shown
|
||||
/// </summary>
|
||||
public bool PurchasedWithOrders { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'SEO' tab is shown
|
||||
/// </summary>
|
||||
public bool Seo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product attributes' tab is shown
|
||||
/// </summary>
|
||||
public bool ProductAttributes { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Purchased with orders' tab is shown
|
||||
/// </summary>
|
||||
public bool PurchasedWithOrders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Product attributes' tab is shown
|
||||
/// </summary>
|
||||
public bool ProductAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Specification attributes' tab is shown
|
||||
/// </summary>
|
||||
public bool SpecificationAttributes { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Specification attributes' tab is shown
|
||||
/// </summary>
|
||||
public bool SpecificationAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Manufacturers' field is shown
|
||||
/// </summary>
|
||||
public bool Manufacturers { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Manufacturers' field is shown
|
||||
/// </summary>
|
||||
public bool Manufacturers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Stock quantity history' tab is shown
|
||||
/// </summary>
|
||||
public bool StockQuantityHistory { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 'Stock quantity history' tab is shown
|
||||
/// </summary>
|
||||
public bool StockQuantityHistory { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,28 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product manufacturer mapping
|
||||
/// </summary>
|
||||
public partial class ProductManufacturer : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a product manufacturer mapping
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
public partial class ProductManufacturer : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the manufacturer identifier
|
||||
/// </summary>
|
||||
public int ManufacturerId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the manufacturer identifier
|
||||
/// </summary>
|
||||
public int ManufacturerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is featured
|
||||
/// </summary>
|
||||
public bool IsFeaturedProduct { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is featured
|
||||
/// </summary>
|
||||
public bool IsFeaturedProduct { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product picture mapping
|
||||
/// </summary>
|
||||
public partial class ProductPicture : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a product picture mapping
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
public partial class ProductPicture : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the picture identifier
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the picture identifier
|
||||
/// </summary>
|
||||
public int PictureId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product-product tag mapping class
|
||||
/// </summary>
|
||||
public partial class ProductProductTagMapping : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a product-product tag mapping class
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
public partial class ProductProductTagMapping : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product tag identifier
|
||||
/// </summary>
|
||||
public int ProductTagId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the product tag identifier
|
||||
/// </summary>
|
||||
public int ProductTagId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +1,70 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product review
|
||||
/// </summary>
|
||||
public partial class ProductReview : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// Represents a product review
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
public partial class ProductReview : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the content is approved
|
||||
/// </summary>
|
||||
public bool IsApproved { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the content is approved
|
||||
/// </summary>
|
||||
public bool IsApproved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the title
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the review text
|
||||
/// </summary>
|
||||
public string ReviewText { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the review text
|
||||
/// </summary>
|
||||
public string ReviewText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reply text
|
||||
/// </summary>
|
||||
public string ReplyText { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the reply text
|
||||
/// </summary>
|
||||
public string ReplyText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value indicating whether the customer is already notified of the reply to review
|
||||
/// </summary>
|
||||
public bool CustomerNotifiedOfReply { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the value indicating whether the customer is already notified of the reply to review
|
||||
/// </summary>
|
||||
public bool CustomerNotifiedOfReply { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Review rating
|
||||
/// </summary>
|
||||
public int Rating { get; set; }
|
||||
/// <summary>
|
||||
/// Review rating
|
||||
/// </summary>
|
||||
public int Rating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Review helpful votes total
|
||||
/// </summary>
|
||||
public int HelpfulYesTotal { get; set; }
|
||||
/// <summary>
|
||||
/// Review helpful votes total
|
||||
/// </summary>
|
||||
public int HelpfulYesTotal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Review not helpful votes total
|
||||
/// </summary>
|
||||
public int HelpfulNoTotal { get; set; }
|
||||
/// <summary>
|
||||
/// Review not helpful votes total
|
||||
/// </summary>
|
||||
public int HelpfulNoTotal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product review helpfulness
|
||||
/// </summary>
|
||||
public partial class ProductReviewHelpfulness : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product review identifier
|
||||
/// Represents a product review helpfulness
|
||||
/// </summary>
|
||||
public int ProductReviewId { get; set; }
|
||||
public partial class ProductReviewHelpfulness : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product review identifier
|
||||
/// </summary>
|
||||
public int ProductReviewId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether a review a helpful
|
||||
/// </summary>
|
||||
public bool WasHelpful { get; set; }
|
||||
/// <summary>
|
||||
/// A value indicating whether a review a helpful
|
||||
/// </summary>
|
||||
public bool WasHelpful { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,25 @@
|
|||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product review and review type mapping
|
||||
/// </summary>
|
||||
public partial class ProductReviewReviewTypeMapping : BaseEntity, ILocalizedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product review identifier
|
||||
/// Represents a product review and review type mapping
|
||||
/// </summary>
|
||||
public int ProductReviewId { get; set; }
|
||||
public partial class ProductReviewReviewTypeMapping : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product review identifier
|
||||
/// </summary>
|
||||
public int ProductReviewId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the review type identifier
|
||||
/// </summary>
|
||||
public int ReviewTypeId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the review type identifier
|
||||
/// </summary>
|
||||
public int ReviewTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rating
|
||||
/// </summary>
|
||||
public int Rating { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the rating
|
||||
/// </summary>
|
||||
public int Rating { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,38 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the product sorting
|
||||
/// </summary>
|
||||
public enum ProductSortingEnum
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Position (display order)
|
||||
/// Represents the product sorting
|
||||
/// </summary>
|
||||
Position = 0,
|
||||
public enum ProductSortingEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// Position (display order)
|
||||
/// </summary>
|
||||
Position = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Name: A to Z
|
||||
/// </summary>
|
||||
NameAsc = 5,
|
||||
/// <summary>
|
||||
/// Name: A to Z
|
||||
/// </summary>
|
||||
NameAsc = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Name: Z to A
|
||||
/// </summary>
|
||||
NameDesc = 6,
|
||||
/// <summary>
|
||||
/// Name: Z to A
|
||||
/// </summary>
|
||||
NameDesc = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Price: Low to High
|
||||
/// </summary>
|
||||
PriceAsc = 10,
|
||||
/// <summary>
|
||||
/// Price: Low to High
|
||||
/// </summary>
|
||||
PriceAsc = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Price: High to Low
|
||||
/// </summary>
|
||||
PriceDesc = 11,
|
||||
/// <summary>
|
||||
/// Price: High to Low
|
||||
/// </summary>
|
||||
PriceDesc = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Product creation date
|
||||
/// </summary>
|
||||
CreatedOn = 15,
|
||||
/// <summary>
|
||||
/// Product creation date
|
||||
/// </summary>
|
||||
CreatedOn = 15,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +1,54 @@
|
|||
using Nop.Core.Domain.Localization;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product specification attribute
|
||||
/// </summary>
|
||||
public partial class ProductSpecificationAttribute : BaseEntity, ILocalizedEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a product specification attribute
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute type ID
|
||||
/// </summary>
|
||||
public int AttributeTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the specification attribute identifier
|
||||
/// </summary>
|
||||
public int SpecificationAttributeOptionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom value
|
||||
/// </summary>
|
||||
public string CustomValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the attribute can be filtered by
|
||||
/// </summary>
|
||||
public bool AllowFiltering { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the attribute will be shown on the product page
|
||||
/// </summary>
|
||||
public bool ShowOnProductPage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute control type
|
||||
/// </summary>
|
||||
public SpecificationAttributeType AttributeType
|
||||
public partial class ProductSpecificationAttribute : BaseEntity, ILocalizedEntity
|
||||
{
|
||||
get => (SpecificationAttributeType)AttributeTypeId;
|
||||
set => AttributeTypeId = (int)value;
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute type ID
|
||||
/// </summary>
|
||||
public int AttributeTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the specification attribute identifier
|
||||
/// </summary>
|
||||
public int SpecificationAttributeOptionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom value
|
||||
/// </summary>
|
||||
public string CustomValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the attribute can be filtered by
|
||||
/// </summary>
|
||||
public bool AllowFiltering { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the attribute will be shown on the product page
|
||||
/// </summary>
|
||||
public bool ShowOnProductPage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute control type
|
||||
/// </summary>
|
||||
public SpecificationAttributeType AttributeType
|
||||
{
|
||||
get => (SpecificationAttributeType)AttributeTypeId;
|
||||
set => AttributeTypeId = (int)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Seo;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product tag
|
||||
/// </summary>
|
||||
public partial class ProductTag : BaseEntity, ILocalizedEntity, ISlugSupported
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// Represents a product tag
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class ProductTag : BaseEntity, ILocalizedEntity, ISlugSupported
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,28 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product template
|
||||
/// </summary>
|
||||
public partial class ProductTemplate : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the template name
|
||||
/// Represents a product template
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public partial class ProductTemplate : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the template name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the view path
|
||||
/// </summary>
|
||||
public string ViewPath { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the view path
|
||||
/// </summary>
|
||||
public string ViewPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a comma-separated list of product type identifiers NOT supported by this template
|
||||
/// </summary>
|
||||
public string IgnoredProductTypes { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets a comma-separated list of product type identifiers NOT supported by this template
|
||||
/// </summary>
|
||||
public string IgnoredProductTypes { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product type
|
||||
/// </summary>
|
||||
public enum ProductType
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple
|
||||
/// Represents a product type
|
||||
/// </summary>
|
||||
SimpleProduct = 5,
|
||||
public enum ProductType
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple
|
||||
/// </summary>
|
||||
SimpleProduct = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Grouped (product with variants)
|
||||
/// </summary>
|
||||
GroupedProduct = 10,
|
||||
}
|
||||
/// <summary>
|
||||
/// Grouped (product with variants)
|
||||
/// </summary>
|
||||
GroupedProduct = 10,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the product URL structure type enum
|
||||
/// </summary>
|
||||
public enum ProductUrlStructureType
|
||||
{
|
||||
/// <summary>
|
||||
/// Product only (e.g. '/product-seo-name')
|
||||
/// </summary>
|
||||
Product = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Category (the most nested), then product (e.g. '/category-seo-name/product-seo-name')
|
||||
/// </summary>
|
||||
CategoryProduct = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Manufacturer, then product (e.g. '/manufacturer-seo-name/product-seo-name')
|
||||
/// </summary>
|
||||
ManufacturerProduct = 20
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product video mapping
|
||||
/// </summary>
|
||||
public partial class ProductVideo : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the video identifier
|
||||
/// </summary>
|
||||
public int VideoId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
|
|
@ -1,27 +1,28 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a record to manage product inventory per warehouse
|
||||
/// </summary>
|
||||
public partial class ProductWarehouseInventory : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// Represents a record to manage product inventory per warehouse
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
public partial class ProductWarehouseInventory : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the warehouse identifier
|
||||
/// </summary>
|
||||
public int WarehouseId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the warehouse identifier
|
||||
/// </summary>
|
||||
public int WarehouseId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stock quantity
|
||||
/// </summary>
|
||||
public int StockQuantity { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the stock quantity
|
||||
/// </summary>
|
||||
public int StockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reserved quantity (ordered but not shipped yet)
|
||||
/// </summary>
|
||||
public int ReservedQuantity { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the reserved quantity (ordered but not shipped yet)
|
||||
/// </summary>
|
||||
public int ReservedQuantity { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a recurring product cycle period
|
||||
/// </summary>
|
||||
public enum RecurringProductCyclePeriod
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Days
|
||||
/// Represents a recurring product cycle period
|
||||
/// </summary>
|
||||
Days = 0,
|
||||
public enum RecurringProductCyclePeriod
|
||||
{
|
||||
/// <summary>
|
||||
/// Days
|
||||
/// </summary>
|
||||
Days = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Weeks
|
||||
/// </summary>
|
||||
Weeks = 10,
|
||||
/// <summary>
|
||||
/// Weeks
|
||||
/// </summary>
|
||||
Weeks = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Months
|
||||
/// </summary>
|
||||
Months = 20,
|
||||
/// <summary>
|
||||
/// Months
|
||||
/// </summary>
|
||||
Months = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Years
|
||||
/// </summary>
|
||||
Years = 30,
|
||||
}
|
||||
/// <summary>
|
||||
/// Years
|
||||
/// </summary>
|
||||
Years = 30,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a related product
|
||||
/// </summary>
|
||||
public partial class RelatedProduct : BaseEntity
|
||||
namespace Nop.Core.Domain.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the first product identifier
|
||||
/// Represents a related product
|
||||
/// </summary>
|
||||
public int ProductId1 { get; set; }
|
||||
public partial class RelatedProduct : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the first product identifier
|
||||
/// </summary>
|
||||
public int ProductId1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the second product identifier
|
||||
/// </summary>
|
||||
public int ProductId2 { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the second product identifier
|
||||
/// </summary>
|
||||
public int ProductId2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the display order
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue