diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml
deleted file mode 100644
index d7716a9d7c..0000000000
--- a/.github/workflows/spellcheck.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Documentation Checks
-
-on:
- push:
- branches:
- - dev
- paths:
- # This ensures the check will only be run when something changes in the docs content
- - "docs/en/**/*"
- pull_request:
- branches:
- - dev
- paths:
- - "docs/en/**/*"
-permissions:
- contents: read # to fetch code (actions/checkout)
-jobs:
- spellcheck:
- name: "Docs: Spellcheck (En)"
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- name: Check out the code
- - uses: actions/setup-node@v1
- name: Setup node
- with:
- node-version: "16"
- - run: npm install -g cspell
- name: Install cSpell
- - run: cspell --config ./cSpell.json "docs/en/**/*.md" --no-progress # Update for path to the markdown files
- name: Run cSpell
diff --git a/.gitignore b/.gitignore
index ccb98f81a0..a386e86320 100644
--- a/.gitignore
+++ b/.gitignore
@@ -326,4 +326,6 @@ deploy/_run_all_log.txt
# No commit yarn.lock files in the subfolders of templates directory
-templates/**/yarn.lock
\ No newline at end of file
+templates/**/yarn.lock
+templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Logs/logs.txt
+templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/Properties/launchSettings.json
diff --git a/Directory.Build.props b/Directory.Build.props
index c3495c54d5..43a884f556 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -17,7 +17,7 @@
4.3.0
- 4.0.3
+ 4.1.0
2.4.1
diff --git a/README.md b/README.md
index 332cf2adc5..c3c7679aa3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ABP Framework
-
+
[](https://codecov.io/gh/abpframework/abp)
[](https://www.nuget.org/packages/Volo.Abp.Core)
[](https://www.nuget.org/packages/Volo.Abp.Core)
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ar.json
index b71f392b54..577d69806f 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ar.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ar.json
@@ -5,11 +5,11 @@
"Welcome": "أهلا و سهلا",
"UseOneOfTheFollowingLinksToContinue": "استخدم أحد الروابط التالية للمتابعة",
"FrameworkHomePage": "الصفحة الرئيسية للنظام",
- "FrameworkDocumentation": "وثائق النظام",
+ "FrameworkDocumentation": "مستندات النظام",
"OfficialBlog": "المدونة الرسمية",
"CommercialHomePage": "الصفحة الرئيسية للنظام التجاري",
"CommercialSupportWebSite": "موقع الدعم الفني للنظام التجاري",
- "CommunityWebSite": "موقع مجتمع ABP",
+ "CommunityWebSite": "موقع منتدى ABP",
"ManageAccount": "حسابي | ABP.IO",
"ManageYourAccount": "إدارة حسابك"
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ar.json
index d9d3597d14..6e9253891e 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ar.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ar.json
@@ -169,7 +169,7 @@
"QuestionCountsMustBeGreaterThanZero": "يجب أن يكون TotalQuestionCount و RemainingQuestionCount صفرًا أو أكبر من الصفر!",
"UnlimitedQuestionCount": "عدد أسئلة غير محدود",
"Notes": "ملاحظات",
- "Menu:Community": "المجتمع",
+ "Menu:Community": "المنتدى",
"Menu:Posts": "مقالات",
"Wait": "انتظر",
"Approve": "موافقة",
@@ -183,7 +183,7 @@
"PostHasBeenWaiting": "تم إيقاف المقال",
"PostHasBeenApproved": "تم الموافقة على المقال",
"PostHasBeenRejected": "تم رفض المقال",
- "Permission:Community": "المجتمع",
+ "Permission:Community": "المنتدى",
"Permission:CommunityPost": "مقال",
"Link": "رابط",
"Enum:ContentSource:0": "Github",
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
index 541d1eaa62..1dbca493c5 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
@@ -407,6 +407,27 @@
"ExportToExcel": "Export to Excel",
"OverallTotalPrice": "Overall Total Price",
"OverallDiscountPrice": "Overall Discount Price",
- "OverallDiscountText": "Overall Discount Text"
+ "OverallDiscountText": "Overall Discount Text",
+ "SelectReport": "- Select Report -",
+ "NoDataAvailable": "No data available",
+ "StatisticsOfCachedContents": "Statistics of cached NuGet package contents for nuget.abp.io",
+ "Compact": "Compact",
+ "EditSettings": "Edit Settings",
+ "CurrentEstimatedSize": "Current Estimated Size",
+ "CurrentEntryCount": "Current Entry Count",
+ "TotalHits": "Total Hits",
+ "TotalMisses": "Total Misses",
+ "NoResponseFrom": "No response from",
+ "ContentCacheSlidingExpirationByDay": "Content Cache Sliding Expiration By Day",
+ "MaxDaysForCaching": "Max Days For Caching",
+ "Enabled": "Enabled",
+ "Menu:NugetPackagesContentCache": "NuGet Packages Content Cache",
+ "NugetPackagesContentCache": "NuGet Content Cache",
+ "SlidingExpritionByDayInfo": "Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration.",
+ "MaxDaysForCachingInfo": "Gets or sets an absolute expiration time, relative to now.",
+ "CurrentEstimatedSizeInfo": "Indicates an estimated sum of all the NuGet packages' content size currently in the memory cache",
+ "CurrentEntryCountInfo": "Indicates the number of instances currently in the memory cache.",
+ "TotalHitsInfo": "Indicates the total number of cache misses. A cache hit occurs when a file is requested from a cache and the cache is able to fulfill that request.",
+ "TotalMissesInfo": "Indicates the total number of cache hits. A cache miss is when the cache does not contain the requested content."
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json
index eae9afa02b..82cc7724bf 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json
@@ -9,36 +9,179 @@
"Volo.AbpIo.Domain:010009": "لا يمكن أن يكون عدد المطورين المسموح لهم أقل من 0!",
"Volo.AbpIo.Domain:010010": "تم تجاوز الحد الأقصى لعدد عناوين mac!",
"Volo.AbpIo.Domain:010011": "لا يمكن أن تحتوي الرخصة الشخصية على أكثر من مطور واحد!",
- "Volo.AbpIo.Domain:010012": "لا يمكن تمديد الترخيص بعد شهر واحد من انتهاء صلاحية الترخيص!",
+ "Volo.AbpIo.Domain:010012": "لا يمكن تمديد الرخصة بعد شهر واحد من انتهاء صلاحية الرخصة!",
"Volo.AbpIo.Domain:020001": "تعذر حذف حزمة NPM هذه لأن \"{NugetPackages}\" حزم Nuget تعتمد على هذه الحزمة.",
"Volo.AbpIo.Domain:020002": "تعذر حذف حزمة NPM هذه لأن \"{Modules}\" الوحدات النمطية تستخدم هذه الحزمة.",
"Volo.AbpIo.Domain:020003": "تعذر حذف حزمة NPM هذه لأن \"{Modules}\" الوحدات النمطية تستخدم هذه الحزمة و \"{NugetPackages}\" حزم Nuget تعتمد على هذه الحزمة.",
"Volo.AbpIo.Domain:020004": "تعذر حذف حزمة Nuget هذه لأن \"{Modules}\" الوحدات النمطية تستخدم هذه الحزمة.",
+ "Volo.AbpIo.Domain:030001": "اسم هذه المنظمة موجود بالفعل.",
+ "Volo.AbpIo.Domain:030002": "بمجرد التفعيل، لا يمكنك تبديل الرخصة التجريبية إلى الحالة المطلوبة!",
+ "Volo.AbpIo.Domain:030003": "لا يوجد مثل هذه الحالة!",
+ "Volo.AbpIo.Domain:030004": "لا يمكن تغيير الحالة بسبب خطأ غير متوقع!",
+ "Volo.AbpIo.Domain:030005": "يمكن تحديث تاريخ البدء والانتهاء عندما تكون الرخصة التجريبية في حالة -منشطة-!",
+ "Volo.AbpIo.Domain:030006": "يجب أن يكون تاريخ الانتهاء أكبر من تاريخ البدء!",
+ "Volo.AbpIo.Domain:030007": "تم تفعيل هذه الرخصة التجريبية بالفعل!",
+ "Volo.AbpIo.Domain:030008": "يمكن تحديد تاريخ الشراء فقط عندما تكون الحالة -تم الشراء-!",
+ "Volo.AbpIo.Domain:030009": "المستخدم ليس موجود!",
+ "Volo.AbpIo.Domain:030010": "لشراء الرخصة التجريبية ، تحتاج أولاً إلى تفعيل الرخصة التجريبية الخاص بك!",
+ "Volo.AbpIo.Domain:030011": "لا يمكنك حذف رخصة تجريبية عند شرائها!",
+ "Volo.AbpIo.Domain:070000": "لا يمكن أن يحتوي اسم المؤسسة إلا على أحرف لاتينية وأرقام ونقاط وواصلات!",
+ "Volo.AbpIo.Domain:070001": "لا يمكن أن يحتوي اسم الشركة إلا على أحرف لاتينية وأرقام ونقاط وواصلات!",
"WantToLearn?": "تريد أن تتعلم؟",
"ReadyToGetStarted?": "على استعداد للبدء؟",
- "JoinOurCommunity": "انضم إلى مجتمعنا",
+ "JoinOurCommunity": "انضم إلى منتدانا",
"GetStartedUpper": "دعنا نبدأ",
"ForkMeOnGitHub": "GitHub أعطنى تنبيه على",
"Features": "خصائص",
"GetStarted": "دعنا نبدأ",
- "Documents": "وثائق",
- "Community": "مجتمع",
+ "Documents": "المستندات",
+ "Community": "منتدى",
"ContributionGuide": "دليل المساهمة",
"Blog": "المدونة",
"Commercial": "تجارى",
"MyAccount": "حسابي",
"Permission:License": "رخصة",
"Permission:UserInfo": "معلومات مفيدة",
- "SeeDocuments": "انظر للوثائق",
+ "SeeDocuments": "انظر للمستندات",
"Samples": "عينات",
"Framework": "إطار العمل",
"Support": "الدعم",
- "FreeDDDBook": "كتاب إلكتروني مجاني DDD",
+ "FreeDDDBook": "كتاب تصميم مقاد بالنطاق DDD إلكتروني مجاني",
"New": "جديد",
- "Volo.AbpIo.Domain:020005": "لا يمكن أن تكون سنة تمديد الترخيص أقل من {MinExtendLicenseYear} سنة وأكبر من {MaxExtendLicenseYear} عام (سنوات)",
- "TrialLicensePeriodHasExpired": "انتهت فترة الترخيص التجريبي الخاص بك قبل {0} يوم.",
- "TrialLicensePeriodWillExpire": "ستنتهي فترة الترخيص التجريبي الخاص بك في غضون {0} يوم.",
- "TrialLicensePeriodExpireToday": "ستنتهي فترة الترخيص التجريبي الخاص بك اليوم.",
- "PurchaseNow": "شراء الآن!"
+ "Volo.AbpIo.Domain:020005": "لا يمكن أن تكون سنة تمديد الرخصة أقل من {MinExtendLicenseYear} سنة وأكبر من {MaxExtendLicenseYear} عام (سنوات)",
+ "TrialLicensePeriodHasExpired": "انتهت فترة الرخصة التجريبية الخاصة بك قبل {0} يوم.",
+ "TrialLicensePeriodWillExpire": "ستنتهي فترة الرخصة التجريبية الخاصة بك في غضون {0} يوم.",
+ "TrialLicensePeriodExpireToday": "ستنتهي فترة الرخصة التجريبية الخاصة بك اليوم.",
+ "PurchaseNow": "شراء الآن!",
+ "LatestReleaseLogs": "أحدث سجلات الإصدار",
+ "RoadMap": "خريطة الطريق",
+ "FAQ": "الاسئلة الشائعة",
+ "SourceCode": "الكود الأصل",
+ "SeeAllPosts": "انظر جميع المنشورات",
+ "Contribute": "مساهمة",
+ "LiveDemo": "عرض مباشر",
+ "GetLicense": "احصل على رخصة",
+ "OpenSource": "مصدر مفتوح",
+ "WebApplication": "تطبيق ويب",
+ "MeetTheABP": "تعرف على ABP",
+ "CompleteWebDevelopment": "تطوير ويب كامل",
+ "Platform": "منصة",
+ "ABPDescription": "اطار عمل ABP هو بنية تحتية كاملة لإنشاء تطبيقات ويب حديثة باتباع أفضل ممارسات واتفاقيات تطوير البرامج.",
+ "StrongInfrastructure": "بنية تحتية قوية",
+ "CompleteArchitecture": "معمارية كاملة",
+ "DeveloperFocused": "مركزة على المطور",
+ "ShareYourExperiences": "شارك خبراتك مع إطار عمل ABP",
+ "LatestPosts": "آخر المنشورات",
+ "LatestVideos": "أحدث مقاطع الفيديو",
+ "Views": "المشاهدات",
+ "LearnLatestNewsAboutABPFramework": "احصل على معلومات حول الأحداث في ABP مثل الإصدارات الجديدة والمصادر المجانية والمشاركات والمزيد.",
+ "DeveloperTools": "ادوات المطور",
+ "StartupTemplates": "عارضات بدء التشغيل",
+ "ApplicationModules": "وحدات التطبيق",
+ "UI": "واجهة المستخدم",
+ "Themes": "السمات",
+ "Premium": "ممتاز",
+ "PrivacyPolicy": "سياسة الخصوصية",
+ "TermsAndConditions": "البنود و الظروف",
+ "WouldLikeToReceiveMarketingMaterials": "أرغب في تلقي مواد تسويقية مثل صفقات المنتجات والعروض الخاصة.",
+ "JoinOurMarketingNewsletter": "انضم إلى النشرة الإخبارية التسويقية",
+ "CommunityPrivacyPolicyConfirmation": "أوافق على الشروط والأحكام و سياسة الخصوصية.",
+ "WouldLikeToReceiveNotification": "أرغب في تلقي آخر الأخبار من مواقع abp.io.",
+ "CommercialNewsletterConfirmationMessage": "أنا أوافق على البنود و الظروف و سياسة الخصوصية.",
+ "FreeDDDEBook": "كتاب تصميم مقاد بالنطاق DDD إلكتروني مجاني",
+ "AdditionalServices": "خدمات إضافية",
+ "Learn": "تعلم",
+ "AccountOverview": "نظرة عامة على الحساب",
+ "MyOrganizations": "منظماتي",
+ "MySupportQuestions": "أسئلة الدعم الخاصة بي",
+ "MyProfile": "صفحتي الشخصية",
+ "Logout": "تسجيل الخروج",
+ "Home": "الصفحة الرئيسية",
+ "Posts": "المنشورات",
+ "Videos": "مقاطع الفيديو",
+ "JoinTheABPCommunity": "انضم إلى منتدى ABP",
+ "SubmitYourPost": "سلم منشورك",
+ "Modules": "الوحدات",
+ "Tools": "الادوات",
+ "Pricing": "التسعير",
+ "ChangeLogs": "تغيير السجلات",
+ "SubscribeToNewsletter": "اشترك في النشرة الإخبارية",
+ "SubscribeToNewsletterDescription": "احصل على معلومات حول الأحداث في ABP مثل الإصدارات الجديدة والمصادر المجانية والمشاركات والمزيد.",
+ "EmailAddress": "البريد الالكترونى",
+ "Subscribe": "اشتراك",
+ "WelcomeToABP": "مرحبًا بك في ABP",
+ "EULA": "اتفاقية مستخدم الرخصة EULA",
+ "ABPCommercialIntroductionMessage": "وحدات تطبيق مبنية مسبقا ، وعارضات بدء تشغيل متقدمة ، وأدوات التطوير السريع للتطبيقات ، وسمات واجهة مستخدم ااحترافية ، ودعم متميز.",
+ "MasteringAbpFrameworkEBook": "إتقان إطار عمل ABP",
+ "MasteringTheABPFrameworkExplanation": "سيساعدك هذا الكتاب ، الذي كتبه مبتكر إطار عمل ABP ، على اكتساب فهم كامل للإطار وتقنيات تطوير تطبيقات الويب الحديثة.",
+ "Speakers": "المتحدثين",
+ "PreviousEvents": "الأحداث السابقة",
+ "WatchTheEvent": "شاهد الحدث",
+ "RegisterNow": "سجل الان",
+ "ThereIsNoEvent": "لا يوجد حدث.",
+ "Events": "الأحداث",
+ "Volo.AbpIo.Domain:080000": "يوجد بالفعل عنصر شراء اسمه \"{Name}\"",
+ "MasteringAbpFrameworkBook": "الكتاب: إتقان إطار عمل ABP",
+ "ABPIO-CommonPreferenceDefinition": "احصل على آخر الأخبار حول منصة ABP مثل المنشورات الجديدة والأحداث والمزيد.",
+ "BuiltOn": "مبني على",
+ "AbpFramework": "إطار عمل ABP",
+ "Volo.AbpIo.Domain:080001": "لا يمكن أن يكون وقت البدء أكبر من وقت الانتهاء",
+ "Enum:BookType:0": "إتقان إطار عمل ABP",
+ "Enum:PurchasePlatform:0": "أمازون",
+ "Enum:PurchasePlatform:1": "باكت",
+ "Copied": "تم النسخ!",
+ "CouldNotCopy": "تعذر النسخ!",
+ "CopyNotSupportByYourBrowser": "هذه الميزة لا تعمل في المتصفح الذي تستخدمه.",
+ "City": "المدينة",
+ "ZipCode": "الرمز البريدي",
+ "Address": "العنوان",
+ "Homepage": "الصفحة الرئيسية",
+ "Year": "السنة",
+ "Copyright": "حقوق النشر © {1}",
+ "DomainDrivenDesign": "التصميم المُقاد بالنطاق DDD",
+ "CrossCuttingConcerns": "اهتمامات مشتركة",
+ "AbpCommunity": "منتدى ABP",
+ "Footer_GithubStarCount": "{0} نجمة على GitHub",
+ "Footer_NugetDownloadCount": "{0} تنزيلات على NuGet",
+ "AbpDescription": "ABP هو إطار تطبيق مفتوح المصدر يركز على تطوير تطبيقات الويب القائمة على AspNet Core. لا تكرر نفسك، ركز على كود عملك الخاص.",
+ "Layout_AbpFramework_MetaTitle": "إطار عمل ABP - إطار عمل تطبيق ويب مفتوح المصدر",
+ "CommunityTalks_CountdownDays": "أيام",
+ "CommunityTalks_CountdownHours": "ساعات",
+ "CommunityTalks_CountdownMinutes": "دقيقة",
+ "CommunityTalks_CountdownSeconds": "ثانية",
+ "SeePreviousEvents": "انظر الأحداث السابقة",
+ "CookieConsent_Accept": "قبول",
+ "CookieConsent_Explanation_1": "نحن نستخدم ملفات تعريف الارتباط لنمنحك أفضل تجربة على موقعنا.",
+ "CookieConsent_Explanation_2": "إذا واصلت التصفح ، فإنك توافق على سياسة الخصوصية وسياسة ملفات تعريف الارتباط..",
+ "Error_Page_400_Title": "كانت هناك مشكلة في خدمة الصفحة المطلوبة.",
+ "Error_Page_400_Description_1": "عادةً ما يعني هذا حدوث خطأ غير متوقع أثناء معالجة طلبك.",
+ "Error_Page_400_Description_2": "إذا استمرت المشكلة، اتصل بنا على info@abp.io وسنساعدك في الوصول إلى طريقك.",
+ "GoToHomepage": "اذهب إلى الصفحة الرئيسية",
+ "Error_Page_404_Title": "الصفحة غير موجودة!",
+ "Error_Page_404_Description_1": "هذه ليست الصفحة التي تبحث عنها.",
+ "Error_Page_500_Title": "يبدو أنه حدث خطأ ما!",
+ "Error_Page_500_Description_1": "نحن نتتبع هذه الأخطاء تلقائيًا ، ولكن إذا استمرت المشكلة فلا تتردد في
الاتصال بنا. في غضون ذلك ، حاول تحديث الصفحة.",
+ "Error_Page_500_Description_2": "تواصل معنا على info@abp.io.",
+ "Books": "الكتب",
+ "ABPDiscordServer": "ABP سيرفر الدسكورد",
+ "ABPCommunityTalks": "برامج منتدى ABP الحوارية",
+ "ABPCommunityPosts": "منشورات منتدى ABP",
+ "BuyAndGetMonths": "شراء 12 شهر، احصل على 14 شهرا!",
+ "GetYourDeal": "احصل على صفقتك",
+ "BuyOrRenewLicense": "اشترِ أو جدد الرخصة الآن واحصل على شهرين إضافيين!",
+ "BuyOrRenewLicenseToGetExtra2Months": "اشترِ أو جدد الرخصة الآن واحصل على شهرين إضافيين! اسرع! ⏰ آخر يوم: {0}",
+ "HurryUp": "اسرع!",
+ "LastDay": "آخر يوم: {0}",
+ "BuyNewLicenseBetweenDatesToGetBenefit": "اشتر رخصة جديدة بين {0} و {1} للاستفادة لمدة شهرين إضافيين!",
+ "CheckAllCommunityTalks": "تحقق من جميع منشورات المنتدى",
+ "ReadMore": "قراءة المزيد",
+ "Post": "نشر",
+ "ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "استكشف المحتويات التي أنشأها فريق ABP الأساسي ومنتدى ABP.",
+ "WelcomeFallCampaign": "مرحبا بكم في حملة الخريف!",
+ "GiveAwayForNewPurchases": "سيتم منح التدريب لتطوير التطبيقات مجانا مع عمليات الشراء الجديدة!",
+ "BlackFriday": "السوداء الجمعة",
+ "ValidForExistingCustomers": "صالح أيضًا
للعملاء الحاليين!",
+ "CampaignBetweenDates": "من {0}
إلى {1}",
+ "SaveUpTo": "وفر ماقد يصل الى ${0}K"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
index f73d317fad..ca8ed7326d 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
@@ -26,6 +26,7 @@
"Volo.AbpIo.Domain:030009": "User not found!",
"Volo.AbpIo.Domain:030010": "To purchase the trial license, you first need to activate your trial license!",
"Volo.AbpIo.Domain:030011": "You cannot delete a trial license when it is purchased!",
+ "Volo.AbpIo.Domain:030012": "A user is entitled to have only 1 free trial period. You already used your trial license.",
"Volo.AbpIo.Domain:070000": "The organization name can only contain latin letters, numbers, dots and hyphens!",
"Volo.AbpIo.Domain:070001": "The company name can only contain latin letters, numbers, dots, space and hyphens!",
"WantToLearn?": "Want to learn?",
@@ -183,6 +184,9 @@
"BlackFriday": "BLACK FRIDAY",
"ValidForExistingCustomers": "Also valid for the
existing customers!",
"CampaignBetweenDates": "From {0}
to {1}",
- "SaveUpTo": "SAVE UP TO${0}K"
+ "SaveUpTo": "SAVE UP TO${0}K",
+ "ImplementingDDD": "Implementing Domain Driven Design",
+ "ExploreTheEBook": "Explore the E-Book",
+ "ExploreTheBook": "Explore the Book"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
index 4b883b430c..0dcd69bbd8 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
@@ -179,6 +179,10 @@
"Post": "邮政",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "探索核心 ABP 团队和 ABP 社区创建的内容。",
"WelcomeFallCampaign": "欢迎秋季活动!",
- "GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!"
+ "GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!",
+ "BlackFriday": "黑色 星期五",
+ "ValidForExistingCustomers": "也适用于
现有用户!",
+ "CampaignBetweenDates": "从 {0}
至 {1}",
+ "SaveUpTo": "最多节省 至${0}K"
}
-}
\ No newline at end of file
+}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/ar.json
new file mode 100644
index 0000000000..d441da99a4
--- /dev/null
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/ar.json
@@ -0,0 +1,7 @@
+{
+ "culture": "ar",
+ "texts": {
+ "AbpTitle": "إطار عمل ABP - إطار عمل تطبيق ويب مفتوح المصدر",
+ "AbpDescription": "ABP هو إطار عمل مفتوح المصدر يركز على تطوير تطبيقات الويب القائمة على AspNet Core. لا تكرر نفسك ، ركز على كود عملك الخاص."
+ }
+}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
index 9dd80cb92a..b202302d8e 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
@@ -57,14 +57,14 @@
"PremiumSupport": "دعم متميز",
"PremiumForumSupport": "دعم المنتدى المتميز",
"UI": "واجهة المستخدم",
- "Themes": "ثيمات",
+ "Themes": "السمات",
"JoinOurNewsletter": "اشترك في صحيفتنا الإخبارية",
"Send": "يرسل",
"Learn": "يتعلم",
"AdditionalServices": "خدمات إضافية",
"WhatIsABPFramework": "ما هو إطار ABP؟",
"OpenSourceBaseFramework": "إطار عمل قاعدة مفتوحة المصدر",
- "ABPFrameworkExplanation": "
يعتمد ABP Commercial على إطار عمل ABP ، وهو إطار عمل تطبيقات ويب مفتوح المصدر وقائم على المجتمع لـ ASP.NET Core.
يوفر إطار عمل ABP بنية تحتية ممتازة للكتابة القابلة للصيانة والتوسعة وشفرة قابلة للاختبار مع أفضل الممارسات. p>
مبنية ومتكاملة مع الأدوات الشائعة التي تعرفها بالفعل. منحنى تعليمي منخفض ، وتكيف سهل ، وتطور مريح. p>",
+ "ABPFrameworkExplanation": "
يعتمد ABP Commercial على إطار عمل ABP ، وهو إطار عمل تطبيقات ويب مفتوح المصدر وقائم على المنتدى لـ ASP.NET Core.
يوفر إطار عمل ABP بنية تحتية ممتازة للكتابة القابلة للصيانة والتوسعة وشفرة قابلة للاختبار مع أفضل الممارسات. p>
مبنية ومتكاملة مع الأدوات الشائعة التي تعرفها بالفعل. منحنى تعليمي منخفض ، وتكيف سهل ، وتطور مريح. p>",
"Modular": "معياري",
"MicroserviceCompatible": "متوافق مع Microservice",
"DomainDrivenDesignInfrastructure": "بنية تحتية للتصميم يحركها المجال",
@@ -158,7 +158,7 @@
"WhatIsTheABPCommercial": "ما هو برنامج ABP التجاري؟",
"WhatAreDifferencesThanAbpFramework": "ما هي الاختلافات بين إطار عمل ABP مفتوح المصدر وإطار عمل ABP التجاري؟",
"ABPCommercialExplanation": "ABP Commercial عبارة عن مجموعة من الوحدات النمطية والأدوات والسمات والخدمات المتميزة التي تم إنشاؤها فوق إطار عمل ABP مفتوح المصدر. يتم تطوير ودعم ABP Commercial من قبل نفس الفريق الذي يقف وراء إطار عمل ABP.",
- "WhatAreDifferencesThanABPFrameworkExplanation": "
إطار عمل ABP عبارة عن إطار عمل لتطوير تطبيقات معياري وقابل للتخصيص ومتوافق مع خدمة دقيقة لـ ASP.NET Core. إنه يوفر بنية كاملة وبنية تحتية قوية لتجعلك تركز على رمز عملك الخاص بدلاً من تكرار نفسك لكل مشروع جديد. يعتمد على أفضل ممارسات تطوير البرامج والأدوات الشائعة التي تعرفها بالفعل.
إطار عمل ABP مجاني تمامًا ومفتوح المصدر وقائم على المجتمع. كما أنه يوفر سمة مجانية وبعض الوحدات النمطية مسبقة الصنع (مثل إدارة الهوية وإدارة المستأجر). p>",
+ "WhatAreDifferencesThanABPFrameworkExplanation": "
إطار عمل ABP عبارة عن إطار عمل لتطوير تطبيقات معياري وقابل للتخصيص ومتوافق مع خدمة دقيقة لـ ASP.NET Core. إنه يوفر بنية كاملة وبنية تحتية قوية لتجعلك تركز على رمز عملك الخاص بدلاً من تكرار نفسك لكل مشروع جديد. يعتمد على أفضل ممارسات تطوير البرامج والأدوات الشائعة التي تعرفها بالفعل.
إطار عمل ABP مجاني تمامًا ومفتوح المصدر وقائم على المنتدى. كما أنه يوفر سمة مجانية وبعض الوحدات النمطية مسبقة الصنع (مثل إدارة الهوية وإدارة المستأجر). p>",
"VisitTheFrameworkVSCommercialDocument": "قم بزيارة الرابط التالي للحصول على مزيد من المعلومات {1} ",
"ABPCommercialFollowingBenefits": "يضيف ABP Commercial المزايا التالية إلى جانب إطار عمل ABP ؛",
"Professional": "احترافي",
@@ -208,7 +208,7 @@
"MicroserviceSupportExplanation3": "نحن نقدم نموذجًا من حل تجريبي للخدمة المصغرة يوضح تنفيذ بنية الخدمة الدقيقة لمساعدتك في إنشاء الحل الخاص بك.",
"MicroserviceSupportExplanation4": "لذا ، فإن الإجابة المختصرة هي \" نعم ، فهي تدعم بنية الخدمات الدقيقة strong>\".",
"MicroserviceSupportExplanation5": "ومع ذلك ، يعد نظام الخدمة الصغيرة حلاً وسيكون لكل حل متطلبات مختلفة ، وطوبولوجيا الشبكة ، وسيناريوهات الاتصال ، وإمكانيات المصادقة ، وقرارات فصل/مشاركة قاعدة البيانات ، وتكوينات وقت التشغيل ، وتكاملات أنظمة الجهات الخارجية وغيرها الكثير.",
- "MicroserviceSupportExplanation6": "يوفر إطار عمل ABP و ABP Commercial بنية تحتية لسيناريوهات الخدمة الصغيرة والوحدات النمطية المتوافقة مع الخدمة الصغيرة والعينات والوثائق لمساعدتك في بناء الحل الخاص بك. ولكن لا تتوقع تنزيل الحل الذي تحلم به والذي تم إنشاؤه مسبقًا من أجلك مباشرةً. ستحتاج إلى فهمها وجمع بعض الأجزاء معًا بناءً على متطلباتك.",
+ "MicroserviceSupportExplanation6": "يوفر إطار عمل ABP و ABP Commercial بنية تحتية لسيناريوهات الخدمة الصغيرة والوحدات النمطية المتوافقة مع الخدمة الصغيرة والعينات والمستندات لمساعدتك في بناء الحل الخاص بك. ولكن لا تتوقع تنزيل الحل الذي تحلم به والذي تم إنشاؤه مسبقًا من أجلك مباشرةً. ستحتاج إلى فهمها وجمع بعض الأجزاء معًا بناءً على متطلباتك.",
"WhereCanIDownloadSourceCode": "أين يمكنني تنزيل شفرة المصدر؟",
"WhereCanIDownloadSourceCodeExplanation": "يمكنك تنزيل الكود المصدري لجميع وحدات ABP والحزم والسمات Angular عبر ABP Suite أو ABP CLI. راجع كيفية تنزيل شفرة المصدر؟ ",
"ComputerLimitation": "كم عدد أجهزة الكمبيوتر التي يمكن للمطور تسجيل الدخول إليها عند تطوير برنامج ABP؟",
@@ -252,7 +252,7 @@
"SeeABPSuiteDocument": "راجع مستند ABP Suite للتعرف على استخدام ABP Suite.",
"AskQuestionsOnSupport": "يمكنك طرح الأسئلة على الدعم التجاري لبرنامج ABP.",
"Documentation": "توثيق",
- "SeeModulesDocument": "راجع مستند الوحدات للحصول على قائمة بجميع الوحدات التجارية (الموالية) ووثائقها.",
+ "SeeModulesDocument": "راجع مستند الوحدات للحصول على قائمة بجميع الوحدات التجارية (الموالية) ومستنداتها.",
"Pricing": "التسعير",
"PricingExplanation": "اختر الميزات والوظائف التي يحتاجها عملك اليوم. قم بالترقية بسهولة مع نمو عملك.",
"Team": "فريق",
@@ -311,7 +311,7 @@
"New": "جديد",
"MongoDB": "MongoDB",
"EBookDDD": "تصميم يحرك مجال الكتاب الإلكتروني",
- "PracticalGuideForImplementingDDD": "هذا الكتاب هو دليل عملي لتنفيذ التصميم المستند إلى المجال باستخدام إطار عمل ABP.",
+ "PracticalGuideForImplementingDDD": "هذا الكتاب هو دليل عملي لتنفيذ التصميم المُقاد بالمجال باستخدام إطار عمل ABP.",
"IntroducingDDD": "تقديم تصميم يحركه المجال",
"DDDLayersAndCleanArchitecture": "طبقات DDD والعمارة النظيفة",
"LayeringOfADotnetSolution": "طبقات حل NET",
@@ -322,9 +322,9 @@
"Download": "تحميل",
"DDDEBook": "DDD الكتاب الإلكتروني",
"ImplementingDDD": "تنفيذ تصميم يحركه المجال",
- "DDDBookExplanation": "دليل عملي لتنفيذ التصميم المستند إلى المجال باستخدام إطار عمل ABP.",
+ "DDDBookExplanation": "دليل عملي لتنفيذ التصميم المُقاد بالمجال باستخدام إطار عمل ABP.",
"Overview": "ملخص",
- "DDDBookPracticalGuide": "هذا دليل عملي لتنفيذ التصميم المستند إلى المجال (DDD). بينما تعتمد تفاصيل التنفيذ على البنية التحتية لـ ABP Framework ، فإن المفاهيم الأساسية والمبادئ والأنماط قابلة للتطبيق في أي نوع من الحلول ، حتى لو لم يكن حل .NET.",
+ "DDDBookPracticalGuide": "هذا دليل عملي لتنفيذ التصميم المُقاد بالمجال (DDD). بينما تعتمد تفاصيل التنفيذ على البنية التحتية لـ ABP Framework ، فإن المفاهيم الأساسية والمبادئ والأنماط قابلة للتطبيق في أي نوع من الحلول ، حتى لو لم يكن حل .NET.",
"TableOfContents": "جدول المحتويات",
"IntroductionToImplementingDDD": "مقدمة في تنفيذ التصميم على أساس المجال",
"WhatIsDDD": "ما هو المجال على أساس التصميم؟",
@@ -382,6 +382,403 @@
"RenewLicenseEarly": "إذا قمت بتجديد رخصتي في وقت مبكر ، هل سأحصل على السنة كاملة؟",
"RenewLicenseEarylExplanation": "عند تجديد الترخيص الخاص بك قبل تاريخ انتهاء الترخيص الخاص بك ، ستتم إضافة سنة واحدة إلى تاريخ انتهاء الترخيص الخاص بك. على سبيل المثال ، إذا انتهت صلاحية ترخيصك في {0} -06-06 وقمت بتجديده في {0} -01-01 ، فسيكون تاريخ انتهاء صلاحية الترخيص الجديد {1} -06-06.",
"discountForYears": "{0}٪ خصم لمدة {1} سنة (سنوات)",
- "BlackFridayDiscount": "خصم الجمعة السوداء"
+ "BlackFridayDiscount": "خصم الجمعة السوداء",
+ "OpenSourceWebApplication": "تطبيق ويب مفتوح المصدر",
+ "CompleteWebDevelopment": "تطوير ويب كامل",
+ "ABPFrameworkDescription": "إطار عمل ABP هو بنية تحتية كاملة لإنشاء تطبيقات ويب حديثة من خلال اتباع أفضل ممارسات تطوير البرامج والاتفاقيات.",
+ "CommunityDescription": "شارك خبراتك مع إطار عمل ABP!",
+ "GetStarted": "ابدأ",
+ "Views": "المشاهدات",
+ "LatestPosts": "آخر المنشورات",
+ "PreBuiltApplication": "تطبيق مبني مسبقًا",
+ "DatabaseProviders": "مزودي قواعد البيانات",
+ "UIFrameworks": "أطر عمل واجهة المستخدم",
+ "UsefulLinks": "روابط مفيدة",
+ "Platform": "منصة",
+ "CoolestCompaniesUseABPCommercial": "أروع الشركات تستخدم بالفعل ABP التجاري.",
+ "UserInterface": "واجهة المستخدم",
+ "APIGateway": "بوابة API",
+ "Microservice": "خدمة مصغرة",
+ "Database": "قاعدة البيانات",
+ "Architecture": "البنية",
+ "MicroserviceArchitectureExplanation": "هذه بنية حل كاملة تتكون من تطبيقات متعددة وبوابات API وخدمات مصغرة وقواعد بيانات لبناء حل خدمات مصغرة قابل للتطوير باستخدام أحدث التقنيات.",
+ "BusinessLogic": "منطق الأعمال",
+ "DataAccessLayer": "طبقة الوصول إلى البيانات",
+ "Monolith": "متراصة",
+ "ModularArchitectureExplanation": "يوفر قالب بدء التشغيل هذا بنية حل ذات طبقات ووحدات تعتمد على التصميم المُقاد بالمجال DDD لإنشاء قاعدة بيانات نظيفة وقابلة للصيانة.",
+ "SeeDetails": "انظر التفاصيل",
+ "SeeDocumentation": "تحقق من المستندات",
+ "Bs5Compatible": "سمة احترافية متوافقة مع Bootstrap 5 ، مثالية لموقعك الإداري.",
+ "LeptonXTheme": "سمة LeptonX",
+ "LeptonXDark": "LeptonX داكن",
+ "LeptonXLight": "فاتح LeptonX",
+ "LeptonXSemiDark": "LeptonX شبه داكن",
+ "BuiltOnBs5Library": "بنيت على مكتبة Bootstrap 5",
+ "FullyCompatibleWithBs5": "100٪ متوافق مع بنية Bootstrap 5 HTML وفئات CSS",
+ "ResponsiveAndMobileCompatible": "دعم اليمين-لليسار سريع الاستجابة ومتوافق مع الأجهزة المحمولة",
+ "ProvidesStylesForDatatables": "يوفر أنماطًا لجداول البيانات",
+ "MultipleLayoutOptions": "خيارات تخطيط متعددة",
+ "EasilyInstallAndUpgrade": "سهولة التثبيت والترقية",
+ "SupportForum": "منتدى الدعم",
+ "TrustedBy": "موثوق به من قبل",
+ "OurPricing": "تسعيرنا",
+ "Plans": "الخطط",
+ "NameSurname": "الاسم واللقب",
+ "Unspecified": "غير محدد",
+ "LicenceType": "نوع الرخصة",
+ "LicenseDiscountWarning": "تستخدم صفحة الخصم هذه رمز الخصم الافتراضي ولمطوري VOLOSOFT. لا تعمل روابط الشراء أدناه.",
+ "DiscountedLicenseExplanation": "أسعار التراخيص هذه مخصصة للشركات الناشئة الصغيرة والمطورين الفرديين والطلاب والمنظمات غير الربحية والمشاريع!",
+ "General": "عام",
+ "License": "رخصة",
+ "Development": "تطوير",
+ "Payment": "دفع",
+ "WatchExplainerVideo": "فلنلتقي! شاهد الفيديو التوضيحي",
+ "LightDarkAndSemiDarkThemes": "فاتح ، داكن وشبه داكن",
+ "LeptonXThemeExplanation": "يمكن لـ Lepton Theme تغيير المظهر الخاص بك وفقًا لإعدادات نظامك.",
+ "PRO": "برو",
+ "WelcomeToABPCommercial": "مرحبا بك في ABP تجاري!",
+ "YourAccountDetails": "تفاصيل حسابك",
+ "OrganizationName": "اسم المنظمة",
+ "AddDevelopers": "أضف المطورين",
+ "StartDevelopment": "ابدأ التطوير",
+ "CreateAndRunApplicationUsingStartupTemplate": "تعرف على كيفية إنشاء تطبيق ويب جديد وتشغيله باستخدام نموذج بدء التشغيل التجاري ABP.",
+ "CommunityDescription2": "community.abp.io هو مكان حيث يمكن للأشخاص مشاركة المقالات ذات الصلة بـ ABP. ابحث عن المقالات والبرامج التعليمية وعينات التعليمات البرمجية ودراسات الحالة وقابل أشخاصًا في نفس المسار الذي تقابله.",
+ "UseABPSuiteExplanation": "استخدم ABP Suite لتنزيل الكود المصدري للوحدات والسمات.",
+ "ManageModulesWithSuite": "يمكنك أيضًا إدارة وحدات ABP الخاصة بك باستخدام Suite.",
+ "LearnHowToInstallSuite": "تعرف على كيفية تثبيت واستخدام ABP Suite.",
+ "SeeMore": "رؤية المزيد",
+ "SeeLess": "رؤية أقل",
+ "LayeredSolutionStructure": "هيكل الحل متعدد الطبقات",
+ "LayeredSolutionStructureExplanation": "يتم وضع الحل على أساس مبادئ وأنماط التصميم المُقاد بالمجال DDD لعزل منطق عملك عن البنية التحتية والتكامل ولتعظيم إمكانية صيانة الكود وإعادة استخدامه. يوفر إطار عمل ABP بالفعل تجريدات وفئات أساسية وأدلة لتنفيذ التصميم المُقاد بالمجال DDD حقًا لتطبيقك.",
+ "MultipleUIOptions": "خيارات متعددة لواجهة المستخدم",
+ "MultipleUIOptionsExplanation": "نحن نحب طرقًا مختلفة لإنشاء واجهة المستخدم. يوفر حل بدء التشغيل هذا ثلاثة خيارات مختلفة لإطار عمل واجهة المستخدم لتطبيق عملك.",
+ "MultipleDatabaseOptions": "خيارات قاعدة بيانات متعددة",
+ "MultipleDatabaseOptionsExplanation": "لديك خياران لموفر قاعدة البيانات (بالإضافة إلى استخدام كليهما في تطبيق واحد). استخدم Entity Framework Core للعمل مع أي قاعدة بيانات علائقية واستخدم Dapper اختياريًا عندما تحتاج إلى كتابة استعلامات منخفضة المستوى للحصول على أداء أفضل. يعد MongoDB خيارًا آخر إذا كنت بحاجة إلى استخدام قاعدة بيانات NoSQL قائمة على المستندات. في حين أن هؤلاء الموفرين مدمجون جيدًا وملخصون ومهيئون مسبقًا ، يمكنك في الواقع التفاعل مع أي نظام قاعدة بيانات يمكنك استخدامه مع .NET.",
+ "ModularArchitectureExplanation2": "النمطية هي مواطن من الدرجة الأولى في منصة ABP.IO. يتم تقسيم جميع وظائف التطبيق إلى وحدات اختيارية معزولة جيدًا. يأتي حل بدء التشغيل بالفعل مع وحدات ABP Commercial الأساسية المثبتة مسبقًا. يمكنك أيضًا إنشاء الوحدات النمطية الخاصة بك لبناء نظام معياري لتطبيقك الخاص.",
+ "MultiTenancyForSaasBusiness": "متعدد الإيجارات لأعمال SaaS الخاصة بك",
+ "MultiTenancyForSaasBusinessExplanation": "توفر ABP Commercial نظامًا كاملاً ومتعدد الإيجارات لإنشاء أنظمة SaaS (البرمجيات كخدمة). يسمح للمستأجرين بمشاركة قواعد البيانات الخاصة بهم أو الحصول عليها من خلال إنشاء قاعدة بيانات سريعة ونظام الهجرة.",
+ "MicroserviceStartupSolution": "حل بدء تشغيل الخدمات المصغرة",
+ "MicroserviceArchitectureExplanation2": "يمكنك الحصول عليه لنظام الخدمات المصغرة التالي الخاص بك للاستفادة من الحل الأساسي المبني مسبقًا والتجربة المحسنة.",
+ "PreIntegratedTools": "مُدمج مسبقًا في الأدوات الشائعة",
+ "PreIntegratedToolsExplanation": "الحل مدمج بالفعل مع الأدوات والتقنيات المتوافقة مع معايير الصناعة، بينما يمكنك دائمًا تغييرها والدمج مع أدواتك المفضلة.",
+ "SingleSignOnAuthenticationServer": "خادم مصادقة الدخول الموحد",
+ "SingleSignOnAuthenticationServerExplanation": "يحتوي الحل على تطبيق خادم مصادقة تستخدمه التطبيقات الأخرى كخادم تسجيل دخول فردي مع ميزات إدارة الوصول إلى واجهة برمجة التطبيقات. يعتمد على IdentityServer.",
+ "WebAppsWithGateways": "تطبيقين ويب مع بوابتين API",
+ "WebAppsWithGatewaysExplanation": "يحتوي الحل على تطبيقين للويب ، يحتوي كل منهما على بوابة API مخصصة (BFF - نمط الواجهة الخلفية للواجهة الأمامية).",
+ "BackOfficeApplication": "تطبيق المكتب الخلفي",
+ "BackOfficeApplicationExplanation": "تطبيق الويب الفعلي لنظامك، مع خيارات إطار عمل متعددة لواجهة المستخدم. يمكنك إنشاء أي نوع من تطبيقات الأعمال.",
+ "LandingWebsite": "موقع هبوط",
+ "LandingWebsiteExplanation": "موقع عام للهبوط / موقع عام يمكن استخدامه لعدة أغراض، مثل التعريف بشركتك وبيع منتجاتك وما إلى ذلك.",
+ "ABPFrameworkEBook": "الكتاب الإلكتروني لإتقان إطار عمل ABP",
+ "MasteringAbpFrameworkEBookDescription": "مشمول في رخصة ABP التجارية الخاصة بك",
+ "FullName": "الاسم الكامل",
+ "LicenseTypeNotCorrect": "نوع الرخصة خاطئ!",
+ "Trainings": "التدريبات",
+ "ChooseTrainingPlaceholder": "اختر التدريب ...",
+ "DoYouNeedTrainings": "هل تحتاج إلى أحد هذه التدريبات؟",
+ "DoYouNeedTraining": "هل تحتاج إلى تدريب {0}؟",
+ "GetInTouchUs": "تواصل معنا",
+ "ForMoreInformationClickHere": "لمزيد من المعلومات, اضغط هنا.",
+ "IsGetOnboardingTraining": "هل ترغب في الحصول على تدريب تطوير تطبيقات الويب والإعداد؟",
+ "OnboardingWebApplicationDevelopmentTrainingMessage": "لجدولة تقويم التدريب الخاص بك، الرجاء التواصل مع {0} بعد انشاء المنظمة",
+ "CustomPurchaseMessage": "للخطوة التالية، انقر على {0} للاتصال بنا.",
+ "Note": "ملحوظة",
+ "AdditionalNote": "ملحوظة إضافية",
+ "OnboardingTrainingFaqTitle": "هل لديكم تدريب على ABP؟",
+ "OnboardingTrainingFaqExplanation": "نعم، لدينا خدمات تدريب ABP لمساعدتك في بدء مشروع ABP الخاص بك بسرعة. ستتعرف على ABP من أحد أعضاء فريق ABP الأساسي وستحصل على المهارات اللازمة لبدء مشروع ABP الخاص بك. خلال التدريب، سنشرح كيفية إعداد بيئة التطوير الخاصة بك، وتثبيت الأدوات المطلوبة، وإنشاء صفحة CRUD تعمل بكامل طاقتها. سيكون التدريب مباشرًا وسيتم استخدام برنامج Zoom، ونحن منفتحون لاستخدام منصات اجتماعات أخرى عبر الإنترنت. ستكون لغة التدريب هي اللغة الإنجليزية. يمكنك أيضًا طرح أسئلتك حول برنامج ABP أثناء الجلسات. سيتم التخطيط للوقت والتاريخ المناسبين لكلا الطرفين. للحصول على مزيد من المعلومات، تواصل معنا على info@abp.io.",
+ "AddBasket": "إضافة إلى السلة",
+ "SendTrainingRequest": "إرسال طلب تدريب",
+ "OnlyEnglishVersionOfThisDocumentIsTheRecentAndValid": "* النسخة الإنجليزية من هذه الوثيقة هي الأحدث وستتم العةدة اليها خلال أي نزاع.",
+ "Pricing_Page_Title": "الخطط والتسعير",
+ "Pricing_Page_Description": "اختر الميزات والوظائف التي يحتاجها عملك اليوم. شراء رخصة تجارية ABP وإنشاء مشاريع غير محدودة.",
+ "Pricing_Page_HurryUp": "أسرع!",
+ "Pricing_Page_BuyLicense": "اشترِ رخصة من أسعار 2021 حتى 16 يناير!",
+ "Pricing_Page_ValidForExistingCustomers": "صالحة أيضًا للعملاء الحاليين وتجديد الرخص.",
+ "Pricing_Page_Hint1": "يشمل سعر الرخصة عددًا معينًا من مقاعد المطورين. إذا كان لديك المزيد من المطورين ، فيمكنك دائمًا شراء مقاعد إضافية.",
+ "Pricing_Page_Hint2": "يمكنك شراء المزيد من رخص المطورين الآن أو في المستقبل. تعتمد الرخص على المقاعد، لذا يمكنك نقل مقعد من مطور إلى آخر.",
+ "Pricing_Page_Hint3": "يمكنك تطوير عدد غير محدود من المنتجات المختلفة برخصتك.",
+ "Pricing_Page_Hint4": "ABP Suite هي أداة تساعد في تطويرك لتحسين إنتاجيتك. يدعم إنشاء صفحات CRUD وإنشاء مشاريع جديدة.",
+ "Pricing_Page_Hint5": "يمكنك استخدام جميع الوحدات المدمجة مسبقًا في تطبيقاتك.",
+ "Pricing_Page_Hint6": "يمكنك استخدام جميع السمات المبنية مسبقًا في تطبيقاتك.",
+ "Pricing_Page_Hint7": "عارضة بدء التشغيل هو أحد حلول Visual Studio لجعلك تبدأ بمشروعك. تمت إضافة جميع الوحدات الأساسية وتهيئتها مسبقًا لك.",
+ "Pricing_Page_Hint8": "يشرح الكتاب الإلكتروني اتقان إطار عمل ABP كيفية تنفيذ حلول .NET مع أفضل الممارسات. يُباع الكتاب على Amazon.com ويمكنك تنزيله مجانًا ضمن رخصتك.",
+ "Pricing_Page_Hint9": "يمكنك تنزيل الكود المصدري لأي وحدة نمطية. قد ترغب في إضافة شفرة المصدر إلى الحل الخاص بك لإجراء تغييرات جذرية أو الاحتفاظ بها لنفسك لأسباب أمنية.",
+ "Pricing_Page_Hint10": "الرخص لمدى الحياة. هذا يعني أنه يمكنك الاستمرار في تطوير تطبيقك إلى الأبد. يتم منح الوصول إلى أحدث إصدار والحصول على الدعم خلال فترة الرخصة (عام واحد ما لم تقم بتجديده).",
+ "Pricing_Page_Hint11": "لا قيود على النشر! يمكنك النشر على أي عدد تريده من الخوادم ، بما في ذلك الخدمات السحابية أو المحلية.",
+ "Pricing_Page_Hint12": "يمكنك تحديث الوحدات والسمات والأدوات إلى أحدث إصدار خلال فترة الرخصة النشطة. بعد انتهاء صلاحية الرخصة الخاصة بك، تحتاج إلى تجديدها، للاستمرار في الحصول على تحديثات لإصلاحات الأخطاء، والميزات الجديدة والتحسينات.",
+ "Pricing_Page_Hint13": "يمكنك الحصول على الدعم المتميز لمدة عام واحد (يمكنك تجديد رخصتك لتمديده).",
+ "Pricing_Page_Hint14": "رخص الفريق والعمل لها حد لعدد الحوادث/الأسئلة. إذا اشتريت رخصة مطور إضافية، فسيزيد حد الحوادث بمقدار {0} (لرخصة الفريق) أو {1} (لرخصة العمل) لكل مطور.",
+ "Pricing_Page_Hint15": "الدعم الخاص تتضمنه فقط رخصة المؤسسة. يمكنك إرسال بريد إلكتروني مباشرة إلى فريق ABP أو طرح أسئلة على support.abp.io مع خيار التذكرة الخاصة. التذاكر الخاصة غير مرئية للعامة.",
+ "Pricing_Page_Hint16": "يمكنك تنزيل الكود المصدري لجميع سمات ABP. قد ترغب في إضافة الكود المصدري إلى الحل الخاص بك لإجراء تغييرات جذرية أو الاحتفاظ بها لنفسك لأسباب أمنية.",
+ "Pricing_Page_Testimonial_1": "سمحت ABP التجاري لشركة SC Ventures بتقديم منصة SaaS لقاعدة بيانات ذات صوامع متعددة المستأجرين على مستوى البنوك في 9 أشهر لدعم تمويل سلسلة التوريد للحسابات المدينة / الحسابات الدائنة لفواتير ذات قيمة كبيرة من العديد من المراسي المتكاملة. مكنت نمطية ABP الفريق من التسليم في وقت قياسي ، واجتياز جميع VAPT ، ونشر مكدس الخدمات المصغرة في حاويات عبر CI / CD كامل وخطوط الأنابيب في الإنتاج.",
+ "Pricing_Page_Testimonial_2": "نحن نرى قيمة استخدام ABP التجاري لتقليل النفقات العامة لمشاريع التطوير المخصصة. والفريق قادر على توحيد نمط الكود في تدفقات المشروع المختلفة. نرى المزيد من الإمكانات في إطار العمل بالنسبة لنا لبناء ميزات جديدة بشكل أسرع من ذي قبل. نحن على ثقة من أننا سنرى باستمرار قيمة الاستفادة من ABP التجاري.",
+ "Pricing_Page_Testimonial_3": "نحن نحب ABP. ليس علينا كتابة كل شيء من الصفر. نبدأ من الميزات الجاهزة ونركز فقط على ما نحتاج حقًا إلى كتابته. أيضًا ، ABP مصمم جيدًا والشفرة عالية الجودة مع عدد أقل من الأخطاء. إذا كان علينا كتابة كل ما نحتاجه بمفردنا ، فقد نضطر إلى قضاء سنوات. مرة أخرى نحب أن الإصدار الجديد ، أو إصلاح المشكلة ، أو التحسين يأتي قريبًا جدًا كل أسبوعين. نحن لا ننتظر طويلا.",
+ "Pricing_Page_Testimonial_4": "ABP التجاري منتج رائع يوصى به. تسويق المنتجات التجارية لعملائنا في منصة واحدة قابلة للتكوين. البداية السريعة التي يوفرها إطار العمل والأدوات لأي فريق تستحق كل سنت. كانت ABP هي الأنسب لاحتياجاتنا.",
+ "Pricing_Page_Testimonial_5": "إطار عمل ABP ليس مجرد إطار عمل ، ولكنه أيضًا دليل لتطوير / إدارة المشروع ، لأنه يوفر تدريب DDD و GenericRepository و DI و Microservice و Modularity. حتى إذا كنت لن تستخدم الإطار نفسه ، يمكنك تطوير نفسك باستخدام docs.abp.io المُعد جيدًا ومهنيًا (OpenIdict و Redis و Quartz وما إلى ذلك). نظرًا لأن العديد من الأشياء مبنية مسبقًا ، فإنها تقصر وقت تطوير المشروع بشكل كبير (مثل صفحة تسجيل الدخول ، ومعالجة الاستثناءات ، وتصفية البيانات ، والبذر ، وتسجيل التدقيق ، والترجمة ، ووحدة التحكم في واجهة برمجة التطبيقات التلقائية ، وما إلى ذلك). كمثال من تطبيقنا ، لقد استخدمت Local Event Bus للتحكم في المخزون. لذلك ، أنا قادر على إدارة حركات الأوامر عن طريق كتابة معالج الأسهم. إنه لأمر رائع ألا تضيع الوقت من أجل CreationTime ، CreatorId. يتم ملؤها تلقائيًا.",
+ "AbpBookDownloadArea_ClaimYourEBook": "احصل على كتاب إتقان إطار عمل ABP الالكتروني الخاص بك",
+ "AddMemberModal_Warning_1": "اذا كان المستخدم الذي تحاول اضافته لا يوجد في النظام, يرجى مطالبة عضو فريقك بالتسجيل في {0} ومشاركة اسم المستخدم الخاص بحسابه معك.",
+ "MyOrganizations_Detail_WelcomeMessage": "مرحبًا بك في مؤسستك، {0}",
+ "MyOrganizations_Detail_OrganizationManagement": "إدارة المنظمة",
+ "OrganizationDisplayName": "اسم عرض المنظمة",
+ "MyOrganizations_Detail_EditDisplayName": "تعديل اسم العرض",
+ "MyOrganizations_Detail_UpgradeYourLicense": "قم بترقية رخصتك",
+ "MyOrganizations_Detail_LicenseStartAndExpiryDate": "تاريخ بدء الترخيص - تاريخ انتهاء الصلاحية",
+ "MyOrganizations_Detail_OwnerRightInfo": "أنت تستخدم {0} من {1} حقوق المالك.",
+ "MyOrganizations_Detail_CopyApiKey": "انسخ المفتاح",
+ "MyOrganizations_Detail_ApiKeyDescription": "مفتاح API هو الرمز المميز لحزم PRO المستضافة على {1}.",
+ "MyOrganizations_Detail_YourPrivateNugetSource": "مصدر NuGet الخاص بك هو {0}",
+ "MyOrganizations_Detail_PrivateNugetSourceWarning": "تتم إضافة هذا تلقائيًا كوجز إلى NuGet.Config في حل ABP الخاص بك. لا تشارك مفتاحك الخاص مع مستخدمين غير مصرح لهم!",
+ "MyOrganizations_Detail_DeveloperSeatInfo": "أنت تستخدم {0} من {1} مقاعد مطوري البرامج.",
+ "NeedMoreSeatsForYourTeam": "هل تحتاج إلى المزيد من المقاعد لفريقك؟",
+ "MyOrganizations_Detail_PricePerYear": "{0} / سنويًا",
+ "MyOrganizations_Detail_PurchaseDeveloperSeats": "شراء مقاعد المطور",
+ "Invoices": "الفواتير",
+ "RequestInvoice": "طلب الفاتورة",
+ "OrderNumber": "رقم الطلب",
+ "Date": "التاريخ",
+ "Products": "المنتجات",
+ "TotalPrice": "السعر الإجمالي",
+ "ThereIsNoInvoice": "لا يوجد فاتورة",
+ "MyOrganizations_Detail_PaymentProviderInfo": "إذا اشتريت ترخيصك من خلال بوابة {0}، فإنها تُرسل فاتورة PDF إلى عنوان بريدك الإلكتروني، راجع {0} الفواتير.",
+ "MyOrganizations_Detail_PayUInfo": "إذا كنت قد اشتريت من خلال بوابة PayU، انقر على زر \"طلب الفاتورة\" واملأ معلومات الفوترة.",
+ "MyOrganizations_Detail_ConclusionInfo": "سيتم إنهاء طلب الفاتورة في غضون {0} يوم عمل.",
+ "ExtendYourLicense": "قم بتمديد رخصة {0} الخاصة بك",
+ "Continue": "استمر",
+ "DownloadInvoiceModal_DownloadInvoice": "تحميل فاتورة",
+ "DownloadInvoiceModal_SaveInformationOnlyOnce": "يمكنك حفظ معلومات الفواتير الخاصة بك مرة واحدة فقط.",
+ "InvoiceModal_EnterCompanyName": "أدخل اسم شركتك القانوني...",
+ "InvoiceModal_EnterCompanyAddress": "أدخل عنوان شركتك القانوني...",
+ "InvoiceModal_EnterTaxNumber": "أدخل رقم الضريبة / ضريبة القيمة المضافة إذا كان متاحًا...",
+ "RequestInvoiceModal_EnterNotes": "أدخل رسالتك الإضافية حول فاتورتك...",
+ "PrePayment_PayWithIyzico": "ستدفع عن طريق Iyzico",
+ "ContinueToCheckout": "الاستمرار في الخروج",
+ "PrePayment_IyzicoRedirectionInfo": "ستتم إعادة توجيهك إلى بوابة الدفع Iyzico لإكمال عملية الشراء بشكل آمن.",
+ "PrePayment_IyzicoAcceptVisaAndMasterCard": "تقبل Iyzico بطاقات الفيزا وماستركارد.",
+ "Purchase": "شراء",
+ "AcceptTermsAndConditions": "لقد قرأت وفهمت وقبلت سياسة الخصوصية، البنود والشروط و اتفاقية مستخدم الرخصة EULA.",
+ "AcceptTermsAndConditionsWarningMessage": "الرجاء قبول سياسة الخصوصية والشروط والأحكام",
+ "SelectGatewayToContinue": "الرجاء تحديد بوابة للمتابعة!",
+ "GatewaySelection_SelectGateway": "حدد بوابة الدفع",
+ "GatewaySelection_RedirectionMessage": "بعد ذلك، ستتم إعادة توجيهك إلى موقع بوابة الدفع المحدد للمعاملة.",
+ "PaymentSucceed_PaymentSuccessMessage": "تم الدفع بنجاح",
+ "PaymentSucceed_ThanksForPurchase": "شكرا لك على الشراء!",
+ "PaymentSucceed_CreateYourOrganization": "أنشئ مؤسستك",
+ "PaymentSucceed_AddMeAsDeveloper": "أنا مطور، أضفني كمطور لمؤسستي.",
+ "PaymentSucceed_CreateOrganization": "إنشاء منظمة",
+ "PaymentSucceed_OrganizationDescription": "المنظمة تتكون من المطورين والمالكين. المطورون هم المستخدمون الذين يكتبون الكود في مشروع ABP وسيستفيدون من موقع {1}. المالكون هم المستخدمون الذين يخصصون مقاعد للمطورين ويديرون الرخصة.",
+ "PaymentSucceed_ViewOrganization": "انقر هنا لعرض المنظمة",
+ "Purchase_TotalAnnualPrice": "المجموع (رسوم سنوية)",
+ "Purchase_TrainingPrice": "سعر التدريب",
+ "Purchase_OnboardingTraining": "تدريب مباشر على ABP وتطوير تطبيقات الويب",
+ "TotalDeveloperPrice": "إجمالي سعر المطور",
+ "Purchase_PricePerDeveloper": "{0} {1} لكل مطور",
+ "Purchase_IncludedDeveloperInfo": "{0} {1} يتضمن.",
+ "Purchase_LicenseExtraDeveloperPurchaseMessage": "رخصة {0} تحتوي على {1} مطور/ين. يمكنك إضافة مطورين إضافيين الآن أو لاحقًا.",
+ "StartupTemplates_Page_Title": "عارضات بدء التشغيل",
+ "StartupTemplates_Page_Description": "يسمح لك ABP التجاري ببناء حلول بأي مستوى من التعقيد. يوفر حلين رئيسيين لبدء التشغيل. يمكنك اختيار واحد قريب من متطلباتك وبناء الحل المخصص الخاص بك فوقه.",
+ "MicroserviceStartupSolutionForDotnet": "حل بدء تشغيل Microservice لـ .NET",
+ "MonolithSolutionForDotnet": "حل مترابط (معياري) لـ .NET",
+ "TrainingDetailsHeaderInfo_TrainingHour": "{0} ساعة (ساعات)",
+ "Trainings_Content": "محتوى التدريب",
+ "Trial_Page_StartYourFreeTrial": "ابدا تجربتك المجانية",
+ "TrialLicenseFeatures": "ستتمكن من الاستفادة من جميع ميزات ABP التجاري",
+ "TrialPeriodDays": "سيكون لديك ترخيص الفريق لمدة {0} يوم",
+ "TrialForumSupportIncident": "سيكون لديك {0} بطاقات دعم المنتدى للحوادث",
+ "Contact_Page_Title": "تواصل مع فريق تطوير ABP",
+ "Contact_Page_Description": "تواصل مع فريق تطوير ABP، إذا كنت بحاجة إلى أي مساعدة أو شاركنا أفكارك وآرائك! فريق دعم ABP جاهز للمساعدة.",
+ "Demo_Page_Title": "إنشاء عرض",
+ "Demo_Page_Description": "أنشئ عرضًا توضيحيًا مجانيًا لمشاهدة نموذج تطبيق تم إنشاؤه باستخدام نموذج بدء التشغيل التجاري ABP. لا تكرر نفسك لمتطلبات التطبيق المشتركة.",
+ "Discounted_Page_Title": "Discounted pricing",
+ "Discounted_Page_Description": "اختر الميزات والوظائف التي يحتاجها عملك اليوم. شراء رخصة تجارية ABP وإنشاء مشاريع غير محدودة",
+ "Faq_Page_Title": "الاسئلة الشائعة (FAQ)",
+ "Faq_Page_Description": "هل لديك اسئلة؟ ابحث عن الأسئلة الشائعة أو اطرح علينا سؤالاً باستخدام نموذج الاتصال.",
+ "Faq_Page_SwiftCode": "رمز سويفت SWIFT",
+ "Faq_Page_BankName": "اسم البنك",
+ "Faq_Page_AccountName": "أسم الحساب",
+ "Faq_Page_AccountNumber": "رقم حساب",
+ "Faq_Page_Currency": "العملة",
+ "Faq_Page_VatNumber": "رقم ضريبة القيمة المضافة",
+ "Faq_Page_OtherCurrenciesInfo": "للعملات الأخرى ، انظر الى جميع الحسابات",
+ "ModuleDetail_Page_Title": "تفاصيل الوحدة - {0}",
+ "ProjectCreatedSuccess_Page_Title": "تم إنشاء مشروعك",
+ "ProjectCreatedSuccess_Page_Description": "تم إنشاء مشروع ABP الخاص بك بنجاح!",
+ "Suite_Page_Title": "ABP Suite - إنشاء صفحات CRUD",
+ "Suite_Page_Description": "يوفر ABP التجاري أدوات تطوير سريعة للتطبيقات لزيادة إنتاجية المطورين. يتيح لك ABP Suite إنشاء صفحات CRUD بسهولة.",
+ "Themes_Page_Title": "سمات واجهة المستخدم الحديثة والوظيفية",
+ "Themes_Page_Description": "يوفر ABP التجاري العديد من سمات واجهة المستخدم الاحترافية والحديثة. أنشئ عرضًا تجريبيًا مجانيًا للحصول على عرض سريع لشكل واجهة المستخدم.",
+ "Tools_Page_Title": "أدوات تطوير التطبيقات السريعة",
+ "Tools_Page_Description": "يوفر ABP التجاري أدوات تطوير سريعة للتطبيقات لزيادة إنتاجية المطورين. يتيح لك ABP Suite إنشاء صفحات CRUD بسهولة.",
+ "DeveloperPrice": "سعر المطور",
+ "AdditionalDeveloperPaymentInfoSection_AdditionalDevelopers": "{0} مطورين",
+ "LicenseRemainingDays": "لمدة {0} يوم",
+ "ExtendPaymentInfoSection_Description": "من خلال تمديد / تجديد رخصتك، ستستمر في الحصول على الدعم المتميز. ستتمكن أيضًا من الحصول على تحديثات رئيسية أو ثانوية للوحدات والسمات. ستتمكن من الاستمرار في إنشاء مشاريع جديدة. وستظل قادرًا على استخدام ABP Suite مما يسرع من تطويرك.",
+ "LicenseRenewalPrice": "سعر تجديد الرخصة",
+ "LicensePrice": "سعر الرخصة",
+ "TrialLicensePaymentInfoSection_Description": "شراء رخصة: بشراء رخصة، ستستمر في الحصول على الدعم المتميز. ستتمكن أيضًا من الحصول على تحديثات رئيسية أو ثانوية للوحدات والسمات. ستتمكن من الاستمرار في إنشاء مشاريع جديدة. وستظل قادرًا على استخدام ABP Suite مما يسرع من تطويرك.
راجع جدول مقارنة الرخصة للتحقق من الاختلافات بين أنواع الرخص.",
+ "SelectTargetLicense": "حدد الرخصة المرادة",
+ "UpgradePaymentInfoSection_ExtendMyLicenseForOneYear": "نعم، قم بتمديد تاريخ انتهاء رخصتي لمدة عام واحد.",
+ "UpgradePaymentInfoSection_WantToExtendLicense": "هل تريد تمديد ترخيصك لمدة {0} سنة (سنوات) أخرى؟",
+ "UpgradePaymentInfoSection_UpgradingWillNotExtendLicense": "لن تؤدي الترقية إلى تمديد تاريخ انتهاء رخصتك!",
+ "UpgradePaymentInfoSection_LicenseUpgradeDescription": "من خلال ترقية رخصتك، ستقوم بالترقية إلى نوع رخصة أعلى مما سيتيح لك الحصول على مزايا إضافية. راجع جدول مقارنة الرخص للتحقق من الاختلافات بين أنواع الرخص.",
+ "Landing_Page_CustomerStories": "قصص العملاء",
+ "Landing_Page_OurGreatCustomers": "عملائنا الكرام",
+ "Landing_Page_WebApplicationFramework": "إطار تطبيق ويب",
+ "Landing_Page_WebDevelopmentPlatform": "منصة تطوير ويب",
+ "Landing_Page_CompleteWebDevelopmentPlatform": "منصة تطوير ويب الكاملة",
+ "Landing_Page_TryFreeDemo": "جرب نسخة تجريبية مجانية",
+ "Landing_Page_StartingPointForWebApplications": "نقطة البداية لتطبيقات الويب القائمة على ASP.NET Core! يعتمد على إطار عمل ABP لأفضل تطوير ويب.",
+ "Landing_Page_AbpProvidesSoftwareInfrastructure": "يوفر إطار عمل ABP بنية أساسية للبرامج لتطوير تطبيقات ويب ممتازة مع أفضل الممارسات.",
+ "Landing_Page_MicroserviceCompatibleArchitecture": "بنية متوافقة مع الخدمات المصغرة",
+ "Landing_Page_PreBuiltApplicationModulesAndThemes": "وحدات وموضوعات التطبيق المبنية مسبقًا",
+ "Landing_Page_MultiTenantArchitecture": "بنية متعددة المستأجرين",
+ "Landing_Page_MultiTenancyDescription": "جعلت تطبيقات SaaS سهلة! متعدد ايجارات متكامل من قاعدة البيانات إلى واجهة المستخدم.",
+ "Landing_Page_DDDIntroduction": "تم تصميمه وتطويره بناءً على أنماط ومبادئ التصميم المُقاد بالمجال DDD. يوفر نموذجًا متعدد الطبقات لتطبيقك.",
+ "Landing_Page_CrossCuttingConcernsInfo": "بنية تحتية كاملة للترخيص والتحقق من الصحة ومعالجة الاستثناءات والتخزين المؤقت وتسجيل التدقيق وإدارة المعاملات والمزيد.",
+ "Landing_Page_PreBuiltApplicationModules": "وحدات التطبيق سابقة الإنشاء والتي تتضمن متطلبات تطبيقات الويب الأكثر شيوعًا.",
+ "Landing_Page_ChatModule": "الدردشة",
+ "Landing_Page_DocsModule": "المستندات",
+ "Landing_Page_FileManagementModule": "إدارة الملفات",
+ "Landing_Page_CustomerStory_1": "سمحت ABP التجاري لشركة SC Ventures بتقديم منصة SaaS لقاعدة بيانات ذات صوامع متعددة المستأجرين على مستوى البنوك في 9 أشهر لدعم تمويل سلسلة التوريد للحسابات المدينة / الحسابات الدائنة لفواتير ذات قيمة كبيرة من العديد من المراسي المتكاملة. مكنت نمطية ABP الفريق من التسليم في وقت قياسي ، واجتياز جميع VAPT ، ونشر مكدس الخدمات المصغرة في حاويات عبر CI / CD كامل وخطوط الأنابيب في الإنتاج.",
+ "Landing_Page_CustomerStory_2": "نحن نرى قيمة استخدام ABP التجاري لتقليل النفقات العامة لمشاريع التطوير المخصصة. والفريق قادر على توحيد نمط الكود في تدفقات المشروع المختلفة. نرى المزيد من الإمكانات في إطار العمل بالنسبة لنا لبناء ميزات جديدة بشكل أسرع من ذي قبل. نحن على ثقة من أننا سنرى باستمرار قيمة الاستفادة من ABP التجاري.",
+ "Landing_Page_CustomerStory_3": "نحن نحب ABP. ليس علينا كتابة كل شيء من الصفر. نبدأ من الميزات الجاهزة ونركز فقط على ما نحتاج حقًا إلى كتابته. أيضًا، ABP مصمم جيدًا والشفرة عالية الجودة مع عدد أقل من الأخطاء. إذا كان علينا كتابة كل ما نحتاجه بمفردنا، فقد نضطر إلى قضاء سنوات. مرة أخرى نحب أن الإصدار الجديد، أو إصلاح المشكلة، \n أو التحسين يأتي قريبًا جدًا كل أسبوعين. نحن لا ننتظر طويلا.",
+ "Landing_Page_CustomerStory_4": "التجاري منتج رائع يوصى به. تسويق المنتجات التجارية لعملائنا في منصة واحدة قابلة للتكوين. البداية السريعة التي يوفرها إطار العمل والأدوات لأي فريق تستحق كل سنت. كانت ABP هي الأنسب لاحتياجاتنا.",
+ "Landing_Page_AdditionalServices": "رخصة مخصصة أو مجمعة، تدريب مبدئي، تدريب مباشر ودعم ، تطوير مشروع مخصص، نقل المشاريع الحالية والمزيد...",
+ "Landing_Page_IncludedDeveloperLicenses": "رخص المطورين المتضمنة {0} ",
+ "Landing_Page_SeeOnDemo": "انظر في العرض",
+ "Landing_Page_LeptonThemes": "سمات ليبتون",
+ "Landing_Page_AccountModuleDescription_1": "تنفذ هذه الوحدة نظام المصادقة على التطبيق:",
+ "Landing_Page_AccountModuleDescription_2": "يقدم صفحة تسجيل الدخول مع اسم المستخدم وكلمة المرور",
+ "Landing_Page_AccountModuleDescription_3": "يقدم صفحة تسجيلل إنشاء حساب جديد.",
+ "Landing_Page_AccountModuleDescription_4": "يقدم صفحة نسيت كلمة المرور لإرسال رابط إعادة تعيين كلمة المرور كبريد إلكتروني.",
+ "Landing_Page_AccountModuleDescription_5": "يوفر وظيفة تأكيد البريد الإلكتروني مع واجهة المستخدم.",
+ "Landing_Page_AccountModuleDescription_6": "تنفذ المصادقة ذات العاملين (الرسائل القصيرة والبريد الإلكتروني).",
+ "Landing_Page_AccountModuleDescription_7": "تنفذ تأمين المستخدم (يقفل الحساب لفترة زمنية محددة عندما يحدث عدد معين من عمليات تسجيل الدخول الفاشلة بسبب بيانات اعتماد غير صالحة خلال فترة زمنية معينة).",
+ "Landing_Page_AccountModuleDescription_8": "ينفذ واجهة مستخدم ووظائف خادم مصادقة Identity Server .",
+ "Landing_Page_AccountModuleDescription_9": "يسمح بالتبديل بين المستأجرين في بيئة متعددة المستأجرين.",
+ "Landing_Page_AccountModuleDescription_10": "يسمح بتغيير لغة واجهة المستخدم للتطبيق.",
+ "Landing_Page_AuditLoggingModuleDescription_1": "توفر هذه الوحدة واجهة مستخدم تقارير سجل التدقيق للبنية التحتية للتدقيق. يسمح بالبحث عن إدخالات سجل التدقيق وتصفيتها وإظهارها وسجلات تغيير الكيان.",
+ "Landing_Page_AuditLoggingModuleDescription_2": "يتكون إدخال سجل التدقيق من بيانات مهمة حول كل طلب عميل:",
+ "Landing_Page_AuditLoggingModuleDescription_3": "URL ،المتصفح، عنوان IP ،اسم العميل",
+ "Landing_Page_AuditLoggingModuleDescription_4": "المستخدم",
+ "Landing_Page_AuditLoggingModuleDescription_5": "طريقة HTTP، رمز حالة إرجاع HTTP",
+ "Landing_Page_AuditLoggingModuleDescription_6": "نجاح / فشل ، تفاصيل الاستثناء إذا كانت متوفرة",
+ "Landing_Page_AuditLoggingModuleDescription_7": "مدة تنفيذ الطلب",
+ "Landing_Page_AuditLoggingModuleDescription_8": "تم إنشاء الكيانات أو حذفها أو تحديثها في هذا الطلب (بخصائص تم تغييرها).",
+ "Landing_Page_BloggingModuleDescription_1": "تضيف هذه الوحدة مدونة بسيطة إلى تطبيق ABP الخاص بك:",
+ "Landing_Page_BloggingModuleDescription_2": "يسمح بإنشاء مدونات متعددة في تطبيق واحد.",
+ "Landing_Page_BloggingModuleDescription_3": "يدعم تنسيق Markdown.",
+ "Landing_Page_BloggingModuleDescription_4": "يسمح لكتابة تعليق على المنشور.",
+ "Landing_Page_BloggingModuleDescription_5": "يسمح بتعيين علامات إلى مشاركات المدونة.",
+ "Landing_Page_BloggingModuleDescription_6": "انظر الى blog.abp.io موقع الويب كمثال حي لوحدة التدوين.",
+ "Landing_Page_ChatModuleDescription_1": "تُستخدم هذه الوحدة للمراسلة في الوقت الفعلي بين المستخدمين في التطبيق.",
+ "Landing_Page_ChatModuleDescription_2": "المراسلة في الوقت الحقيقي على صفحة الدردشة.",
+ "Landing_Page_ChatModuleDescription_3": "بحث المستخدمين في التطبيق عن محادثات جديدة.",
+ "Landing_Page_ChatModuleDescription_4": "قائمة جهات الاتصال للمحادثات الأخيرة.",
+ "Landing_Page_ChatModuleDescription_5": "إشعارات الرسائل الجديدة عندما ينظر المستخدم إلى صفحة أخرى.",
+ "Landing_Page_ChatModuleDescription_6": "إجمالي شارة عدد الرسائل غير المقروءة على أيقونة القائمة.",
+ "Landing_Page_ChatModuleDescription_7": "عدد الرسائل غير المقروءة لكل محادثة.",
+ "Landing_Page_ChatModuleDescription_8": "تحميل كسول المحادثات.",
+ "Landing_Page_DocsModuleDescription_1": "تستخدم هذه الوحدة لإنشاء مواقع الويب الخاصة بالتوثيق الفني؛",
+ "Landing_Page_DocsModuleDescription_2": "تكامل GitHub مدمج: يمكنك كتابة المستندات وإدارتها مباشرةً على GitHub.",
+ "Landing_Page_DocsModuleDescription_3": "دعم تعيين الإصدار strong> المدمج مباشرةً في إصدارات GitHub.",
+ "Landing_Page_DocsModuleDescription_4": "دعم متعدد اللغات strong> (مع دعم احتياطي للغة الافتراضية).",
+ "Landing_Page_DocsModuleDescription_5": "يدعم تنسيقات Markdown strong> و HTML.",
+ "Landing_Page_DocsModuleDescription_6": "يوفر قسم تنقل strong> و مخططًا تفصيليًا strong>.",
+ "Landing_Page_DocsModuleDescription_7": "يسمح باستضافة مستندات مشاريع متعددة strong> في تطبيق واحد.",
+ "Landing_Page_DocsModuleDescription_8": "روابط إلى الملف على GitHub ،بحيث يمكن لأي شخص المساهمة بسهولة من خلال النقر على رابط التعديل.",
+ "Landing_Page_DocsModuleDescription_9": "بالإضافة إلى مصدر GitHub، يسمح ببساطة باستخدام مجلد كمصدر توثيق.",
+ "Landing_Page_FileManagementModuleDescription_1": "تحميل وتنزيل وتنظيم الملفات في هيكل مجلد هرمي.",
+ "Landing_Page_FileManagementModuleDescription_2": "تُستخدم هذه الوحدة لتحميل الملفات وتنزيلها وتنظيمها في هيكل مجلد هرمي. كما أنه متوافق مع تعددية الإيجارات ويمكنك تحديد الحجم الإجمالي للمستأجرين.",
+ "Landing_Page_FileManagementModuleDescription_3": "تعتمد هذه الوحدة على نظام BLOB تخزين، لذا يمكنها استخدام موفري تخزين مختلفين لتخزين محتويات الملف.",
+ "Landing_Page_IdentityModuleDescription_1": "تطبق هذه الوحدة نظام المستخدم والدور للتطبيق:",
+ "Landing_Page_IdentityModuleDescription_2": "مبني على مكتبة Microsoft's ASP.NET Core Identity.",
+ "Landing_Page_IdentityModuleDescription_3": "إدارة الأدوار strong> و المستخدمين strong> في النظام. يُسمح للمستخدم بأن يكون له أدوار متعددة strong>.",
+ "Landing_Page_IdentityModuleDescription_4": "عيِّن الأذونات strong> في الدور ومستوى المستخدم.",
+ "Landing_Page_IdentityModuleDescription_5": "تمكين / تعطيل المصادقة الثنائية strong> و قفل strong> المستخدم لكل مستخدم.",
+ "Landing_Page_IdentityModuleDescription_6": "إدارة ملف المستخدم strong> الأساسي و كلمة المرور strong>.",
+ "Landing_Page_IdentityModuleDescription_7": "إدارة أنواع المطالبات strong> في النظام، وتعيين مطالبات للأدوار والمستخدمين.",
+ "Landing_Page_IdentityModuleDescription_8": "إعداد الصفحة لإدارة تعقيد كلمة المرور strong> وتسجيل دخول المستخدم والحساب وتأمين.",
+ "Landing_Page_IdentityModuleDescription_9": "يدعم مصادقة LDAP .",
+ "Landing_Page_IdentityModuleDescription_10": "يوفر البريد الإلكتروني & amp؛ رقم الهاتف strong> التحقق.",
+ "Landing_Page_IdentityModuleDescription_11": "يدعم تكامل تسجيل الدخول الاجتماعي (Twitter ، Facebook ، GitHub ، إلخ ...).",
+ "Landing_Page_IdentityModuleDescription_12": "إدارة الوحدات التنظيمية strong> في النظام.",
+ "Landing_Page_PaymentModuleDescription_1": "يوفر تكاملاً لبوابات الدفع المختلفة.",
+ "Landing_Page_PaymentModuleDescription_2": "توفر هذه الوحدة تكاملاً لبوابات الدفع ، بحيث يمكنك بسهولة الحصول على مدفوعات من عملائك.",
+ "Landing_Page_PaymentModuleDescription_3": "تدعم هذه الوحدة بوابات الدفع التالية",
+ "Welcome_Page_UseSameCredentialForCommercialWebsites": "استخدم نفس بيانات الاعتماد لكلا commercial.abp.io و support.abp.io.",
+ "WatchCrudPagesVideo": "شاهد فيديو \"Creating CRUD Pages with ABP Suite\"!",
+ "WatchGeneratingFromDatabaseVideo": "Watch the \"ABP Suite: Generating CRUD Pages From Existing Database Tables\" Video!",
+ "WatchTakeCloserLookVideo": "شاهد فيديو \"Take a closer look at the code generation: ABP Suite\"!",
+ "ConfirmedEmailAddressRequiredToStartTrial": "يجب أن يكون لديك عنوان بريد إلكتروني مؤكد لبدء ترخيص تجريبي.",
+ "EmailVerificationMailNotSent": "تعذر إرسال بريد التحقق من البريد الإلكتروني.",
+ "GetConfirmationEmail": "انقر هنا للحصول على بريد إلكتروني للتأكيد إذا لم تكن قد حصلت عليه من قبل.",
+ "WhichLicenseTypeYouAreInterestedIn": "ما نوع الرخصة المهتم بها؟",
+ "DontTakeOurWordForIt": "لا تأخذ كلمتنا لذلك...",
+ "ReadAbpCommercialUsersWantYouToKnow": "اقرأ ما يريد مستخدمو ABP التجاري أن تعرفه",
+ "Testimonial_ShortDescription_1": "جعلت نمطية ABP من الممكن للفريق التسليم في الوقت المناسب.",
+ "Testimonial_ShortDescription_2": "بناء ميزات جديدة بشكل أسرع من ذي قبل.",
+ "Testimonial_ShortDescription_3": "نبدأ من الميزات الجاهزة ونركز فقط على ما نحتاج حقًا إلى كتابته.",
+ "Testimonial_ShortDescription_4": "كانت ABP التجاري هي الأنسب لاحتياجاتنا.",
+ "OnlineReviewersOnAbpCommercial": "مراجعات عبر الإنترنت حول ABP التجاري",
+ "SeeWhatToldAboutAbpCommercial": "تعرف على ما قيل عن ABP التجاري واكتب أفكارك إذا كنت تريد ذلك.",
+ "BlazoriseLicense": "هل نحن بحاجة لشراء رخصة Blazorise؟",
+ "BlazoriseLicenseExplanation": "لدينا اتفاقية بين Volosoft و Megabit، مع هذه الاتفاقية يتم تضمين رخصة Blazorise مع منتجات ABP التجارية وبالتالي لا يحتاج عملاؤنا إلى شراء رخصة Blazorise إضافية.",
+ "ExtendPaymentInfoSection_DeveloperPrice": "{0} x سعر المطور(ين)",
+ "ExtendPaymentInfoSection_DiscountRate": "خصم ٪{0}",
+ "TotalNetPrice": "إجمالي السعر الصافي",
+ "EFCore": "Entity Framework Core",
+ "All": "الجميع",
+ "Mvc": "MVC",
+ "DataBaseProvider": "مزود البيانات",
+ "UIFramework": "إطار عمل واجهة المستخدم",
+ "LeptonXThemeForDashboard": "سمة LeptonX للوحة تحكم المشرف الخاصة بك بواسطة",
+ "AbpPlatform": "منصة ABP",
+ "YouDeserveGoodUXUI": "أنت تستحق واجهة مستخدم جيدة وتجربة مستخدم أفضل. سمة LeptonX من ABP موجود هنا لخدمته.",
+ "ViewLiveDemo": "شاهد العرض التوضيحي المباشر للموضوع",
+ "GetLeptonX": "احصل على LeptonX الآن",
+ "SeeLeptonXDocumentation": "انظر مستندات LeptonX",
+ "SimplifiedMenu": "قائمة مبسطة",
+ "SimplifiedMenuDescription": "يمكنك بسهولة العثور على الصفحة التي تبحث عنها من خلال تصفية القائمة",
+ "YourFavoritePages": "صفحاتك المفضلة في متناول يدك",
+ "YourFavoritePagesDescription": "يمكنك إضافة الصفحة أو إزالتها بسهولة من المفضلة بالنقر فوق رمز النجمة في الزاوية اليمنى العليا من الصفحة.",
+ "BreadCrumbs": "مسار التنقل للتبديل السلس",
+ "BreadCrumbsDescription": "باستخدام Breadcrumb ، يمكنك التبديل إلى الصفحات على نفس المستوى بنقرة واحدة ، حتى عندما تكون القائمة اليسرى مغلقة ، وتعمل على الأجهزة اللوحية والهواتف المحمولة المستجيبة!",
+ "YourMenu": "القائمة الخاصة بك كما يحلو لك",
+ "YourMenuDescription": "قم بتخصيص الرموز القابلة للنقر مباشرةً والمربعات المنسدلة في قائمة المستخدم كما يحلو لك. قائمة المستخدم قابلة للتخصيص بالكامل حسب احتياجاتك",
+ "RtlSupport": "دعم من اليمين إلى اليسار للغتك",
+ "RtlSupportDescription": "سمة LeptonX تدعم اليمين الى اليسار للغتك. توجد خيارات اللغة في قائمة الإعدادات لتتمكن من تغيير اللغة.",
+ "YourColors": "ألوانك على واجهة مستخدم لوحة تحكم المشرف",
+ "YourColorsDescription": "تعمل سمة LeptonX وفقًا لتفضيلات النظام لديك وله سمة فاتحة للوحة القيادة، وسمة داكنة للوحة القيادة، وخيارات سمة شبه داكنة للوحة القيادة.",
+ "ArrangeContentWidth": "رتب بسهولة عرض المحتوى الخاص بك",
+ "ArrangeContentWidthDescription": "يمكنك بسهولة تغيير عرض منطقة المحتوى الخاصة بك.",
+ "LeptonXCompatibleWith": "سمة LeptonX متوافقة مع",
+ "MobileResponsiveTemplate": "نموذج استجابة الجوال",
+ "MobileResponsiveTemplateDescription1": "اقترب من لوحة تحكم ادارة LeptonX من أي جهاز تريده.",
+ "MobileResponsiveTemplateDescription2": "مصمم لتتمكن من استخدامه بسهولة في كل جهاز خاص بك. إنه يستجيب للأجهزة المحمولة وأحجام الكمبيوتر اللوحي.",
+ "TopMenuLayoutOption": "خيار تخطيط القائمة العلوية",
+ "TopMenuLayoutOptionDescription1": "إذا كنت ترغب في إعداد موقع الويب الخاص بك باستخدام لوحة تحكم المسؤول نفسها، فمن الممكن القيام بذلك باستخدام سمة LeptonX!",
+ "TopMenuLayoutOptionDescription2": "فقط جرب تخطيط قائمة LeptonX العلوية الخاصة لتحقيق ذلك!",
+ "EasilyCustomizable": "قابل للتخصيص بسهولة لألوان علامتك التجارية",
+ "EasilyCustomizableDescription1": "يمكنك تخصيص سمة LeptonX باستخدام عدد قليل من متغيرات SCSS. لا تجاوز، لا يوجد تحميل CSS إضافي!",
+ "EasilyCustomizableDescription2": "باستخدام LeptonX، يمكنك ترتيب لوحة تحكم المسؤول الخاصة بك كيفما تشاء.",
+ "IndependentLayout": "منطقة التخطيط والمحتوى المستقلة",
+ "IndependentLayoutDescription1": "تم تصميم البنية التحتية لتخطيط LeptonX بشكل منفصل تمامًا عن المحتوى.",
+ "IndependentLayoutDescription2": "هذا يعني أنه يمكنك تصميم مشروعك بحرية بهيكل محتوى بخلاف Bootstrap إذا كنت تريد ذلك.",
+ "MostUsedLibraries": "تتكامل المكتبات الأكثر استخدامًا مع LeptonX",
+ "MostUsedLibrariesDescription1": "يحتوي LeptonX على مكتباتك الأكثر استخدامًا. يسمح لك باستخدام مكتبات مثل ApexCharts و DataTables و DropZone و FullCalender و JSTree و Select2 و Toastr دون عناء.",
+ "MostUsedLibrariesDescription2": "يدعم LeptonX أيضًا مكتبات MVC Angular و Blazor الخاصة.",
+ "CreateAndCustomize": "قم بإنشاء وتخصيص الصفحات التي تحتاجها في ثوانٍ باستخدام صفحات LeptonX المخصصة",
+ "CreateAndCustomizeDescription": "باستخدام سمة LeptonX، يمكنك أيضًا الوصول إلى العديد من صفحات html المعدة مسبقًا. وتشمل هذه العديد من الصفحات مثل صفحة تسجيل الدخول، والمدونة، والأسئلة الشائعة، وقائمة الاشتراك، والفواتير، والتسعير، وإدارة الملفات.",
+ "LeptonThemeForAdmin": "Lepton للوحة تحكم المشرف الخاصة بك بواسطة سمة",
+ "LeptonThemeForAdminDescription": "لا تزال سمة Lepton متاحة وسيتم الحفاظ عليها. إذا كنت تريد التبديل إلى سمة LeptonX كمستخدم سمة Lepton، يمكنك الاطلاع على المستندات لمعرفة الكيفية.",
+ "LeptonCompatibleWith": "Lepton متوافق مع سمة",
+ "UpgradePaymentInfoSection_DeveloperPrice": "{0} لـ {1} مطور (مطورون) إضافي",
+ "Upgrade": "ترقية",
+ "Renewal": "التجديد",
+ "UpgradePaymentInfoSection_LicensePrice": "رخصة {0}",
+ "UpgradePaymentInfoSection_LicenseRenewalPrice": "تجديد الرخصة",
+ "Total": "المجموع",
+ "SupportPolicyFaqTitle": "ما هي سياسة الدعم الخاصة بك؟",
+ "SupportPolicyFaqExplanation": "نحن ندعم فقط الإصدار الرئيسي النشط والسابق. لا نضمن إصدار تصحيح للإصدارات الرئيسية الثالثة والأقدم. على سبيل المثال ، إذا كان الإصدار النشط هو 7.0.0 ، فسنصدر إصدارات تصحيح لكل من 6.x.x و 7.x.x. إلى جانب ذلك ، نحن نقدم الدعم فقط لإطار عمل ABP والقضايا التجارية المتعلقة بـ ABP. هذا يعني أنه لا يتم تقديم أي دعم لتطبيقات الطرف الثالث والخدمات السحابية والمكتبات الطرفية الأخرى التي تستخدمها منتجات ABP. سنبذل جهودًا معقولة تجاريًا لتزويد عملائنا بالدعم الفني خلال ساعات العمل الرسمية لـ \"Volosoft Bilisim A.S\". من ناحية أخرى ، نحن لا نلتزم بوقت استجابة اتفاقية مستوى الخدمة (SLA) ، لكننا سنحاول الرد على المشكلات الفنية في أسرع وقت ممكن خلال ساعات العمل الرسمية لدينا. ما لم يتم إبرام اتفاقية خاصة مع العميل ، فإننا نقدم الدعم فقط على https://support.abp.io. لدينا أيضًا دعم خاص بالبريد الإلكتروني ، وهو متاح فقط لحاملي تراخيص المؤسسة."
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
index 4f017817dd..51f2dd550a 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
@@ -210,7 +210,7 @@
"TrialPlan": "Do you have a trial plan?",
"TrialPlanExplanation": "It has a 14 days trial period for the ABP Commercial team license. For more information visit here. Furthermore, for the Team licenses we provide a 30 days money-back guarantee. You can just request a refund in the first 30 days. For the Business and Enterprise licenses, we provide 60% refund in 30 days. This is because Business and Enterprise licenses include the full source code of all the modules and the themes.",
"DoYouAcceptBankWireTransfer": "Do you accept bank wire transfers?",
- "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfers.
After sending the license fee via bank transfer, send your receipt and requested license type to accounting@abp.io.
Our international bank account information:",
+ "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfers.
After sending the license fee via bank transfer, send your receipt and requested license type to accounting@volosoft.com.
Our international bank account information:",
"HowToUpgrade": "How to upgrade existing applications when a new version is available?",
"HowToUpgradeExplanation1": "When you create a new application using ABP Commercial, all the modules and theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.",
"HowToUpgradeExplanation2": "In addition to the standard NuGet/NPM upgrades, ABP CLI provides an update command that automatically finds and upgrades all ABP related packages in your solution.",
@@ -735,7 +735,7 @@
"WatchTakeCloserLookVideo": "Watch the \"Take a closer look at the code generation: ABP Suite\" Video!",
"ConfirmedEmailAddressRequiredToStartTrial": "You should have a confirmed email address in order to start a trial license.",
"EmailVerificationMailNotSent": "Email verification mail couldn't send.",
- "GetConfirmationEmail": "Click here to get a confirmation email if you haven't got it before.",
+ "GetConfirmationEmail": "Click here to get a verification email if you haven't got it before.",
"WhichLicenseTypeYouAreInterestedIn": "Which license type you are interested in?",
"DontTakeOurWordForIt": "Don't take our word for it...",
"ReadAbpCommercialUsersWantYouToKnow": "Read what ABP Commercial users want you to know",
@@ -747,7 +747,7 @@
"SeeWhatToldAboutAbpCommercial": "See what has been told about ABP Commercial and write your thoughts if you want.",
"BlazoriseLicense": "Do we need to buy Blazorise license?",
"BlazoriseLicenseExplanation": "We have an agreement between Volosoft and Megabit, with this agreement Blazorise license is bundled with ABP Commercial products therefore our customers do not need to purchase an extra Blazorise license.",
- "ExtendPaymentInfoSection_DeveloperPrice": "{0} x Developer(s) Price",
+ "ExtendPaymentInfoSection_DeveloperPrice": "{0}x Additional Developer(s)",
"ExtendPaymentInfoSection_DiscountRate": "Discount {0}%",
"TotalNetPrice": "Total Net Price",
"EFCore": "Entity Framework Core",
@@ -804,6 +804,21 @@
"UpgradePaymentInfoSection_LicenseRenewalPrice": "License renewal",
"Total": "Total",
"SupportPolicyFaqTitle": "What is your support policy?",
- "SupportPolicyFaqExplanation": "We do support only the active and the previous major version. We do not guarantee a patch release for the 3rd and older major versions. For example, if the active version is 7.0.0, we will release patch releases for both 6.x.x and 7.x.x. Besides, we provide support only for ABP Framework and ABP Commercial related issues. That means no support is given for the 3rd party applications, cloud services and other peripheral libraries used by ABP products. We will use commercially reasonable efforts to provide our customers with technical support during \"Volosoft Bilisim A.S\"s official business hours. On the other hand, we do not commit to a service-level agreement (SLA) response time, but we will try to respond to the technical issues as quickly as possible within our official working hours. Unless a special agreement is made with the customer, we only provide support at https://support.abp.io. We also have private email support, which is only available to Enterprise License holders."
+ "SupportPolicyFaqExplanation": "We do support only the active and the previous major version. We do not guarantee a patch release for the 3rd and older major versions. For example, if the active version is 7.0.0, we will release patch releases for both 6.x.x and 7.x.x. Besides, we provide support only for ABP Framework and ABP Commercial related issues. That means no support is given for the 3rd party applications, cloud services and other peripheral libraries used by ABP products. We will use commercially reasonable efforts to provide our customers with technical support during \"Volosoft Bilisim A.S\"s official business hours. On the other hand, we do not commit to a service-level agreement (SLA) response time, but we will try to respond to the technical issues as quickly as possible within our official working hours. Unless a special agreement is made with the customer, we only provide support at https://support.abp.io. We also have private email support, which is only available to Enterprise License holders.",
+ "TotalDevelopers": "Total {0} developer(s)",
+ "CustomPurchaseExplanation": "Tailored to your specific needs",
+ "WhereDidYouHearAboutUs": "Where did you hear about us?",
+ "Twitter": "Twitter",
+ "Facebook": "Facebook",
+ "Youtube": "YouTube",
+ "Google": "Google",
+ "Github": "GitHub",
+ "Friend": " From a friend",
+ "Other": "Other",
+ "WhereDidYouHearAboutUs_explain": "Specify ...",
+ "DeletingMemberWarningMessage": "\"{0}\" will be removed from the developer list. If you want, you can assign this empty seat to another developer later.",
+ "AdditionalInfo": "If the developer seats are above your requirements, you can reduce them. You can email at info@abp.io to remove some of your developer seats. Clearing unused developer seats will reduce the license renewal cost. If you want, you can re-purchase additional developer seats within your active license period. Note that, since there are {0} developers in this license package, you cannot reduce this number.",
+ "LinkExpiredErrorMessage": "The link you are trying to access is expired.",
+ "ExpirationDate": "Expiration Date"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
index 155218939a..b144988670 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json
@@ -735,7 +735,7 @@
"WatchTakeCloserLookVideo": "Tekintse meg a „Nézze meg közelebbről a kódgenerálást: ABP Suite” videót!",
"ConfirmedEmailAddressRequiredToStartTrial": "A próbalicenc elindításához rendelkeznie kell egy megerősített e-mail címmel.",
"EmailVerificationMailNotSent": "Nem sikerült elküldeni az ellenőrző e-mailt.",
- "GetConfirmationEmail": "Kattintson ide, ha megerősítő e-mailt szeretne kapni, ha még nem kapta meg.",
+ "GetConfirmationEmail": "Kattintson ide, ha megerősítő e-mailt szeretne kapni, ha még nem kapta meg.",
"WhichLicenseTypeYouAreInterestedIn": "Melyik licenctípus érdekli?",
"DontTakeOurWordForIt": "Ne fogadd el a szavunkat...",
"ReadAbpCommercialUsersWantYouToKnow": "Olvassa el, hogy az ABP Commercial felhasználói mit szeretnének tudni",
@@ -745,9 +745,9 @@
"Testimonial_ShortDescription_4": "Az ABP Commercial volt a legjobban megfelelő az igényeinknek.",
"OnlineReviewersOnAbpCommercial": "Online vélemények az ABP Commercial-ról",
"SeeWhatToldAboutAbpCommercial": "Tekintse meg, mit mondtak az ABP Commercialról, és írja le gondolatait, ha akarja.",
- "ExtendPaymentInfoSection_DeveloperPrice": "{0} x Developer(s) Price",
+ "ExtendPaymentInfoSection_DeveloperPrice": "{0}x További Fejlesztő(k)",
"ExtendPaymentInfoSection_DiscountRate": "Kedvezmény: {0}%",
"TotalNetPrice": "Total Net Price",
"BlackFridayDiscount": "Black Friday Kedvezmény"
}
-}
\ No newline at end of file
+}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
index ae7a1cb656..9a21d6de0a 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json
@@ -744,7 +744,7 @@
"WatchTakeCloserLookVideo": "\"Kod üretimine daha yakından bakın: ABP Suite\" videosunu izleyin!",
"ConfirmedEmailAddressRequiredToStartTrial": "Deneme lisansı başlatmak için onaylanmış bir e -posta adresiniz olmalı.",
"EmailVerificationMailNotSent": "E-posta doğrulama postası gönderilemedi.",
- "GetConfirmationEmail": "Daha önce bir onay e-postası almadıysanız almak için buraya tıklayın.",
+ "GetConfirmationEmail": "Daha önce bir onay e-postası almadıysanız almak için buraya tıklayın.",
"WhichLicenseTypeYouAreInterestedIn": "Hangi lisans türüyle ilgileniyorsunuz?",
"BlackFridayDiscount": "Kara Cuma İndirimi"
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
index 0e4a694652..be657892f7 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
@@ -735,13 +735,75 @@
"WatchTakeCloserLookVideo": "观看“详细了解ABP Suite 的代码生成”视频!",
"ConfirmedEmailAddressRequiredToStartTrial": "你应该有一个确认的电子邮件地址,以便开始试用许可证。",
"EmailVerificationMailNotSent": "电子邮件验证邮件不能发送。",
- "GetConfirmationEmail": "点击这里获取确认邮件 如果你还没有收到。",
+ "GetConfirmationEmail": "点击这里获取确认邮件 如果你还没有收到。",
"WhichLicenseTypeYouAreInterestedIn": "你感兴趣的许可证类型是什么?",
+ "DontTakeOurWordForIt": "不要相信我们的话......",
+ "ReadAbpCommercialUsersWantYouToKnow": "阅读 ABP Commercial 用户希望您了解到的内容",
+ "Testimonial_ShortDescription_1": "ABP 的模块化使得团队能够及时交付项目。",
+ "Testimonial_ShortDescription_2": "比以前更快地构建新功能。",
+ "Testimonial_ShortDescription_3": "我们从开箱即用的功能开始,只专注于我们真正需要编写的内容。",
+ "Testimonial_ShortDescription_4": "ABP Commercial 最适合我们的需求。",
+ "OnlineReviewersOnAbpCommercial": "ABP Commercial 在线评论",
+ "SeeWhatToldAboutAbpCommercial": "查看关于 ABP Comemrcial 的报道,并根据需要写下您的想法。",
"BlazoriseLicense": "我们是否需要购买Blazorise许可证?",
"BlazoriseLicenseExplanation": "我公司Volosoft和公司Megabit之间有合作协议,根据此协议,购买将同时包含了Blazorise许可证与ABP商业产品,因此我们的客户不需要再额外购买Blazorise许可证。",
- "ExtendPaymentInfoSection_DeveloperPrice": "{0} x 开发者价格",
+ "ExtendPaymentInfoSection_DeveloperPrice": "{0}x 额外的 开发者价格",
"ExtendPaymentInfoSection_DiscountRate": "折扣 {0}%",
"TotalNetPrice": "总净价",
- "BlackFridayDiscount": "黑色星期五折扣"
+ "EFCore": "实体框架核心",
+ "All": "全部",
+ "Mvc": "MVC",
+ "DataBaseProvider": "数据提供者",
+ "UIFramework": "UI 框架",
+ "LeptonXThemeForDashboard": "管理仪表板的 LeptonX 主题",
+ "AbpPlatform": "ABP 平台",
+ "YouDeserveGoodUXUI": "您值得拥有良好的用户界面和更好的用户体验。 ABP 的 LeptonX Theme 就是为它服务的。",
+ "ViewLiveDemo": "查看在线主题演示",
+ "GetLeptonX": "立即获取 LeptonX",
+ "SeeLeptonXDocumentation": "请参阅 LeptonX 文档",
+ "SimplifiedMenu": "简化菜单",
+ "SimplifiedMenuDescription": "您可以通过过滤菜单轻松找到您要查找的页面",
+ "YourFavoritePages": "您最喜欢的页面触手可及",
+ "YourFavoritePagesDescription": "通过单击页面右上角的星形图标,可以轻松地将页面添加到收藏夹或从中删除。",
+ "BreadCrumbs": "用于无缝切换的页面路径导航",
+ "BreadCrumbsDescription": "使用页面路径导航,您可以一键切换到同一级别的页面,即使是左侧菜单关闭,它适用于平板电脑和移动设备响应!",
+ "YourMenu": "随心所欲的菜单",
+ "YourMenuDescription": "根据需要自定义用户菜单上可直接单击的图标和下拉框。 用户菜单可完全根据您的需要定制",
+ "RtlSupport": "对您的语言的 RTL 支持",
+ "RtlSupportDescription": "LeptonX 主题支持您的语言的 RTL。 语言选项位于设置菜单中,供您更改语言。",
+ "YourColors": "您在管理仪表板 UI 上的颜色",
+ "YourColorsDescription": "LeptonX 主题根据您的系统偏好工作,并具有仪表板浅色主题、仪表板深色主题和仪表板半深色主题选项。",
+ "ArrangeContentWidth": "轻松安排您的内容宽度",
+ "ArrangeContentWidthDescription": "轻松更改内容区域的宽度。",
+ "LeptonXCompatibleWith": "与LeptonX主题兼容",
+ "MobileResponsiveTemplate": "移动设备响应模板",
+ "MobileResponsiveTemplateDescription1": "从您喜欢的任何设备访问您的LeptonX管理仪表板。",
+ "MobileResponsiveTemplateDescription2": "它是为您设计的,在您的每一个设备上都可以轻松使用。它可以在移动设备和平板电脑上响应。",
+ "TopMenuLayoutOption": "菜单顶部布局选项",
+ "TopMenuLayoutOptionDescription1": "如果你想设置你的网站与相同的管理仪表板,通过使用LeptonX主题是可以做到的!",
+ "TopMenuLayoutOptionDescription2": "只需尝试LeptonX顶部菜单布局,就可以实现!",
+ "EasilyCustomizable": "易于定制您的品牌颜色",
+ "EasilyCustomizableDescription1": "您可以仅使用几个SCSS变量自定义LeptonX主题。没有重写,没有额外的CSS加载!",
+ "EasilyCustomizableDescription2": "使用LeptonX,您可以随意安排您的管理仪表板。",
+ "IndependentLayout": "独立的布局和内容区域",
+ "IndependentLayoutDescription1": "LeptonX 的布局基础设施的设计与内容完全分开。",
+ "IndependentLayoutDescription2": "这意味着您可以根据需要使用 Bootstrap 以外的内容结构自由设计您的项目。",
+ "MostUsedLibraries": "与 LeptonX 集成的最常用的库",
+ "MostUsedLibrariesDescription1": "LeptonX包含您最常用的库。它允许您使用库,如 ApexCharts, DataTables, DropZone, FullCalender, JSTree, Select2, Toastr 并毫不费力。",
+ "MostUsedLibrariesDescription2": "LeptonX还支持MVC Angular和Blazor特定的库。",
+ "CreateAndCustomize": "使用LeptonX自定义页面在几秒钟内创建和自定义所需的页面",
+ "CreateAndCustomizeDescription": "通过使用LeptonX Theme,您还可以访问许多预先制作的html页面。这些包括许多页面,如登录页面,博客,常见问题解答,订阅列表,发票,定价,文件管理。",
+ "LeptonThemeForAdmin": "为您的管理仪表板的Lepton主题",
+ "LeptonThemeForAdminDescription": "Lepton主题仍然可用,并将保持。如果您想以Lepton Theme用户的身份切换到LeptonX Theme,您可以查看文档了解如何操作。",
+ "LeptonCompatibleWith": "Lepton主题兼容",
+ "BlackFridayDiscount": "黑色星期五折扣",
+ "UpgradePaymentInfoSection_DeveloperPrice": "{0} 对于 {1} 额外的开发人员",
+ "Upgrade": "升级",
+ "Renewal": "续订",
+ "UpgradePaymentInfoSection_LicensePrice": "{0} 许可证",
+ "UpgradePaymentInfoSection_LicenseRenewalPrice": "许可证续订",
+ "Total": "总计",
+ "SupportPolicyFaqTitle": "您的支持政策是什么?",
+ "SupportPolicyFaqExplanation": "我们只支持有效的和以前的主要版本。 我们不保证为第 3 个和更早的主要版本发布补丁。 例如,如果有效版本是 7.0.0,我们将发布 6.x.x 和 7.x.x 的补丁版本。 此外,我们仅对 ABP Framework 和 ABP Commercial 相关问题提供支持。 这意味着不支持 ABP 产品使用的第 3 方应用程序、云服务和其他外围库。 我们将尽商业上合理的努力在“Volosoft Bilisim A.S”的正式营业时间内为我们的客户提供技术支持。 另一方面,我们不承诺服务级别协议 (SLA) 响应时间,但我们会尽量在我们的官方工作时间内尽快响应技术问题。 除非与客户达成特殊协议,否则我们仅在 https://support.abp.io 上提供支持。 我们还提供私人电子邮件支持,仅适用于企业许可证持有者。"
}
-}
\ No newline at end of file
+}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
index 6408da0f12..0264fe4e31 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json
@@ -1,7 +1,7 @@
{
"culture": "ar",
"texts": {
- "Permission:CommunityPost": "مقالة المجتمع",
+ "Permission:CommunityPost": "مقالة المنتدى",
"Permission:Edit": "تعديل",
"Waiting": "انتظار",
"Approved": "تمت الموافقة",
@@ -24,12 +24,12 @@
"Summary": "الخلاصة",
"MostRead": "الأكثر قراءة",
"Latest": "آخر",
- "ContributeAbpCommunity": "المساهمة في مجتمع ABP",
+ "ContributeAbpCommunity": "المساهمة في منتدى ABP",
"SubmitYourPost": "إرسال مقالتك",
"ContributionGuide": "دليل المساهمة",
"BugReport": "الإبلاغ عن خطأ",
"SeeAllPosts": "انظر جميع المقالات",
- "WelcomeToABPCommunity!": "مرحبًا بك في مجتمع ABP!",
+ "WelcomeToABPCommunity!": "مرحبًا بك في منتدى ABP!",
"MyProfile": "ملفى",
"MyOrganizations": "منظماتي",
"EmailNotValid": "من فضلك أدخل بريد أليكترونى صحيح.",
@@ -94,11 +94,11 @@
"NoThanks": "لا شكرا",
"MaybeLater": "ربما في وقت لاحق",
"JoinOurPostNewsletter": "انضم إلى النشرة الإخبارية لمقالتنا",
- "Community": "تواصل اجتماعي",
+ "Community": "منتدى",
"Marketing": "تسويق",
"CommunityPrivacyPolicyConfirmation": "أوافق على البنود والشروط و سياسة الخصوصية .",
"PostRequestMessageTitle": " افتح مشكلة على GitHub لطلب مقالة/برنامج تعليمي تريد رؤيته على موقع الويب هذا.",
- "PostRequestMessageBody": "هنا ، قائمة المقالات المطلوبة من قبل المجتمع. هل تريد كتابة مقال مطلوب؟ الرجاء الضغط على الطلب والانضمام إلى المناقشة.",
+ "PostRequestMessageBody": "هنا ، قائمة المقالات المطلوبة من قبل المنتدى. هل تريد كتابة مقال مطلوب؟ الرجاء الضغط على الطلب والانضمام إلى المناقشة.",
"Language": "لغة",
"CreatePostLanguageInfo": "لغة محتوى المنشور.",
"VideoPost": "مشاركة الفيديو",
@@ -116,7 +116,7 @@
"GithubPostUrl": "عنوان Url لمقال Github",
"ExternalPostUrl": "عنوان URL للمادة الخارجية",
"CreatePostCoverInfo": "لإنشاء منشور فعال ، أضف صورة غلاف. قم بتحميل صور بنسبة عرض إلى ارتفاع تبلغ 16: 9 للحصول على أفضل عرض. الحد الأقصى لحجم الملف: 1 ميغا بايت.",
- "ThankYouForContribution": "شكرًا لك على المساهمة في مجتمع ABP.",
+ "ThankYouForContribution": "شكرًا لك على المساهمة في منتدى ABP.",
"GithubPost": "المادة جيثب",
"GithubPostSubmitStepOne": " 1. اكتب مقالة في أي مستودع GitHub عام بتنسيق Markdown. مثال ",
"GithubPostSubmitStepTwo": " 2. أرسل عنوان URL لمقالتك باستخدام النموذج.",
@@ -143,6 +143,50 @@
"Volo.AbpIo.Domain:060001": "عنوان URL المصدر (\"{PostUrl}\") ليس عنوان URL لـ Github",
"Volo.AbpIo.Domain:060002": "محتوى المقالة غير متوفر من مورد Github (\"{PostUrl}\").",
"Volo.AbpIo.Domain:060003": "لم يتم العثور على محتوى مقال!",
- "SeeMore": "شاهد المزيد"
+ "SeeMore": "رؤية المزيد",
+ "JoinTheABPCommunity": "انضم إلى منتدى ABP",
+ "ABPCommunityTalks": "برامج منتدى ABP الحوارية",
+ "LiveDemo": "عرض مباشر",
+ "GetLicense": "الحصول على رخصة",
+ "GetStarted": "البدء",
+ "SourceCode": "كود المصدر",
+ "LeaveComment": "اترك تعليقا",
+ "ShowMore": "عرض المزيد",
+ "NoPublishedPostsYet": "لا يوجد أي منشور حتى الآن.",
+ "Name": "الاسم",
+ "Surname": "اسم العائلة",
+ "WebSite": "الموقع الالكتروني",
+ "FullURL": "رابط URL الكامل",
+ "JobTitle": "الوظيفة",
+ "Prev": "سابق",
+ "Previous": "السابق",
+ "Next": "التالي",
+ "Share": "مشاركة",
+ "SortBy": "صنف حسب",
+ "NoPublishedEventsYet": "لا توجد أحداث منشورة حتى الآن.",
+ "SubscribeYoutubeChannel": "اشترك في قناة يوتيوب",
+ "Enum:EventType:0": "البرامج الحوارية",
+ "MemberNotPublishedPostYet": "هذا العضو لم ينشر أي منشورات حتى الآن.",
+ "TimeAgo": "قبل {0}",
+ "Discord_Page_JoinCommunityMessage": "انضم إلى منتدى ABP Discord",
+ "Discord_Page_Announce": "يسعدنا الإعلان عن سيرفر دسكورد الخاص بمنتدى ABP",
+ "Discord_Page_Description_1": "منتدى ABP ينمو منذ اليوم الأول. أردنا الانتقال إلى الخطوة التالية من خلال إنشاء سيرفر دسكورد ABP رسمي حتى يتمكن منتدى ABP من التفاعل مع بعضهم البعض باستخدام عجائب المراسلة الفورية.",
+ "Discord_Page_Description_2": "سيرفر الدسكورد الخاص بمنتدى ABP هو المكان الذي يمكنك فيه عرض إبداعاتك باستخدام إطار عمل ABP، ومشاركة النصائح التي أدت إلى نتيجة معك، ومواكبة آخر الأخبار والإعلانات حول إطار عمل ABP، ما عليك سوى الدردشة مع أعضاء المنتدى لتبادل الأفكار والاستمتاع!",
+ "Discord_Page_Description_3": "سيرفر الدسكورد الخاص بمنتدى ABP هذا هو السيرفر الرسمي مع وجود فريق ABP الأساسي على السيرفر للمراقبة.",
+ "Discord_Page_JoinToServer": "انضم إلى سيرفر ديسكورد ABP",
+ "Events_Page_MetaTitle": "أحداث منتدى ABP",
+ "Events_Page_MetaDescription": "العروض المباشرة، التي يستضيفها فريق ABP، عبارة عن جلسات عفوية مليئة بالمحتوى المجتمعي والعروض التوضيحية والأسئلة والأجوبة والمناقشات حول ما يحدث في ABP.",
+ "Events_Page_Title": "ABP البرامج الحوارية",
+ "Members_Page_WritingFromUser": "اقرأ الكتابة من {0} على منتدى ABP",
+ "Post_Create_Page_MetaTitle": "منشور جديد",
+ "Post_Create_Page_MetaDescription": "أنشئ منشورك لمشاركة تجاربك حول إطار عمل ABP والمساهمة في منتدى ABP.",
+ "Post_Create_Page_CreateNewPost": "إنشاء منشور جديد",
+ "Post_Index_Page_MetaDescription": "الغرض من منتدى ABP هو إنشاء بيئة مساهمة للمطورين الذين يستخدمون إطار عمل برنامج ABP.",
+ "Layout_Title": "{0} | ABP منتدى",
+ "Layout_MetaDescription": "منتدى ABP هو عبارة عن بيئة حيث يمكن للأشخاص مشاركة منشورات حول إطار عمل برنامج ABP ومتابعة المشاريع.",
+ "Index_Page_CommunityIntroduction": "إن هذا محور لإطار عمل ABP و.NET وتطوير البرامج. يمكنك قراءة المقالات ومشاهدة مقاطع الفيديو التعليمية والحصول على معلومات حول تقدم تطوير ABP والأحداث المتعلقة بـ ABP ومساعدة المطورين الآخرين ومشاركة خبرتك مع منتدى ABP.",
+ "TagsInArticle": "العلامات في المقال",
+ "WelcomeToABP": "أهلا بكم في ABP",
+ "IConsentToMedium": ".https://medium.com/volosoft أوافق على نشر هذا المنشور على"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
index ebd1349383..8364b1b4d4 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json
@@ -142,6 +142,7 @@
"MinimumSearchContent": "Musíte zadat alespoň 3 znaky!",
"Volo.AbpIo.Domain:060001": "Zdrojová adresa URL („{PostUrl}“) není adresa URL Github",
"Volo.AbpIo.Domain:060002": "Obsah článku není dostupný ze zdroje Github(\"{PostUrl}\").",
- "Volo.AbpIo.Domain:060003": "Nebyl nalezen žádný obsah článku!"
+ "Volo.AbpIo.Domain:060003": "Nebyl nalezen žádný obsah článku!",
+ "IConsentToMedium": "Souhlasím se zveřejněním tohoto příspěvku na https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
index d4350a5796..6ff6eb1e89 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json
@@ -142,6 +142,7 @@
"Volo.AbpIo.Domain:060001": "Quell-URL(\"{PostUrl}\") ist keine Github-URL",
"Volo.AbpIo.Domain:060002": "Artikelinhalt ist über die Github(\"{PostUrl}\")-Ressource nicht verfügbar.",
"Volo.AbpIo.Domain:060003": "Kein Artikelinhalt gefunden!",
- "SeeMore": "Mehr Sehen"
+ "SeeMore": "Mehr Sehen",
+ "IConsentToMedium": "Ich stimme der Veröffentlichung dieses Beitrags auf https://medium.com/volosoft zu."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
index 6aa6cfb42b..8cd1dc5a58 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
@@ -184,6 +184,7 @@
"Layout_Title": "{0} | ABP Community",
"Layout_MetaDescription": "ABP Community is an environment where people can share posts about ABP framework and follows the projects.",
"Index_Page_CommunityIntroduction": "This is a hub for ABP Framework, .NET and software development. You can read the articles, watch the video tutorials, get informed about ABP’s development progress and ABP-related events, help other developers and share your expertise with the ABP community.",
- "TagsInArticle": "Tags in article"
+ "TagsInArticle": "Tags in article",
+ "IConsentToMedium": "I consent to the publication of this post at https://medium.com/volosoft."
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
index cd5e27df29..f065ed2895 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "La URL de origen (\"{PostUrl}\") no es la URL de Github",
"Volo.AbpIo.Domain:060002": "El contenido del artículo no está disponible en el recurso de Github (\"{PostUrl}\").",
"Volo.AbpIo.Domain:060003": "¡No se encontró contenido del artículo!",
- "SeeMore": "Ver Más"
+ "SeeMore": "Ver Más",
+ "IConsentToMedium": "Acepto la publicación de esta publicación en https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json
index b686f44775..be267cebc0 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "Lähteen URL-osoite (\"{PostUrl}\") ei ole Githubin URL-osoite",
"Volo.AbpIo.Domain:060002": "Artikkelin sisältö ei ole saatavilla Githubin (\"{PostUrl}\") -resurssista.",
"Volo.AbpIo.Domain:060003": "Artikkelin sisältöä ei löytynyt!",
- "SeeMore": "Katso Lisää"
+ "SeeMore": "Katso Lisää",
+ "IConsentToMedium": "Hyväksyn tämän viestin julkaisemisen osoitteessa https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
index c51623e2a5..9321c8511c 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "L'URL source (\"{PostUrl}\") n'est pas une URL Github",
"Volo.AbpIo.Domain:060002": "Le contenu de l'post n'est pas disponible à partir de la ressource Github(\"{PostUrl}\").",
"Volo.AbpIo.Domain:060003": "Aucun contenu d'post trouvé !",
- "SeeMore": "Voir Plus"
+ "SeeMore": "Voir Plus",
+ "IConsentToMedium": "Je consens à la publication de cet article sur https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
index f9746dba44..5a5cf123f2 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "स्रोत URL (\"{PostUrl}\") जीथब URL नहीं है",
"Volo.AbpIo.Domain:060002": "लेख सामग्री Github (\"{PostUrl}\") संसाधन से उपलब्ध नहीं है।",
"Volo.AbpIo.Domain:060003": "कोई लेख सामग्री नहीं मिली!",
- "SeeMore": "और देखें"
+ "SeeMore": "और देखें",
+ "IConsentToMedium": "मैं https://medium.com/volosoft पर इस पोस्ट के प्रकाशन के लिए सहमति देता/देती हूं।"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json
index 01cc09a4dd..71d65ec88f 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json
@@ -184,6 +184,7 @@
"Layout_Title": "{0} | ABP-közösség",
"Layout_MetaDescription": "Az ABP Community egy olyan környezet, ahol az emberek megoszthatnak bejegyzéseket az ABP keretrendszerről, és követhetik a projekteket.",
"Index_Page_CommunityIntroduction": "Ez az ABP Framework, a .NET és a szoftverfejlesztés központja. Elolvashatja a cikkeket, megnézheti az oktatóvideókat, tájékozódhat az ABP fejlesztési előrehaladásáról és az ABP-vel kapcsolatos eseményekről, segíthet más fejlesztőknek, és megoszthatja szakértelmét az ABP közösséggel.",
- "TagsInArticle": "Címkék a cikkben"
+ "TagsInArticle": "Címkék a cikkben",
+ "IConsentToMedium": "Hozzájárulok a bejegyzés közzétételéhez a https://medium.com/volosoft oldalon."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
index 85c4a134a3..b2b7496e10 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "Upprunaslóð (\"{PostUrl} \") er ekki Github slóð",
"Volo.AbpIo.Domain:060002": "Innihald greinar er ekki fáanlegt frá Github (\"{PostUrl} \") resoursum.",
"Volo.AbpIo.Domain:060003": "Innihald greinar fannst ekki!",
- "SeeMore": "Sjá Meira"
+ "SeeMore": "Sjá Meira",
+ "IConsentToMedium": "Ég samþykki birtingu þessarar færslu á https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
index d14c9716e4..58dc93da4d 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "Source URL(\"{PostUrl}\") non è un URL di GitHub",
"Volo.AbpIo.Domain:060002": "Il contenuto dell'articolo non è disponibile dalla risorsa Github(\"{PostUrl}\").",
"Volo.AbpIo.Domain:060003": "Nessun contenuto dell'articolo trovato!",
- "SeeMore": "Vedi Altro"
+ "SeeMore": "Vedi Altro",
+ "IConsentToMedium": "Acconsento alla pubblicazione di questo post su https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
index c0844f722a..f5a183deb5 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json
@@ -142,6 +142,7 @@
"MinimumSearchContent": "U moet minimaal 3 tekens invoeren!",
"Volo.AbpIo.Domain:060001": "Bron-URL (\"{PostUrl}\") is geen Github-URL",
"Volo.AbpIo.Domain:060002": "Artikelinhoud is niet beschikbaar via Github(\"{PostUrl}\") bron.",
- "Volo.AbpIo.Domain:060003": "Geen artikelinhoud gevonden!"
+ "Volo.AbpIo.Domain:060003": "Geen artikelinhoud gevonden!",
+ "IConsentToMedium": "Ik stem in met de publicatie van dit bericht op https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json
index c33e9eeccb..300050adaf 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json
@@ -142,6 +142,7 @@
"MinimumSearchContent": "Musisz wpisać co najmniej 3 znaki!",
"Volo.AbpIo.Domain:060001": "Źródłowy adres URL („{PostUrl}”) nie jest adresem URL Github",
"Volo.AbpIo.Domain:060002": "Treść artykułu nie jest dostępna w zasobach Github(\"{PostUrl}\").",
- "Volo.AbpIo.Domain:060003": "Nie znaleziono treści artykułu!"
+ "Volo.AbpIo.Domain:060003": "Nie znaleziono treści artykułu!",
+ "IConsentToMedium": "Wyrażam zgodę na publikację tego posta na stronie https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json
index 5d5e7b946c..5297799f76 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json
@@ -142,6 +142,7 @@
"MinimumSearchContent": "Você deve inserir pelo menos 3 caracteres!",
"Volo.AbpIo.Domain:060001": "O URL da fonte (\"{PostUrl}\") não é o URL do Github",
"Volo.AbpIo.Domain:060002": "O conteúdo do artigo não está disponível no recurso Github (\"{PostUrl}\").",
- "Volo.AbpIo.Domain:060003": "Nenhum conteúdo do artigo encontrado!"
+ "Volo.AbpIo.Domain:060003": "Nenhum conteúdo do artigo encontrado!",
+ "IConsentToMedium": "Eu concordo com a publicação deste post em https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json
index 5c0afaa19a..e0ea7b8a5e 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "Sursa URL(\"{PostUrl}\") nu este URL GitHub",
"Volo.AbpIo.Domain:060002": "Conţinutul articolului nu este disponibil din resursa de pe GitHub(\"{PostUrl}\").",
"Volo.AbpIo.Domain:060003": "Nu a fost găsit conţinutul articolului!",
- "SeeMore": "Vezi mai mult"
+ "SeeMore": "Vezi mai mult",
+ "IConsentToMedium": "Sunt de acord cu publicarea acestei postări la https://medium.com/volosoft."
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json
index e47a59691e..179d7e176f 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ru.json
@@ -142,6 +142,7 @@
"MinimumSearchContent": "Вы должны ввести минимум 3 символа!",
"Volo.AbpIo.Domain:060001": "Исходный URL (\"{PostUrl}\") не является URL-адресом Github",
"Volo.AbpIo.Domain:060002": "Контент статьи недоступен на ресурсе Github (\"{PostUrl}\").",
- "Volo.AbpIo.Domain:060003": "Контент статьи не найден!"
+ "Volo.AbpIo.Domain:060003": "Контент статьи не найден!",
+ "IConsentToMedium": "Я даю согласие на публикацию этого поста на https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json
index 5abf7e8f07..3cb4d88c69 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json
@@ -143,6 +143,7 @@
"Volo.AbpIo.Domain:060001": "Zdrojová URL(\"{PostUrl}\") nie je URL Githubu",
"Volo.AbpIo.Domain:060002": "Obsah článku nie je dostupný v Github zdroji(\"{PostUrl}\").",
"Volo.AbpIo.Domain:060003": "Nenašiel sa žiadny obsah článku!",
- "SeeMore": "Vidět Víc"
+ "SeeMore": "Vidět Víc",
+ "IConsentToMedium": "Súhlasím so zverejnením tohto príspevku na https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
index 54a49aa5c9..78c6471697 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json
@@ -142,6 +142,7 @@
"MinimumSearchContent": "Vnesti morate vsaj 3 znake!",
"Volo.AbpIo.Domain:060001": "Izvorni URL (\"{PostUrl}\") ni URL Github",
"Volo.AbpIo.Domain:060002": "Vsebina članka ni na voljo iz vira Github(\"{PostUrl}\").",
- "Volo.AbpIo.Domain:060003": "Vsebina članka ni bila najdena!"
+ "Volo.AbpIo.Domain:060003": "Vsebina članka ni bila najdena!",
+ "IConsentToMedium": "Soglašam z objavo te objave na https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
index ec5106293b..f3b7a791c8 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json
@@ -189,6 +189,7 @@
"Post_Index_Page_MetaDescription": "ABP Topluluğu'nun amacı, ABP çerçevesini kullanan geliştiriciler için bir katkı ortamı yaratmaktır.",
"Layout_Title": "{0} | ABP Topluluğu",
"Layout_MetaDescription": "ABP Topluluğu, insanların ABP çerçevesi hakkında paylaşımlarda bulunabileceği ve projeleri takip edebileceği bir ortamdır.",
- "Index_Page_CommunityIntroduction": "Burası ABP Çerçevesi, .NET ve yazılım geliştirme için bir merkezdir. Makaleleri okuyabilir, eğitim videolarını izleyebilir, ABP'nin gelişim süreci ve ABP ile ilgili etkinlikler hakkında bilgi alabilir, diğer geliştiricilere yardımcı olabilir ve uzmanlığınızı ABP topluluğu ile paylaşabilirsiniz."
+ "Index_Page_CommunityIntroduction": "Burası ABP Çerçevesi, .NET ve yazılım geliştirme için bir merkezdir. Makaleleri okuyabilir, eğitim videolarını izleyebilir, ABP'nin gelişim süreci ve ABP ile ilgili etkinlikler hakkında bilgi alabilir, diğer geliştiricilere yardımcı olabilir ve uzmanlığınızı ABP topluluğu ile paylaşabilirsiniz.",
+ "IConsentToMedium": "Bu yazının https://medium.com/volosoft adresinde yayınlanmasına izin veriyorum."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json
index 91794dd10a..d3b8cd2d4b 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json
@@ -142,6 +142,7 @@
"MinimumSearchContent": "Bạn phải nhập ít nhất 3 ký tự!",
"Volo.AbpIo.Domain:060001": "URL nguồn (\"{PostUrl}\") không phải là URL Github",
"Volo.AbpIo.Domain:060002": "Nội dung Bài viết không có sẵn từ tài nguyên Github (\"{PostUrl}\").",
- "Volo.AbpIo.Domain:060003": "Không tìm thấy nội dung bài viết!"
+ "Volo.AbpIo.Domain:060003": "Không tìm thấy nội dung bài viết!",
+ "IConsentToMedium": "Tôi đồng ý với việc xuất bản bài đăng này tại https://medium.com/volosoft."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
index 23a98b5e4f..d460707b70 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
@@ -183,6 +183,8 @@
"Post_Index_Page_MetaDescription": "ABP 社区的目的是为使用 ABP 框架的开发人员创建一个贡献环境。",
"Layout_Title": "{0} | ABP 社区",
"Layout_MetaDescription": "ABP 社区是一个人们可以分享有关 ABP 框架的帖子并关注项目的环境。",
- "Index_Page_CommunityIntroduction": "这是 ABP 框架、.NET 和软件开发的中心。 您可以阅读文章,观看视频教程,了解 ABP 的开发进度和 ABP 相关事件,帮助其他开发人员并与 ABP 社区分享您的专业知识。"
+ "Index_Page_CommunityIntroduction": "这是 ABP 框架、.NET 和软件开发的中心。 您可以阅读文章,观看视频教程,了解 ABP 的开发进度和 ABP 相关事件,帮助其他开发人员并与 ABP 社区分享您的专业知识。",
+ "TagsInArticle": "文章中的标签",
+ "IConsentToMedium": "我同意在 https://medium.com/volosoft 上发布这篇文章。"
}
-}
\ No newline at end of file
+}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/ar.json
index 7a7fc86661..791062cfbc 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/ar.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/ar.json
@@ -14,23 +14,23 @@
"CreateNow": "إنشاء جديد",
"TheStartupProject": "مشروع بدء التشغيل",
"Tutorial": "الدورة التعليمية",
- "UsingCLI": "باستخدام CLI",
+ "UsingCLI": "باستخدام واجهة الأوامر CLI",
"SeeDetails": "انظر التفاصيل",
- "AbpShortDescription": "ABP Framework بنية أساسية كاملة لإنشاء تطبيقات ويب حديثة باتباع أفضل ممارسات واتفاقيات تطوير البرامج.",
- "SourceCodeUpper": "شفرة المصدر",
+ "AbpShortDescription": "إطار عمل ABP هو بنية أساسية كاملة لإنشاء تطبيقات ويب حديثة باتباع أفضل ممارسات واتفاقيات تطوير البرامج.",
+ "SourceCodeUpper": "الكود الأصل",
"LatestReleaseLogs": "أحدث سجلات الإصدار",
"Infrastructure": "البنية التحتية",
"Architecture": "الهيكلة",
"Modular": "معياري",
"DontRepeatYourself": "لا تكرر نفسك",
- "DeveloperFocused": "تركيز المطور",
+ "DeveloperFocused": "مركزة على المطور",
"FullStackApplicationInfrastructure": "البنية التحتية الكاملة للتطبيق.",
- "DomainDrivenDesign": "Domain Driven Design",
- "DomainDrivenDesignExplanation": "تم تصميمه وتطويره بناءً على أنماط ومبادئ DDD. يوفر نموذجًا متعدد الطبقات لتطبيقك.",
+ "DomainDrivenDesign": "التصميم المُقاد بالمجال DDD",
+ "DomainDrivenDesignExplanation": "تم تصميمه وتطويره بناءً على أنماط ومبادئ التصميم المُقاد بالمجال DDD. يوفر نموذجًا متعدد الطبقات لتطبيقك.",
"Authorization": "تفويض",
- "AuthorizationExplanation": "إذن متقدم مع نظام أذونات مستخدم ودور ودقيق. مبني على مكتبة هوية Microsoft.",
- "MultiTenancy": "متعدد الايجار",
- "MultiTenancyExplanationShort": "جعلت تطبيقات SaaS سهلة! متعدد الإيجارات المتكاملة من قاعدة البيانات إلى واجهة المستخدم.",
+ "AuthorizationExplanation": "إذن متقدم وبالغ الدقة مع نظام أذونات للمستخدم والأدوار. مبني على مكتبة Microsoft Identity.",
+ "MultiTenancy": "التعددية",
+ "MultiTenancyExplanationShort": "جعلت تطبيقات SaaS سهلة! تعددية بشكل متكامل من قاعدة البيانات إلى واجهة المستخدم.",
"CrossCuttingConcerns": "اهتمامات مشتركة",
"CrossCuttingConcernsExplanationShort": "بنية تحتية كاملة للتفويض والتحقق من الصحة ومعالجة الاستثناءات والتخزين المؤقت وتسجيل التدقيق وإدارة المعاملات والمزيد.",
"BuiltInBundlingMinification": "تجميع وتصغير مدمج",
@@ -38,17 +38,17 @@
"VirtualFileSystem": "نظام الملفات الافتراضي",
"VirtualFileSystemExplanation": "قم بتضمين العروض والنصوص والأنماط والصور ... في الحزم/المكتبات وإعادة استخدامها في تطبيقات مختلفة.",
"Theming": "تصميم",
- "ThemingExplanationShort": "استخدم وتخصيص سمة واجهة المستخدم القياسية القائمة على التمهيد أو قم بإنشاء سمة خاصة بك.",
- "BootstrapTagHelpersDynamicForms": "أدوات مساعدة علامة التمهيد والنماذج الديناميكية",
- "BootstrapTagHelpersDynamicFormsExplanation": "بدلاً من كتابة التفاصيل المتكررة لمكونات التمهيد يدويًا ، استخدم مساعدي علامات ABP لتبسيطها والاستفادة من التحسس. أنشئ نماذج واجهة مستخدم بسرعة استنادًا إلى نموذج C # باستخدام مساعد علامة النموذج الديناميكي.",
- "HTTPAPIsDynamicProxies": "HTTP APIs & Dynamic Proxies",
+ "ThemingExplanationShort": "استخدم وقم بتخصيص سمة واجهة المستخدم القياسية القائمة على التمهيد أو قم بإنشاء سمة خاصة بك.",
+ "BootstrapTagHelpersDynamicForms": "علامات مساعدة من Bootstrap ونماذج ديناميكية",
+ "BootstrapTagHelpersDynamicFormsExplanation": "بدلاً من كتابة التفاصيل المتكررة لمكونات Bootstrap يدويًا ، استخدم مساعدي علامات ABP لتبسيطها والاستفادة من IntelliSense. أنشئ نماذج واجهة مستخدم بسرعة استنادًا إلى نموذج C# باستخدام مساعد علامة النموذج الديناميكي.",
+ "HTTPAPIsDynamicProxies": "HTTP APIs و Proxies ديناميكية",
"HTTPAPIsDynamicProxiesExplanation": "كشف خدمات التطبيقات تلقائيًا على أنها واجهات برمجة تطبيقات HTTP بنمط REST ، واستهلكها باستخدام وكلاء JavaScript و C # الديناميكيين.",
- "CompleteArchitectureInfo": "هندسة معمارية حديثة لإنشاء حلول برمجية قابلة للصيانة.",
- "DomainDrivenDesignBasedLayeringModelExplanation": "يساعدك على تنفيذ بنية طبقات تستند إلى DDD وبناء قاعدة رمز قابلة للصيانة.",
- "DomainDrivenDesignBasedLayeringModelExplanationCont": "يوفر قوالب بدء التشغيل ، والملخصات ، والفئات الأساسية ، والخدمات ، والوثائق والأدلة لمساعدتك على تطوير تطبيقك بناءً على أنماط ومبادئ DDD.",
+ "CompleteArchitectureInfo": "معمارية حديثة لإنشاء حلول برمجية قابلة للصيانة.",
+ "DomainDrivenDesignBasedLayeringModelExplanation": "يساعدك على تنفيذ بنية طبقات تستند إلى التصميم المُقاد بالمجال DDD وبناء قاعدة رمز قابلة للصيانة.",
+ "DomainDrivenDesignBasedLayeringModelExplanationCont": "يوفر عارضات بدء التشغيل، والملخصات، والفئات الأساسية، والخدمات، والمستندات والأدلة لمساعدتك على تطوير تطبيقك بناءً على أنماط ومبادئ التصميم المُقاد بالمجال DDD.",
"MicroserviceCompatibleModelExplanation": "تم تصميم إطار العمل الأساسي ووحدات ما قبل الإنشاء مع مراعاة بنية الخدمات المصغرة.",
- "MicroserviceCompatibleModelExplanationCont": "يوفر البنية التحتية وعمليات الدمج والعينات والوثائق لتنفيذ حلول الخدمات المصغرة بشكل أسهل ، في حين أنه لا يجلب تعقيدًا إضافيًا إذا كنت تريد تطبيقًا مترابطًا.",
- "ModularInfo": "يوفر ABP نظام وحدة نمطية يسمح لك بتطوير وحدات تطبيق قابلة لإعادة الاستخدام ، والربط بأحداث دورة حياة التطبيق ، والتعبير عن التبعيات بين الأجزاء الأساسية في نظامك.",
+ "MicroserviceCompatibleModelExplanationCont": "يوفر البنية التحتية وعمليات الدمج والعينات والمستندات لتنفيذ حلول الخدمات المصغرة بشكل أسهل ، في حين أنه لا يجلب تعقيدًا إضافيًا إذا كنت تريد تطبيقًا مترابطًا.",
+ "ModularInfo": "يوفر ABP نظام وحدة نمطية يسمح لك بتطوير وحدات تطبيق قابلة لإعادة الاستخدام، والربط بأحداث دورة حياة التطبيق، والتعبير عن التبعيات بين الأجزاء الأساسية في نظامك.",
"PreBuiltModulesThemes": "الوحدات والسمات المبنية مسبقًا",
"PreBuiltModulesThemesExplanation": "الوحدات والسمات مفتوحة المصدر والتجارية جاهزة للاستخدام في تطبيق عملك.",
"NuGetNPMPackages": "حزم NuGet و NPM",
@@ -56,7 +56,7 @@
"ExtensibleReplaceable": "قابل للتوسيع/قابل للاستبدال",
"ExtensibleReplaceableExplanation": "تم تصميم جميع الخدمات والوحدات النمطية في الاعتبار قابلية التوسع. يمكنك استبدال الخدمات والصفحات والأنماط والمكونات.",
"CrossCuttingConcernsExplanation2": "اجعل قاعدة الشفرة أصغر حتى تتمكن من التركيز على الكود الخاص بنشاطك التجاري.",
- "CrossCuttingConcernsExplanation3": "لا ترسل وقتًا لتنفيذ متطلبات التطبيق المشتركة في مشاريع متعددة.",
+ "CrossCuttingConcernsExplanation3": "لا تقضي وقتًا لتنفيذ متطلبات التطبيق المشتركة في مشاريع متعددة.",
"AuthenticationAuthorization": "المصادقة والتفويض",
"ExceptionHandling": "معالجة الاستثناء",
"Validation": "التحقق من الصحة",
@@ -81,22 +81,22 @@
"SeeAllFeatures": "انظر جميع الميزات",
"CLI_CommandLineInterface": "CLI (واجهة سطر الأوامر)",
"CLI_CommandLineInterfaceExplanation": "يتضمن CLI لمساعدتك على أتمتة إنشاء مشاريع جديدة وإضافة وحدات نمطية جديدة.",
- "StartupTemplates": "قوالب بدء التشغيل",
- "StartupTemplatesExplanation": "توفر قوالب بدء التشغيل المختلفة حلاً مهيئًا بالكامل لبدء التطوير السريع.",
- "BasedOnFamiliarTools": "بناءاً على أدوات معروفة",
- "BasedOnFamiliarToolsExplanation": "مصمم ومتكامل مع الأدوات الشائعة التي تعرفها بالفعل. منحنى تعليمي منخفض ، سهل التكيف ، تنمية مريحة.",
+ "StartupTemplates": "عارضات بدء التشغيل",
+ "StartupTemplatesExplanation": "توفر عارضات بدء التشغيل المختلفة حلاً مهيئًا بالكامل لبدء التطوير السريع.",
+ "BasedOnFamiliarTools": "بناءً على أدوات معروفة",
+ "BasedOnFamiliarToolsExplanation": "مصمم ومتكامل مع الأدوات الشائعة التي تعرفها بالفعل. منحنى تعليمي منخفض، سهل التكيف، تنمية مريحة.",
"ORMIndependent": "ORM مستقل",
- "ORMIndependentExplanation": "الإطار الأساسي هو ORM/قاعدة البيانات مستقل ويمكن أن يعمل مع أي مصدر بيانات. يتوفر مقدمو Entity Framework Core و MongoDB بالفعل.",
+ "ORMIndependentExplanation": "الإطار الأساسي هو ORM/قاعدة بيانات مستقل ويمكن أن يعمل مع أي مصدر بيانات. يتوفر مقدمو Entity Framework Core و MongoDB بالفعل.",
"Features": "استكشف ميزات إطار عمل ABP",
- "ABPCLI": "ABP CLI",
+ "ABPCLI": "ABP CLI واجهة سطر الأوامر",
"Modularity": "النمطية",
"BootstrapTagHelpers": "مساعدي علامة التمهيد",
"DynamicForms": "شاشات ديناميكية",
"BundlingMinification": "التجميع والتصغير",
"BackgroundJobs": "وظائف الخلفية",
"BackgroundJobsExplanation": "حدد فئات بسيطة لتنفيذ المهام في الخلفية كما هو الحال في قائمة الانتظار. استخدم مدير الوظائف المدمج أو ادمج مدير الوظائف الخاص بك. تكامل Hangfire و RabbitMQ متاحة بالفعل.",
- "DDDInfrastructure": "البنية التحتية DDD",
- "DomainDrivenDesignInfrastructure": "البنية التحتية للتصميم يحركها المجال",
+ "DDDInfrastructure": "بنية تحتية لـDDD",
+ "DomainDrivenDesignInfrastructure": "البنية التحتية للتصميم المُقاد بالمجال",
"AutoRESTAPIs": "واجهات برمجة تطبيقات Auto REST",
"DynamicClientProxies": "وكلاء العميل الديناميكي",
"DistributedEventBus": "حافلة الحدث الموزعة",
@@ -106,7 +106,7 @@
"ObjectToObjectMapping": "مطابقة كائن إلى كائن",
"ObjectToObjectMappingExplanation": "تجريد كائن لتعيين كائن مع تكامل AutoMapper.",
"EmailSMSAbstractions": "التجريد من البريد الإلكتروني والرسائل النصية القصيرة",
- "EmailSMSAbstractionsWithTemplatingSupport": "تجريدات البريد الإلكتروني والرسائل النصية القصيرة مع دعم القوالب",
+ "EmailSMSAbstractionsWithTemplatingSupport": "تجريدات البريد الإلكتروني والرسائل النصية القصيرة مع دعم العارضات",
"Localization": "التعريب",
"SettingManagement": "إدارة الإعدادات",
"ExtensionMethods": "طرق التمديد",
@@ -116,14 +116,14 @@
"DependencyInjectionByConventions": "حقن التبعية بالاتفاقيات",
"ABPCLIExplanation": "ABP CLI (واجهة سطر الأوامر) هي أداة سطر أوامر لإجراء بعض العمليات الشائعة للحلول المستندة إلى ABP.",
"ModularityExplanation": "يوفر ABP بنية أساسية كاملة لبناء وحدات التطبيق الخاصة بك والتي قد تحتوي على كيانات وخدمات وتكامل قواعد البيانات وواجهات برمجة التطبيقات ومكونات واجهة المستخدم وما إلى ذلك ..",
- "MultiTenancyExplanation": "لا يدعم إطار عمل ABP تطوير التطبيقات متعددة المستأجرين فحسب ، بل يجعل الكود الخاص بك في الغالب غير مدرك للتعددية المستأجرة.",
- "MultiTenancyExplanation2": "يمكن تحديد المستأجر الحالي تلقائيًا ، وعزل بيانات المستأجرين المختلفين عن بعضهم البعض.",
+ "MultiTenancyExplanation": "لا يدعم إطار عمل ABP تطوير التطبيقات المتعددة فحسب، بل يجعل الكود الخاص بك في الغالب غير مدرك للتعددية المستأجرة.",
+ "MultiTenancyExplanation2": "يمكن تحديد المستأجر الحالي تلقائيًا، وعزل بيانات المستأجرين المختلفين عن بعضهم البعض.",
"MultiTenancyExplanation3": "يدعم قاعدة بيانات واحدة وقاعدة بيانات لكل مستأجر ونهج مختلطة.",
"MultiTenancyExplanation4": "أنت تركز على رمز عملك وتدع إطار العمل للتعامل مع عقود الإيجار المتعددة نيابة عنك.",
- "BootstrapTagHelpersExplanation": "بدلاً من كتابة التفاصيل المتكررة لمكونات التمهيد يدويًا ، استخدم مساعدي علامات ABP لتبسيطها والاستفادة من التحسس. يمكنك بالتأكيد استخدام Bootstrap متى احتجت إليه.",
+ "BootstrapTagHelpersExplanation": "بدلاً من كتابة التفاصيل المتكررة لمكونات التمهيد يدويًا، استخدم مساعدي علامات ABP لتبسيطها والاستفادة من IntelliSense. يمكنك بالتأكيد استخدام Bootstrap متى احتجت إليه.",
"DynamicFormsExplanation": "يمكن لمساعدي النموذج الديناميكي وعلامات الإدخال إنشاء النموذج الكامل من فئة C # كنموذج.",
- "AuthenticationAuthorizationExplanation": "خيارات المصادقة والترخيص الغنية المدمجة في ASP.NET Core Identity & IdentityServer4. يوفر نظام إذن موسع ومفصل.",
- "CrossCuttingConcernsExplanation": "لا تكرر نفسك لتنفيذ كل هذه الأشياء الشائعة مرارًا وتكرارًا. ركز على رمز عملك ودع برنامج ABP يقوم بأتمتة ذلك من خلال الاتفاقيات.",
+ "AuthenticationAuthorizationExplanation": "خيارات المصادقة والرخصة الغنية المدمجة في ASP.NET Core Identity & IdentityServer4. يوفر نظام إذن موسع ومفصل.",
+ "CrossCuttingConcernsExplanation": "لا تكرر نفسك لتنفيذ كل هذه الأشياء الشائعة مرارًا وتكرارًا. ركز على كود عملك ودع ABP يقوم بأتمتة ذلك من خلال الاتفاقيات.",
"DatabaseConnectionTransactionManagement": "اتصال قاعدة البيانات وإدارة المعاملات",
"CorrelationIdTracking": "تتبع معرف الارتباط",
"BundlingMinificationExplanation": "تقدم ABP نظام تجميع وتقليل بسيط وديناميكي وقوي ونمطي ومدمج.",
@@ -165,18 +165,18 @@
"BasedLayeringModel": "نموذج الطبقات القائم",
"Microservice": "خدمة مصغرة",
"Compatible": "متوافق",
- "MeeTTheABPCommunityInfo": "مهمتنا هي خلق بيئة حيث يمكن للمطورين مساعدة بعضهم البعض بالمقالات ، والبرامج التعليمية ، ودراسات الحالة ، وما إلى ذلك ، ومقابلة أشخاص متشابهين في التفكير.",
- "JoinTheABPCommunityInfo": "انخرط في مجتمع نابض بالحياة وكن مساهمًا في إطار عمل برنامج ABP!",
+ "MeeTTheABPCommunityInfo": "مهمتنا هي صنع بيئة حيث يمكن للمطورين مساعدة بعضهم البعض بالمقالات، والبرامج التعليمية، ودراسات الحالة، وما إلى ذلك، ومقابلة أشخاص متشابهين في التفكير.",
+ "JoinTheABPCommunityInfo": "انخرط في منتدى نابض بالحياة وكن مساهمًا في إطار عمل ABP!",
"AllPosts": "جميع المقالات",
"SubmitYourPost": "إرسال مقالتك",
- "DynamicClientProxyDocument": "اطلع على وثائق الوكيل الديناميكي للعميل لـ JavaScript و C # .",
+ "DynamicClientProxyDocument": "اطلع على مستندات الوكيل الديناميكي للعميل لـ JavaScript و C # .",
"EmailSMSAbstractionsDocument": "راجع مستندات المراسلة بالبريد الإلكتروني و إرسال الرسائل القصيرة للحصول على مزيد من المعلومات.",
"CreateProjectWizard": "ينشئ هذا المعالج مشروعًا جديدًا من قالب بدء التشغيل الذي تم تكوينه بشكل صحيح لبدء الانتقال إلى مشروعك.",
"TieredOption": "ينشئ حلاً متدرجًا حيث يتم فصل طبقات واجهة برمجة تطبيقات الويب و Http فعليًا. إذا لم يتم تحديده ، يتم إنشاء حل متعدد الطبقات يكون أقل تعقيدًا ومناسبًا لمعظم السيناريوهات.",
"SeparateIdentityServerOption": "يفصل جانب الخادم إلى تطبيقين: الأول مخصص لخادم الهوية والآخر مخصص لواجهة برمجة تطبيقات HTTP من جانب الخادم.",
"UseslatestPreVersion": "يستخدم أحدث نسخة تجريبية",
- "ReadTheDocumentation": " اقرأ span> الوثائق span>",
- "Documentation": "توثيق",
+ "ReadTheDocumentation": " اقرأ span> المستندات span>",
+ "Documentation": "المستندات",
"GettingStartedTutorial": "برنامج تعليمي للشروع في العمل",
"ApplicationDevelopmentTutorial": "البرنامج التعليمي لتطوير التطبيقات",
"TheStartupTemplate": "نموذج بدء التشغيل",
@@ -195,50 +195,50 @@
"MultipleUIOptionsExplanation": "تم تصميم إطار العمل الأساسي كواجهة مستخدم مستقلة ويمكن أن يعمل مع أي نوع من أنظمة واجهة المستخدم ، في حين أن هناك العديد من الخيارات المدمجة والمبنية مسبقًا متوفرة خارج الصندوق.",
"MultipleDBOptionsExplanation": "يمكن أن يعمل إطار العمل مع أي مصدر بيانات ، بينما يتم تطوير ودعم مقدمي الخدمات التاليين رسميًا ؛",
"SelectLanguage": "اختار اللغة",
- "LatestPostOnCommunity": "أحدث مقال عن مجتمع ABP",
+ "LatestPostOnCommunity": "أحدث مقال عن منتدى ABP",
"Register": "يسجل",
"IsDownloadable": "قابل للتنزيل",
"DatabaseOptions": "خيارات قاعدة البيانات",
"BackToPackagesPage": "العودة إلى صفحة الباقات",
- "HowToInstall": "كيفية تثبيت",
+ "HowToInstall": "كيفية التثبيت",
"SeeOnNpm": "انظر على NPM",
- "SeeOnNuget": "انظر على نوجيت",
+ "SeeOnNuget": "انظر على Nuget",
"MVCGulpCommandExplanation": "إذا كنت تستخدم واجهة مستخدم MVC (Razor Pages) ، فقم بتشغيل الأمر \"gulp\" بعد تثبيت الحزمة.",
"UsingABPCLI": "استخدام Abp CLI ",
- "WithoutABPCLI": "بدون ABP CLI",
- "ABPCLIModuleDependency": "يضيف Abp Cli اعتمادًا على الوحدة تلقائيًا.",
+ "WithoutABPCLI": "بدون واجهة سطر الأوامر ABP CLI",
+ "ABPCLIModuleDependency": "يضيف واجهة سطر الأوامر ABP Cli اعتمادًا على الوحدة تلقائيًا.",
"AddModuleDependency": "ثم أضف تبعية الوحدة النمطية",
"Packages": "الحزم",
"NugetPackages": "حزم نوجيت",
"NPMPackages": "حزم NPM",
"SeeDocs": "انظر المستندات",
- "None": "لا أحد",
+ "None": "لا يوجد",
"Application": "تطبيق",
"Module": "وحدة",
"PackageName": "اسم الحزمة",
- "LicenseURL": "URL الترخيص",
+ "LicenseURL": "رابط الرخصة",
"License": "رخصة",
"ProjectCreationSuccessMessage": "تم إنشاء مشروعك بنجاح",
"HowToRunSolution": "كيف تدير الحل الخاص بك؟",
"GettingStartedMessage": "راجع مستند البدء لمعرفة كيفية تكوين الحل وتشغيله.",
"WebAppDevTutorial": "دروس تطوير تطبيقات الويب",
"WebAppDevTutorialMessage": "انظر وثيقة البرنامج التعليمي لتطوير تطبيقات الويب للحصول على عينة تطوير خطوة بخطوة.",
- "CommunityPosts": "مقالات المجتمع",
- "CommunityPostMessage": "تحقق من منصة مجتمع ABP لقراءة مقالات مفيدة حول إطار عمل ABP.",
+ "CommunityPosts": "مقالات المنتدى",
+ "CommunityPostMessage": "تحقق من منصة منتدى ABP لقراءة مقالات مفيدة حول إطار عمل ABP.",
"InvestigateSolutionDetails": "تحقق من تفاصيل الحل",
"StartupTemplateDocumentationMessage": "راجع مستند نموذج بدء تشغيل التطبيق للتعرف على بنية الحل وهيكله.",
"ClientSideDevelopment": "تطوير جانب العميل",
"ClientSideDevelopmentDocumentationMessage": "راجع مستند {0} للتعرف على النقاط الأساسية لتطوير واجهة المستخدم (جانب العميل).",
"DatabaseProviderDocumentationMessage": "راجع مستند {0} للتعرف على النقاط الرئيسية لتطوير طبقة قاعدة البيانات.",
"ABPCommercialExplanationMessage": "يوفر ABP Commercial وحدات وسمات وأدوات متميزة ودعمًا لإطار عمل ABP.",
- "ImplementingDDD": "تنفيذ تصميم يحركه المجال",
- "DDDBookExplanation": "دليل عملي لتنفيذ التصميم المستند إلى المجال باستخدام إطار عمل ABP.",
+ "ImplementingDDD": "تنفيذ التصميم المُقاد بالمجال",
+ "DDDBookExplanation": "دليل عملي لتنفيذ التصميم المُقاد بالمجال باستخدام إطار عمل ABP.",
"Overview": "ملخص",
- "DDDBookPracticalGuide": "هذا دليل عملي لتنفيذ التصميم المستند إلى المجال (DDD). بينما تستند تفاصيل التنفيذ إلى البنية الأساسية لـ ABP Framework ، يمكن تطبيق المفاهيم والمبادئ والنماذج الأساسية على أي حل ، حتى لو لم يكن حل .NET.",
+ "DDDBookPracticalGuide": "هذا دليل عملي لتنفيذ التصميم المُقاد بالمجال (DDD). بينما تستند تفاصيل التنفيذ إلى البنية الأساسية لإطار عمل ABP، يمكن تطبيق المفاهيم والمبادئ والنماذج الأساسية على أي حل، حتى لو لم يكن حل.NET.",
"TableOfContents": "جدول المحتويات",
- "IntroductionToImplementingDDD": "مقدمة في تنفيذ التصميم على أساس المجال",
- "WhatIsDDD": "ما هو المجال على أساس التصميم؟",
- "Implementation": "تطبيق",
+ "IntroductionToImplementingDDD": "مقدمة في تنفيذ التصميم المُقاد بالمجال",
+ "WhatIsDDD": "ما هو التصميم المُقاد بالمجال؟",
+ "Implementation": "التطبيق",
"TheBigPicture": "الصورة الكبيرة",
"TheBuildingBlock": "حجر الاساس",
"ExampleUseCase": "مثال على حالة الاستخدام",
@@ -256,10 +256,10 @@
"Surname": "اسم العائلة",
"CompanyName": "اسم الشركة",
"DoYouAgreePrivacyPolicy": "أوافق على البنود والشروط و سياسة الخصوصية .",
- "Free": "حر",
- "DDDEBook": "DDD الكتاب الإلكتروني",
- "PracticalGuideForImplementingDDD": "هذا الكتاب هو دليل عملي لتنفيذ التصميم المستند إلى المجال باستخدام إطار عمل ABP.",
- "IntroducingDDD": "تقديم تصميم يحركه المجال",
+ "Free": "مجاني",
+ "DDDEBook": "DDD الكتاب الإلكتروني التصميم المُقاد بالمجال",
+ "PracticalGuideForImplementingDDD": "هذا الكتاب هو دليل عملي لتنفيذ التصميم المُقاد بالمجال باستخدام إطار عمل ABP.",
+ "IntroducingDDD": "مقدمو للتصميم المُقاد بالمجال",
"DDDLayersAndCleanArchitecture": "طبقات DDD والعمارة النظيفة",
"LayeringOfADotnetSolution": "طبقات حل NET",
"ImplementingDDDBuildingBlocks": "تنفيذ اللبنات الأساسية DDD",
@@ -267,12 +267,143 @@
"SamplesAndDiscussions": "العينات والمناقشات",
"EmailNotValid": "رجاء قم بإدخال بريد الكتروني صحيح.",
"WeWillSendYouADownloadLink": "تم إرسال ارتباط لتنزيل الكتاب الإلكتروني إلى {0}. تحقق من صندوق الوارد الخاص بك ، البريد غير المرغوب فيه أو صناديق البريد العشوائي!",
- "GoHome": "اذهب للمنزل",
+ "GoHome": "اذهب للصفحة الرئيسية",
"InvalidFormInputs": "من فضلك ، اكتب المعلومات الصحيحة المحددة في النموذج.",
"DDDBookEmailBody": "شكرا لك.
لتنزيل كتابك ، انقر هنا .",
"SubscribeToNewsletter": "اشترك في النشرة الإخبارية للحصول على معلومات حول الأحداث في منصة ABP.IO ، مثل الإصدارات الجديدة والمقالات والعروض والمزيد.",
"FirstEdition": "الطبعة الأولى",
"ThankYou": "شكرا لك!",
- "CheckboxMandatory": "تحتاج إلى التحقق من هذا للمتابعة!"
+ "CheckboxMandatory": "تحتاج إلى التحقق من هذا للمتابعة!",
+ "UserInterface": "واجهة المستخدم",
+ "APIGateway": "بوابة API",
+ "Database": "قاعدة البيانات",
+ "Saas": "Saas",
+ "OpenSourceWebApp": "مفتوح المصدر
تطبيق ويب",
+ "Framework": "إطار العمل",
+ "AuditLoggingExplanation": "تتبع تلقائيًا جميع العمليات وتغييرات البيانات في نظامك.",
+ "AbpNewCommandExplanation": "ينشئ حلولًا جديدة باستخدام عارضات بدء تشغيل ABP.",
+ "AbpAddModuleCommandExplanation": "يثبت وحدات التطبيق سابقة الإنشاء على الحل الخاص بك",
+ "AbpUpdateCommandExplanation": "يقوم تلقائيًا بتحديث جميع حزم NuGet و NPM المتعلقة بـ ABP في الحل الخاص بك.",
+ "ExploreAllCLICommands": "استكشف جميع أوامر CLI",
+ "ExploreDocumentationAndGuides": "استكشف المستندات والأدلة الشاملة.",
+ "Documentations": "المستندات",
+ "Views": "المشاهدات",
+ "EnterYouEmailToGetNews": "أدخل بريدك الإلكتروني للحصول على آخر الأخبار حول إطار عمل ABP",
+ "Tiered": "متدرج",
+ "SeparateIdentityServer": "خادم هوية منفصل",
+ "ProgressiveWebApplication": "تطبيق ويب تقدمي",
+ "Preview": "معاينة",
+ "CreateANewSolution": "قم بإنشاء حل جديد",
+ "ABPFrameworkFeatures": "إطار عمل ABP ميزات",
+ "Commercial": "تجاري",
+ "ThirdPartyTools": "أدوات الطرف الثالث",
+ "Back": "عودة",
+ "Community": "المنتدى",
+ "SeeMore": "رؤية المزيد",
+ "DetailsOfTheEBook": "تفاصيل الكتاب الإلكتروني",
+ "JoinOurMarketingNewsletter": "انضم إلى النشرة الإخبارية التسويقية",
+ "FrameworkNewsletterConfirmationMessage": "أنا أوافق على البنود و الظروف وسياسة الخصوصية.",
+ "GetYourFreeEBook": "احصل على كتابك إلالكتروني المجاني DDD",
+ "EverythingYouNeedToKnow": "كل ما تحتاج إلى معرفته.",
+ "PreOrderNow": "اطلب مسبقًا الآن",
+ "UITheming": "سمات واجهة المستخدم",
+ "UIThemingExplanation": "أنشئ سمات وتخطيطات لواجهة مستخدم قابلة لإعادة الاستخدام أو استخدم أحد سمات واجهة المستخدم المبنية مسبقًا.",
+ "DataFilteringExplanation2": "تصفية تلقائيًا عند الاستعلام من قاعدة البيانات لتنفيذ أنماط بسهولة مثل الحذف الناعم والتعددية.",
+ "NeedHelp": "تحتاج مساعدة؟",
+ "GiveYourProjectAName": "امنح مشروعك اسما",
+ "SelectProjectType": "حدد نوع المشروع",
+ "SelectUIFramework": "حدد إطار عمل واجهة المستخدم",
+ "SelectDatabaseProvider": "حدد موفر قاعدة البيانات",
+ "SelectDatabaseManagementSystem": "حدد نظام إدارة قواعد البيانات",
+ "InstallingTheABPCLI": "تثبيت واجهة سطر الأوامر ABP CLI",
+ "CreateYourProjectNow": "أنشئ مشروعك الآن",
+ "OrderOn": "اطلب على {0}",
+ "DownloadFreeDDDBook": "تنزيل كتاب DDD المجاني",
+ "WhatIsABPFramework": "ما هو إطار عمل ABP؟",
+ "TenantDatabase": "قاعدة بيانات المستأجر {0}",
+ "SharedDatabase": "قاعدة بيانات مشتركة",
+ "ConnectionResolver": "محلل الاتصال",
+ "TenantBasedDataFilter": "عامل تصفية بيانات المستأجر",
+ "ApplicationCode": "كود التطبيق",
+ "TenantResolution": "قرار المستأجر",
+ "TenantUser": "مستخدم المستأجر {0}",
+ "CardTitle": "عنوان البطاقة",
+ "View": "العرض",
+ "Model": "النموذج",
+ "Email": "البريد الإلكتروني",
+ "Password": "كلمة المرور",
+ "Address": "العنوان",
+ "Gender": "نوع الجنس",
+ "Male": "ذكر",
+ "Female": "أنثى",
+ "Submit": "تسليم",
+ "Unspecified": "غير محدد",
+ "StaticFileMiddleware": "البرامج الوسيطة للملفات الثابتة",
+ "RazorViewEngine": "محرك عرض Razor",
+ "PhysicalFiles": "الملفات المادية (wwwroot)",
+ "EmbeddedFiles": "الملفات المضمنة (DLL)",
+ "DynamicFiles": "الملفات الديناميكية (الذاكرة)",
+ "BuildSolutionsWithAbp": "أنشئ حلول .NET قابلة للصيانة باتباع أفضل ممارسات تطوير البرامج باستخدام ABP.",
+ "BuyOnAmazon": "شراء على أمازون",
+ "BuyOnPackt": "شراء على باكت",
+ "Discounted": "مخفضة",
+ "MasteringAbpFramework_Book_KeyFeatures": "الميزات الرئيسية",
+ "MasteringAbpFramework_Book_Key_Features_Description_1": "قم ببناء حلول برمجية قوية وقابلة للصيانة وموحدة وقابلة للتطوير باستخدام ABP إطار عمل.",
+ "MasteringAbpFramework_Book_Key_Features_Description_2": "تعرف على كيفية تنفيذ مبادئ صلبة والتصميم المُقاد بالمجال في تطبيقات الويب الخاصة بك.",
+ "MasteringAbpFramework_Book_Key_Features_Description_3": "اكتشف كيف يعمل إطار عمل ABP على تسريع دورة التطوير عن طريق أتمتة المهام المتكررة.",
+ "MasteringAbpFramework_Book_Description": "وصف الكتاب",
+ "MasteringAbpFramework_Book_Description_Details_1": "ABP إطار عمل هو بنية أساسية كاملة لإنشاء تطبيقات ويب حديثة باتباع أفضل \n ممارسات واتفاقيات تطوير البرامج. مع إطار العمل والنظام البيئي عالي المستوى لـ ABP ، يمكنك\n تنفيذ مبدأ عدم تكرار نفسك (DRY) والتركيز على كود عملك.",
+ "MasteringAbpFramework_Book_Description_Details_2": "هذا الكتاب الذي كتبه مبتكر ABP إطار عمل، سيساعدك على اكتساب فهم كامل\n للإطار وتقنيات تطوير تطبيقات الويب الحديثة. من خلال التفسيرات خطوة بخطوة للمفاهيم الأساسية\n والأمثلة العملية، ستفهم متطلبات حل الويب الحديث وكيف يجعل ABP\n إطار عمل تطوير الحلول الخاصة بك أمرًا ممتعًا. ستكتشف المتطلبات\n المشتركة لتطوير تطبيقات الويب الخاصة بالمؤسسات واستكشاف البنية التحتية التي توفرها ABP. في جميع\n أنحاء الكتاب ، ستتعامل مع أفضل ممارسات تطوير البرامج لبناء حلول ويب قابلة للصيانة\n وقابلة للصيانة.",
+ "MasteringAbpFramework_Book_Description_Details_3": "بنهاية هذا الكتاب ، ستتمكن من إنشاء حل ويب كامل يسهل\n تطويره وصيانته واختباره.",
+ "MasteringAbpFramework_Book_WhatYouWillLearn": "ماذا ستتعلم",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_1": "قم بإعداد بيئة التطوير وابدأ مع إطار عمل ABP.",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_2": "اعمل مع Entity Framework Core وMongoDB لتطوير طبقة الوصول إلى البيانات الخاصة بك.",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_3": "فهم الاهتمامات المتداخلة وكيف تعمل ABP على أتمتة المهام المتكررة.",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_4": "تحكم في تنفيذ التصميم المستند إلى المجال باستخدام إطار عمل ABP.",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_5": "قم ببناء صفحات ومكونات واجهة المستخدم باستخدام ASP.NET Core MVC (Razor Pages) و Blazor.",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_6": "العمل مع التعددية لإنشاء تطبيقات ويب معيارية.",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_7": "افهم النموذجية وأنشئ وحدات تطبيق قابلة لإعادة الاستخدام.",
+ "MasteringAbpFramework_Book_What_You_Will_Learn_8": "اكتب اختبارات الوحدة والتكامل وواجهة المستخدم باستخدام إطار عمل ABP.",
+ "MasteringAbpFramework_Book_WhoIsThisBookFor": "لمن هذا الكتاب",
+ "MasteringAbpFramework_Book_WhoIsThisBookFor_Description": "هذا الكتاب مخصص لمطوري الويب الذين يرغبون في تعلم معماريات البرامج وأفضل الممارسات لبناء\n حلول قائمة على الويب يمكن صيانتها باستخدام تقنيات Microsoft وإطار عمل ABP. المعرفة الأساسية لـ C #\n و ASP.NET Core ضرورية للبدء في هذا الكتاب.",
+ "ComputersAndTechnology": "الكمبيوتر والتكنولوجيا",
+ "BuildingMicroserviceSolutions": "بناء حلول الخدمات المصغرة",
+ "MicroserviceBookPracticalGuide": "هذا الكتاب هو دليل مرجعي لتطوير وإدارة التطبيقات القائمة على الخدمات المصغرة باستخدام إطار عمل ABP. يشير إلى the تطبيق مرجعي لعينة خدمة مصغرة من .NET:: eShopOnContainers ويناقش التصميم المعماري وأساليب التنفيذ باستخدام إطار عمل ABP. بنهاية هذا الكتاب ، ستتعرف على كيفية تعامل ABP مع تعقيدات الخدمات المصغرة الشائعة مثل التفويض والمعاملات الموزعة والاتصالات بين الخدمات المصغرة والنشر وما إلى ذلك.",
+ "IntroducingTheSolution": "نقدم حل eShopOnAbp",
+ "RunningTheSolution": "تشغيل الحل",
+ "UnderstandingTheAuthenticationSystem": "فهم نظام المصادقة",
+ "ExploringTheApplications": "استكشاف التطبيقات",
+ "UnderstandingTheAPIGateways": "فهم بوابات API",
+ "DevelopingTheMicroservices": "تطوير الخدمات المصغرة",
+ "UnderstandingTheInfrastructure": "فهم البنية التحتية",
+ "DiggingInTheUseCases": "التنقيب في وقائع الاستخدام",
+ "DeployingTheSolution": "نشر الحل",
+ "ThisBookIsInDraftStageAndIsNotCompletedYet": "هذا الكتاب في مرحلة المسودة ولم يكتمل بعد.",
+ "Authors": "المؤلفون",
+ "MicroserviceEBook": "الكتاب الإلكتروني للخدمة المصغرة",
+ "SelectUITheme": "حدد سمة واجهة المستخدم",
+ "LeptonXLiteTheme": "LeptonX Lite سمة",
+ "BasicTheme": "Basic سمة",
+ "LeptonXLiteThemeInfo": " سمة Bootstrap UI عصرية وأنيقة. مثالي إذا كنت ترغب في الحصول على سمة واجهة مستخدم جاهزة للإنتاج. هذا هو أحدث موضوع وهو الافتراضي.",
+ "BasicThemeInfo": "سمة واجهة مستخدم بسيطة بألوان وأنماط Bootstrap بسيطة. مثالي إذا كنت ستقوم ببناء سمة واجهة المستخدم الخاصة بك.",
+ "SeeDocumentation": "انظر الى المستندات.",
+ "SeeFullScreen": "🖼️ انظر الى الصورة",
+ "BuildingMicroserviceSolutionsShortDescription": "هذا الكتاب هو دليل مرجعي لتطوير وإدارة التطبيقات القائمة على الخدمات المصغرة باستخدام إطار عمل ABP.",
+ "InstallAbpCliMessage": "قم بتثبيت واجهة سطر الأوامر ABP CLI في محطة سطر الأوامر، إذا لم تكن قد قمت بتثبيتها من قبل:",
+ "Terminal": "محطة سطر الأوامر",
+ "Copy": "نسخ",
+ "RunTheFollowingCommand": "قم بتشغيل الأمر التالي في محطة سطر الأوامر:",
+ "ChangeSolutionOptionsBelow": "يمكنك تغيير خيارات الحل أدناه.",
+ "MultiLayerApplication": "تطبيق
متعدد الطبقات",
+ "MultiLayerApplicationExplanation1": "يُنشئ حلاً متعدد الطبقات يعتمد على ممارسات التصميم المُقاد بالمجال.",
+ "MultiLayerApplicationExplanation2": "يوصى به للمشاريع طويلة الأجل التي تحتاج إلى قاعدة بيانات قابلة للصيانة والتوسيع.",
+ "SingleLayerApplication": "تطبيق
أحادي الطبقة",
+ "SingleLayerApplicationExplanation1": "يقوم بإنشاء تطبيق ويب أحادي الطبقة. ",
+ "SingleLayerApplicationExplanation2": "موصى به لبناء تطبيق بمعمارية أبسط وأسهل في الفهم.",
+ "ApplicationModule": "وحدة
التطبيق",
+ "SeeTheScreenshot": "انظر الى الصورة",
+ "Details": "التفاصيل",
+ "ApplicationModuleExplanation1": "إنشاء حل وحدة تطبيق ذات طبقات كاملة وقابلة لإعادة الاستخدام.",
+ "ApplicationModuleExplanation2": "يمكنك استخدام هذا الخيار لإنشاء وحدات نمطية لتطبيقك المعياري."
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
index c55dd947f3..751c5b6481 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
@@ -406,6 +406,29 @@
"SingleLayerApplicationExplanation1": "Creates a single-layer web application. ",
"SingleLayerApplicationExplanation2": "Recommended for building an application with a simpler and easy to understand architecture.",
"ApplicationModule": "Application
Module",
- "SeeTheScreenshot": "See the screenshot"
+ "SeeTheScreenshot": "See the screenshot",
+ "ApplicationModuleExplanation1": "Creates a reusable, fully layered application module solution.",
+ "ApplicationModuleExplanation2": "You can use this option to create modules for your modular application.",
+ "Expert": "ABP Expert",
+ "Expert_": "Expert",
+ "Partner": "ABP Partner",
+ "Partner_": "Partnership",
+ "WebSite": "Web Site",
+ "Industry": "Industry",
+ "Location": "Location",
+ "Contact": "Contact",
+ "ConsultantType": "Consultancy Type",
+ "Expert_Year": "Expertise Year",
+ "Partner_Year": "Partnership Year",
+ "SpokenLanguages": "Spoken Languages",
+ "SocialMedia": "Social Media",
+ "CompanyInfo": "Company Info",
+ "WhoWeAre": "Who We Are",
+ "RecentActivities": "Recent Activities",
+ "Date": "Date",
+ "Activity": "Activity",
+ "Type": "Type",
+ "Contribution": "Contribution",
+ "Info": "Info"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
index 76863f9e64..a6ef45db1d 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
@@ -372,8 +372,42 @@
"MasteringAbpFramework_Book_WhoIsThisBookFor": "这本书是给谁看的",
"MasteringAbpFramework_Book_WhoIsThisBookFor_Description": "本书适用于希望学习软件架构和最佳实践的 Web 开发人员,以使用 Microsoft 技术和 ABP 框架构建\n 可维护的基于 Web 的解决方案。 C#\n 和 ASP.NET Core 的基本知识是开始阅读本书所必需的。",
"ComputersAndTechnology": "计算机与技术",
+ "BuildingMicroserviceSolutions": "构建微服务解决方案",
+ "MicroserviceBookPracticalGuide": "本书是使用 ABP 框架开发和管理基于微服务的应用程序的参考指南。 它引用了 .NET 微服务示例参考应用程序:eShopOnContainers,并讨论了使用 ABP 框架的架构设计和实现方法。 读完本书,您将了解 ABP 如何处理常见的微服务复杂性,例如授权、分布式事务、微服务间通信、部署等。",
+ "IntroducingTheSolution": "介绍 eShopOnAbp 解决方案",
+ "RunningTheSolution": "运行解决方案",
+ "UnderstandingTheAuthenticationSystem": "了解身份验证系统",
+ "ExploringTheApplications": "探索应用",
+ "UnderstandingTheAPIGateways": "了解 API 网关",
+ "DevelopingTheMicroservices": "开发微服务",
+ "UnderstandingTheInfrastructure": "了解基础设施",
+ "DiggingInTheUseCases": "挖掘用例",
+ "DeployingTheSolution": "部署解决方案",
"ThisBookIsInDraftStageAndIsNotCompletedYet": "这本书正在草案阶段,还没有完成。",
- "SelectUITheme": "选择 UI 主题"
-
+ "Authors": "作者",
+ "MicroserviceEBook": "微服务电子书",
+ "SelectUITheme": "选择 UI 主题",
+ "LeptonXLiteTheme": "LeptonX Lite 主题",
+ "BasicTheme": "基本主题",
+ "LeptonXLiteThemeInfo": " 现代时尚的 Bootstrap UI 主题。 如果你想要一个生产就绪的 UI 主题,这是理想的选择。 这是最新的主题,也是默认主题。",
+ "BasicThemeInfo": "具有简单 Bootstrap 颜色和样式的极简 UI 主题。 如果您要构建自己的 UI 主题,则非常适合。",
+ "SeeDocumentation": "请参阅 文档.",
+ "SeeFullScreen": "🖼️ 如截图所示",
+ "BuildingMicroserviceSolutionsShortDescription": "本书是使用 ABP 框架开发和管理基于微服务的应用程序的参考指南。",
+ "InstallAbpCliMessage": "如果之前没有安装,请在命令行终端中安装 ABP CLI:",
+ "Terminal": "终端",
+ "Copy": "复制",
+ "RunTheFollowingCommand": "在命令行终端中运行以下命令:",
+ "ChangeSolutionOptionsBelow": "您可以更改下面的解决方案选项。",
+ "MultiLayerApplication": "多层
应用",
+ "MultiLayerApplicationExplanation1": "基于领域驱动设计实践创建完全分层的解决方案。",
+ "MultiLayerApplicationExplanation2": "推荐用于需要可维护和可扩展代码库的长期项目。",
+ "SingleLayerApplication": "单层
应用",
+ "SingleLayerApplicationExplanation1": "创建单层 Web 应用程序。 ",
+ "SingleLayerApplicationExplanation2": "推荐用于构建具有更简单且易于理解的体系结构的应用程序。",
+ "ApplicationModule": "应用程序模块",
+ "SeeTheScreenshot": "如截图所示",
+ "ApplicationModuleExplanation1": "创建可重用的、完全分层的应用程序模块解决方案。",
+ "ApplicationModuleExplanation2": "您可以使用此选项为您的模块化应用程序创建模块。"
}
-}
\ No newline at end of file
+}
diff --git a/cSpell.json b/cSpell.json
deleted file mode 100644
index dd74bfb264..0000000000
--- a/cSpell.json
+++ /dev/null
@@ -1,151 +0,0 @@
-{
- "version": "0.2",
- "language": "en",
- "words": [
- "ABP's",
- "abpframework",
- "Antiforgery",
- "appsettings",
- "aspnet",
- "aspnetcore",
- "Autofac",
- "automagically",
- "Blazor",
- "CQRS",
- "crossfade",
- "Dapr",
- "Datagrid's",
- "Datatable",
- "datepicker",
- "dismissable",
- "dockerized",
- "entrypoints",
- "findability",
- "hoverable",
- "Iddict",
- "IntelliCode",
- "Keysize",
- "Linq",
- "Microservices",
- "middlewares",
- "Minifier",
- "multitenancy",
- "multitenant",
- "Navs",
- "Newtonsoft",
- "Npgsql",
- "oidc",
- "overridable",
- "Parameterless",
- "Passwordless",
- "PKCE",
- "preconfigured",
- "proxying",
- "redirections",
- "scrollbars",
- "signin",
- "Templating",
- "textboxes",
- "toolset",
- "unsubscription",
- "Xunit"
- ],
- "ignoreWords": [
- "Aliyun",
- "Allibone",
- "Blazorise",
- "Boutwell",
- "Cmskit",
- "connectionstrings",
- "Devart",
- "Formik",
- "Halil",
- "Hanselman",
- "hikalkan",
- "Ibrahim",
- "İbrahim",
- "Kalkan",
- "Kirti",
- "Kommunity",
- "Kulkarni",
- "Luxon",
- "malihu",
- "Malik",
- "Masis",
- "Minio",
- "NGXS",
- "NSWAG",
- "Scriban",
- "Serilog",
- "Shoudly",
- "Shouldly",
- "Sweetalert",
- "Syncfusion",
- "Telerik",
- "Timeago",
- "Toastr",
- "Volo",
- "Volosoft",
- "Xeevis"
- ],
- "patterns": [
- {
- "name": "Markdown links",
- "pattern": "\\((.*)\\)",
- "description": ""
- },
- {
- "name": "Markdown code blocks",
- "pattern": "/^(\\s*`{3,}).*[\\s\\S]*?^\\1/gmx",
- "description": "Taken from the cSpell example at https://cspell.org/configuration/patterns/#verbose-regular-expressions"
- },
- {
- "name": "Inline code blocks",
- "pattern": "\\`([^\\`\\r\\n]+?)\\`",
- "description": "https://stackoverflow.com/questions/41274241/how-to-capture-inline-markdown-code-but-not-a-markdown-code-fence-with-regex"
- },
- {
- "name": "Link contents",
- "pattern": "\\",
- "description": ""
- },
- {
- "name": "Snippet references",
- "pattern": "-- snippet:(.*)",
- "description": ""
- },
- {
- "name": "Snippet references 2",
- "pattern": "\\<\\[sample:(.*)",
- "description": "another kind of snippet reference"
- },
- {
- "name": "Multi-line code blocks",
- "pattern": "/^\\s*```[\\s\\S]*?^\\s*```/gm"
- },
- {
- "name": "HTML Tags",
- "pattern": "<[^>]*>",
- "description": "Reference: https://stackoverflow.com/questions/11229831/regular-expression-to-remove-html-tags-from-a-string"
- },
- {
- "name": "Markdown Image",
- "pattern": "!\\[(.*)\\]\\((.*)\\)"
- }
- ],
- "ignoreRegExpList": [
- "Markdown links",
- "Markdown code blocks",
- "Inline code blocks",
- "Link contents",
- "Snippet references",
- "Snippet references 2",
- "Multi-line code blocks",
- "HTML Tags",
- "Markdown Image"
- ],
- "ignorePaths": [
- "**/*Release/Post.md",
- "**/*Preview/POST.md"
- ]
-}
diff --git a/common.props b/common.props
index f8b7c620c2..faab44ccd2 100644
--- a/common.props
+++ b/common.props
@@ -1,7 +1,7 @@
latest
- 7.0.0
+ 7.1.0
$(NoWarn);CS1591;CS0436
https://abp.io/assets/abp_nupkg.png
https://abp.io/
diff --git a/docs/en/Application-Startup.md b/docs/en/Application-Startup.md
index 1c5bf9b139..2839fcb19c 100644
--- a/docs/en/Application-Startup.md
+++ b/docs/en/Application-Startup.md
@@ -204,6 +204,7 @@ We've passed a lambda method to configure the `ApplicationName` option. Here's a
* `ApplicationName`: A human-readable name for the application. It is a unique value for an application.
* `Configuration`: Can be used to setup the [application configuration](Configuration.md) when it is not provided by the hosting system. It is not needed for ASP.NET Core and other .NET hosted applications. However, if you've used `AbpApplicationFactory` with an internal service provider, you can use this option to configure how the application configuration is built.
+* `Environment`: Environment name for the application.
* `PlugInSources`: A list of plugin sources. See the [Plug-In Modules documentation](PlugIn-Modules) to learn how to work with plugins.
* `Services`: The `IServiceCollection` object that can be used to register service dependencies. You generally don't need that, because you configure your services in your [module class](Module-Development-Basics.md). However, it can be used while writing extension methods for the `AbpApplicationCreationOptions` class.
@@ -253,6 +254,54 @@ The `IAbpApplication` interface extends the `IApplicationInfoAccessor` interface
`IAbpApplication` is disposable. Always dispose of it before exiting your application.
+## IAbpHostEnvironment
+
+Sometimes, while creating an application, we need to get the current hosting environment and take actions according to that. In such cases, we can use some services such as [IWebHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.iwebhostenvironment?view=aspnetcore-7.0) or [IWebAssemblyHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.webassembly.hosting.iwebassemblyhostenvironment) provided by .NET, in the final application.
+
+However, we can not use these services in a class library, which is used by the final application. ABP Framework provides the `IAbpHostEnvironment` service, which allows you to get the current environment name whenever you want. `IAbpHostEnvironment` is used by the ABP Framework in several places to perform specific actions by the environment. For example, ABP Framework reduces the cache duration on the **Development** environment for some services.
+
+`IAbpHostEnvironment` obtains the current environment name by the following order:
+
+1. Gets and sets the environment name if it's specified in the `AbpApplicationCreationOptions`.
+2. Tries to obtain the environment name from the `IWebHostEnvironment` or `IWebAssemblyHostEnvironment` services for ASP.NET Core & Blazor WASM applications if the environment name isn't specified in the `AbpApplicationCreationOptions`.
+3. Sets the environment name as **Production**, if the environment name is not specified or can not be obtained from the services.
+
+You can configure the `AbpApplicationCreationOptions` [options class](Options.md) while creating the ABP application and set an environment name to its `Environment` property. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `Environment` option as shown below:
+
+```csharp
+await builder.AddApplicationAsync(options =>
+{
+ options.Environment = Environments.Staging; //or directly set as "Staging"
+});
+```
+
+Then, whenever you need to get the current environment name or check the environment, you can use the `IAbpHostEnvironment` interface:
+
+```csharp
+public class MyDemoService
+{
+ private readonly IAbpHostEnvironment _abpHostEnvironment;
+
+ public MyDemoService(IAbpHostEnvironment abpHostEnvironment)
+ {
+ _abpHostEnvironment = abpHostEnvironment;
+ }
+
+ public void MyMethod()
+ {
+ var environmentName = _abpHostEnvironment.EnvironmentName;
+
+ if (_abpHostEnvironment.IsDevelopment()) { /* ... */ }
+
+ if (_abpHostEnvironment.IsStaging()) { /* ... */ }
+
+ if (_abpHostEnvironment.IsProduction()) { /* ... */ }
+
+ if (_abpHostEnvironment.IsEnvironment("custom-environment")) { /* ... */ }
+ }
+}
+```
+
## .NET Generic Host & ASP.NET Core Integrations
`AbpApplicationFactory` can create a standalone ABP application container without any external dependency. However, in most cases, you will want to integrate it with [.NET's generic host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) or ASP.NET Core. For such usages, ABP provides built-in extension methods to easily create an ABP application container that is well-integrated to these systems.
diff --git a/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md b/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md
new file mode 100644
index 0000000000..73a0d7589c
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md
@@ -0,0 +1,129 @@
+# How to add a custom grant type in OpenIddict
+
+## ITokenExtensionGrant
+
+Create a class that inherits `ITokenExtensionGrant`, and then register it with the framework.
+
+In the `MyTokenExtensionGrant` class below we try to get the token details, The `ForbidResult` handles the failure case and `SignInResult` returns a new token response, You can pass more parameters to implement business checks.
+
+```cs
+public class MyTokenExtensionGrant : ITokenExtensionGrant
+{
+ public const string ExtensionGrantName = "MyTokenExtensionGrant";
+
+ public string Name => ExtensionGrantName;
+ public async Task HandleAsync(ExtensionGrantContext context)
+ {
+ var userToken = context.Request.GetParameter("token").ToString();
+
+ if (string.IsNullOrEmpty(userToken))
+ {
+ return new ForbidResult(
+ new[] {OpenIddictServerAspNetCoreDefaults.AuthenticationScheme},
+ properties: new AuthenticationProperties(new Dictionary
+ {
+ [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest
+ }!));
+ }
+
+ var transaction = await context.HttpContext.RequestServices.GetRequiredService().CreateTransactionAsync();
+ transaction.EndpointType = OpenIddictServerEndpointType.Introspection;
+ transaction.Request = new OpenIddictRequest
+ {
+ ClientId = context.Request.ClientId,
+ ClientSecret = context.Request.ClientSecret,
+ Token = userToken
+ };
+
+ var notification = new OpenIddictServerEvents.ProcessAuthenticationContext(transaction);
+ var dispatcher = context.HttpContext.RequestServices.GetRequiredService();
+ await dispatcher.DispatchAsync(notification);
+
+ if (notification.IsRejected)
+ {
+ return new ForbidResult(
+ new []{ OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
+ properties: new AuthenticationProperties(new Dictionary
+ {
+ [OpenIddictServerAspNetCoreConstants.Properties.Error] = notification.Error ?? OpenIddictConstants.Errors.InvalidRequest,
+ [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = notification.ErrorDescription,
+ [OpenIddictServerAspNetCoreConstants.Properties.ErrorUri] = notification.ErrorUri
+ }));
+ }
+
+ var principal = notification.GenericTokenPrincipal;
+ if (principal == null)
+ {
+ return new ForbidResult(
+ new []{ OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
+ properties: new AuthenticationProperties(new Dictionary
+ {
+ [OpenIddictServerAspNetCoreConstants.Properties.Error] = notification.Error ?? OpenIddictConstants.Errors.InvalidRequest,
+ [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = notification.ErrorDescription,
+ [OpenIddictServerAspNetCoreConstants.Properties.ErrorUri] = notification.ErrorUri
+ }));
+ }
+
+ var userId = principal.FindUserId();
+ var userManager = context.HttpContext.RequestServices.GetRequiredService();
+ var user = await userManager.GetByIdAsync(userId.Value);
+ var userClaimsPrincipalFactory = context.HttpContext.RequestServices.GetRequiredService>();
+ var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
+ claimsPrincipal.SetScopes(principal.GetScopes());
+ claimsPrincipal.SetResources(await GetResourcesAsync(context, principal.GetScopes()));
+ await context.HttpContext.RequestServices.GetRequiredService().SetAsync(principal);
+ return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);
+ }
+
+ private async Task> GetResourcesAsync(ExtensionGrantContext context, ImmutableArray scopes)
+ {
+ var resources = new List();
+ if (!scopes.Any())
+ {
+ return resources;
+ }
+
+ await foreach (var resource in context.HttpContext.RequestServices.GetRequiredService().ListResourcesAsync(scopes))
+ {
+ resources.Add(resource);
+ }
+ return resources;
+ }
+}
+```
+
+```cs
+public override void PreConfigureServices(ServiceConfigurationContext context)
+{
+ //...
+ PreConfigure(builder =>
+ {
+ builder.Configure(openIddictServerOptions =>
+ {
+ openIddictServerOptions.GrantTypes.Add(MyTokenExtensionGrant.ExtensionGrantName);
+ });
+ });
+ //...
+}
+
+public override void ConfigureServices(ServiceConfigurationContext context)
+{
+ //...
+ Configure(options =>
+ {
+ options.Grants.Add(MyTokenExtensionGrant.ExtensionGrantName, new MyTokenExtensionGrant());
+ });
+ //...
+}
+```
+
+
+
+
+
+
+## Source code
+
+https://github.com/abpframework/abp/commit/3210f138454697647689b4868c8d4b7b3da02d44
+
+
diff --git a/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman1.png b/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman1.png
new file mode 100644
index 0000000000..1e2c7535c5
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman1.png differ
diff --git a/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman2.png b/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman2.png
new file mode 100644
index 0000000000..c3dbb7324a
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/postman2.png differ
diff --git a/docs/en/Community-Articles/2022-11-22-The-new-EF-Core-interceptors/POST.md b/docs/en/Community-Articles/2022-11-22-The-new-EF-Core-interceptors/POST.md
new file mode 100644
index 0000000000..4539d81891
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-22-The-new-EF-Core-interceptors/POST.md
@@ -0,0 +1,47 @@
+# The new EF Core interceptors
+
+## Interceptors
+
+EF Core 7 has made a lot of enhancements to interceptors, You can see the list from [EF Core improved interceptors](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#new-and-improved-interceptors-and-events).
+
+* Interception for creating and populating new entity instances (aka "materialization")
+* Interception to modify the LINQ expression tree before a query is compiled
+* Interception for optimistic concurrency handling (DbUpdateConcurrencyException)
+* Interception for connections before checking if the connection string has been set
+* Interception for when EF Core has finished consuming a result set, but before that result set is closed
+* Interception for the creation of a DbConnection by EF Core
+* Interception for DbCommand after it has been initialized
+
+## Lazy initialization of `connection string`
+
+You generally don't need to use [this](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#lazy-initialization-of-a-connection-string) feature, ABP has its own [connection string feature](https://docs.abp.io/en/abp/latest/Connection-Strings).
+
+The framework will automatically handle the module or multi-tenant connection string
+
+## Add interceptors in `AbpDbContext`
+
+[Add interceptors](https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/interceptors#registering-interceptors) is very simple, Add your `interceptors` in the `OnConfiguring` method of `DbContext`
+
+````csharp
+public class BookStoreDbContext : AbpDbContext,
+{
+
+ public BookStoreDbContext(DbContextOptions options)
+ : base(options)
+ {
+
+ }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ optionsBuilder.AddInterceptors(new MyEfCorenterceptor());
+
+ base.OnConfiguring(optionsBuilder);
+ }
+}
+````
+
+> Some interceptors may be [Singleton](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0#service-lifetimes) services. This means a single instance is used by many `DbContext` instances. The implementation must be thread-safe.
+
+
+See the [EF Core Interceptors documentation](https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/interceptors) for more information.
diff --git a/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/POST.md b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/POST.md
new file mode 100644
index 0000000000..9663ac1f83
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/POST.md
@@ -0,0 +1,197 @@
+# gRPC - JSON Transcoding
+
+In this article, I'll show you one of the new features that came with .NET 7: **JSON Transcoding**.
+
+> I've created a community article to highlight some interesting features (briefly) that are now available with the release of .NET 7. You can check it from [here](https://community.abp.io/posts/whats-new-with-.net-7-tlq2g43w).
+
+## What is gRPC? What are the pros and cons?
+
+[gRPC](https://grpc.io/) is a high-performance RPC framework and uses HTTP/2 and Protobuf (protocol buffers).
+
+### gRPC - Advantages
+
+* **Lightweight messages**: Payload is much smaller than JSON.
+* **High performance**: Much faster than REST and JSON communication.
+* **Faster serialization/deserialization**: Protobufs are binary encoded. This means that it will use fewer CPU cycles to serialize/deserialize the messages.
+
+### gRPC - Disadvantages
+
+* **Lack of maturity**
+* **Limited browser support - hard to test (in a way)**: Since gRPC relies on HTTP/2, you can’t call a gRPC service from a web browser directly. This is where JSON Transcoding comes into play.
+
+## What is JSON Transcoding?
+
+Despite the benefits that gRPC provides, REST APIs have an important place in modern web applications. We can easily create, expose and test them.
+
+On the other hand, gRPC services are not easy to test as mentioned above. We can’t access a gRPC service yet through the browser since browsers don’t understand binary protocols. We need to use some tools to access endpoints, such as [gRPCurl](https://github.com/fullstorydev/grpcurl).
+
+
+
+JSON Transcoding is an extension for ASP.NET Core that creates RESTful JSON APIs for gRPC services and overcomes this problem. You can see an illustration down below:
+
+
+
+By using JSON Transcoding, we can expose our gRPC services and access them via HTTP call. Let's see it in action.
+
+## Creating & Exposing gRPC Services in ABP Based Applications
+
+> You can find the source code of the application at https://github.com/EngincanV/abp-grpc-json-transcoding.
+
+### Creating the Application
+
+Create an empty folder, open a command-line terminal and type the following command in the terminal window to create a new ABP solution using the ABP CLI:
+
+```csharp
+abp new BookStore -t app --ui angular --preview
+```
+
+I've created an application with the Angular UI, but the UI is not important for this tutorial, you can select your favorite UI.
+
+> **Note:** Your application should be ABP 7.0+.
+
+### Configuring gRPC & JSON Transcoding
+
+In this solution, `*.HttpApi.Host` is the project that configures and runs the server-side application. So, we will make changes in that project.
+
+First, we need to add two packages ([Grpc.AspNetCore](https://www.nuget.org/packages/Grpc.AspNetCore) and [Microsoft.AspNetCore.Grpc.JsonTranscoding](https://www.nuget.org/packages/Microsoft.AspNetCore.Grpc.JsonTranscoding)) into this project with the following commands:
+
+```bash
+dotnet add package Grpc.AspNetCore
+dotnet add package Microsoft.AspNetCore.Grpc.JsonTranscoding
+```
+
+Then, add the [*google/api/http.proto*](https://github.com/dotnet/aspnetcore/blob/main/src/Grpc/JsonTranscoding/test/testassets/Sandbox/google/api/http.proto) and [*google/api/annotations.proto*](https://github.com/dotnet/aspnetcore/blob/main/src/Grpc/JsonTranscoding/test/testassets/Sandbox/google/api/annotations.proto) files under to the **google/api** folder. These files are required for JSON Transcoding and contain all the stuff related to that.
+
+
+
+Then, open the module class (`BookStoreHttpApiHostModule.cs`) and update it as below:
+
+```csharp
+public class BookStoreHttpApiHostModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ // other configurations...
+
+ context.Services.AddGrpc().AddJsonTranscoding(); //add this line
+
+ }
+
+ public override void OnApplicationInitialization(ApplicationInitializationContext context)
+ {
+ var app = context.GetApplicationBuilder();
+
+ // other middlewares...
+
+ app.UseConfiguredEndpoints(builder =>
+ {
+ builder.MapGrpcService(); //add this line
+ });
+ }
+}
+```
+
+* Here, we've registered the related gRPC services into the DI container by calling the `AddGdpr()` and `AddJsonTranscoding()` methods.
+* Also, we've defined our gRPC service: `BookAppService`. We have not created this service yet and will create it in the next section.
+
+So far, we've added the required packages to our project and made the related configurations. Now we can start creating our gRPC services.
+
+### Implementing gRPC Services
+
+Create a **Protos** folder and define a `bookstore.proto` file in it. Then fill it with the below content:
+
+```proto
+syntax = "proto3";
+
+import "google/api/annotations.proto"; //import related Google APIs
+
+option csharp_namespace = "BookStore";
+
+message Book {
+ string title = 1;
+ string author = 2;
+ int32 page_count = 3;
+ optional string language = 4;
+}
+
+message GetBookListRequest {}
+
+message GetBookListResponse { repeated Book books = 1; }
+
+service BookApp {
+ rpc GetBookList(GetBookListRequest) returns (GetBookListResponse) {
+ option (google.api.http) = {
+ get: "/v1/book-store/books" //expose from this url
+ };
+ }
+}
+```
+
+* Here, we've defined the `bookstore.proto` file. You can consider this file as a contract that the server and client have agreed on.
+* An important point here, we've imported the `google/api/annotations.proto` (we added this file under the **google/api** folder in the previous section) and by using the `google.api.http` we are exposing this gRPC service (with the specified URL).
+* So, when we run the application we can send a request to that endpoint and see the result.
+* Thanks to JSON Transcoding, we don't need to use any other tools to test our gRPC services anymore!
+
+Let's mark the `bookstore.proto` file as our protobuf file in the `*.HttpApi.Host.csproj` file:
+
+```xml
+
+
+
+```
+
+For the final step, we just need to define a service that corresponds to the service that we've defined in the `bookstore.proto` file. Voilà 🎉!
+
+So, create a new class named `BookAppService` under the `Services` folder:
+
+```csharp
+using System.Threading.Tasks;
+using Grpc.Core;
+
+namespace BookStore.Services;
+
+public class BookAppService : BookApp.BookAppBase
+{
+ //notice: we did not create GetBookListResponse and GetBookListRequest classes
+ //Grpc.AspNetCore package did behalf of us
+ public override Task GetBookList(GetBookListRequest request, ServerCallContext context)
+ {
+ var response = new GetBookListResponse();
+ response.Books.Add(new Book
+ {
+ Title = "The Hitchhiker's Guide to the Galaxy",
+ Author = "Douglas Adams",
+ PageCount = 42
+ });
+ response.Books.Add(new Book
+ {
+ Title = "The Lord of the Rings",
+ Author = "J.R.R. Tolkien",
+ PageCount = 1234
+ });
+
+ return Task.FromResult(response);
+ }
+}
+```
+
+* Here, as you notice we've inherited our service from the `BookAppBase` class. You might wonder where it came from.
+* The **Grpc.AspNetCore** package creates auto-generated classes from our protobuf file (`bookstore.proto`). We just need to inherit from `BookAppService` class (it's matched with the service name that we've defined in protobuf file), override the `GetBookList` method and implement it. That's it.
+* Also, notice we did not create the other classes such as `Book`, `GetBookListRequest` and `GetBookListResponse`. These are all auto-generated from our protobuf file.
+
+We've created a gRPC service and now let's run our application (before running the application, run the `*.DbMigrator` project to create the database and seed the initial data) and see it in action.
+
+
+
+## Conclusion
+
+In this article, I've briefly introduced the JSON Transcoding feature that was shipped with .NET 7 and showed it in an ABP Based application.
+
+> See the [gRPC JSON transcoding in ASP.NET Core gRPC apps](https://learn.microsoft.com/en-us/aspnet/core/grpc/json-transcoding?view=aspnetcore-7.0) documentation for more information.
+
+## References
+
+* https://devblogs.microsoft.com/dotnet/announcing-grpc-json-transcoding-for-dotnet/
+* https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#json-transcoding
+* https://learn.microsoft.com/en-us/aspnet/core/grpc/json-transcoding?view=aspnetcore-7.0
+* https://sahansera.dev/building-grpc-server-dotnet/
diff --git a/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/google-api-folders.png b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/google-api-folders.png
new file mode 100644
index 0000000000..7719dc3915
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/google-api-folders.png differ
diff --git a/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/grpc-result.png b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/grpc-result.png
new file mode 100644
index 0000000000..e43d06e745
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/grpc-result.png differ
diff --git a/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/grpcurl.png b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/grpcurl.png
new file mode 100644
index 0000000000..053629d04d
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/grpcurl.png differ
diff --git a/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/json-transcoding.png b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/json-transcoding.png
new file mode 100644
index 0000000000..a80c7e3bb1
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-22-gRPC-JSON-Transcoding/json-transcoding.png differ
diff --git a/docs/en/Community-Articles/2022-11-23-Signalr-client-results/POST.md b/docs/en/Community-Articles/2022-11-23-Signalr-client-results/POST.md
new file mode 100644
index 0000000000..f2b9c30c7d
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-23-Signalr-client-results/POST.md
@@ -0,0 +1,71 @@
+# Signalr Client results
+
+ASP.NET Core 7 supports [requesting a result from a client](https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#signalr), in this article, we will show you how to use client results with the ABP framework.
+
+## Create a SignalR hub
+
+```csharp
+public class ChatHub : AbpHub
+{
+ public async Task WaitForMessage(string connectionId)
+ {
+ var message = await Clients.Client(connectionId).InvokeAsync("GetMessage");
+ return message;
+ }
+}
+```
+
+* ChatHub inherits from `AbpHub` that has useful base properties like `CurrentUser`.
+* Define the `WaitForMessage` method to call the client's `GetMessage` method and get the return value.
+
+> Using `InvokeAsync` from a Hub method requires setting the [MaximumParallelInvocationsPerClient](https://learn.microsoft.com/en-us/aspnet/core/signalr/configuration?view=aspnetcore-7.0&tabs=dotnet#configure-server-options) option to a value greater than 1.
+
+## Client
+
+Clients should return results in their `.On(...)` handlers.
+
+### .NET Client
+
+```csharp
+hubConnection.On("GetMessage", async () =>
+{
+ Console.WriteLine("Enter message:");
+ var message = await Console.In.ReadLineAsync();
+ return message;
+});
+```
+
+### JavaScript client
+
+```js
+connection.on("GetMessage", function () {
+
+ const message = prompt("Enter message:");
+ return message;
+});
+```
+
+## Strongly-typed hubs
+
+We can use strongly-typed instead of `InvokeAsync` by inheriting from `AbpHub` or `Hub`:
+
+```csharp
+public interface IClient
+{
+ Task GetMessage();
+}
+
+public class ChatHub : AbpHub
+{
+ public async Task WaitForMessage(string connectionId)
+ {
+ string message = await Clients.Client(connectionId).GetMessage();
+ return message;
+ }
+}
+```
+
+## See also:
+
+* [ABP SignalR-Integration documentation](https://docs.abp.io/en/abp/latest/SignalR-Integration)
+* [Microsoft client-results documentation](https://learn.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-7.0#client-results)
diff --git a/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/Post.md b/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/Post.md
new file mode 100644
index 0000000000..bc3050e071
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/Post.md
@@ -0,0 +1,139 @@
+# Inheritance Strategies in Entity Framework Core 7
+
+In this article, I'll show you all the object mapping strategies of EF Core and especially the new one: **TPC Inheritance Mapping**. TPC mapping is the new feature introduced with Entity Framework Core 7. It's called Table Per Concrete type inheritance mapping. I'll explain the EF Core's inheritance mapping strategies with how the inherited entity and the specific information are saved into a relational database. I'll also explain which strategy is best for your case.
+
+By default, EF Core maps an inheritance hierarchy of .NET types to a single database table. And it's called TPH (table-per-hierarchy) mapping.
+
+In the previous versions EF Core , Table Per Hierarchy (TPH) or Table Per Type (TPT) were supported. And now TPC came!
+
+To explain in a more clear way, I'll use the following entity model.
+`Car`, `Bus` and `Motorcylce` are inherited from the `Vehicle` object.
+
+```csharp
+public abstract class Vehicle
+{
+ public int Id { get; set; }
+ public string VehicleModel { get; set; }
+}
+
+//////////////////////////////////////
+
+public class Car : Vehicle
+{
+ public string Segment { get; set; }
+}
+
+//////////////////////////////////////
+
+public class Bus : Vehicle
+{
+ public int? SeatCount { get; set; }
+}
+
+//////////////////////////////////////
+
+public class Motorcycle : Vehicle
+{
+ public int CylinderCount { get; set; }
+}
+```
+
+
+
+
+
+Here's the list of available strategies to map `Car`, `Bus` and `Motorcylce`:
+
+## TPH (Table Per Hierarchy)
+
+TPH maps an inheritance hierarchy of .NET types to **a single database table**. So a single table is created for all types. This is the default behavior of EF Core. If you explicitly want to use this strategy, you can write `UseTphMappingStrategy()` to the root entity in the `OnModelCreating()` method in the `DbContext` class. As you see from the following table, it adds an extra column, `Discriminator` to separate the `Car`, `Bus` and `Motorcycle`. This way of persistence is an unnormalized form.
+
+```sql
+CREATE TABLE [Vehicles] (
+ [Id] int NOT NULL IDENTITY,
+ [Discriminator] nvarchar(max) NOT NULL, -- "Car", "Bus", "Motorcylce"
+ [VehicleModel] nvarchar(max) NOT NULL,
+ [Segment] nvarchar(max) NULL,
+ [SeatCount] int NULL,
+ [CylinderCount] int NOT NULL
+);
+```
+
+## TPT (Table per Type)
+
+With the TPT strategy, a different table is created for every type. The table itself is used to determine the type of the object saved, and each table contains only columns for the properties of that type. The data is saved in multiple tables, and this way of persistence is normalized form.
+
+```sql
+CREATE TABLE [Vehicles] (
+ [Id] int NOT NULL IDENTITY,
+ [VehicleModel] nvarchar(max) NOT NULL
+);
+
+CREATE TABLE [Car] (
+ [Id] int NOT NULL,
+ [Segment] nvarchar(max) NULL
+);
+
+CREATE TABLE [Bus] (
+ [Id] int NOT NULL,
+ [SeatCount] int NULL,
+);
+
+CREATE TABLE [Motorcycle] (
+ [Id] int NOT NULL,
+ [CylinderCount] int NOT NULL
+);
+```
+
+If you explicitly want to use this strategy, you can write `UseTptMappingStrategy()` to the root entity in the `OnModelCreating()` method in the `DbContext` class.
+
+## TPC (Table Per Concrete type)
+
+It's the new strategy that has been introduced with EF Core 7. In this way, a different table is created for each concrete type. While in the TPT, the table itself indicates the type of the object saved, in TPC, each table contains columns for every property in the concrete type *and its base types*. As you see from the following tables, no `Vehicles` table is being created. The `VehicleModel` field is common in each table.
+
+
+
+```sql
+CREATE TABLE [Car] (
+ [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [VehicleIds]),
+ [VehicleModel] nvarchar(max) NOT NULL, -- common field for each type
+ [Segment] nvarchar(max) NULL
+);
+
+CREATE TABLE [Bus] (
+ [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [VehicleIds]),
+ [VehicleModel] nvarchar(max) NOT NULL, -- common field for each type
+ [SeatCount] int NULL
+);
+
+CREATE TABLE [Motorcycle] (
+ [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [VehicleIds]),
+ [VehicleModel] nvarchar(max) NOT NULL, -- common field for each type
+ [CylinderCount] int NOT NULL
+);
+```
+
+This strategy maps each .NET type to **a different database table**. You write `UseTpcMappingStrategy()` to the root entity in the root entity in the `OnModelCreating()` method in the `DbContext` class.
+
+
+
+## How do you choose which mapping type is best for you?
+
+* **TPH:** In most cases, the TPH mapping is a good choice which is the default one. This way, the table columns increase because all the properties are saved in the same table, but it's easy to query in a single table.
+
+* **TPT** In this form, data is persisted in a normalized form. For this reason, you can choose TPT, but generally, it's not being used. But the disadvantage is when you filter by `VehicleModel` column, it must join `Vehicles`, `Bus`, `Car`, `Motorcycle` tables. So it's not a feasible fetching way.
+* **TPC:** This new strategy is similar to TPT but resolves some of the TPT problems. Because it stores the data in its own table, it doesn't divide across multiple tables. This option can be chosen when the mapped hierarchy is large and has many concrete types, each with many properties.
+
+For example, if `Car`, `Bus`, or `Motorcycle` have 20 properties for each type, then it's better to store them in a separate table, so we should use TPC. But if these entities have only 3-5 properties, then TPH is the way to go.
+
+
+
+> ### Jeremy Likness says;
+>
+> - If your code mostly queries entities of a single leaf type, then use TPC because the storage requirements are smaller since there are no null columns and no discriminator no index is ever needed on the discriminator column, which would slow down updates and possibly also queries. An index may not be needed when using TPH either, but that depends on various factors. If your code mostly queries for entities of many types, such as writing queries against the base type, lean towards TPH. If your database system supports it (for example SQL Server), then consider using sparse columns for columns that will be rarely populated. Use TPT only if constrained to do so by external factors.
+
+
+
+*References:*
+
+* https://devblogs.microsoft.com/dotnet/announcing-ef7-preview5/#primary-keys
\ No newline at end of file
diff --git a/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/banner.png b/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/banner.png
new file mode 100644
index 0000000000..df09d819de
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/banner.png differ
diff --git a/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/inheritance.png b/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/inheritance.png
new file mode 100644
index 0000000000..63fe5da1ab
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-25-EFCore-Mapping-Strategies/inheritance.png differ
diff --git a/docs/en/Community-Articles/2022-11-25-JSON-columns/Database.png b/docs/en/Community-Articles/2022-11-25-JSON-columns/Database.png
new file mode 100644
index 0000000000..c8a5516aa6
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-25-JSON-columns/Database.png differ
diff --git a/docs/en/Community-Articles/2022-11-25-JSON-columns/post.md b/docs/en/Community-Articles/2022-11-25-JSON-columns/post.md
new file mode 100644
index 0000000000..4f69b4eb1f
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-25-JSON-columns/post.md
@@ -0,0 +1,134 @@
+# JSON Columns in Entity Framework Core 7
+
+In this article, we will see how to use the new **JSON Columns** features that came with EF Core 7 in an ABP based application (with examples).
+
+## JSON Columns
+
+Most relational databases support columns that contain JSON documents. The JSON in these columns can be drilled into with queries. This allows, for example, filtering and sorting by the elements of the documents, as well as projection of elements out of the documents into results. JSON columns allow relational databases to take on some of the characteristics of document databases, creating a useful hybrid between these two database management approaches.
+
+EF7 contains provider-agnostic support for JSON columns, with an implementation for SQL Server. This support allows the mapping of aggregates built from .NET types to JSON documents. Normal LINQ queries can be used on the aggregates, and these will be translated to the appropriate query constructs needed to drill into the JSON. EF7 also supports updating and saving changes to JSON documents.
+
+> You can find more information about JSON columns in EF Core's [documentation](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns).
+
+### Mapping JSON Columns
+
+In EF Core, aggregate types can be defined using `OwnsOne` and `OwnsMany` methods. `OwnsOne` can be used to map a single aggregate and the `OwnsMany` method can be used to map a collection of aggregates.
+
+With EF 7, we have a new extension method for mapping property to a JSON Column: `ToJson`. We can use this method to mark a property as a JSON Column. The property can be of any type that can be serialized to JSON.
+
+The following example shows how to map a JSON column to an aggregate type:
+
+```csharp
+public class ContactDetails
+{
+ public Address Address { get; set; }
+ public string? Phone { get; set; }
+}
+
+public class Address
+{
+ public Address(string street, string city, string postcode, string country)
+ {
+ Street = street;
+ City = city;
+ Postcode = postcode;
+ Country = country;
+ }
+
+ public string Street { get; set; }
+ public string City { get; set; }
+ public string Postcode { get; set; }
+ public string Country { get; set; }
+}
+
+public class Person : AggregateRoot
+{
+ public string Name { get; set; } = null!;
+ public ContactDetails ContactDetails { get; set; } = null!;
+}
+```
+
+* Above, we have defined an aggregate type `ContactDetails` that contains an `Address` and a `Phone` number. The aggregate type is configured in `OnModelCreating` using `OwnsOne` and `ToJson` methods below.
+* The `Address` property is mapped to a JSON column using `ToJson`, and the `Phone` property is mapped to a regular column. This requires just one call to **ToJson()** when configuring the aggregate type:
+
+```csharp
+
+public class MyDbContext : AbpDbContext
+{
+ public DbSet Persons { get; set; }
+
+ public MyDbContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
+
+ builder.Entity(b =>
+ {
+ b.ToTable(MyProjectConsts.DbTablePrefix + "Persons", MyProjectConsts.DbSchema);
+ b.ConfigureByConvention();
+ b.OwnsOne(x=>x.ContactDetails, c =>
+ {
+ c.ToJson(); //mark as JSON Column
+ c.OwnsOne(cd => cd.Address);
+ });
+ });
+ }
+}
+```
+
+### Querying JSON Columns
+
+Queries into JSON columns work just the same as querying into any other aggregate type in EF Core. That's it, just use the LINQ! Here are some examples:
+
+```csharp
+var persons = await (await GetDbSetAsync()).ToListAsync();
+
+var contacts = await (await GetDbSetAsync()).Select(person => new
+{
+ person,
+ person.ContactDetails.Phone, //query over JSON column
+ Addresses = person.ContactDetails.Address //query over JSON column
+}).ToListAsync();
+
+var addresses = await (await GetDbSetAsync()).Select(person => new
+{
+ person,
+ Addresses = person.ContactDetails.Address //query over JSON column
+}).ToListAsync();
+```
+
+### Updating JSON Columns
+
+You can update JSON columns the same as updating any record by using the `UpdateAsync` method. The following example shows how to update a JSON column:
+
+```csharp
+var person = await (await GetDbSetAsync()).FirstAsync();
+
+person.ContactDetails.Phone = "123456789";
+person.ContactDetails.Address = new Address("Street", "City", "Postcode", "Country");
+await UpdateAsync(person, true);
+```
+
+### JSON Column in a Database
+
+After you've configured the database relations, created a new migration and applied it to database you will have a database table like below:
+
+
+
+As you can see, thanks to JSON Columns feature the **ContactDetails** row has JSON content and we can use it in a query or update it from our application with the LINQ JSON query support that mentioned above.
+
+### Conclusion
+
+In this article, I've briefly introduced the JSON Columns feature that was shipped with EF Core 7. It's pretty straightforward to use JSON Columns in an ABP based application. You can see the examples above and give it a try!
+
+### The Source Code
+* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreJSONColumnDemo).
+
+### References
+
+* [https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns)
+* [https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities](https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities)
\ No newline at end of file
diff --git a/docs/en/Community-Articles/2022-11-27-gRPC-Health-Checks/POST.md b/docs/en/Community-Articles/2022-11-27-gRPC-Health-Checks/POST.md
new file mode 100644
index 0000000000..70bcb15e38
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-27-gRPC-Health-Checks/POST.md
@@ -0,0 +1,73 @@
+# gRPC - Health Checks
+
+In this article we will show how to use gRPC health checks with the ABP Framework.
+
+## Health Checks
+
+ASP.NET Core 7 supports gRPC health checks. Health Checks allow us to determine the overall health and availability of our application infrastructure. They are exposed as HTTP endpoints and can be configured to provide information for various monitoring scenarios, such as the response time and memory usage of our application, or whether our application can communicate with our database provider.
+
+### gRPC Health Checks
+
+The [gRPC health checking protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) is a standard for reporting the health of gRPC server apps. An app exposes health checks as a gRPC service. They are typically used with an external monitoring service to check the status of an app.
+
+### Grpc.AspNetCore.HealthChecks
+
+ASP.NET Core supports the gRPC health checking protocol with the [Grpc.AspNetCore.HealthChecks](https://www.nuget.org/packages/Grpc.AspNetCore.HealthChecks) package. Results from .NET health checks are reported to callers.
+
+## Using gRPC Health Checks with the ABP Framework
+
+In this article, I'm assuming you've used gRPC with ABP before. If you are still having problems with this, it may be good for you to review this article.
+https://community.abp.io/posts/using-grpc-with-the-abp-framework-2dgaxzw3
+
+### Set up gRPC Health Checks
+
+In this solution, `*.HttpApi.Host` is the project that configures and runs the server-side application. So, we will make changes in that project.
+
+* Add the `Grpc.AspNetCore.HealthChecks` package to your project.
+
+```bash
+dotnet add package Grpc.AspNetCore.HealthChecks
+```
+
+* `AddGrpcHealthChecks` to register services that enable health checks.
+
+```csharp
+public override void ConfigureServices(ServiceConfigurationContext context)
+{
+ // Other configurations...
+
+ context.Services.AddGrpcHealthChecks()
+ .AddCheck("SampleHealthCheck", () => HealthCheckResult.Healthy());
+}
+```
+* `MapGrpcHealthChecksService` to add a health check service endpoint.
+
+```csharp
+public override void OnApplicationInitialization(ApplicationInitializationContext context)
+{
+ // Other middlewares...
+
+ app.UseConfiguredEndpoints(builder =>
+ {
+ builder.MapGrpcHealthChecksService();
+ });
+}
+```
+
+### Calling Health Checks From a Client
+
+Now that our server is configured for gRPC health checks, we can test it by creating a basic console client.
+
+```csharp
+var channel = GrpcChannel.ForAddress("https://localhost:44357");
+var client = new Health.HealthClient(channel);
+
+var response = await client.CheckAsync(new HealthCheckRequest());
+var status = response.Status;
+
+Console.WriteLine($"Health Status: {status}");
+```
+
+## References
+
+- https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#grpc-health-checks-in-aspnet-core
diff --git a/docs/en/Community-Articles/2022-11-28-EF-Core-Entity-Dependency-Injection/POST.md b/docs/en/Community-Articles/2022-11-28-EF-Core-Entity-Dependency-Injection/POST.md
index 2759bfed16..8ed92d43c2 100644
--- a/docs/en/Community-Articles/2022-11-28-EF-Core-Entity-Dependency-Injection/POST.md
+++ b/docs/en/Community-Articles/2022-11-28-EF-Core-Entity-Dependency-Injection/POST.md
@@ -2,6 +2,8 @@
[Dependency injection](https://docs.abp.io/en/abp/latest/Dependency-Injection) is a widely-used pattern of obtaining references to other services from our classes. It is a built-in feature when you develop ASP.NET Core applications. In this article, I will explain why we may need to have references to other services in an entity class and how we can implement Entity Framework Core's new `IMaterializationInterceptor` interface to provide these services to the entities using the standard dependency injection system.
+> You can find the source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreEntityDependencyInjectionDemo).
+
## The Problem
While developing applications based on [Domain-Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design) (DDD) patterns, we typically write our business code inside [application services](https://docs.abp.io/en/abp/latest/Application-Services), [domain services](https://docs.abp.io/en/abp/latest/Domain-Services) and [entities](https://docs.abp.io/en/abp/latest/Entities). Since the application and domain service instances are created by the dependency injection system, they can inject services into their constructors.
@@ -367,6 +369,11 @@ In this article, I tried to investigate all aspects of injecting services into e
Injecting services into entities seems a certain way of forcing some business rules in your entities. However, because of the current technical limitations, design issues and usage difficulties, I don't suggest to depend on services in your entities. Instead, create domain services when you need to implement a business rule that depends on external services and entities.
+## The Source Code
+
+* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreEntityDependencyInjectionDemo).
+* You can see [this pull request](https://github.com/abpframework/abp-samples/pull/207/files) for the changes I've done after creating the application.
+
## See Also
* [What's new in EF Core 7.0](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew)
diff --git a/docs/en/Community-Articles/2022-11-28-Model-building-conventions-ef7/POST.md b/docs/en/Community-Articles/2022-11-28-Model-building-conventions-ef7/POST.md
new file mode 100644
index 0000000000..998b1e9735
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-28-Model-building-conventions-ef7/POST.md
@@ -0,0 +1,88 @@
+# Model building conventions in Entity Framework Core 7.0
+
+In this article, I will show you one of the new features of EF Core 7 named "Model building conventions".
+
+Entity Framework Core uses a metadata model to describe how entity types are mapped to the database. Before EF Core 7.0, it was not possible to remove or replace existing conventions or add new conventions. With EF Core 7.0, this is now possible. To read more about it, you can visit its [documentation](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#model-building-conventions).
+
+EF Core uses many built-in conventions. You can see the full list of the conventions on `IConvetion` Interface API [documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.conventions.iconvention?view=efcore-7.0).
+
+If you want to add, remove or replace a convention, you need to override `ConfigureConventions` method of your DbContext as shown below;
+
+```csharp
+protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
+{
+ configurationBuilder.Conventions.Add(_ => new MyCustomConvention());
+}
+```
+
+## Allowed Operations
+
+### Removing an existing convention
+
+Existing conventions provided by EF Core are well thought and useful, but sometimes some of them might not be a good candidate for your application. In such cases, you can remove an existing convention as shown below;
+
+```csharp
+protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
+{
+ configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
+}
+```
+
+### Adding a new convention
+
+Just like removing a convention, we can add a completely new convention as well. You can define many different conventions here. For example, you can specify a standard precision for all decimal fields in your entities.
+
+```csharp
+public class DecimalPrecisionConvention : IModelFinalizingConvention
+ {
+ public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext context)
+ {
+ foreach (var property in modelBuilder.Metadata.GetEntityTypes()
+ .SelectMany(
+ entityType => entityType.GetDeclaredProperties()
+ .Where(
+ property => property.ClrType == typeof(decimal))))
+ {
+ property.Builder.HasPrecision(2);
+ }
+ }
+ }
+```
+
+Note that, conventions are executed in the order they are added. So you need to be careful in which order they are added.
+
+### Replacing an existing convention
+
+Sometimes, a default convention might work slightly different than what your app expects. In such cases, you can create your own implementation by inheriting from that convention and replace the default one. For example, you can create a convention as shown below;
+
+```csharp
+public class MyCustomConvention : ADefaultEfCoreConvention
+{
+ public MyCustomConvention(ProviderConventionSetBuilderDependencies dependencies)
+ : base(dependencies)
+ {
+ }
+ // override the methods you want to change.
+}
+```
+
+Then, you can replace the default one with your implementation as shown below;
+
+```csharp
+protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
+{
+ configurationBuilder.Conventions.Replace(
+ serviceProvider => new MyCustomConvention(
+ serviceProvider.GetRequiredService()));
+}
+```
+
+As a final note, conventions never override configuration marked as **DataAnnotation** or **Explicit**. This means that, even if there is a convention, if the property has a `DataAnnotation` attribute or configuration in `OnModelCreating`, convetion will not be used. Here are the configuration types EF Core uses;
+
+* **Explicit:** The model element was explicitly configured in OnModelCreating
+* **DataAnnotation:** The model element was configured using a mapping attribute (aka data annotation) on the CLR type
+* **Convention:** The model element was configured by a model building convention
+
+## Using in ABP-based solution
+
+Since ABP uses EF Core, you can use this feature in ABP as well.
\ No newline at end of file
diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md
new file mode 100644
index 0000000000..ac517506ec
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md
@@ -0,0 +1,71 @@
+# Bulk Operations with Entity Framework Core 7.0
+Entity Framework tracks all the entity changes and applies those changes to the database one by one when the `SaveChanges()` method is called. There was no way to execute bulk operations in Entity Framework Core without a dependency.
+
+As you know the [Entity Framework Extensions](https://entityframework-extensions.net/bulk-savechanges) library was doing it but it was not free.
+
+There was no other solution until now. [Bulk Operations](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) are now available in Entity Framework Core with .NET 7.
+
+With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelete` available to execute bulk operations. It's a similar usage with the Entity Framework Core Extensions library if you're familiar with it.
+
+You can visit the microsoft example [here](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) about how to use it.
+
+
+It can be easily used with the DbContext.
+
+```csharp
+await context.Tags.Where(t => t.Text.Contains(".NET")).ExecuteDeleteAsync();
+```
+
+## Using with ABP Framework
+ABP Framework provides an abstraction over database operations and implements generic repository pattern. So, DbContext can't be accessed outside of [repositories](https://docs.abp.io/en/abp/latest/Repositories).
+
+You can use the `ExecuteUpdate` and `ExecuteDelete` methods inside a repository.
+
+```csharp
+public class BookEntityFrameworkCoreRepository : EfCoreRepository, IBookRepository
+{
+ public BookEntityFrameworkCoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
+ {
+ }
+
+ public async Task UpdateListingAsync()
+ {
+ var dbSet = await GetDbSetAsync();
+
+ await dbSet
+ .Where(x => x.IsListed && x.PublishedOn.Year <= 2022)
+ .ExecuteUpdateAsync(s => s.SetProperty(x => x.IsListed, x => false));
+ }
+
+ public async Task DeleteOldBooksAsync()
+ {
+ var dbSet = await GetDbSetAsync();
+
+ await dbSet
+ .Where(x => x.PublishedOn.Year <= 2000)
+ .ExecuteDeleteAsync();
+ }
+}
+```
+
+There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0.
+
+```csharp
+public class MyDomainService : DomainService
+{
+ protected IRepository BookRepository { get; }
+
+ public MyDomainService(IRepository bookRepository)
+ {
+ BookRepository = bookRepository;
+ }
+
+ public async Task CreateBooksAsync(List books)
+ {
+ // It'll use bulk inserting automatically.
+ await BookRepository.InsertManyAsync(books);
+ }
+}
+```
+
+> If you use `ExecuteDeleteAsync` or `ExecuteUpdateAsync`, then ABP's soft delete and auditing features can not work. Because these ABP features work with EF Core's change tracking system and these new methods doesn't work with the change tracking system. So, use them carefully.
diff --git a/docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md b/docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md
new file mode 100644
index 0000000000..53903e1ccc
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md
@@ -0,0 +1,208 @@
+# Value generation for DDD guarded types with Entity Framework Core 7.0
+
+In domain-driven design (DDD), *guarded keys* can improve the type safety of key properties. This is achieved by wrapping the key type in another type which is specific to the use of the key. In this article, I will explain the cases why you may need to use guarded types and discuss the advantages and limitations when implementing to an ABP application.
+
+> You can find the source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreGuardedTypeDemo).
+
+## The Problem
+
+While developing an applications, there are many cases where we manually assign foreign keys that can be in guid type or integer type, etc. This manual assignment mistakes can cause miss-match of unique identifiers, such as **assigning a product ID to a category**, that can be hard to detect in the future.
+
+Here is a very simplified sample of wrong assignment when trying to update a product category:
+
+````csharp
+public class ProductAppService : MyProductStoreAppService, IProductAppService
+{
+ private readonly IRepository _productRepository;
+
+ public ProductAppService(IRepository productRepository)
+ {
+ _productRepository = productRepository;
+ }
+
+ public async Task UpdateProductCategoryAsync(Guid productId, Guid categoryId)
+ {
+ var productToUpdate = await _productRepository.GetAsync(productId);
+ productToUpdate.CategoryId = productId; // Wrong assignment that causes error only at run-time
+
+ await _productRepository.UpdateAsync(productToUpdate);
+ }
+}
+````
+
+While the sample demonstrates a very simple mistake, it is easier to come across similar mistakes when the business logic gets more complex especially when you are using methods with **multiple foreign key arguments**. The next section offers using guarded types to prevent these kind of problems as a solution to the problem.
+
+## The Solution
+
+Strongly-typed IDs (*guarded keys*) is a DDD approach to address this problem. One of the main problems with .NET users was handling the persisting these objects. With EFCore7, key properties can be guarded with type safety seamlessly.
+
+To use guarded keys, update your aggregate root or entity unique identifier with a complex type to overcome *primitive obsession*:
+
+````csharp
+public readonly struct CategoryId
+{
+ public CategoryId(Guid value) => Value = value;
+ public Guid Value { get; }
+}
+
+public readonly struct ProductId
+{
+ public ProductId(Guid value) => Value = value;
+ public Guid Value { get; }
+}
+````
+
+You can now use these keys for your aggregate roots or entities:
+
+```csharp
+public class Product : AggregateRoot
+{
+ public ProductId Id { get; set; }
+ public string Name { get; set; }
+ public CategoryId CategoryId { get; set; }
+
+ private Product() { }
+
+ public Product(ProductId id, string name) : base(id)
+ {
+ Name = Check.NotNullOrEmpty(name, nameof(name));
+ }
+}
+
+public class Category : AggregateRoot
+{
+ public CategoryId Id { get; set; }
+ public string Name { get; set; }
+ public List Products { get; } = new();
+
+ private Category() { }
+
+ public Category(CategoryId id, string name) : base(id)
+ {
+ Name = Check.NotNullOrEmpty(name, nameof(name));
+ }
+}
+```
+
+`ProductId` and `CategoryId` guarded key types shown in the sample use `Guid` key values, which means Guid values will be used in the mapped database tables. This is achieved by defining [value converters](https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions) for the types.
+
+Override the `ConfigureConventions` method of your DbContext to use the value converters:
+
+````csharp
+protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
+{
+ configurationBuilder.Properties().HaveConversion();
+ configurationBuilder.Properties().HaveConversion();
+}
+
+private class ProductIdConverter : ValueConverter
+{
+ public ProductIdConverter()
+ : base(v => v.Value, v => new(v))
+ {
+ }
+}
+
+private class CategoryIdConverter : ValueConverter
+{
+ public CategoryIdConverter()
+ : base(v => v.Value, v => new(v))
+ {
+ }
+}
+````
+
+> The code here uses `struct` types. This means they have appropriate value-type semantics for use as keys. If `class` types are used instead, then they need to either override equality semantics or also specify a [value comparer](https://learn.microsoft.com/en-us/ef/core/modeling/value-comparers).
+
+Now, you can use generic (or custom) repositories of ABP using the guarded type as the key for the repository:
+
+```csharp
+public class ProductStoreDataSeedContributor : IDataSeedContributor, ITransientDependency
+{
+ private readonly IRepository _categoryRepository;
+ private readonly IRepository _productRepository;
+
+ public ProductStoreDataSeedContributor(
+ IRepository categoryRepository,
+ IRepository productRepository
+ )
+ {
+ _categoryRepository = categoryRepository;
+ _productRepository = productRepository;
+ }
+
+ // ...
+}
+```
+
+You can also use `integer` as guarded type for your key properties and use [Sequence-based key generation for SQL Server](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#sequence-based-key-generation-for-sql-server) for value generation.
+
+## Discussions
+
+In this section, I will discuss the use cases of guarded types and limitations when implementing to an ABP application.
+
+### Do I need to use guarded types?
+
+If you are already following the best practices of [Domain-Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design), you are aware that **updates** and **creations** of an aggregate are done **over** the aggregate root itself as a whole unit. And entity state changes of an aggregate root should be done using the [domain services](https://docs.abp.io/en/abp/latest/Domain-Services). Domain services should already validate the entity.
+
+**Example: Using domain service to update product:**
+
+````csharp
+public class ProductManager : DomainService
+{
+ private readonly IRepository _productRepository;
+
+ public ProductManager(IRepository productRepository)
+ {
+ _productRepository = productRepository;
+ }
+
+ public Task AssignCategory(Product product, Category category)
+ {
+ // ...
+
+ product.CategoryId = category.Id;
+
+ //..
+ }
+}
+````
+
+In this sample, domain service validates that both **product** and the **category** entities, passed by the application layer, are valid objects since they are not key properties. However, manual assignment is already in place and more complex the domain logic, higher to miss out mistakes. At the end, it will depend on your tolerance level for developer errors comparing to the time you want to spend time on additional code base for guarded types.
+
+### Limitations
+
+One important limitation is automatic value generation when using `Guid` as guarded type for your key properties. The basic repository can not generate the unique identifier automatically by the time this article is written:
+
+```csharp
+public readonly struct ProductId
+{
+ public ProductId(Guid value) => Value = value;
+ public Guid Value { get; }
+}
+```
+
+you need to generate the unique identifier **manually**:
+
+````csharp
+var newProduct = await _productRepository.InsertAsync(
+ new Product(new ProductId(_guidGenerator.Create()), "New product")
+);
+````
+
+## Conclusion
+
+In this article, I tried to explain DDD guarded types for key properties and value generation for these properties using Entity Framework 7.0 and ABP.
+
+Using strongly-typed key properties reduce the chance of unnoticed errors. Admittedly it increases the code complexity by adding new types to your solution with extra coding, especially if you are using classes as keys. Guarded types provide improved safety for your code base at the expense of additional code complexity as in many DDD concepts and patterns.
+
+If you have a large team working on a large scale solution containing complex business logics where key assignments are abundant or if you are using methods with multiple foreign key arguments, I personally suggest using guarded types.
+
+## The Source Code
+
+* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreGuardedTypeDemo).
+
+## See Also
+
+* [What's new in EF Core 7.0](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew)
+* [ABP Framework: Domain Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design)
\ No newline at end of file
diff --git a/docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md
new file mode 100644
index 0000000000..d26f6c4aaa
--- /dev/null
+++ b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md
@@ -0,0 +1,231 @@
+# Rate Limiting with ASP.NET Core 7.0
+
+Rate limiting is a way of controlling the traffic that a web application or API receives. In other words, rate limiting helps you control the amount of traffic each user has access to at any given time. This is extremely useful when you want to manage the load on your server or services, avoid going over your monthly data transfer limit and allow the system to continue to function and meet service level agreements, even when an increase in demand places an extreme load on resources.
+
+In this article, we will look at what rate limiting is, why we need to use it, how the different rate limiting algorithms provided with .NET 7.0 work, and best practices for using rate limiting in your application.
+
+## What is rate limiting?
+
+Whether accidental or intentional, users may exhaust resources in a way that impacts others. When a number of requests are received on to resources for a long time, the server can run out of those resources. These resources can include memory, threads, connections, or anything else that is limited. To avoid this situation, set rate limiters. Rate limiters control the consumption of resources used by an instance of an application, a user, an individual tenant, or an entire service.
+
+## Why do you need to use rate limiting?
+
+A rate limiting system is crucial in any application where you have to control or throttle user requests or traffic. This is especially true in applications running on a cloud hosting platform because the user’s traffic can affect the whole server where the application is hosted.
+
+Why do you need to implement rate limiting? Here are a few reasons:
+
+- To ensure that a system continues to meet service level agreements (SLA).
+- To prevent a single user, tenant, service, or so on from monopolizing the resources provided by an application.
+- To help cost-optimize a system by limiting the maximum resource levels needed to keep it functioning.
+
+## Rate limiter algorithms
+
+The [`RateLimiterOptionsExtensions`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.ratelimiting.ratelimiteroptionsextensions) class provides the following extension methods for rate limiting:
+
+- **[Fixed window](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#fixed-window-limit)**: Fixed-window limits—such as 3,000 requests per hour or 10 requests per day—are easy to state, but they are subject to spikes at the edges of the window, as available quota resets. Consider, for example, a limit of 3,000 requests per hour, which still allows for a spike of all 3,000 requests to be made in the first minute of the hour, which might overwhelm the service.
+- [**Sliding window**:](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#sliding-window-limit) Sliding windows have the benefits of a fixed window, but the rolling window of time smoothes out bursts. Systems such as Redis facilitate this technique with expiring keys.
+- [**Token bucket**](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#token-bucket-limit): A token bucket maintains a rolling and accumulating budget of usage as a balance of tokens. A token bucket adds tokens at some rate. When a service request is made, the service attempts to withdraw a token (decrementing the token count) to fulfill the request. If there are no tokens in the bucket, the service has reached its limit and responds with backpressure.
+- [**Concurrency**](https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0#concurrency-limiter): A concurrency limiter is the simplest form of rate limiting. It doesn’t look at time, just at number of concurrent requests.
+
+In order to be a more realistic example, instead of making an example with each rate limiter algorithm, we will implement the following three algorithms in an **ABP-based** application.
+
+1. We will add a `SlidingWindowLimiter` with a partition for all anonymous users.
+2. We will add a `TokenBucketRateLimiter` with a partition for each authenticated user.
+3. We will add a `ConcurrencyLimiter` with a partition for each Tenant.
+
+**Note:** The following sample isn't meant for production code but is an example of how to use the limiters in ABP-based applications.
+
+### Limiter with `OnRejected`, `RetryAfter`, and `GlobalLimiter`
+
+#### Add rate limiter
+
+Let's create the following method in the `MyProjectNameWebModule.cs` class in the `MyProjectName.Web` project.
+
+**Note:** If the `**.Web` project is not in your application, you can do the same in the project where your application is hosted.
+
+```csharp
+private void ConfigureRateLimiters(ServiceConfigurationContext context)
+{
+ context.Services.AddRateLimiter(limiterOptions =>
+ {
+ limiterOptions.OnRejected = (context, cancellationToken) =>
+ {
+ if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
+ {
+ context.HttpContext.Response.Headers.RetryAfter =
+ ((int) retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
+ }
+
+ context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
+ context.HttpContext.RequestServices.GetService()?
+ .CreateLogger("Microsoft.AspNetCore.RateLimitingMiddleware")
+ .LogWarning("OnRejected: {RequestPath}", context.HttpContext.Request.Path);
+
+ return new ValueTask();
+ };
+
+ limiterOptions.AddPolicy("UserBasedRateLimiting", context =>
+ {
+ var currentUser = context.RequestServices.GetService();
+
+ if (currentUser is not null && currentUser.IsAuthenticated)
+ {
+ return RateLimitPartition.GetTokenBucketLimiter(currentUser.UserName, _ => new TokenBucketRateLimiterOptions
+ {
+ TokenLimit = 10,
+ QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
+ QueueLimit = 3,
+ ReplenishmentPeriod = TimeSpan.FromMinutes(1),
+ TokensPerPeriod = 4,
+ AutoReplenishment = true
+ });
+ }
+
+ return RateLimitPartition.GetSlidingWindowLimiter("anonymous-user",
+ _ => new SlidingWindowRateLimiterOptions
+ {
+ PermitLimit = 2,
+ QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
+ QueueLimit = 1,
+ Window = TimeSpan.FromMinutes(1),
+ SegmentsPerWindow = 2
+ });
+ });
+
+ limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create(context =>
+ {
+ var currentTenant = context.RequestServices.GetService();
+
+ if (currentTenant is not null && currentTenant.IsAvailable)
+ {
+ return RateLimitPartition.GetConcurrencyLimiter(currentTenant!.Name, _ => new ConcurrencyLimiterOptions
+ {
+ PermitLimit = 5,
+ QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
+ QueueLimit = 1
+ });
+ }
+
+ return RateLimitPartition.GetNoLimiter("host");
+ });
+ });
+}
+```
+
+In the above example, the `TokenBucketLimiter` is used for each authenticated user, while the `SlidingWindowLimiter` is used for all anonymous users. Additionally, as a global limiter, the `ConcurrencyLimiter` is used for each tenant, while rate limiting is disabled for the host(tenant is not available). Also, for requests that are rejected when the limit is reached, sets the response status code to [429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) and the response mentions when to retry (if available from the rate-limiting metadata).
+
+Let's call the `ConfigureRateLimiters` method that we created in the `ConfigureServices` method.
+
+The final version of the `ConfigureServices` method:
+
+```csharp
+public override void ConfigureServices(ServiceConfigurationContext context)
+{
+ var hostingEnvironment = context.Services.GetHostingEnvironment();
+ var configuration = context.Services.GetConfiguration();
+
+ ConfigureBundles();
+ ConfigureUrls(configuration);
+ ConfigurePages(configuration);
+ ConfigureAuthentication(context);
+ ConfigureImpersonation(context, configuration);
+ ConfigureAutoMapper();
+ ConfigureVirtualFileSystem(hostingEnvironment);
+ ConfigureNavigationServices();
+ ConfigureAutoApiControllers();
+ ConfigureSwaggerServices(context.Services);
+ ConfigureExternalProviders(context);
+ ConfigureHealthChecks(context);
+ ConfigureCookieConsent(context);
+ ConfigureTheme();
+
+ Configure(options =>
+ {
+ options.IsDynamicPermissionStoreEnabled = true;
+ });
+
+ ConfigureRateLimiters(context); // added
+}
+```
+
+#### Add RateLimiter middleware
+
+Add the following line just before the `app.UseConfiguredEndpoints(...)` line to add the `RateLimiter` middleware to your ASP.NET Core request pipeline:
+
+```csharp
+app.UseRateLimiter();
+```
+
+#### Use rate limiter for all controllers
+
+Let's edit the `ConfiguredEndpoints` middleware as follows:
+
+```csharp
+app.UseConfiguredEndpoints(endpoints =>
+{
+ endpoints.MapRazorPages()
+ .DisableRateLimiting();
+
+ endpoints.MapControllers()
+ .RequireRateLimiting("UserBasedRateLimiting");
+});
+```
+
+- **DisableRateLimiting:** It is used to disable the `ConcurrencyLimiter` for razor pages, which we set globally when the tenant is available.
+- **RequireRateLimiting:** We have enabled the rate limiter, which we define according to whether the user is authenticated or not, for all controllers.
+
+## `EnableRateLimiting` and `DisableRateLimiting` attributes
+
+It's kind of unrealistic to always use rate limiting for all controllers or pages. Sometimes, we may want to throttle a particular endpoint or page. In such cases, we can use the `EnableRateLimiting` and `DisableRateLimiting` attributes. The `EnableRateLimiting` and `DisableRateLimiting` attributes can be applied to a controller, action method, or razor rage. Check [here](https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0#enableratelimiting-and-disableratelimiting-attributes) for more.
+
+## Rate limit an HTTP handler
+
+Rate limiting when sending an HTTP request can be a good practice, especially in service-to-service communication. Because, resources are consumed by apps that rely on them, and when an app makes too many requests for a single resource, it can lead to *resource contention*. Resource contention occurs when a resource is consumed by too many clients, and the resource is unable to serve all of the apps that are requesting it. This can result in a poor user experience, and in some cases, it can even lead to a denial of service (DoS) attack. Since there are similar codes, I will not mention an example in this article, but to avoid such situations, you can write your own HTTP handler as [here](https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter#implement-a-delegatinghandler-subclass).
+
+## How does it work?
+
+[System.Threading.RateLimiting](https://www.nuget.org/packages/System.Threading.RateLimiting) provides the primitives for writing rate limiters as well as providing a few commonly used algorithms built-in. The main type is the abstract base class [RateLimiter](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiter.cs).
+
+```csharp
+public abstract class RateLimiter : IAsyncDisposable, IDisposable
+{
+ public abstract int GetAvailablePermits();
+ public abstract TimeSpan? IdleDuration { get; }
+
+ public RateLimitLease Acquire(int permitCount = 1);
+ public ValueTask WaitAsync(int permitCount = 1, CancellationToken cancellationToken = default);
+
+ public void Dispose();
+ public ValueTask DisposeAsync();
+}
+```
+
+`RateLimiter` contains `Acquire` and `WaitAsync` as the core methods for trying to gain permits for a resource that is being protected. Depending on the application, the protected resource may need to acquire more than 1 permits, so `Acquire` and `WaitAsync` both accept an optional `permitCount` parameter. `Acquire` is a synchronous method that will check if enough permits are available or not and return a `RateLimitLease` which contains information about whether you successfully acquired the permits or not. `WaitAsync` is similar to `Acquire` except that it can support queuing permit requests which can be de-queued at some point in the future when the permits become available, which is why it’s asynchronous and accepts an optional `CancellationToken` to allow canceling the queued request.
+
+`RateLimitLease` has an `IsAcquired` property which is used to see if the permits were acquired. Additionally, the `RateLimitLease` may contain metadata such as a suggested retry-after period if the lease failed. Finally, the `RateLimitLease` is disposable and should be disposed when the code is done using the protected resource. The disposal will let the `RateLimiter` know to update its limits based on how many permits were acquired.
+
+## Limitations
+
+In most cases, the rate-limiting middleware provided with ASP.NET 7.0 will meet your requirements. However, if you would want to return statistics about your limits (e.g. [the way GitHub does](https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limit-http-headers)), you’ll find out that the ASP.NET rate limiting middleware does not support this. You won’t have access to the “number of requests remaining” or other metadata. Not in `OnRejected`, and definitely not if you want to return this data as headers on every request.
+
+## Best practices for rate limiting
+
+In order to use rate limiting properly, you need to have a solid understanding of the types of limiting available, as well as the data rate and data volume of your service. You also need to have a clear idea of how many users you expect to use your service as well as how they will interact with it. The best practices for rate limiting are as follows:
+- Find the right rate limiter algorithm for your endpoint. I mean, the cost of an endpoint should be considered when selecting a limiter. The cost of an endpoint includes the resources used, for example, time, data access, CPU, and I/O.
+- Set realistic limits. Once you’ve figured out all the above, you need to set realistic limits for each service. Then, before deploying an app using rate limiting to production, stress test the app to validate the rate limiters and options used. For example, create a [JMeter script](https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.html) with a tool like [BlazeMeter](https://guide.blazemeter.com/hc/articles/207421695-Writing-your-first-JMeter-script) or [Apache JMeter HTTP(S) Test Script Recorder](https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.html) and load the script to [Azure Load Testing](https://learn.microsoft.com/en-us/azure/load-testing/overview-what-is-azure-load-testing).
+- In response to rate-limiting, intermittent, or non-specific errors, a client should generally retry the request after a delay. It is a best practice for this delay to increase exponentially after each failed request, which is referred to as *exponential backoff*. When many clients might be making schedule-based requests (such as fetching results every hour), additional random time (*jitter*) should be applied to the request timing, the backoff period, or both of them to ensure that these multiple client instances don't become periodic [thundering herd](https://www.wikiwand.com/en/Thundering_herd_problem), and cause a form of DDoS themselves.
+
+## Conclusion
+
+In this article, we’ve covered what rate limiting is, why you need to use it and the best practices for doing so. We’ve also looked at how to use three rate-limiting algorithms that are provided with .NET 7.0 on ABP-based applications and how rate-limiting works. Now that you’re familiar with the concept of rate limiting, it’s time to start implementing rate limiting in your application. This will allow you to control the traffic and ensure that your application is running smoothly without any issues.
+
+## References
+
+- https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0
+- https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
+- https://blog.maartenballiauw.be/post/2022/09/26/aspnet-core-rate-limiting-middleware.html
+- https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter
+- https://learn.microsoft.com/en-us/azure/architecture/patterns/rate-limiting-pattern
+- https://learn.microsoft.com/en-us/azure/architecture/patterns/throttling
+- https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet
+- https://cloud.google.com/architecture/rate-limiting-strategies-techniques
diff --git a/docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png
new file mode 100644
index 0000000000..cedae39a3b
Binary files /dev/null and b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png differ
diff --git a/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/Post.md b/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/Post.md
new file mode 100644
index 0000000000..fd98089505
--- /dev/null
+++ b/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/Post.md
@@ -0,0 +1,68 @@
+# How to Use the Weixin Authentication for MVC / Razor Page Applications
+
+This guide demonstrates how to integrate Weixin to an ABP application that enables users to sign in using OAuth 2.0 with credentials.
+
+## Create a sandbox account
+
+If you don't have a production account, you can create a sendbox account for testing: https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
+
+In this article we will use the sandbox account.
+
+> You should configure the callback domain name on the Weixin open platform
+
+## AddWeixin
+
+You need to install `AspNet.Security.OAuth.Weixin` package to your **.Web** project.
+
+In your **.Web** project, locate your **ApplicationWebModule** and modify `ConfigureAuthentication` method with the following:
+
+```csharp
+private void ConfigureAuthentication(ServiceConfigurationContext context)
+{
+ var configuration = context.Services.GetConfiguration();
+ context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
+ context.Services.AddAuthentication()
+ .AddWeixin(options =>
+ {
+ options.ClientId = configuration["Weixin:ClientId"];
+ options.ClientSecret = configuration["Weixin:ClientSecret"];
+ });
+}
+```
+
+Updating `appsettings.json` to add `Weixin` section:
+
+````json
+ "Weixin": {
+ "ClientId": "",
+ "ClientSecret": ""
+ }
+````
+
+## Web page authorization
+
+Now you can run the application to login with Weixin.
+
+
+
+It will redirect to weixin platform to scan the QR code.
+
+> The sandbox account lacks the necessary scope, so it may not work properly.
+
+## Official account authorization
+
+Updating `AddWeixin`:
+
+```csharp
+context.Services.AddAuthentication()
+ .AddWeixin(options =>
+ {
+ options.ClientId = configuration["Weixin:ClientId"];
+ options.ClientSecret = configuration["Weixin:ClientSecret"];
+ options.AuthorizationEndpoint = "https://open.weixin.qq.com/connect/oauth2/authorize";
+ });
+```
+
+Now you can use WeChat app to open the web application URL to login with weixin.
+
+
\ No newline at end of file
diff --git a/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/login-with-weixin.jpg b/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/login-with-weixin.jpg
new file mode 100644
index 0000000000..cfb248ebc4
Binary files /dev/null and b/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/login-with-weixin.jpg differ
diff --git a/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/offical-account.jpg b/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/offical-account.jpg
new file mode 100644
index 0000000000..8157a4e952
Binary files /dev/null and b/docs/en/Community-Articles/2022-12-29-Use-Weixin-Authentication-for-MVC-Applications/offical-account.jpg differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/POST.md b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/POST.md
new file mode 100644
index 0000000000..fb964fa730
--- /dev/null
+++ b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/POST.md
@@ -0,0 +1,300 @@
+# What's new with .NET 7?
+
+In this article, I will highlight a few interesting features that are now available with the release of C# 11 and .NET 7.
+
+> If you are considering using ABP 7.0, you should update your projects to .NET 7. There is a good community article you might want to check out 👉 "[Upgrade Your Existing Projects to .NET7](https://community.abp.io/posts/upgrade-your-existing-projects-to-.net7-nmx6vm9m)".
+
+There are many new features coming with this release. We are going to examine new features within 4 sub-sections: ASP.NET Core, C#11, EF 7 and MAUI. Let's start with ASP.NET Core.
+
+## ASP.NET Core
+
+We will see the following features in this section:
+
+* Rate Limiting
+* Output Caching
+* Built-in HTTP/3 Support
+* gRPC - JSON Transcoding
+* Blazor
+ * Custom Elements
+ * Improvements on JavaScript Interop for WASM
+
+### Rate Limiting
+
+Rate limiting is a way to limit request frequency for a limited time. Before .NET 7 we didn’t have built-in support so we would need to implement it ourselves, use some NuGet packages around or let the CDN provider do this on server level on behalf of us (like Cloudflare).
+
+With .NET 7, built-in Rate Limiting support has been added and we can easily define rate-limiting policies and attach them to our endpoints.
+
+*Defining rate-limiting policy and registering the required services to the DI container*:
+
+
+
+*Adding the middleware to the request pipeline and using the defined policy*:
+
+
+
+### Output Caching
+
+Output Caching is a new middleware that provides a caching mechanism and allows you to store results from your web application and serve them from a cache rather than computing them every time. This improves performance and frees up resources for other tasks.
+
+It’s pretty straightforward to use output caching in minimal APIs.
+You just need to create an endpoint and use the `CacheOutput` method with an expiration time. Then when someone sends a request to your endpoint, it will be cached for a specified time and not calculate the result every time.
+
+For the following example, the result will be the same for 10 minutes:
+
+```csharp
+app.MapGet("/cached-output", () => $"Minute: {DateTime.Now.Minute}")
+ .CacheOutput(options =>
+ {
+ output.Expire(TimeSpan.FromMinutes(10));
+ });
+```
+
+### Built-in HTTP/3 Support
+
+In .NET 6, HTTP/3 was introduced for experimental purposes and now with .NET 7 it’s fully supported. But it's not enabled by default, it's understandable since it's still new and only %20 of the applications currently use it, on the other hand, HTTP/2 is used by almost every application.
+
+To enable the HTTP/3 support, we need to configure it in our **Program.cs** file:
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+builder.WebHost.ConfigureKestrel((context, options) =>
+{
+ options.ListenAnyIP(5001, listenOptions =>
+ {
+ listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
+ listenOptions.UseHttps();
+ });
+});
+```
+
+### gRPC - JSON Transcoding
+
+gRPC is a high-performance RPC framework and uses HTTP/2 and Protobuf. Despite the benefits that gRPC brings, REST APIs have an important place in modern web applications.
+
+gRPC - JSON Transcoding is an extension for ASP.NET Core that creates RESTful JSON APIs for gRPC services.
+
+
+
+It's a really good feature and allows us to expose our gRPC services as REST APIs. It's worth checking out 👉 [JSON Transcoding](https://learn.microsoft.com/en-us/aspnet/core/grpc/json-transcoding?view=aspnetcore-7.0).
+
+> I've created a separate community article for this feature. Check it from [here](https://community.abp.io/posts/grpc-json-transcoding-16eai2zw).
+
+### Blazor
+
+We are going to cover two new features for Blazor:
+
+* Custom Elements
+* Improvements on JavaScript Interop for WASM
+
+#### Custom Elements
+
+Blazor Custom Elements provide a way to dynamically render Razor Components from other SPA frameworks/libraries such as Angular and React.
+
+To be able to use custom elements, there are two steps that need to be done:
+
+**1-) Registering a Razor Component as a Custom Element:**
+
+```csharp
+builder.Services.AddServerSideBlazor(options =>
+{
+ options.RootComponents.RegisterCustomElement("my-counter");
+});
+```
+
+**2-) Using the Custom Element**
+
+```html
+
+```
+
+#### Improvements on JavaScript Interop for WASM
+
+JavaScript `[JsImport]` / `[JsExport]` interop API released with .NET 7.
+
+* To import a JS function to call it from C# -> `[JsImport]`
+* To export a .NET method so that it can be called from JavaScript -> `[JsExport]` attributes should be used.
+
+## C# 11
+
+There are great features that came with C# 11. In this article, we are going to only cover the following ones:
+
+* Required Members
+* Generic Attributes
+* Raw String Literals
+* List Patterns
+
+### Required Members
+
+C# 11 introduces a new **required** keyword to allow us to ensure property initialization on object creation.
+
+We just need to use the **required** keyword before the property type. That’s it. Then if we try to create an object without initializing the required properties, compile-time errors will be shown:
+
+
+
+### Generic Attributes
+
+Generic Attributes are also one of the good features that came with C# 11. Before C#11, creating a typed attribute was cumbersome. We would need to pass a **Type** object as an argument to our constructor and assign it to a property in our attribute class and do stuff with this **Type** property.
+
+With C#11, Generic Attributes are introduced. Now, it’s possible to easily create generic attributes like creating generic classes:
+
+```csharp
+//defining a generic attribute
+public class GenericAttribute : Attribute { }
+
+//using the attribute
+[GenericAttribute]
+public int MyMethod() => default;
+```
+
+### Raw String Literals
+
+Raw String Literals is a great syntactic sugar that came with C#11. It allows containing of arbitrary text without escaping.
+
+By wrapping a string with three double quotes (”””...”””), we are free to put any string value into variables:
+
+```csharp
+var jsonContent = """
+{
+ "name": ".NET 7",
+ "feature": "Raw String Literals"
+}
+""";
+```
+
+Also, we can use string interpolation with this new syntax. We just need to add a $ sign before the first triple quotes:
+
+
+
+### List Patterns
+
+C# 11 introduces the “List Pattern”. It expands the pattern matching for lists and arrays. There are three different ways for list pattern matching:
+
+#### 1-) Discard Pattern
+
+This pattern can be helpful to match one or more elements from a sequence if we know the length of the sequence.
+
+
+
+#### 2-) Range Pattern
+
+If the length of the sequence is not known, then the range pattern may be useful. We can use this pattern to check the first or/and last element from a sequence.
+
+
+
+#### 3-) var Pattern
+
+This pattern allows us to capture an element at that position and use the variable in our code.
+
+
+
+## Entity Framework Core 7
+
+There are too many improvements and new features that were shipped with EF Core 7. Here's what we're going to cover in this article:
+
+* JSON Columns
+* Improvements on Bulk Updates & Deletes
+* Performance Improvements on SaveChanges & SaveChangesAsync
+
+### JSON Columns
+
+EF 7 supports JSON Columns and this allows the mapping of aggregates built from .NET types to JSON documents.
+
+Thus, it's kind of combines relational databases and document-based databases in a way.
+
+We can easily mark a column as a JSON column on the `OnModelCreating` method of our `DbContext` class:
+
+```csharp
+protected override void OnModelCreating(ModelBuilder modelBuilder)
+{
+ modelBuilder.Entity().OwnsOne(
+ author => author.Contact, ownedNavigationBuilder =>
+ {
+ ownedNavigationBuilder.ToJson();
+
+ ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
+ });
+}
+```
+
+Also with this version, LINQ JSON query support has been announced. So, we can query over JSON Columns using LINQ as below:
+
+```csharp
+var posts = await context.Posts
+ .AsNoTracking()
+ .Select(
+ post => new
+ {
+ post.Author!.Name,
+ post.Metadata!.Views,
+ Searches = post.Metadata.TopSearches,
+ Commits = post.Metadata.Updates
+ })
+ .ToListAsync();
+```
+
+### Improvements on Bulk Updates & Deletes
+
+EF 7 introduces the new **ExecuteUpdateAsync** and **ExecuteDeleteAsync** methods. By using these methods while making bulk updates or deletes, we can take out the change tracker in this process and this brings great performance gains.
+
+Example:
+
+```csharp
+await (await GetDbContextAsync()).Tags
+ .Where(tag => tag.Description.Contains("ABP"))
+ .ExecuteDeleteAsync();
+```
+
+### Performance Improvements on SaveChanges & SaveChangesAsync
+
+With EF 7, there are significant performance improvements on SaveChanges & SaveChangesAsync methods. According to the EF Core team, in some cases, saving changes is now four times faster than EF 6. You can see a simple benchmark result from the EF Core blog post here.
+
+
+
+Even after inserting just four records, there is a significant performance gain.
+
+## .NET MAUI 7
+
+As you know, .NET MAUI is a cross-platform framework for creating native mobile and desktop applications by using C# and XAML. By using the .NET MAUI, apps that can run on Android, IOS, macOS and Windows from a single-code base can be developed.
+
+It’s a new technology, so it's evolving and the .NET MAUI team introduces good features with every release.
+
+In this article, I will only mention a new feature called "Map Control" and some enhancements made with this release.
+
+### Map Control
+
+.NET MAUI 7 introduces **Map Control**. This provides us a good native map experience.
+
+It supports pins, polygons, circles, geolocations and much more...
+
+
+
+### Improvements on Mobile Rendering & Desktop Enhancements
+
+.NET MAUI 7 came with optimized rendering for mobile applications and is much faster than .NET MAUI 6.
+
+Also, there are some good enhancements on the desktop side:
+
+* Window size and position,
+* Context Menus (which there was a kind of bug and was not seen on some MAUI Controls for Windows applications),
+* Tooltips,
+* Gestures and more...
+
+---
+
+## Conclusion
+
+In this article, I've highlighted some features that were shipped with .NET 7.
+
+> I've added a references section down below, so you can check the references and see other features that came with this version.
+
+Thanks for reading the article and I hope you find it helpful. See you in the next one!
+
+## References
+
+* https://devblogs.microsoft.com/dotnet/announcing-dotnet-7/
+* https://devblogs.microsoft.com/dotnet/announcing-asp-net-core-in-dotnet-7
+* https://devblogs.microsoft.com/dotnet/announcing-ef7
+* https://devblogs.microsoft.com/dotnet/dotnet-maui-dotnet-7
+* https://devblogs.microsoft.com/dotnet/welcome-to-csharp-11
+* https://www.milanjovanovic.tech/blog/whats-new-in-dotnet-7
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/benchmark.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/benchmark.png
new file mode 100644
index 0000000000..3a391f93e7
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/benchmark.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/discard-pattern.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/discard-pattern.png
new file mode 100644
index 0000000000..89d08d14ec
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/discard-pattern.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/json-transcoding.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/json-transcoding.png
new file mode 100644
index 0000000000..a80c7e3bb1
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/json-transcoding.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/maui.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/maui.png
new file mode 100644
index 0000000000..6fe3f39d5c
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/maui.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/range-pattern.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/range-pattern.png
new file mode 100644
index 0000000000..6aaefabe47
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/range-pattern.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/rate-limiting-1.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/rate-limiting-1.png
new file mode 100644
index 0000000000..bb58778da1
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/rate-limiting-1.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/rate-limiting-2.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/rate-limiting-2.png
new file mode 100644
index 0000000000..8e3e93c331
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/rate-limiting-2.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/raw-string-literals.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/raw-string-literals.png
new file mode 100644
index 0000000000..490b9a6414
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/raw-string-literals.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/required-members.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/required-members.png
new file mode 100644
index 0000000000..dbf94e18a1
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/required-members.png differ
diff --git a/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/var-pattern.png b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/var-pattern.png
new file mode 100644
index 0000000000..ebdaf52973
Binary files /dev/null and b/docs/en/Community-Articles/2022-18-11-whats-new-with-net7/var-pattern.png differ
diff --git a/docs/en/Distributed-Event-Bus.md b/docs/en/Distributed-Event-Bus.md
index cc63e98fcd..8a2bb60a18 100644
--- a/docs/en/Distributed-Event-Bus.md
+++ b/docs/en/Distributed-Event-Bus.md
@@ -296,6 +296,74 @@ This example;
> Distributed event system use the [object to object mapping](Object-To-Object-Mapping.md) system to map `Product` objects to `ProductEto` objects. So, you need to configure the object mapping (`Product` -> `ProductEto`) too. You can check the [object to object mapping document](Object-To-Object-Mapping.md) to learn how to do it.
+## Entity Synchronizer
+
+In a distributed (or microservice) system, it is typical to subscribe to change events for an [entity](Entities.md) type of another service, so you can get notifications when the subscribed entity changes. In that case, you can use ABP's Pre-Defined Events as explained in the previous section.
+
+If your purpose is to store your local copies of a remote entity, you typically subscribe to create, update and delete events of the remote entity and update your local database in your event handler. ABP provides a pre-built `EntitySynchronizer` base class to make that operation easier for you.
+
+Assume that there is a `Product` entity (probably an aggregate root entity) in a Catalog microservice, and you want to keep copies of the products in your Ordering microservice, with a local `OrderProduct` entity. In practice, properties of the `OrderProduct` class will be a subset of the `Product` properties, because not all the product data is needed in the Ordering microservice (however, you can make a full copy if you need). Also, the `OrderProduct` entity may have additional properties that are populated and used in the Ordering microservice.
+
+The first step to establish the synchronization is to define an ETO (Event Transfer Object) class in the Catalog microservice that is used to transfer the event data. Assuming the `Product` entity has a `Guid` key, your ETO can be as shown below:
+
+````
+[EventName("product")]
+public class ProductEto : EntityEto
+{
+ // Your Product properties here...
+}
+````
+
+`ProductEto` can be put in a shared project (DLL) that is referenced by the Catalog and the Ordering microservices. Alternatively, you can put a copy of the `ProductEto` class in the Ordering microservice if you don't want to introduce a common project dependency between the services. In this case, the `EventName` attribute becomes critical to map the `ProductEto` classes across two services (you should use the same event name).
+
+Once you define an ETO class, you should configure the ABP Framework to publish auto (create, update and delete) events for the `Product` entity, as explained in the previous section:
+
+````csharp
+Configure(options =>
+{
+ options.AutoEventSelectors.Add();
+ options.EtoMappings.Add();
+});
+````
+
+Finally, you should create a class in the Ordering microservice, that is derived from the `EntitySynchronizer` class:
+
+````csharp
+public class ProductSynchronizer : EntitySynchronizer
+{
+ public ProductSynchronizer(
+ IObjectMapper objectMapper,
+ IRepository repository
+ ) : base(objectMapper, repository)
+ {
+ }
+}
+````
+
+The main point of this class is it subscribes to the create, update and delete events of the source entity and updates the local entity in the database. It uses the [Object Mapper](Object-To-Object-Mapping.md) system to create or update the `OrderProduct` objects from the `ProductEto` objects. So, you should also configure the object mapper to make it properly work. Otherwise, you should manually perform the object mapping by overriding the `MapToEntityAsync(TSourceEntityEto)` and `MapToEntityAsync(TSourceEntityEto,TEntity)` methods in your `ProductSynchronizer` class.
+
+If your entity has a composite primary key (see the [Entities document](Entities.md)), then you should inherit from the `EntitySynchronizer` class (just don't use the `Guid` generic argument in the previous example) and implement `FindLocalEntityAsync` to find the entity in your local database using the `Repository`.
+
+`EntitySynchronizer` is compatible with the *Entity Versioning* system (see the [Entities document](Entities.md)). So, it works as expected even if the events are received as disordered. If the entity's version in your local database is newer than the entity in the received event, then the event is ignored. You should implement the `IHasEntityVersion` interface for the entity and ETO classes (for this example, you should implement for the `Product`, `ProductEto` and `OrderProduct` classes).
+
+If you want to ignore some type of change events, you can set `IgnoreEntityCreatedEvent`, `IgnoreEntityUpdatedEvent` and `IgnoreEntityDeletedEvent` in the constructor of your class. Example:
+
+````csharp
+public class ProductSynchronizer
+ : EntitySynchronizer
+{
+ public ProductSynchronizer(
+ IObjectMapper objectMapper,
+ IRepository repository
+ ) : base(objectMapper, repository)
+ {
+ IgnoreEntityDeletedEvent = true;
+ }
+}
+````
+
+> Notice that the `EntitySynchronizer` can only create/update the entities after you use it. If you have an existing system with existing data, you should manually copy the data for one time, because the `EntitySynchronizer` starts to work.
+
## Transaction and Exception Handling
Distributed event bus works in-process (since default implementation is `LocalDistributedEventBus`) unless you configure an actual provider (e.g. [Kafka](Distributed-Event-Bus-Kafka-Integration.md) or [RabbitMQ](Distributed-Event-Bus-RabbitMQ-Integration.md)). In-process event bus always executes event handlers in the same [unit of work](Unit-Of-Work.md) scope that you publishes the events in. That means, if an event handler throws an exception, then the related unit of work (the database transaction) is rolled back. In this way, your application logic and event handling logic becomes transactional (atomic) and consistent. If you want to ignore errors in an event handler, you must use a `try-catch` block in your handler and shouldn't re-throw the exception.
diff --git a/docs/en/Entities.md b/docs/en/Entities.md
index a1558a73ae..124e0adab1 100644
--- a/docs/en/Entities.md
+++ b/docs/en/Entities.md
@@ -324,6 +324,21 @@ It's designed as read-only and automatically invalidates a cached entity if the
> See the [Entity Cache](Entity-Cache.md) documentation for more information.
+## Versioning Entities
+
+ABP defines the `IHasEntityVersion` interface for automatic versioning of your entities. It only provides a single `EntityVersion` property, as shown in the following code block:
+
+````csharp
+public interface IHasEntityVersion
+{
+ int EntityVersion { get; }
+}
+````
+
+If you implement the `IHasEntityVersion` interface, ABP automatically increases the `EntityVersion` value whenever you update your entity. The initial `EntityVersion` value will be `0`, when you first create an entity and save to the database.
+
+> ABP can not increase the version if you directly execute SQL `UPDATE` commands in the database. It is your responsibility to increase the `EntityVersion` value in that case. Also, if you are using the aggregate pattern and change sub-collections of an aggregate root, it is your responsibility if you want to increase the version of the aggregate root object.
+
## Extra Properties
ABP defines the `IHasExtraProperties` interface that can be implemented by an entity to be able to dynamically set and get properties for the entity. `AggregateRoot` base class already implements the `IHasExtraProperties` interface. If you've derived from this class (or one of the related audit class defined above), you can directly use the API.
diff --git a/docs/en/Modules/Cms-Kit/Comments.md b/docs/en/Modules/Cms-Kit/Comments.md
index e5a5da592e..ac8853a4d8 100644
--- a/docs/en/Modules/Cms-Kit/Comments.md
+++ b/docs/en/Modules/Cms-Kit/Comments.md
@@ -1,10 +1,10 @@
# CMS Kit: Comments
-CMS kit provides a **comment** system to add comments feature to any kind of resource, like blog posts, products, etc.
+CMS kit provides a **comment** system to add the comment feature to any kind of resource, like blog posts, products, etc.
## Options
-The comment system provides a mechanism to group comment definitions by entity types. For example, if you want to use comment system for blog posts and products, you need to define two entity types named `BlogPosts` and `Product`, and add comments under these entity types.
+The comment system provides a mechanism to group comment definitions by entity types. For example, if you want to use the comment system for blog posts and products, you need to define two entity types named `BlogPosts` and `Product`, and add comments under these entity types.
`CmsKitCommentOptions` can be configured in the domain layer, in the `ConfigureServices` method of your [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics). Example:
@@ -12,6 +12,7 @@ The comment system provides a mechanism to group comment definitions by entity t
Configure(options =>
{
options.EntityTypes.Add(new CommentEntityTypeDefinition("Product"));
+ options.IsRecaptchaEnabled = true; //false by default
});
```
@@ -20,6 +21,7 @@ Configure(options =>
`CmsKitCommentOptions` properties:
- `EntityTypes`: List of defined entity types(`CmsKitCommentOptions`) in the comment system.
+- `IsRecaptchaEnabled`: This flag enables or disables the reCaptcha for the comment system. You can set it as **true** if you want to use reCaptcha in your comment system.
`CommentEntityTypeDefinition` properties:
@@ -77,7 +79,7 @@ A comment represents a written comment from a user.
This module follows the [Repository Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories) guide.
-Following custom repositories are defined for this feature:
+The following custom repositories are defined for this feature:
- `ICommentRepository`
@@ -93,8 +95,8 @@ This module follows the [Domain Services Best Practices & Conventions](https://d
#### Application services
-- `CommentAdminAppService` (implements `ICommentAdminAppService`): Implements the use cases of comment management system, like listing or removing comments etc.
-- `CommentPublicAppService` (implements `ICommentPublicAppService`): Implements the use cases of comment management on the public websites, like listing comments, adding comments etc.
+- `CommentAdminAppService` (implements `ICommentAdminAppService`): Implements the use cases of the comment management system, like listing or removing comments etc.
+- `CommentPublicAppService` (implements `ICommentPublicAppService`): Implements the use cases of the comment management on the public websites, like listing comments, adding comments etc.
### Database providers
diff --git a/docs/en/Themes/LeptonXLite/AspNetCore.md b/docs/en/Themes/LeptonXLite/AspNetCore.md
index f480141120..b134135a04 100644
--- a/docs/en/Themes/LeptonXLite/AspNetCore.md
+++ b/docs/en/Themes/LeptonXLite/AspNetCore.md
@@ -38,7 +38,7 @@ Configure(options =>
// Remove the following line
- BasicThemeBundles.Styles.Global,
// Add the following line instead
-+ LeptonXLiteThemeBundles.Styles.Global
++ LeptonXLiteThemeBundles.Styles.Global,
bundle =>
{
bundle.AddFiles("/global-styles.css");
diff --git a/docs/en/Tutorials/Todo/Single-Layer/todo-single-layer-ui-initial.png b/docs/en/Tutorials/Todo/Single-Layer/todo-single-layer-ui-initial.png
index 2393505147..a1d113f574 100644
Binary files a/docs/en/Tutorials/Todo/Single-Layer/todo-single-layer-ui-initial.png and b/docs/en/Tutorials/Todo/Single-Layer/todo-single-layer-ui-initial.png differ
diff --git a/docs/en/Tutorials/Todo/todo-list.png b/docs/en/Tutorials/Todo/todo-list.png
index e2eaf8bc46..6394dd8f29 100644
Binary files a/docs/en/Tutorials/Todo/todo-list.png and b/docs/en/Tutorials/Todo/todo-list.png differ
diff --git a/docs/en/Tutorials/Todo/todo-ui-initial.png b/docs/en/Tutorials/Todo/todo-ui-initial.png
index 44be4ceaf6..9cc1b2e2be 100644
Binary files a/docs/en/Tutorials/Todo/todo-ui-initial.png and b/docs/en/Tutorials/Todo/todo-ui-initial.png differ
diff --git a/docs/en/UI/AspNetCore/Customization-User-Interface.md b/docs/en/UI/AspNetCore/Customization-User-Interface.md
index 64184bf806..8b5a065e86 100644
--- a/docs/en/UI/AspNetCore/Customization-User-Interface.md
+++ b/docs/en/UI/AspNetCore/Customization-User-Interface.md
@@ -63,6 +63,15 @@ The account module defines a `Login.cshtml` file under the `Pages/Account` folde
You typically want to copy the original `.cshtml` file of the module, then make the necessary changes. You can find the original file [here](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml). Do not copy the `Login.cshtml.cs` file which is the code behind file for the razor page and we don't want to override it yet (see the next section).
+> Don't forget to add [_ViewImports.cshtml](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-7.0#importing-shared-directives) if the page you want to override contains [ABP Tag Helpers](../Tag-Helpers/Index.md).
+
+````csharp
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI
+@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
+@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
+````
+
That's all, you can change the file content however you like.
### Completely Overriding a Razor Page
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs
index 21f29f995c..36ca9b4b55 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs
@@ -33,6 +33,10 @@ public static class AbpWebAssemblyHostBuilderExtensions
var application = await builder.Services.AddApplicationAsync(opts =>
{
options?.Invoke(new AbpWebAssemblyApplicationCreationOptions(builder, opts));
+ if (opts.Environment.IsNullOrWhiteSpace())
+ {
+ opts.Environment = builder.HostEnvironment.Environment;
+ }
});
return application;
@@ -57,6 +61,10 @@ public static class AbpWebAssemblyHostBuilderExtensions
var application = builder.Services.AddApplication(opts =>
{
options?.Invoke(new AbpWebAssemblyApplicationCreationOptions(builder, opts));
+ if (opts.Environment.IsNullOrWhiteSpace())
+ {
+ opts.Environment = builder.HostEnvironment.Environment;
+ }
});
return application;
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs
index bbcf1d5a83..bb9f0774bc 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/AbpWebAssemblyServiceCollectionExtensions.cs
@@ -1,16 +1,31 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+using Microsoft.Extensions.Hosting;
using Volo.Abp;
namespace Microsoft.Extensions.DependencyInjection;
public static class AbpWebAssemblyServiceCollectionExtensions
{
- public static WebAssemblyHostBuilder GetHostBuilder(
- [NotNull] this IServiceCollection services)
+ public static WebAssemblyHostBuilder GetHostBuilder([NotNull] this IServiceCollection services)
{
Check.NotNull(services, nameof(services));
return services.GetSingletonInstance();
}
+
+ public static IWebAssemblyHostEnvironment GetWebAssemblyHostEnvironment(this IServiceCollection services)
+ {
+ var webAssemblyHostEnvironment = services.GetSingletonInstanceOrNull();
+
+ if (webAssemblyHostEnvironment == null)
+ {
+ return new EmptyWebAssemblyHostEnvironment()
+ {
+ Environment = Environments.Development
+ };
+ }
+
+ return webAssemblyHostEnvironment;
+ }
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/EmptyWebAssemblyHostEnvironment.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/EmptyWebAssemblyHostEnvironment.cs
new file mode 100644
index 0000000000..6393948af7
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/Extensions/DependencyInjection/EmptyWebAssemblyHostEnvironment.cs
@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public class EmptyWebAssemblyHostEnvironment : IWebAssemblyHostEnvironment
+{
+ public string Environment { get; set; }
+
+ public string BaseAddress { get; set; }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs
index 12877f0c31..b18fdab82e 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs
@@ -24,6 +24,12 @@ public class AbpAspNetCoreComponentsWebAssemblyModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
+ var abpHostEnvironment = context.Services.GetSingletonInstance();
+ if (abpHostEnvironment.EnvironmentName.IsNullOrWhiteSpace())
+ {
+ abpHostEnvironment.EnvironmentName = context.Services.GetWebAssemblyHostEnvironment().Environment;
+ }
+
PreConfigure(options =>
{
options.ProxyClientBuildActions.Add((_, builder) =>
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCacheOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCacheOptions.cs
new file mode 100644
index 0000000000..c27670246e
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCacheOptions.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Volo.Abp.AspNetCore.Mvc.Client;
+
+public class AbpAspNetCoreMvcClientCacheOptions
+{
+ public TimeSpan TenantConfigurationCacheAbsoluteExpiration { get; set; }
+
+ public TimeSpan ApplicationConfigurationDtoCacheAbsoluteExpiration { get; set; }
+
+ public AbpAspNetCoreMvcClientCacheOptions()
+ {
+ TenantConfigurationCacheAbsoluteExpiration = TimeSpan.FromMinutes(5);
+ ApplicationConfigurationDtoCacheAbsoluteExpiration = TimeSpan.FromSeconds(300);
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs
index ae5421d947..610a41eefd 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs
@@ -1,3 +1,5 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.Modularity;
@@ -9,5 +11,16 @@ namespace Volo.Abp.AspNetCore.Mvc.Client;
)]
public class AbpAspNetCoreMvcClientModule : AbpModule
{
-
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var abpHostEnvironment = context.Services.GetAbpHostEnvironment();
+ if (abpHostEnvironment.IsDevelopment())
+ {
+ Configure(options =>
+ {
+ options.TenantConfigurationCacheAbsoluteExpiration = TimeSpan.FromSeconds(5);
+ options.ApplicationConfigurationDtoCacheAbsoluteExpiration = TimeSpan.FromSeconds(5);
+ });
+ }
+ }
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
index 405d43906d..6f2cc1a05f 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
@@ -2,6 +2,7 @@ using System;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies;
using Volo.Abp.Caching;
@@ -18,18 +19,21 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
protected AbpApplicationLocalizationClientProxy ApplicationLocalizationClientProxy { get; }
protected ICurrentUser CurrentUser { get; }
protected IDistributedCache Cache { get; }
+ protected AbpAspNetCoreMvcClientCacheOptions Options { get; }
public MvcCachedApplicationConfigurationClient(
IDistributedCache cache,
AbpApplicationConfigurationClientProxy applicationConfigurationAppService,
ICurrentUser currentUser,
- IHttpContextAccessor httpContextAccessor,
- AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy)
+ IHttpContextAccessor httpContextAccessor,
+ AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy,
+ IOptions options)
{
ApplicationConfigurationAppService = applicationConfigurationAppService;
CurrentUser = currentUser;
HttpContextAccessor = httpContextAccessor;
ApplicationLocalizationClientProxy = applicationLocalizationClientProxy;
+ Options = options.Value;
Cache = cache;
}
@@ -48,7 +52,7 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
async () => await GetRemoteConfigurationAsync(),
() => new DistributedCacheEntryOptions
{
- AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(300) //TODO: Should be configurable.
+ AbsoluteExpirationRelativeToNow = Options.ApplicationConfigurationDtoCacheAbsoluteExpiration
}
);
@@ -68,7 +72,7 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
IncludeLocalizationResources = false
}
);
-
+
var localizationDto = await ApplicationLocalizationClientProxy.GetAsync(
new ApplicationLocalizationRequestDto {
CultureName = config.Localization.CurrentCulture.Name,
@@ -77,7 +81,7 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
);
config.Localization.Resources = localizationDto.Resources;
-
+
return config;
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs
index dd4411255e..3f091f8db8 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcRemoteTenantStore.cs
@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Options;
using Pages.Abp.MultiTenancy.ClientProxies;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
using Volo.Abp.Caching;
@@ -16,15 +17,18 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
protected AbpTenantClientProxy TenantAppService { get; }
protected IHttpContextAccessor HttpContextAccessor { get; }
protected IDistributedCache Cache { get; }
+ protected AbpAspNetCoreMvcClientCacheOptions Options { get; }
public MvcRemoteTenantStore(
AbpTenantClientProxy tenantAppService,
IHttpContextAccessor httpContextAccessor,
- IDistributedCache cache)
+ IDistributedCache cache,
+ IOptions options)
{
TenantAppService = tenantAppService;
HttpContextAccessor = httpContextAccessor;
Cache = cache;
+ Options = options.Value;
}
public async Task FindAsync(string name)
@@ -42,9 +46,8 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name)),
() => new DistributedCacheEntryOptions
{
- AbsoluteExpirationRelativeToNow =
- TimeSpan.FromMinutes(5) //TODO: Should be configurable.
- }
+ AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
+ }
);
if (httpContext != null)
@@ -70,9 +73,8 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id)),
() => new DistributedCacheEntryOptions
{
- AbsoluteExpirationRelativeToNow =
- TimeSpan.FromMinutes(5) //TODO: Should be configurable.
- }
+ AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
+ }
);
if (httpContext != null)
@@ -98,9 +100,8 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
() => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name))),
() => new DistributedCacheEntryOptions
{
- AbsoluteExpirationRelativeToNow =
- TimeSpan.FromMinutes(5) //TODO: Should be configurable.
- }
+ AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
+ }
);
if (httpContext != null)
@@ -126,9 +127,8 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
() => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id))),
() => new DistributedCacheEntryOptions
{
- AbsoluteExpirationRelativeToNow =
- TimeSpan.FromMinutes(5) //TODO: Should be configurable.
- }
+ AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
+ }
);
if (httpContext != null)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs
index 6169154904..8d41b1e991 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs
@@ -8,7 +8,7 @@ public class AbpCardTagHelperService : AbpTagHelperService
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
- output.Attributes.AddClass("card");
+ output.Attributes.AddClass("card mb-3");
SetBorder(context, output);
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
index 5778e47692..92f3769264 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
@@ -10,6 +10,14 @@ public class AbpInputTagHelper : AbpTagHelper
label.AddCssClass(isCheckbox ? "form-check-label" : "form-label");
+ if (!TagHelper.LabelTooltip.IsNullOrEmpty())
+ {
+ label.Attributes.Add("data-bs-toggle", "tooltip");
+ label.Attributes.Add("data-bs-placement", TagHelper.LabelTooltipPlacement);
+ if (TagHelper.LabelTooltipHtml)
+ {
+ label.Attributes.Add("data-bs-html", "true");
+ }
+ label.Attributes.Add("title", TagHelper.LabelTooltip);
+ label.InnerHtml.AppendHtml($" ");
+ }
+
return label.ToHtmlString();
}
@@ -337,7 +349,24 @@ public class AbpInputTagHelperService : AbpTagHelperService
attributeList.AddClass(isCheckbox ? "form-check-label" : "form-label");
- return await labelTagHelper.RenderAsync(attributeList, context, _encoder, "label", TagMode.StartTagAndEndTag);
+ if (!TagHelper.LabelTooltip.IsNullOrEmpty())
+ {
+ attributeList.Add("data-bs-toggle", "tooltip");
+ attributeList.Add("data-bs-placement", TagHelper.LabelTooltipPlacement);
+ if (TagHelper.LabelTooltipHtml)
+ {
+ attributeList.Add("data-bs-html", "true");
+ }
+ attributeList.Add("title", TagHelper.LabelTooltip);
+ }
+
+ var innerOutput = await labelTagHelper.ProcessAndGetOutputAsync(attributeList, context, "label", TagMode.StartTagAndEndTag);
+ if (!TagHelper.LabelTooltip.IsNullOrEmpty())
+ {
+ innerOutput.Content.AppendHtml($" ");
+ }
+
+ return innerOutput.Render(_encoder);
}
protected virtual void ConvertToTextAreaIfTextArea(TagHelperOutput tagHelperOutput)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs
index 3026860ed6..9b730d5c0d 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs
@@ -84,7 +84,7 @@ public class AbpTabsTagHelperService : AbpTagHelperService
var id = TagHelper.Name + "Content";
var wrapper = new TagBuilder("div");
- wrapper.AddCssClass("tab-content");
+ wrapper.AddCssClass("tab-content pt-3");
wrapper.Attributes.Add("id", id);
wrapper.InnerHtml.AppendHtml(contents);
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hr.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hr.json
new file mode 100644
index 0000000000..83f6459a7c
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hr.json
@@ -0,0 +1,13 @@
+{
+ "culture": "hr",
+ "texts": {
+ "GivenTenantIsNotExist": "Odabrani tenant ne postoji: {0}",
+ "GivenTenantIsNotAvailable": "Odabrani tenant nije dostupan: {0}",
+ "Tenant": "Tenant",
+ "Switch": "promjena",
+ "Name": "Naziv",
+ "SwitchTenantHint": "Polje naziv ostavite prazno kako bi se prebacili na host-ovu stranu.",
+ "SwitchTenant": "Promjena tenant-a",
+ "NotSelected": "Nije odabrano"
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsScriptContributor.cs
index 9294efd2b3..0b330751b6 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsScriptContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsScriptContributor.cs
@@ -6,6 +6,6 @@ public class ChartjsScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
- context.Files.Add("/libs/chart.js/chart.js");
+ context.Files.Add("/libs/chart.js/chart.umd.js");
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/WebApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/WebApplicationBuilderExtensions.cs
index 692825520b..f044348052 100644
--- a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/WebApplicationBuilderExtensions.cs
+++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/WebApplicationBuilderExtensions.cs
@@ -9,7 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection;
public static class WebApplicationBuilderExtensions
{
- public async static Task AddApplicationAsync(
+ public static async Task AddApplicationAsync(
[NotNull] this WebApplicationBuilder builder,
[CanBeNull] Action optionsAction = null)
where TStartupModule : IAbpModule
@@ -18,10 +18,14 @@ public static class WebApplicationBuilderExtensions
{
options.Services.ReplaceConfiguration(builder.Configuration);
optionsAction?.Invoke(options);
+ if (options.Environment.IsNullOrWhiteSpace())
+ {
+ options.Environment = builder.Environment.EnvironmentName;
+ }
});
}
- public async static Task AddApplicationAsync(
+ public static async Task AddApplicationAsync(
[NotNull] this WebApplicationBuilder builder,
[NotNull] Type startupModuleType,
[CanBeNull] Action optionsAction = null)
@@ -30,6 +34,10 @@ public static class WebApplicationBuilderExtensions
{
options.Services.ReplaceConfiguration(builder.Configuration);
optionsAction?.Invoke(options);
+ if (options.Environment.IsNullOrWhiteSpace())
+ {
+ options.Environment = builder.Environment.EnvironmentName;
+ }
});
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs
index 9f613122c8..bad4b7c491 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs
@@ -1,10 +1,8 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
+using System;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders;
-using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.VirtualFileSystem;
using Volo.Abp.Auditing;
@@ -31,6 +29,15 @@ namespace Volo.Abp.AspNetCore;
)]
public class AbpAspNetCoreModule : AbpModule
{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ var abpHostEnvironment = context.Services.GetSingletonInstance();
+ if (abpHostEnvironment.EnvironmentName.IsNullOrWhiteSpace())
+ {
+ abpHostEnvironment.EnvironmentName = context.Services.GetHostingEnvironment().EnvironmentName;
+ }
+ }
+
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAuthorization();
diff --git a/framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasEntityVersion.cs b/framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasEntityVersion.cs
new file mode 100644
index 0000000000..a6abf99040
--- /dev/null
+++ b/framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasEntityVersion.cs
@@ -0,0 +1,12 @@
+namespace Volo.Abp.Auditing;
+
+///
+/// A version value that is increased whenever the entity is changed.
+///
+public interface IHasEntityVersion
+{
+ ///
+ /// A version value that is increased whenever the entity is changed.
+ ///
+ int EntityVersion { get; }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs
index 16632420d7..335916f068 100644
--- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs
+++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs
@@ -39,6 +39,14 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
SetDeleterId(targetObject);
}
+ public virtual void IncrementEntityVersionProperty(object targetObject)
+ {
+ if (targetObject is IHasEntityVersion objectWithEntityVersion)
+ {
+ ObjectHelper.TrySetProperty(objectWithEntityVersion, x => x.EntityVersion, x => x.EntityVersion + 1);
+ }
+ }
+
protected virtual void SetCreationTime(object targetObject)
{
if (!(targetObject is IHasCreationTime objectWithCreationTime))
@@ -177,4 +185,4 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
ObjectHelper.TrySetProperty(deletionAuditedObject, x => x.DeleterId, () => CurrentUser.Id);
}
-}
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditPropertySetter.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditPropertySetter.cs
index 80d1b89fe5..9e5cd0120d 100644
--- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditPropertySetter.cs
+++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditPropertySetter.cs
@@ -7,4 +7,6 @@ public interface IAuditPropertySetter
void SetModificationProperties(object targetObject);
void SetDeletionProperties(object targetObject);
+
+ void IncrementEntityVersionProperty(object targetObject);
}
diff --git a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs
index 4d03bb2597..5085eecdc2 100644
--- a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs
+++ b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs
@@ -36,6 +36,7 @@ public static class AbpAutoMapperExtensibleDtoExtensions
return result;
})
)
+ .ForSourceMember(x => x.ExtraProperties, x => x.DoNotValidate())
.AfterMap((source, destination, context) =>
{
if (mapToRegularProperties)
diff --git a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperConfigurationContext.cs b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperConfigurationContext.cs
index ee7630c122..c8754abd37 100644
--- a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperConfigurationContext.cs
+++ b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperConfigurationContext.cs
@@ -6,6 +6,7 @@ namespace Volo.Abp.AutoMapper;
public class AbpAutoMapperConfigurationContext : IAbpAutoMapperConfigurationContext
{
public IMapperConfigurationExpression MapperConfiguration { get; }
+
public IServiceProvider ServiceProvider { get; }
public AbpAutoMapperConfigurationContext(
diff --git a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs
index 18fef842da..6c1ba35ee3 100644
--- a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs
+++ b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs
@@ -26,45 +26,37 @@ public class AbpAutoMapperModule : AbpModule
{
context.Services.AddAutoMapperObjectMapper();
- context.Services.AddSingleton(CreateMappings);
- context.Services.AddSingleton(provider => provider.GetRequiredService());
- }
-
- private MapperAccessor CreateMappings(IServiceProvider serviceProvider)
- {
- using (var scope = serviceProvider.CreateScope())
+ context.Services.AddSingleton(sp =>
{
- var options = scope.ServiceProvider.GetRequiredService>().Value;
-
- void ConfigureAll(IAbpAutoMapperConfigurationContext ctx)
+ using (var scope = sp.CreateScope())
{
- foreach (var configurator in options.Configurators)
+ var options = scope.ServiceProvider.GetRequiredService>().Value;
+
+ var mapperConfiguration = new MapperConfiguration(mapperConfigurationExpression =>
{
- configurator(ctx);
- }
- }
+ var autoMapperConfigurationContext = new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider);
- options.Configurators.Insert(0, ctx => ctx.MapperConfiguration.ConstructServicesUsing(serviceProvider.GetService));
+ foreach (var configurator in options.Configurators)
+ {
+ configurator(autoMapperConfigurationContext);
+ }
+ });
- void ValidateAll(IConfigurationProvider config)
- {
foreach (var profileType in options.ValidatingProfiles)
{
- config.Internal().AssertConfigurationIsValid(((Profile)Activator.CreateInstance(profileType)).ProfileName);
+ mapperConfiguration.Internal().AssertConfigurationIsValid(((Profile)Activator.CreateInstance(profileType)).ProfileName);
}
- }
- var mapperConfiguration = new MapperConfiguration(mapperConfigurationExpression =>
- {
- ConfigureAll(new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider));
- });
+ return mapperConfiguration;
+ }
+ });
- ValidateAll(mapperConfiguration);
+ context.Services.AddTransient(sp => sp.GetRequiredService().CreateMapper(sp.GetService));
- return new MapperAccessor
- {
- Mapper = new Mapper(mapperConfiguration)
- };
- }
+ context.Services.AddTransient(sp => new MapperAccessor()
+ {
+ Mapper = sp.GetRequiredService()
+ });
+ context.Services.AddTransient(provider => provider.GetRequiredService());
}
}
diff --git a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperAutoObjectMappingProvider.cs b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperAutoObjectMappingProvider.cs
index 50c41c2ef5..fcddf444ca 100644
--- a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperAutoObjectMappingProvider.cs
+++ b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperAutoObjectMappingProvider.cs
@@ -1,5 +1,4 @@
using Volo.Abp.ObjectMapping;
-
namespace Volo.Abp.AutoMapper;
public class AutoMapperAutoObjectMappingProvider : AutoMapperAutoObjectMappingProvider, IAutoObjectMappingProvider
diff --git a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/MapperAccessor.cs b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/MapperAccessor.cs
index d35049eeff..7e2f94819f 100644
--- a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/MapperAccessor.cs
+++ b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/MapperAccessor.cs
@@ -1,4 +1,4 @@
-using AutoMapper;
+using AutoMapper;
namespace Volo.Abp.AutoMapper;
diff --git a/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs b/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs
index 5a66e1b247..198dce54fc 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs
+++ b/framework/src/Volo.Abp.BlazoriseUI/AbpBlazoriseUIModule.cs
@@ -1,5 +1,7 @@
using Blazorise;
+using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Application;
using Volo.Abp.AspNetCore.Components.Web;
using Volo.Abp.Authorization;
@@ -11,7 +13,7 @@ namespace Volo.Abp.BlazoriseUI;
typeof(AbpAspNetCoreComponentsWebModule),
typeof(AbpDddApplicationContractsModule),
typeof(AbpAuthorizationModule)
- )]
+)]
public class AbpBlazoriseUIModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
@@ -27,6 +29,7 @@ public class AbpBlazoriseUIModule : AbpModule
options.DebounceInterval = 800;
});
+ context.Services.Replace(ServiceDescriptor.Scoped());
context.Services.AddSingleton(typeof(AbpBlazorMessageLocalizerHelper<>));
}
-}
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BlobStoring.Minio/Volo.Abp.BlobStoring.Minio.csproj b/framework/src/Volo.Abp.BlobStoring.Minio/Volo.Abp.BlobStoring.Minio.csproj
index cd20d43162..2b1b19e92f 100644
--- a/framework/src/Volo.Abp.BlobStoring.Minio/Volo.Abp.BlobStoring.Minio.csproj
+++ b/framework/src/Volo.Abp.BlobStoring.Minio/Volo.Abp.BlobStoring.Minio.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs
index 8c72e7f023..3b62cd4983 100644
--- a/framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs
+++ b/framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs
@@ -37,7 +37,11 @@ public class MinioBlobProvider : BlobProviderBase, ITransientDependency
await CreateBucketIfNotExists(client, containerName);
}
- await client.PutObjectAsync(containerName, blobName, args.BlobStream, args.BlobStream.Length);
+ await client.PutObjectAsync(new PutObjectArgs()
+ .WithBucket(containerName)
+ .WithObject(blobName)
+ .WithStreamData(args.BlobStream)
+ .WithObjectSize(args.BlobStream.Length));
}
public override async Task DeleteAsync(BlobProviderDeleteArgs args)
@@ -46,13 +50,14 @@ public class MinioBlobProvider : BlobProviderBase, ITransientDependency
var client = GetMinioClient(args);
var containerName = GetContainerName(args);
- if (await BlobExistsAsync(client, containerName, blobName))
+ if (!await BlobExistsAsync(client, containerName, blobName))
{
- await client.RemoveObjectAsync(containerName, blobName);
- return true;
+ return false;
}
- return false;
+ await client.RemoveObjectAsync(new RemoveObjectArgs().WithBucket(containerName).WithObject(blobName));
+ return true;
+
}
public override async Task ExistsAsync(BlobProviderExistsArgs args)
@@ -76,7 +81,7 @@ public class MinioBlobProvider : BlobProviderBase, ITransientDependency
}
var memoryStream = new MemoryStream();
- await client.GetObjectAsync(containerName, blobName, (stream) =>
+ await client.GetObjectAsync(new GetObjectArgs().WithBucket(containerName).WithObject(blobName).WithCallbackStream(stream =>
{
if (stream != null)
{
@@ -87,7 +92,7 @@ public class MinioBlobProvider : BlobProviderBase, ITransientDependency
{
memoryStream = null;
}
- });
+ }));
return memoryStream;
}
@@ -110,20 +115,20 @@ public class MinioBlobProvider : BlobProviderBase, ITransientDependency
protected virtual async Task CreateBucketIfNotExists(MinioClient client, string containerName)
{
- if (!await client.BucketExistsAsync(containerName))
+ if (!await client.BucketExistsAsync(new BucketExistsArgs().WithBucket(containerName)))
{
- await client.MakeBucketAsync(containerName);
+ await client.MakeBucketAsync(new MakeBucketArgs().WithBucket(containerName));
}
}
protected virtual async Task BlobExistsAsync(MinioClient client, string containerName, string blobName)
{
// Make sure Blob Container exists.
- if (await client.BucketExistsAsync(containerName))
+ if (await client.BucketExistsAsync(new BucketExistsArgs().WithBucket(containerName)))
{
try
{
- await client.StatObjectAsync(containerName, blobName);
+ await client.StatObjectAsync(new StatObjectArgs().WithBucket(containerName).WithObject(blobName));
}
catch (Exception e)
{
diff --git a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs
index 55bb3dc8cc..61f2ef9de8 100644
--- a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs
+++ b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs
@@ -14,6 +14,7 @@ public class Program
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
+ var loggerOutputTemplate = "{Message:lj}{NewLine}{Exception}";
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
@@ -26,8 +27,8 @@ public class Program
.MinimumLevel.Override("Volo.Abp.Cli", LogEventLevel.Information)
#endif
.Enrich.FromLogContext()
- .WriteTo.File(Path.Combine(CliPaths.Log, "abp-cli-logs.txt"))
- .WriteTo.Console(theme: AnsiConsoleTheme.Sixteen)
+ .WriteTo.File(Path.Combine(CliPaths.Log, "abp-cli-logs.txt"), outputTemplate: loggerOutputTemplate)
+ .WriteTo.Console(theme: AnsiConsoleTheme.Sixteen, outputTemplate: loggerOutputTemplate)
.CreateLogger();
using (var application = AbpApplicationFactory.Create(
diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs
index 278814780d..3ad02c7f1d 100644
--- a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs
+++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs
@@ -39,16 +39,22 @@ public static class ServiceCollectionApplicationExtensions
{
return await AbpApplicationFactory.CreateAsync(startupModuleType, services, optionsAction);
}
-
+
[CanBeNull]
public static string GetApplicationName(this IServiceCollection services)
{
return services.GetSingletonInstance().ApplicationName;
}
-
+
[NotNull]
public static string GetApplicationInstanceId(this IServiceCollection services)
{
return services.GetSingletonInstance().InstanceId;
}
+
+ [NotNull]
+ public static IAbpHostEnvironment GetAbpHostEnvironment(this IServiceCollection services)
+ {
+ return services.GetSingletonInstance();
+ }
}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
index fd91b09982..609b880566 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
@@ -5,6 +5,7 @@ using System.Reflection;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal;
@@ -51,6 +52,10 @@ public abstract class AbpApplicationBase : IAbpApplication
services.AddSingleton(this);
services.AddSingleton(this);
services.AddSingleton(this);
+ services.AddSingleton(new AbpHostEnvironment()
+ {
+ EnvironmentName = options.Environment
+ });
services.AddCoreServices();
services.AddCoreAbpServices(this, options);
@@ -224,6 +229,8 @@ public abstract class AbpApplicationBase : IAbpApplication
}
_configuredServices = true;
+
+ TryToSetEnvironment(Services);
}
private void CheckMultipleConfigureServices()
@@ -313,6 +320,8 @@ public abstract class AbpApplicationBase : IAbpApplication
}
_configuredServices = true;
+
+ TryToSetEnvironment(Services);
}
private static string GetApplicationName(AbpApplicationCreationOptions options)
@@ -340,4 +349,13 @@ public abstract class AbpApplicationBase : IAbpApplication
return null;
}
+
+ private static void TryToSetEnvironment(IServiceCollection services)
+ {
+ var abpHostEnvironment = services.GetSingletonInstance();
+ if (abpHostEnvironment.EnvironmentName.IsNullOrWhiteSpace())
+ {
+ abpHostEnvironment.EnvironmentName = Environments.Production;
+ }
+ }
}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs
index 24a1bf8ee6..e1f7f4c997 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs
@@ -20,10 +20,13 @@ public class AbpApplicationCreationOptions
public AbpConfigurationBuilderOptions Configuration { get; }
public bool SkipConfigureServices { get; set; }
-
+
[CanBeNull]
public string ApplicationName { get; set; }
+ [CanBeNull]
+ public string Environment { get; set; }
+
public AbpApplicationCreationOptions([NotNull] IServiceCollection services)
{
Services = Check.NotNull(services, nameof(services));
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpHostEnvironment.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpHostEnvironment.cs
new file mode 100644
index 0000000000..a2682909a1
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpHostEnvironment.cs
@@ -0,0 +1,6 @@
+namespace Volo.Abp;
+
+public class AbpHostEnvironment : IAbpHostEnvironment
+{
+ public string EnvironmentName { get; set; }
+}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpHostEnvironmentExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpHostEnvironmentExtensions.cs
new file mode 100644
index 0000000000..57d7a44c06
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpHostEnvironmentExtensions.cs
@@ -0,0 +1,38 @@
+using System;
+using Microsoft.Extensions.Hosting;
+
+namespace Volo.Abp;
+
+public static class AbpHostEnvironmentExtensions
+{
+ public static bool IsDevelopment(this IAbpHostEnvironment hostEnvironment)
+ {
+ Check.NotNull(hostEnvironment, nameof(hostEnvironment));
+
+ return hostEnvironment.IsEnvironment(Environments.Development);
+ }
+
+ public static bool IsStaging(this IAbpHostEnvironment hostEnvironment)
+ {
+ Check.NotNull(hostEnvironment, nameof(hostEnvironment));
+
+ return hostEnvironment.IsEnvironment(Environments.Staging);
+ }
+
+ public static bool IsProduction(this IAbpHostEnvironment hostEnvironment)
+ {
+ Check.NotNull(hostEnvironment, nameof(hostEnvironment));
+
+ return hostEnvironment.IsEnvironment(Environments.Production);
+ }
+
+ public static bool IsEnvironment(this IAbpHostEnvironment hostEnvironment, string environmentName)
+ {
+ Check.NotNull(hostEnvironment, nameof(hostEnvironment));
+
+ return string.Equals(
+ hostEnvironment.EnvironmentName,
+ environmentName,
+ StringComparison.OrdinalIgnoreCase);
+ }
+}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs
index c137f1fc9c..9007b334b4 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs
@@ -60,10 +60,11 @@ public static class AbpCrossCuttingConcerns
public static IDisposable Applying(object obj, params string[] concerns)
{
AddApplied(obj, concerns);
- return new DisposeAction(() =>
+ return new DisposeAction>(static (state) =>
{
+ var (obj, concerns) = state;
RemoveApplied(obj, concerns);
- });
+ }, (obj, concerns));
}
public static string[] GetApplieds(object obj)
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs
index ba0b61eed7..0bb9cf46a8 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/DisposeAction.cs
@@ -5,7 +5,7 @@ namespace Volo.Abp;
///
/// This class can be used to provide an action when
-/// Dipose method is called.
+/// Dispose method is called.
///
public class DisposeAction : IDisposable
{
@@ -27,3 +27,34 @@ public class DisposeAction : IDisposable
_action();
}
}
+
+///
+/// This class can be used to provide an action when
+/// Dispose method is called.
+/// The type of the parameter of the action.
+///
+public class DisposeAction : IDisposable
+{
+ private readonly Action _action;
+
+ [CanBeNull]
+ private readonly T _parameter;
+
+ ///
+ /// Creates a new object.
+ ///
+ /// Action to be executed when this object is disposed.
+ /// The parameter of the action.
+ public DisposeAction(Action action, T parameter)
+ {
+ Check.NotNull(action, nameof(action));
+
+ _action = action;
+ _parameter = parameter;
+ }
+
+ public void Dispose()
+ {
+ _action(_parameter);
+ }
+}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IAbpHostEnvironment.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpHostEnvironment.cs
new file mode 100644
index 0000000000..383361b9ed
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/IAbpHostEnvironment.cs
@@ -0,0 +1,6 @@
+namespace Volo.Abp;
+
+public interface IAbpHostEnvironment
+{
+ string EnvironmentName { get; set; }
+}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs
index bb30087056..9743898496 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs
@@ -83,6 +83,6 @@ public static class DirectoryHelper
Directory.SetCurrentDirectory(targetDirectory);
- return new DisposeAction(() => { Directory.SetCurrentDirectory(currentDirectory); });
+ return new DisposeAction(Directory.SetCurrentDirectory, currentDirectory);
}
}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
index adf2890b21..d2e15fdf25 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
@@ -29,11 +29,12 @@ public static class CultureHelper
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = uiCulture ?? culture;
- return new DisposeAction(() =>
+ return new DisposeAction>(static (state) =>
{
+ var (currentCulture, currentUiCulture) = state;
CultureInfo.CurrentCulture = currentCulture;
CultureInfo.CurrentUICulture = currentUiCulture;
- });
+ }, (currentCulture, currentUiCulture));
}
public static bool IsRtl => CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs
index 79693a9acb..7eaed3821b 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/SemaphoreSlimExtensions.cs
@@ -80,9 +80,9 @@ public static class SemaphoreSlimExtensions
private static IDisposable GetDispose(this SemaphoreSlim semaphoreSlim)
{
- return new DisposeAction(() =>
+ return new DisposeAction(static (semaphoreSlim) =>
{
semaphoreSlim.Release();
- });
+ }, semaphoreSlim);
}
}
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs
index 4c1c068a81..8a2983b076 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs
@@ -20,3 +20,8 @@ public class EntityEto : EtoBase
KeysAsString = keysAsString;
}
}
+
+public abstract class EntityEto : IEntityEto
+{
+ public TKey Id { get; set; }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntitySynchronizer.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntitySynchronizer.cs
new file mode 100644
index 0000000000..b971acc25f
--- /dev/null
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntitySynchronizer.cs
@@ -0,0 +1,172 @@
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Volo.Abp.Auditing;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Domain.Repositories;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.ObjectMapping;
+using Volo.Abp.Uow;
+
+namespace Volo.Abp.Domain.Entities.Events.Distributed;
+
+public abstract class EntitySynchronizer :
+ EntitySynchronizer
+ where TEntity : class, IEntity
+ where TSourceEntityEto : IEntityEto
+{
+ protected new IRepository Repository { get; }
+
+ protected EntitySynchronizer(IObjectMapper objectMapper, IRepository repository) :
+ base(objectMapper, repository)
+ {
+ Repository = repository;
+ }
+
+ protected override Task FindLocalEntityAsync(TSourceEntityEto eto)
+ {
+ return Repository.FindAsync(eto.Id);
+ }
+}
+
+public abstract class EntitySynchronizer :
+ IDistributedEventHandler>,
+ IDistributedEventHandler>,
+ IDistributedEventHandler>,
+ ITransientDependency
+ where TEntity : class, IEntity
+{
+ protected IObjectMapper ObjectMapper { get; }
+ protected IRepository Repository { get; }
+
+ protected bool IgnoreEntityCreatedEvent { get; set; }
+ protected bool IgnoreEntityUpdatedEvent { get; set; }
+ protected bool IgnoreEntityDeletedEvent { get; set; }
+
+ public EntitySynchronizer(
+ IObjectMapper objectMapper,
+ IRepository repository)
+ {
+ ObjectMapper = objectMapper;
+ Repository = repository;
+ }
+
+ public virtual async Task HandleEventAsync(EntityCreatedEto eventData)
+ {
+ if (IgnoreEntityCreatedEvent)
+ {
+ return;
+ }
+
+ await TryCreateOrUpdateEntityAsync(eventData.Entity);
+ }
+
+ public virtual async Task HandleEventAsync(EntityUpdatedEto eventData)
+ {
+ if (IgnoreEntityUpdatedEvent)
+ {
+ return;
+ }
+
+ await TryCreateOrUpdateEntityAsync(eventData.Entity);
+ }
+
+ public virtual async Task HandleEventAsync(EntityDeletedEto eventData)
+ {
+ if (IgnoreEntityDeletedEvent)
+ {
+ return;
+ }
+
+ await TryDeleteEntityAsync(eventData.Entity);
+ }
+
+ [UnitOfWork]
+ protected virtual async Task TryCreateOrUpdateEntityAsync(TSourceEntityEto eto)
+ {
+ var localEntity = await FindLocalEntityAsync(eto);
+
+ if (!await IsEtoNewerAsync(eto, localEntity))
+ {
+ return false;
+ }
+
+ if (localEntity == null)
+ {
+ localEntity = await MapToEntityAsync(eto);
+
+ if (localEntity is IHasEntityVersion versionedLocalEntity && eto is IHasEntityVersion versionedEto)
+ {
+ ObjectHelper.TrySetProperty(
+ versionedLocalEntity,
+ x => x.EntityVersion,
+ () => versionedEto.EntityVersion
+ );
+ }
+
+ await Repository.InsertAsync(localEntity);
+ }
+ else
+ {
+ await MapToEntityAsync(eto, localEntity);
+
+ if (localEntity is IHasEntityVersion versionedLocalEntity && eto is IHasEntityVersion versionedEto)
+ {
+ /* The version will auto-increment by one when the repository updates the entity.
+ * So, we are decreasing it as a workaround here.
+ */
+ var entityVersion = versionedEto.EntityVersion - 1;
+ ObjectHelper.TrySetProperty(
+ versionedLocalEntity,
+ x => x.EntityVersion,
+ () => entityVersion
+ );
+ }
+
+ await Repository.UpdateAsync(localEntity);
+ }
+
+ return true;
+ }
+
+ protected virtual Task MapToEntityAsync(TSourceEntityEto eto)
+ {
+ return Task.FromResult(ObjectMapper.Map(eto));
+ }
+
+ protected virtual Task MapToEntityAsync(TSourceEntityEto eto, TEntity localEntity)
+ {
+ ObjectMapper.Map(eto, localEntity);
+ return Task.CompletedTask;
+ }
+
+ [UnitOfWork]
+ protected virtual async Task TryDeleteEntityAsync(TSourceEntityEto eto)
+ {
+ var localEntity = await FindLocalEntityAsync(eto);
+
+ if (localEntity == null)
+ {
+ return false;
+ }
+
+ await Repository.DeleteAsync(localEntity, true);
+
+ return true;
+ }
+
+ [ItemCanBeNull]
+ protected abstract Task FindLocalEntityAsync(TSourceEntityEto eto);
+
+ protected virtual Task IsEtoNewerAsync(TSourceEntityEto eto, [CanBeNull] TEntity localEntity)
+ {
+ if (localEntity is IHasEntityVersion versionedLocalEntity && eto is IHasEntityVersion versionedEto)
+ {
+ /* We are also accepting the same version because
+ * the entity may be updated, but the version might not be changed.
+ */
+ return Task.FromResult(versionedEto.EntityVersion >= versionedLocalEntity.EntityVersion);
+ }
+
+ return Task.FromResult(true);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityEto.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityEto.cs
new file mode 100644
index 0000000000..289f5354fd
--- /dev/null
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityEto.cs
@@ -0,0 +1,9 @@
+namespace Volo.Abp.Domain.Entities.Events.Distributed;
+
+public interface IEntityEto
+{
+ ///
+ /// Unique identifier for this entity.
+ ///
+ TKey Id { get; set; }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
index 70332d54fc..da915626a8 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
@@ -68,6 +68,20 @@ public interface IRepository : IReadOnlyRepository, IBasicRepo
bool autoSave = false,
CancellationToken cancellationToken = default
);
+
+ ///
+ /// Deletes all entities those fit to the given predicate.
+ /// It directly deletes entities from database, without fetching them.
+ /// Some features (like soft-delete, multi-tenancy and audit logging) won't work, so use this method carefully when you need it.
+ /// Use the DeleteAsync method if you need to these features.
+ ///
+ /// A condition to filter entities
+ /// A to observe while waiting for the task to complete.
+ ///
+ Task DeleteDirectAsync(
+ [NotNull] Expression> predicate,
+ CancellationToken cancellationToken = default
+ );
}
public interface IRepository : IRepository, IReadOnlyRepository, IBasicRepository
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
index dbdadcccdb..d843584fc4 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
@@ -63,6 +63,8 @@ public abstract class RepositoryBase : BasicRepositoryBase, IR
public abstract Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default);
+ public abstract Task DeleteDirectAsync(Expression> predicate, CancellationToken cancellationToken = default);
+
protected virtual TQueryable ApplyDataFilters(TQueryable query)
where TQueryable : IQueryable
{
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLInboxConfigExtensions.cs
deleted file mode 100644
index 5579c9f90f..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLInboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class MySQLInboxConfigExtensions
-{
- public static void UseMySQL(this InboxConfig outboxConfig)
- where TDbContext : IHasEventInbox
- {
- outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLOutboxConfigExtensions.cs
deleted file mode 100644
index 97d1d321cb..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/DistributedEvents/MySQLOutboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class MySQLOutboxConfigExtensions
-{
- public static void UseMySQL(this OutboxConfig outboxConfig)
- where TDbContext : IHasEventOutbox
- {
- outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs
deleted file mode 100644
index 63ba8379e0..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface IOracleDbContextEventInbox : IDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
-
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs
deleted file mode 100644
index 32aa01be86..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface IOracleDbContextEventOutbox : IDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs
deleted file mode 100644
index 93d740dc21..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Options;
-using Volo.Abp.EventBus.Distributed;
-using Volo.Abp.Timing;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class OracleDbContextEventInbox : DbContextEventInbox, IOracleDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
- public OracleDbContextEventInbox(
- IDbContextProvider dbContextProvider,
- IClock clock,
- IOptions eventBusBoxesOptions)
- : base(dbContextProvider, clock, eventBusBoxesOptions)
- {
- }
-
- [UnitOfWork]
- public override async Task MarkAsProcessedAsync(Guid id)
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"UPDATE \"{tableName}\" SET \"Processed\" = '1', \"ProcessedTime\" = TO_DATE('{Clock.Now}', 'yyyy-mm-dd hh24:mi:ss') WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- [UnitOfWork]
- public override async Task DeleteOldEventsAsync()
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
- var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
-
- var sql = $"DELETE FROM \"{tableName}\" WHERE \"Processed\" = '1' AND \"CreationTime\" < TO_DATE('{timeToKeepEvents}', 'yyyy-mm-dd hh24:mi:ss')";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- protected virtual string GuidToOracleType(Guid id)
- {
- return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs
deleted file mode 100644
index 0a2286bbb7..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class OracleDbContextEventOutbox : DbContextEventOutbox, IOracleDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
- public OracleDbContextEventOutbox(IDbContextProvider dbContextProvider)
- : base(dbContextProvider)
- {
- }
-
- [UnitOfWork]
- public override async Task DeleteAsync(Guid id)
- {
- var dbContext = (IHasEventOutbox)await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"DELETE FROM \"{tableName}\" WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- protected virtual string GuidToOracleType(Guid id)
- {
- return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs
deleted file mode 100644
index a6cd040103..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class OracleInboxConfigExtensions
-{
- public static void UseOracle(this InboxConfig outboxConfig)
- where TDbContext : IHasEventInbox
- {
- outboxConfig.ImplementationType = typeof(IOracleDbContextEventInbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs
deleted file mode 100644
index 07e5dc004f..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class OracleOutboxConfigExtensions
-{
- public static void UseOracle(this OutboxConfig outboxConfig)
- where TDbContext : IHasEventOutbox
- {
- outboxConfig.ImplementationType = typeof(IOracleDbContextEventOutbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs
index 25b0b088d7..3b4129b272 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs
@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
-using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
@@ -19,8 +18,5 @@ public class AbpEntityFrameworkCoreOracleDevartModule : AbpModule
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary;
}
});
-
- context.Services.AddTransient(typeof(IOracleDbContextEventOutbox<>), typeof(OracleDbContextEventOutbox<>));
- context.Services.AddTransient(typeof(IOracleDbContextEventInbox<>), typeof(OracleDbContextEventInbox<>));
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs
deleted file mode 100644
index 63ba8379e0..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventInbox.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface IOracleDbContextEventInbox : IDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
-
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs
deleted file mode 100644
index 32aa01be86..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/IOracleDbContextEventOutbox.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface IOracleDbContextEventOutbox : IDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs
deleted file mode 100644
index 93d740dc21..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Options;
-using Volo.Abp.EventBus.Distributed;
-using Volo.Abp.Timing;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class OracleDbContextEventInbox : DbContextEventInbox, IOracleDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
- public OracleDbContextEventInbox(
- IDbContextProvider dbContextProvider,
- IClock clock,
- IOptions eventBusBoxesOptions)
- : base(dbContextProvider, clock, eventBusBoxesOptions)
- {
- }
-
- [UnitOfWork]
- public override async Task MarkAsProcessedAsync(Guid id)
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"UPDATE \"{tableName}\" SET \"Processed\" = '1', \"ProcessedTime\" = TO_DATE('{Clock.Now}', 'yyyy-mm-dd hh24:mi:ss') WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- [UnitOfWork]
- public override async Task DeleteOldEventsAsync()
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
- var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
-
- var sql = $"DELETE FROM \"{tableName}\" WHERE \"Processed\" = '1' AND \"CreationTime\" < TO_DATE('{timeToKeepEvents}', 'yyyy-mm-dd hh24:mi:ss')";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- protected virtual string GuidToOracleType(Guid id)
- {
- return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs
deleted file mode 100644
index 0a2286bbb7..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventOutbox.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class OracleDbContextEventOutbox : DbContextEventOutbox, IOracleDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
- public OracleDbContextEventOutbox(IDbContextProvider dbContextProvider)
- : base(dbContextProvider)
- {
- }
-
- [UnitOfWork]
- public override async Task DeleteAsync(Guid id)
- {
- var dbContext = (IHasEventOutbox)await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"DELETE FROM \"{tableName}\" WHERE \"Id\" = HEXTORAW('{GuidToOracleType(id)}')";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- protected virtual string GuidToOracleType(Guid id)
- {
- return BitConverter.ToString(id.ToByteArray()).Replace("-", "").ToUpper();
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs
deleted file mode 100644
index a6cd040103..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleInboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class OracleInboxConfigExtensions
-{
- public static void UseOracle(this InboxConfig outboxConfig)
- where TDbContext : IHasEventInbox
- {
- outboxConfig.ImplementationType = typeof(IOracleDbContextEventInbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs
deleted file mode 100644
index 07e5dc004f..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleOutboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class OracleOutboxConfigExtensions
-{
- public static void UseOracle(this OutboxConfig outboxConfig)
- where TDbContext : IHasEventOutbox
- {
- outboxConfig.ImplementationType = typeof(IOracleDbContextEventOutbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs
index 4347d9446a..54627a90c0 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs
@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
-using Volo.Abp.EntityFrameworkCore.DistributedEvents;
using Volo.Abp.Guids;
using Volo.Abp.Modularity;
@@ -17,8 +16,5 @@ public class AbpEntityFrameworkCoreOracleModule : AbpModule
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary;
}
});
-
- context.Services.AddTransient(typeof(IOracleDbContextEventOutbox<>), typeof(OracleDbContextEventOutbox<>));
- context.Services.AddTransient(typeof(IOracleDbContextEventInbox<>), typeof(OracleDbContextEventInbox<>));
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs
deleted file mode 100644
index b81eb2bff1..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventInbox.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface IPostgreSqlDbContextEventInbox : IDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
-
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs
deleted file mode 100644
index e21632dc4e..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/IPostgreSqlDbContextEventOutbox.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface IPostgreSqlDbContextEventOutbox : IDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs
deleted file mode 100644
index 054ed78ee7..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Options;
-using Volo.Abp.EventBus.Distributed;
-using Volo.Abp.Timing;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class PostgreSqlDbContextEventInbox : DbContextEventInbox, IPostgreSqlDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
- public PostgreSqlDbContextEventInbox(
- IDbContextProvider dbContextProvider,
- IClock clock,
- IOptions eventBusBoxesOptions)
- : base(dbContextProvider, clock, eventBusBoxesOptions)
- {
- }
-
- [UnitOfWork]
- public override async Task MarkAsProcessedAsync(Guid id)
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"UPDATE \"{tableName}\" SET \"Processed\" = '1', \"ProcessedTime\" = '{Clock.Now}' WHERE \"Id\" = '{id}'";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- [UnitOfWork]
- public override async Task DeleteOldEventsAsync()
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
- var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
-
- var sql = $"DELETE FROM \"{tableName}\" WHERE \"Processed\" = '1' AND \"CreationTime\" < '{timeToKeepEvents}'";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventOutbox.cs
deleted file mode 100644
index 7c5ef12fa7..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventOutbox.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class PostgreSqlDbContextEventOutbox : DbContextEventOutbox, IPostgreSqlDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
- public PostgreSqlDbContextEventOutbox(IDbContextProvider dbContextProvider) : base(dbContextProvider)
- {
- }
-
- [UnitOfWork]
- public async override Task DeleteAsync(Guid id)
- {
- var dbContext = (IHasEventOutbox)await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"DELETE FROM \"{tableName}\" WHERE \"Id\" = '{id}'";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlInboxConfigExtensions.cs
deleted file mode 100644
index 57d50f24eb..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlInboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class PostgreSqlInboxConfigExtensions
-{
- public static void UseNpgsql(this InboxConfig outboxConfig)
- where TDbContext : IHasEventInbox
- {
- outboxConfig.ImplementationType = typeof(IPostgreSqlDbContextEventInbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlOutboxConfigExtensions.cs
deleted file mode 100644
index 3d88398068..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlOutboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class PostgreSqlOutboxConfigExtensions
-{
- public static void UseNpgsql(this OutboxConfig outboxConfig)
- where TDbContext : IHasEventOutbox
- {
- outboxConfig.ImplementationType = typeof(IPostgreSqlDbContextEventOutbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs
index 8fffb8359c..2b679cccef 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs
@@ -1,6 +1,4 @@
-using Microsoft.Extensions.DependencyInjection;
-using Volo.Abp.EntityFrameworkCore.DistributedEvents;
-using Volo.Abp.Guids;
+using Volo.Abp.Guids;
using Volo.Abp.Modularity;
namespace Volo.Abp.EntityFrameworkCore.PostgreSql;
@@ -19,8 +17,5 @@ public class AbpEntityFrameworkCorePostgreSqlModule : AbpModule
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString;
}
});
-
- context.Services.AddTransient(typeof(IPostgreSqlDbContextEventOutbox<>), typeof(PostgreSqlDbContextEventOutbox<>));
- context.Services.AddTransient(typeof(IPostgreSqlDbContextEventInbox<>), typeof(PostgreSqlDbContextEventInbox<>));
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerInboxConfigExtensions.cs
deleted file mode 100644
index e5ac47a18f..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerInboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class SqlServerInboxConfigExtensions
-{
- public static void UseSqlServer(this InboxConfig outboxConfig)
- where TDbContext : IHasEventInbox
- {
- outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerOutboxConfigExtensions.cs
deleted file mode 100644
index e69005e6c9..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlServerOutboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class SqlServerOutboxConfigExtensions
-{
- public static void UseSqlServer(this OutboxConfig outboxConfig)
- where TDbContext : IHasEventOutbox
- {
- outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteInboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteInboxConfigExtensions.cs
deleted file mode 100644
index 317ca6fabc..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteInboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class SqliteInboxConfigExtensions
-{
- public static void UseSqlite(this InboxConfig outboxConfig)
- where TDbContext : IHasEventInbox
- {
- outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventInbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteOutboxConfigExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteOutboxConfigExtensions.cs
deleted file mode 100644
index e533b94b92..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqliteOutboxConfigExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.EventBus.Distributed;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public static class SqliteOutboxConfigExtensions
-{
- public static void UseSqlite(this OutboxConfig outboxConfig)
- where TDbContext : IHasEventOutbox
- {
- outboxConfig.ImplementationType = typeof(ISqlRawDbContextEventOutbox);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
index ab4cd0049f..e9e1f2c618 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
@@ -304,6 +304,13 @@ public class EfCoreRepository : RepositoryBase, IE
}
}
+ public override async Task DeleteDirectAsync(Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ var dbContext = await GetDbContextAsync();
+ var dbSet = dbContext.Set();
+ await dbSet.Where(predicate).ExecuteDeleteAsync(GetCancellationToken(cancellationToken));
+ }
+
public virtual async Task EnsureCollectionLoadedAsync(
TEntity entity,
Expression>> propertyExpression,
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
index 411c0a4519..77cc348e80 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
@@ -459,6 +459,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext,
{
if (entry.State == EntityState.Modified && entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never))
{
+ IncrementEntityVersionProperty(entry);
SetModificationAuditProperties(entry);
if (entry.Entity is ISoftDelete && entry.Entity.As().IsDeleted)
@@ -574,6 +575,11 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext,
AuditPropertySetter?.SetDeletionProperties(entry.Entity);
}
+ protected virtual void IncrementEntityVersionProperty(EntityEntry entry)
+ {
+ AuditPropertySetter?.IncrementEntityVersionProperty(entry.Entity);
+ }
+
protected virtual void ConfigureBaseProperties(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType)
where TEntity : class
{
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs
index 7b58a91fc7..c98ef478d4 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreModule.cs
@@ -28,8 +28,5 @@ public class AbpEntityFrameworkCoreModule : AbpModule
context.Services.TryAddTransient(typeof(IDbContextProvider<>), typeof(UnitOfWorkDbContextProvider<>));
context.Services.AddTransient(typeof(IDbContextEventOutbox<>), typeof(DbContextEventOutbox<>));
context.Services.AddTransient(typeof(IDbContextEventInbox<>), typeof(DbContextEventInbox<>));
-
- context.Services.AddTransient(typeof(ISqlRawDbContextEventOutbox<>), typeof(SqlRawDbContextEventOutbox<>));
- context.Services.AddTransient(typeof(ISqlRawDbContextEventInbox<>), typeof(SqlRawDbContextEventInbox<>));
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs
index 1035716491..f5953ea3b1 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs
@@ -32,10 +32,7 @@ public class DbContextEventInbox : IDbContextEventInbox
public virtual async Task EnqueueAsync(IncomingEventInfo incomingEvent)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
-
- dbContext.IncomingEvents.Add(
- new IncomingEventRecord(incomingEvent)
- );
+ dbContext.IncomingEvents.Add(new IncomingEventRecord(incomingEvent));
}
[UnitOfWork]
@@ -60,11 +57,8 @@ public class DbContextEventInbox : IDbContextEventInbox
public virtual async Task MarkAsProcessedAsync(Guid id)
{
var dbContext = await DbContextProvider.GetDbContextAsync();
- var incomingEvent = await dbContext.IncomingEvents.FindAsync(id);
- if (incomingEvent != null)
- {
- incomingEvent.MarkAsProcessed(Clock.Now);
- }
+ await dbContext.IncomingEvents.Where(x => x.Id == id).ExecuteUpdateAsync(x =>
+ x.SetProperty(p => p.Processed, _ => true).SetProperty(p => p.ProcessedTime, _ => Clock.Now));
}
[UnitOfWork]
@@ -79,9 +73,8 @@ public class DbContextEventInbox : IDbContextEventInbox
{
var dbContext = await DbContextProvider.GetDbContextAsync();
var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
- var oldEvents = await dbContext.IncomingEvents
+ await dbContext.IncomingEvents
.Where(x => x.Processed && x.CreationTime < timeToKeepEvents)
- .ToListAsync();
- dbContext.IncomingEvents.RemoveRange(oldEvents);
+ .ExecuteDeleteAsync();
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs
index c2816ea4c3..fecfd1e8ce 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventOutbox.cs
@@ -24,9 +24,7 @@ public class DbContextEventOutbox : IDbContextEventOutbox : IDbContextEventOutbox x.Id == id).ExecuteDeleteAsync();
}
[UnitOfWork]
public virtual async Task DeleteManyAsync(IEnumerable ids)
{
var dbContext = (IHasEventOutbox)await DbContextProvider.GetDbContextAsync();
- var outgoingEvents = await dbContext.OutgoingEvents.Where(x => ids.Contains(x.Id)).ToListAsync();
- if (outgoingEvents.Any())
- {
- dbContext.RemoveRange(outgoingEvents);
- }
+ await dbContext.OutgoingEvents.Where(x => ids.Contains(x.Id)).ExecuteDeleteAsync();
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs
deleted file mode 100644
index 73d324eedc..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventInbox.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface ISqlRawDbContextEventInbox : IDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs
deleted file mode 100644
index 7a6608146a..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/ISqlRawDbContextEventOutbox.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public interface ISqlRawDbContextEventOutbox : IDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs
deleted file mode 100644
index 0aa99e7df7..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Options;
-using Volo.Abp.EventBus.Distributed;
-using Volo.Abp.Timing;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class SqlRawDbContextEventInbox : DbContextEventInbox, ISqlRawDbContextEventInbox
- where TDbContext : IHasEventInbox
-{
- public SqlRawDbContextEventInbox(
- IDbContextProvider dbContextProvider,
- IClock clock,
- IOptions eventBusBoxesOptions)
- : base(dbContextProvider, clock, eventBusBoxesOptions)
- {
- }
-
- [UnitOfWork]
- public override async Task MarkAsProcessedAsync(Guid id)
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"UPDATE {tableName} SET Processed = '1', ProcessedTime = '{Clock.Now}' WHERE Id = '{id.ToString().ToUpper()}'";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-
- [UnitOfWork]
- public override async Task DeleteOldEventsAsync()
- {
- var dbContext = await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.IncomingEvents.EntityType.GetSchemaQualifiedTableName();
- var timeToKeepEvents = Clock.Now - EventBusBoxesOptions.WaitTimeToDeleteProcessedInboxEvents;
-
- var sql = $"DELETE FROM {tableName} WHERE Processed = '1' AND CreationTime < '{timeToKeepEvents}'";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventOutbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventOutbox.cs
deleted file mode 100644
index 4c3717d80d..0000000000
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventOutbox.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Volo.Abp.Uow;
-
-namespace Volo.Abp.EntityFrameworkCore.DistributedEvents;
-
-public class SqlRawDbContextEventOutbox : DbContextEventOutbox, ISqlRawDbContextEventOutbox
- where TDbContext : IHasEventOutbox
-{
- public SqlRawDbContextEventOutbox(IDbContextProvider dbContextProvider)
- : base(dbContextProvider)
- {
- }
-
- [UnitOfWork]
- public override async Task DeleteAsync(Guid id)
- {
- var dbContext = (IHasEventOutbox)await DbContextProvider.GetDbContextAsync();
- var tableName = dbContext.OutgoingEvents.EntityType.GetSchemaQualifiedTableName();
-
- var sql = $"DELETE FROM {tableName} WHERE Id = '{id.ToString().ToUpper()}'";
- await dbContext.Database.ExecuteSqlRawAsync(sql);
- }
-}
diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs
index db59346465..32952a7050 100644
--- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs
+++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs
@@ -29,6 +29,7 @@ public class IdentityModelAuthenticationService : IIdentityModelAuthenticationSe
protected IdentityModelHttpRequestMessageOptions IdentityModelHttpRequestMessageOptions { get; }
protected IDistributedCache TokenCache { get; }
protected IDistributedCache DiscoveryDocumentCache { get; }
+ protected IAbpHostEnvironment AbpHostEnvironment { get; }
public IdentityModelAuthenticationService(
IOptions options,
@@ -37,7 +38,8 @@ public class IdentityModelAuthenticationService : IIdentityModelAuthenticationSe
ICurrentTenant currentTenant,
IOptions identityModelHttpRequestMessageOptions,
IDistributedCache tokenCache,
- IDistributedCache discoveryDocumentCache)
+ IDistributedCache discoveryDocumentCache,
+ IAbpHostEnvironment abpHostEnvironment)
{
ClientOptions = options.Value;
CancellationTokenProvider = cancellationTokenProvider;
@@ -45,6 +47,7 @@ public class IdentityModelAuthenticationService : IIdentityModelAuthenticationSe
CurrentTenant = currentTenant;
TokenCache = tokenCache;
DiscoveryDocumentCache = discoveryDocumentCache;
+ AbpHostEnvironment = abpHostEnvironment;
IdentityModelHttpRequestMessageOptions = identityModelHttpRequestMessageOptions.Value;
Logger = NullLogger.Instance;
}
@@ -97,11 +100,12 @@ public class IdentityModelAuthenticationService : IIdentityModelAuthenticationSe
}
tokenCacheItem = new IdentityModelTokenCacheItem(tokenResponse.AccessToken);
- await TokenCache.SetAsync(cacheKey, tokenCacheItem,
- new DistributedCacheEntryOptions
- {
- AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(configuration.CacheAbsoluteExpiration)
- });
+ await TokenCache.SetAsync(cacheKey, tokenCacheItem, new DistributedCacheEntryOptions
+ {
+ AbsoluteExpirationRelativeToNow = AbpHostEnvironment.IsDevelopment()
+ ? TimeSpan.FromSeconds(5)
+ : TimeSpan.FromSeconds(configuration.CacheAbsoluteExpiration)
+ });
}
return tokenCacheItem.AccessToken;
@@ -146,7 +150,9 @@ public class IdentityModelAuthenticationService : IIdentityModelAuthenticationSe
await DiscoveryDocumentCache.SetAsync(tokenEndpointUrlCacheKey, discoveryDocumentCacheItem,
new DistributedCacheEntryOptions
{
- AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(configuration.CacheAbsoluteExpiration)
+ AbsoluteExpirationRelativeToNow = AbpHostEnvironment.IsDevelopment()
+ ? TimeSpan.FromSeconds(5)
+ : TimeSpan.FromSeconds(configuration.CacheAbsoluteExpiration)
});
}
diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
index 61bb9745e2..cfddbc6464 100644
--- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
+++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
@@ -157,6 +157,11 @@ public class MemoryDbRepository : RepositoryBase : RepositoryBase> predicate, CancellationToken cancellationToken = default)
+ {
+ await DeleteAsync(predicate, true, cancellationToken);
+ }
+
public override async Task InsertAsync(
TEntity entity,
bool autoSave = false,
@@ -222,6 +232,7 @@ public class MemoryDbRepository : RepositoryBase
{
cancellationToken = GetCancellationToken(cancellationToken);
+ IncrementEntityVersionProperty(entity);
SetModificationAuditProperties(entity);
if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted)
@@ -484,6 +485,20 @@ public class MongoDbRepository
await DeleteManyAsync(entities, autoSave, cancellationToken);
}
+ public override async Task DeleteDirectAsync(Expression> predicate, CancellationToken cancellationToken = default)
+ {
+ cancellationToken = GetCancellationToken(cancellationToken);
+
+ var dbContext = await GetDbContextAsync(cancellationToken);
+ var collection = dbContext.Collection();
+
+ await collection.DeleteManyAsync(
+ dbContext.SessionHandle,
+ Builders.Filter.Where(predicate),
+ cancellationToken: cancellationToken
+ );
+ }
+
[Obsolete("Use GetQueryableAsync method.")]
protected override IQueryable GetQueryable()
{
@@ -654,6 +669,11 @@ public class MongoDbRepository
AuditPropertySetter.SetDeletionProperties(entity);
}
+ protected virtual void IncrementEntityVersionProperty(TEntity entity)
+ {
+ AuditPropertySetter.IncrementEntityVersionProperty(entity);
+ }
+
protected virtual void TriggerDomainEvents(object entity)
{
var generatesDomainEventsEntity = entity as IGeneratesDomainEvents;
diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs
index a86ce2a40c..fb5d2b147c 100644
--- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs
+++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenant.cs
@@ -27,9 +27,11 @@ public class CurrentTenant : ICurrentTenant, ITransientDependency
{
var parentScope = _currentTenantAccessor.Current;
_currentTenantAccessor.Current = new BasicTenantInfo(tenantId, name);
- return new DisposeAction(() =>
+
+ return new DisposeAction>(static (state) =>
{
- _currentTenantAccessor.Current = parentScope;
- });
+ var (currentTenantAccessor, parentScope) = state;
+ currentTenantAccessor.Current = parentScope;
+ }, (_currentTenantAccessor, parentScope));
}
}
diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
index db543bb67f..2e32a1b7dc 100644
--- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
+++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
@@ -45,6 +45,11 @@ public static class HasExtraPropertiesExtensions
return (TProperty)TypeDescriptor.GetConverter(conversionType).ConvertFromInvariantString(value.ToString());
}
+ if (conversionType.IsEnum)
+ {
+ return (TProperty)value;
+ }
+
return (TProperty)Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture);
}
@@ -129,7 +134,7 @@ public static class HasExtraPropertiesExtensions
{
Check.NotNull(source, nameof(source));
Check.NotNull(other, nameof(other));
-
+
return source.ExtraProperties.HasSameItems(other.ExtraProperties);
}
}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs
index 9c27fd166a..1d3e296e38 100644
--- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/CurrentPrincipalAccessorBase.cs
@@ -21,9 +21,11 @@ public abstract class CurrentPrincipalAccessorBase : ICurrentPrincipalAccessor
{
var parent = Principal;
_currentPrincipal.Value = principal;
- return new DisposeAction(() =>
+
+ return new DisposeAction, ClaimsPrincipal>>(static (state) =>
{
- _currentPrincipal.Value = parent;
- });
+ var (currentPrincipal, parent) = state;
+ currentPrincipal.Value = parent;
+ }, (_currentPrincipal, parent));
}
}
diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs
index 7d5c81abb4..06b71d16f3 100644
--- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs
+++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs
@@ -46,18 +46,21 @@ public class AmbientDataContextAmbientScopeProvider : IAmbientScopeProvider
+ return new DisposeAction, ScopeItem, IAmbientDataContext, string>>(static (state) =>
{
- ScopeDictionary.TryRemove(item.Id, out item);
+ var (scopeDictionary, item, dataContext, contextKey) = state;
+
+ scopeDictionary.TryRemove(item.Id, out item);
if (item.Outer == null)
{
- _dataContext.SetData(contextKey, null);
+ dataContext.SetData(contextKey, null);
return;
}
- _dataContext.SetData(contextKey, item.Outer.Id);
- });
+ dataContext.SetData(contextKey, item.Outer.Id);
+
+ }, (ScopeDictionary, item, _dataContext, contextKey));
}
private ScopeItem GetCurrentItem(string contextKey)
diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs
index dd33bd1ba1..3d353921d9 100644
--- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs
+++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalSimpleScopeExtensions.cs
@@ -9,9 +9,10 @@ public static class AsyncLocalSimpleScopeExtensions
{
var previousValue = asyncLocal.Value;
asyncLocal.Value = value;
- return new DisposeAction(() =>
+ return new DisposeAction, T>>(static (state) =>
{
+ var (asyncLocal, previousValue) = state;
asyncLocal.Value = previousValue;
- });
+ }, (asyncLocal, previousValue));
}
}
diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/hr.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/hr.json
new file mode 100644
index 0000000000..e359210aae
--- /dev/null
+++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/hr.json
@@ -0,0 +1,6 @@
+{
+ "culture": "hr",
+ "texts": {
+ "Menu:Administration": "Administracija"
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json
new file mode 100644
index 0000000000..9cbef79b12
--- /dev/null
+++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json
@@ -0,0 +1,53 @@
+{
+ "culture": "hr",
+ "texts": {
+ "Languages": "Jezici",
+ "AreYouSure": "Jeste li sigurni?",
+ "Cancel": "Odustani",
+ "Clear": "Očisti",
+ "Yes": "Da",
+ "No": "Ne",
+ "Ok": "Ok",
+ "Close": "Zatvori",
+ "Save": "Spremi",
+ "SavingWithThreeDot": "Spremanje...",
+ "Actions": "Akcije",
+ "Delete": "Izbrisati",
+ "SuccessfullyDeleted": "Uspješno izbrisano",
+ "Edit": "Uredi",
+ "Refresh": "Osvježi",
+ "Language": "Jezik",
+ "LoadMore": "Učitaj još",
+ "ProcessingWithThreeDot": "Obrada...",
+ "LoadingWithThreeDot": "Učitavanje...",
+ "Welcome": "Dobrodošli",
+ "Login": "Prijava",
+ "Register": "Registracija",
+ "Logout": "Odjava",
+ "Submit": "Pošalji",
+ "Back": "Nazad",
+ "PagerSearch": "Pretraga",
+ "PagerNext": "Sljedeći",
+ "PagerPrevious": "Prethodni",
+ "PagerFirst": "Prvi",
+ "PagerLast": "Zadnji",
+ "PagerInfo": "Prikazano _START_ do _END_ do _TOTAL_ zapisa",
+ "PagerInfo{0}{1}{2}": "Prikazano {0} do {1} od {2} zapisa",
+ "PagerInfoEmpty": "Prikazano 0 do 0 od 0 zapisa",
+ "PagerInfoFiltered": "(filtrirano od _MAX_ ukupnih zapisa)",
+ "NoDataAvailableInDatatable": "Nema dostupnih podataka",
+ "Total": "ukupno",
+ "Selected": "odabrano",
+ "PagerShowMenuEntries": "Pokaži _MENU_ zapise",
+ "DatatableActionDropdownDefaultText": "Akcije",
+ "ChangePassword": "Promjena lozinke",
+ "PersonalInfo": "Moj profil",
+ "AreYouSureYouWantToCancelEditingWarningMessage": "Imate nespremljene promjene.",
+ "GoHomePage": "Natrag na početnu",
+ "GoBack": "Idi natrag",
+ "Search": "Pretraga",
+ "ItemWillBeDeletedMessageWithFormat": "{0} zapis će biti obrisan!",
+ "ItemWillBeDeletedMessage": "Ovaj zapis će biti obrisan!",
+ "ManageYourAccount": "Upravljaj korisničkim računom"
+ }
+}
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json
new file mode 100644
index 0000000000..6bb5cf7d5e
--- /dev/null
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json
@@ -0,0 +1,36 @@
+{
+ "culture": "hr",
+ "texts": {
+ "'{0}' and '{1}' do not match.": "'{0}' i '{1}' se ne podudaraju.",
+ "The {0} field is not a valid credit card number.": "{0} nije važeći broj kreditne kartice.",
+ "{0} is not valid.": "{0} nije valjan.",
+ "The {0} field is not a valid e-mail address.": "{0} nije valjana e-mail adresa.",
+ "The {0} field only accepts files with the following extensions: {1}": "{0} polje prihvaća samo datoteke sa sljedećim ekstenzijama: {1}",
+ "The field {0} must be a string or array type with a maximum length of '{1}'.": "Polje {0} mora biti text ili niz sa maksimalnom dužinom od '{1}'.",
+ "The field {0} must be a string or array type with a minimum length of '{1}'.": "Polje {0} mora biti text ili niz sa minimalnom dužinom od '{1}'.",
+ "The {0} field is not a valid phone number.": "{0} nije valjan broj telefona.",
+ "The field {0} must be between {1} and {2}.": "{0} mora biti između {1} i {2}.",
+ "The field {0} must match the regular expression '{1}'.": "{0} ne odgovara traženom formatu.",
+ "The {0} field is required.": "{0} polje je obavezno.",
+ "The field {0} must be a string with a maximum length of {1}.": "Polje {0} mora biti text sa maksimalnom dužinom od {1}.",
+ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Polje {0} mora biti text sa minimalnom du�inom od {2} i maksimalnom dužinom od {1}.",
+ "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} nije valjan potpuno kvalificirani http, https, ili ftp URL.",
+ "The field {0} is invalid.": "Polje {0} nije važeće.",
+ "ThisFieldIsNotAValidCreditCardNumber.": "Ovo polje nije važeći broj kreditne kartice.",
+ "ThisFieldIsNotValid.": "Ovo polje nije valjano.",
+ "ThisFieldIsNotAValidEmailAddress.": "Ovo polje nije valjana e-mail adresa.",
+ "ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "Ovo polje prihvaća samo datoteke sa sljedećim ekstenzijama: {0}",
+ "ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthOf{0}": "Ovo polje mora biti text ili niz sa maksimalnom dužinom od '{0}'.",
+ "ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "Ovo polje mora biti text ili niz sa minimalnom dužinom od '{0}'.",
+ "ThisFieldIsNotAValidPhoneNumber.": "Ovo polje nije važeći broj telefona.",
+ "ThisFieldMustBeBetween{0}And{1}": "Ovo polje mora biti između {0} i {1}.",
+ "ThisFieldMustBeGreaterThanOrEqual{0}": "Ovo polje mora biti veće od ili jednako {0}.",
+ "ThisFieldMustBeLessOrEqual{0}": "Ovo polje mora biti manje od ili jednako {0}.",
+ "ThisFieldMustMatchTheRegularExpression{0}": "Ovo polje mora odgovarati regularnom izrazu '{0}'.",
+ "ThisFieldIsRequired.": "Ovo polje je obavezno.",
+ "ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "Ovo polje mora biti niz maksimalne duljine od: {0}.",
+ "ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "Ovo polja mora biti tekst sa minimalnom duljinom od {1} i maksimalnom duljinom od {0}.",
+ "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "Ovo polje nije važeći potpuno kvalificirani http, https ili ftp URL.",
+ "ThisFieldIsInvalid.": "Ovo polje je nevažeće."
+ }
+}
diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/hr.json b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/hr.json
new file mode 100644
index 0000000000..ddc677eb2a
--- /dev/null
+++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/hr.json
@@ -0,0 +1,10 @@
+{
+ "culture": "hr",
+ "texts": {
+ "DisplayName:PersonModel:BirthDate1": "Datum rođenja1",
+ "DisplayName:BirthDate2": "Datum rođenja2",
+ "PersonModel:BirthDate3": "Datum rođenja3",
+ "BirthDate": "Datum rođenja",
+ "Value1": "Vrijednost jedan"
+ }
+}
diff --git a/framework/test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AbpHostEnvironment_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AbpHostEnvironment_Tests.cs
new file mode 100644
index 0000000000..16799162bb
--- /dev/null
+++ b/framework/test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AbpHostEnvironment_Tests.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Configuration.Memory;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.AspNetCore;
+
+public class AbpHostEnvironment_Tests : AbpAspNetCoreTestBase
+{
+ protected override IHostBuilder CreateHostBuilder()
+ {
+ var builder = base.CreateHostBuilder();
+ builder.ConfigureHostConfiguration(x => x.Sources.Insert(0,
+ new MemoryConfigurationSource()
+ {
+ InitialData = new List>
+ {
+ new(HostDefaults.EnvironmentKey, Environments.Staging),
+ }
+ }));
+ return builder;
+ }
+
+ [Fact]
+ public void Should_Set_Environment_From_IWebHostEnvironment()
+ {
+ var abpHostEnvironment = GetRequiredService();
+ abpHostEnvironment.EnvironmentName.ShouldBe(Environments.Staging);
+ }
+}
+
+public class AbpHostEnvironment_Async_Initialize_Tests
+{
+ [Fact]
+ public async Task Should_Set_Environment_From_AspNetCore()
+ {
+ var builder = WebApplication.CreateBuilder(new WebApplicationOptions
+ {
+ EnvironmentName = Environments.Staging
+ });
+ builder.Host.UseAutofac();
+ await builder.AddApplicationAsync();
+ var app = builder.Build();
+ await app.InitializeApplicationAsync();
+
+ var abpHostEnvironment = app.Services.GetRequiredService();
+ abpHostEnvironment.EnvironmentName.ShouldBe(Environments.Staging);
+
+ builder = WebApplication.CreateBuilder(new WebApplicationOptions
+ {
+ EnvironmentName = Environments.Staging
+ });
+ builder.Host.UseAutofac();
+ var abpApp = await AbpApplicationFactory.CreateAsync(builder.Services);
+ app = builder.Build();
+ await app.InitializeApplicationAsync();
+
+ abpHostEnvironment = abpApp.Services.GetRequiredService();
+ abpHostEnvironment.EnvironmentName.ShouldBe(Environments.Staging);
+ }
+}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetterTestBase.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetterTestBase.cs
index 184bbeaa34..f9c860ca6d 100644
--- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetterTestBase.cs
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetterTestBase.cs
@@ -48,7 +48,7 @@ public class AuditPropertySetterTestBase
}
- public class MyAuditedObject : IMultiTenant, IFullAuditedObject
+ public class MyAuditedObject : IMultiTenant, IFullAuditedObject, IHasEntityVersion
{
public Guid? TenantId { get; set; }
public DateTime CreationTime { get; set; }
@@ -58,5 +58,6 @@ public class AuditPropertySetterTestBase
public bool IsDeleted { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? DeleterId { get; set; }
+ public int EntityVersion { get; set; }
}
}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetter_EntityVersion_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetter_EntityVersion_Tests.cs
new file mode 100644
index 0000000000..51985ea5f9
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetter_EntityVersion_Tests.cs
@@ -0,0 +1,21 @@
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.Auditing;
+
+public class AuditPropertySetter_EntityVersion_Tests : AuditPropertySetterTestBase
+{
+ [Fact]
+ public void Should_Do_Nothing_For_Non_Audited_Entity()
+ {
+ AuditPropertySetter.IncrementEntityVersionProperty(new MyEmptyObject());
+ }
+
+ [Fact]
+ public void Should_Increment_EntityVersion()
+ {
+ AuditPropertySetter.IncrementEntityVersionProperty(TargetObject);
+
+ TargetObject.EntityVersion.ShouldBe(1);
+ }
+}
diff --git a/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_CustomServiceConstruction_Tests.cs b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_CustomServiceConstruction_Tests.cs
index 5dadd2cc6e..7ed652a4d7 100644
--- a/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_CustomServiceConstruction_Tests.cs
+++ b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_CustomServiceConstruction_Tests.cs
@@ -1,6 +1,7 @@
using System;
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
@@ -25,6 +26,7 @@ public class AutoMapper_CustomServiceConstruction_Tests : AbpIntegratedTest(source).Name.ShouldBe(nameof(CustomMappingAction));
}
@@ -33,6 +35,9 @@ public class AutoMapper_CustomServiceConstruction_Tests : AbpIntegratedTest(sp => sp.GetRequiredService().CreateMapper()));
+
Configure(options =>
{
options.AddMaps();
diff --git a/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_Dependency_Injection_Tests.cs b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_Dependency_Injection_Tests.cs
index 164a9c41cd..2df82d386e 100644
--- a/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_Dependency_Injection_Tests.cs
+++ b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_Dependency_Injection_Tests.cs
@@ -1,4 +1,6 @@
-using AutoMapper;
+using System;
+using AutoMapper;
+using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Testing;
@@ -9,13 +11,6 @@ namespace Volo.Abp.AutoMapper;
public class AutoMapper_Dependency_Injection_Tests : AbpIntegratedTest
{
- private readonly IObjectMapper _objectMapper;
-
- public AutoMapper_Dependency_Injection_Tests()
- {
- _objectMapper = GetRequiredService();
- }
-
[Fact]
public void Should_Registered_AutoMapper_Service()
{
@@ -30,7 +25,12 @@ public class AutoMapper_Dependency_Injection_Tests : AbpIntegratedTest(sourceModel).Name.ShouldBe(nameof(CustomMappingActionService));
+ using (var scope = ServiceProvider.CreateScope())
+ {
+ scope.ServiceProvider.GetRequiredService().Map(sourceModel).Name.ShouldBe(nameof(CustomMappingActionService));
+ }
+
+ CustomMappingAction.IsDisposed.ShouldBeTrue();
}
public class SourceModel
@@ -51,8 +51,10 @@ public class AutoMapper_Dependency_Injection_Tests : AbpIntegratedTest
+ public class CustomMappingAction : IMappingAction, IDisposable
{
+ public static bool IsDisposed = false;
+
private readonly CustomMappingActionService _customMappingActionService;
public CustomMappingAction(CustomMappingActionService customMappingActionService)
@@ -64,6 +66,11 @@ public class AutoMapper_Dependency_Injection_Tests : AbpIntegratedTest OnApplicationShutdownAsync(context));
+ }
+
+ public async override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
+ {
+ var minioClient = new MinioClient().WithEndpoint(_endPoint).WithCredentials(_accessKey, _secretKey).Build();
+ if (await minioClient.BucketExistsAsync(new BucketExistsArgs().WithBucket(_randomContainerName)))
{
- var objects = await minioClient.ListObjectsAsync(_randomContainerName, null, true).ToList();
+ var objects = await minioClient.ListObjectsAsync(new ListObjectsArgs().WithBucket(_randomContainerName)
+ .WithPrefix(null).WithRecursive(true)).ToList();
foreach (var item in objects)
{
- await minioClient.RemoveObjectAsync(_randomContainerName, item.Key);
+ await minioClient.RemoveObjectAsync(new RemoveObjectArgs().WithBucket(_randomContainerName)
+ .WithObject(item.Key));
}
- await minioClient.RemoveBucketAsync(_randomContainerName);
+ await minioClient.RemoveBucketAsync(new RemoveBucketArgs().WithBucket(_randomContainerName));
}
-
}
}
diff --git a/framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobContainer_Tests.cs
index 320a68620e..74857b7fcb 100644
--- a/framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobContainer_Tests.cs
+++ b/framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobContainer_Tests.cs
@@ -1,3 +1,15 @@
using Xunit;
namespace Volo.Abp.BlobStoring.Minio;
+
+/*
+//Please set the correct connection string in secrets.json and continue the test.
+public class MinioBlobContainer_Tests : BlobContainer_Tests
+{
+ public MinioBlobContainer_Tests()
+ {
+
+
+ }
+}
+*/
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs
index cf54b1492a..f910517bbc 100644
--- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs
+++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs
@@ -4,6 +4,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using NSubstitute;
using Shouldly;
using Volo.Abp.DependencyInjection;
@@ -204,6 +205,37 @@ public class AbpApplication_Initialize_Tests
}
}
+ [Fact]
+ public void Should_Set_And_Get_Environment()
+ {
+ // Default environment is Production
+ using (var application = AbpApplicationFactory.Create())
+ {
+ var abpHostEnvironment = application.Services.GetSingletonInstance();
+ abpHostEnvironment.EnvironmentName.ShouldBe(Environments.Production);
+
+ application.Initialize();
+
+ abpHostEnvironment = application.ServiceProvider.GetRequiredService();
+ abpHostEnvironment.EnvironmentName.ShouldBe(Environments.Production);
+ }
+
+ // Set environment
+ using (var application = AbpApplicationFactory.Create(options =>
+ {
+ options.Environment = Environments.Staging;
+ }))
+ {
+ var abpHostEnvironment = application.Services.GetSingletonInstance();
+ abpHostEnvironment.EnvironmentName.ShouldBe(Environments.Staging);
+
+ application.Initialize();
+
+ abpHostEnvironment = application.ServiceProvider.GetRequiredService();
+ abpHostEnvironment.EnvironmentName.ShouldBe(Environments.Staging);
+ }
+ }
+
[Fact]
public async Task Should_Resolve_Root_Service_Provider()
{
diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj b/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj
index ce26f12583..5472aa96b2 100644
--- a/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj
+++ b/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj
@@ -8,7 +8,10 @@
+
+
+
diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/EntitySynchronizers/EntitySynchronizer_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/EntitySynchronizers/EntitySynchronizer_Tests.cs
new file mode 100644
index 0000000000..7f15bd5b20
--- /dev/null
+++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/EntitySynchronizers/EntitySynchronizer_Tests.cs
@@ -0,0 +1,252 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AutoMapper;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using Volo.Abp.Autofac;
+using Volo.Abp.AutoMapper;
+using Volo.Abp.Data;
+using Volo.Abp.Domain.Entities.Events.Distributed.EntitySynchronizers.WithEntityVersion;
+using Volo.Abp.Domain.Entities.Events.Distributed.EntitySynchronizers.WithoutEntityVersion;
+using Volo.Abp.Domain.Repositories;
+using Volo.Abp.MemoryDb;
+using Volo.Abp.Modularity;
+using Volo.Abp.Testing;
+using Volo.Abp.Uow;
+using Xunit;
+
+namespace Volo.Abp.Domain.Entities.Events.Distributed.EntitySynchronizers;
+
+public class EntitySynchronizer_Tests : AbpIntegratedTest
+{
+ [Fact]
+ public async Task Should_Handle_Entity_Created_Event()
+ {
+ var authorId = Guid.NewGuid();
+
+ var uowManager = GetRequiredService();
+ using var uow = uowManager.Begin();
+
+ var authorSynchronizer = GetRequiredService();
+ var repository = GetRequiredService>();
+
+ (await repository.FindAsync(authorId)).ShouldBeNull();
+
+ var remoteAuthorEto = new RemoteAuthorEto { Id = authorId, Name = "New" };
+
+ await authorSynchronizer.HandleEventAsync(new EntityCreatedEto(remoteAuthorEto));
+
+ var author = await repository.FindAsync(authorId);
+ author.ShouldNotBeNull();
+ author.Name.ShouldBe("New");
+ }
+
+ [Fact]
+ public async Task Should_Handle_Entity_Update_Event()
+ {
+ var authorId = Guid.NewGuid();
+
+ var uowManager = GetRequiredService();
+ using var uow = uowManager.Begin();
+
+ var authorSynchronizer = GetRequiredService();
+ var repository = GetRequiredService>();
+
+ await repository.InsertAsync(new Author(authorId, "Old"), true);
+
+ var author = await repository.FindAsync(authorId);
+ author.ShouldNotBeNull();
+ author.Id.ShouldBe(authorId);
+ author.Name.ShouldBe("Old");
+
+ var remoteAuthorEto = new RemoteAuthorEto { Id = authorId, Name = "New" };
+
+ await authorSynchronizer.HandleEventAsync(new EntityUpdatedEto(remoteAuthorEto));
+
+ author = await repository.FindAsync(authorId);
+ author.ShouldNotBeNull();
+ author.Id.ShouldBe(authorId);
+ author.Name.ShouldBe("New");
+ }
+
+ [Fact]
+ public async Task Should_Handle_Entity_Deleted_Event()
+ {
+ var authorId = Guid.NewGuid();
+
+ var uowManager = GetRequiredService();
+ using var uow = uowManager.Begin();
+
+ var authorSynchronizer = GetRequiredService();
+ var repository = GetRequiredService>();
+
+ await repository.InsertAsync(new Author(authorId, "Old"), true);
+
+ var author = await repository.FindAsync(authorId);
+ author.ShouldNotBeNull();
+ author.Id.ShouldBe(authorId);
+ author.Name.ShouldBe("Old");
+
+ var remoteAuthorEto = new RemoteAuthorEto { Id = authorId, Name = "New" };
+
+ await authorSynchronizer.HandleEventAsync(new EntityDeletedEto(remoteAuthorEto));
+
+ (await repository.FindAsync(authorId)).ShouldBeNull();
+
+ await Should.NotThrowAsync(() =>
+ authorSynchronizer.HandleEventAsync(new EntityDeletedEto(remoteAuthorEto)));
+ }
+
+ [Fact]
+ public async Task Should_Handle_Versioned_Entity_Created_Event()
+ {
+ var bookId = Guid.NewGuid();
+
+ var uowManager = GetRequiredService();
+ using var uow = uowManager.Begin();
+
+ var bookSynchronizer = GetRequiredService();
+ var repository = GetRequiredService>();
+
+ (await repository.FindAsync(bookId)).ShouldBeNull();
+
+ var remoteBookEto = new RemoteBookEto { Id = bookId, EntityVersion = 0, Sold = 1 };
+
+ await bookSynchronizer.HandleEventAsync(new EntityCreatedEto(remoteBookEto));
+
+ var book = await repository.FindAsync(bookId);
+ book.ShouldNotBeNull();
+ book.EntityVersion.ShouldBe(remoteBookEto.EntityVersion);
+ book.Sold.ShouldBe(1);
+ }
+
+ [Fact]
+ public async Task Should_Handle_Versioned_Entity_Update_Event()
+ {
+ var bookId = Guid.NewGuid();
+
+ var uowManager = GetRequiredService();
+ using var uow = uowManager.Begin();
+
+ var bookSynchronizer = GetRequiredService();
+ var repository = GetRequiredService>();
+
+ await repository.InsertAsync(new Book(bookId, 1), true);
+
+ var book = await repository.FindAsync(bookId);
+ book.ShouldNotBeNull();
+ book.Id.ShouldBe(bookId);
+ book.EntityVersion.ShouldBe(0);
+
+ var remoteBookEto = new RemoteBookEto { Id = bookId, EntityVersion = 0, Sold = 1 };
+
+ await bookSynchronizer.HandleEventAsync(new EntityUpdatedEto(remoteBookEto));
+
+ book = await repository.FindAsync(bookId);
+ book.ShouldNotBeNull();
+ book.EntityVersion.ShouldBe(0);
+ book.Sold.ShouldBe(1);
+
+ remoteBookEto.EntityVersion = 1;
+ remoteBookEto.Sold = 2;
+
+ await bookSynchronizer.HandleEventAsync(new EntityUpdatedEto