diff --git a/docs/en/CLI.md b/docs/en/CLI.md index d51dd91b5d..c5eb38bbbe 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -129,6 +129,7 @@ abp update [options] * `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default * `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. * `--check-all`: Check the new version of each package separately. Default is `false`. +* `--version` or `-v`: Specifies the version to use for update. If not specified, latest version is used. ### add-package diff --git a/docs/en/UI/AspNetCore/Data-Tables.md b/docs/en/UI/AspNetCore/Data-Tables.md index a9dbf1eecf..b97d49d562 100644 --- a/docs/en/UI/AspNetCore/Data-Tables.md +++ b/docs/en/UI/AspNetCore/Data-Tables.md @@ -105,6 +105,32 @@ The `abp.libs.datatables.createAjax` method (used in the example above) adapts r This works automatically, so most of the times you don't need to know how it works. See the [DTO document](../../Data-Transfer-Objects.md) if you want to learn more about `IPagedAndSortedResultRequest`, `IPagedResult` and other standard interfaces and base DTO classes those are used in client to server communication. +The `createAjax` also supports you to customize request parameters and handle the responses. + +**Example:** + +````csharp +var inputAction = function () { + return { + id: $('#Id').val(), + name: $('#Name').val(), + }; +}; + +var responseCallback = function(result) { + + // your custom code. + + return { + recordsTotal: result.totalCount, + recordsFiltered: result.totalCount, + data: result.items + }; +}; + +ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList, inputAction, responseCallback) +```` + ### Row Actions `rowAction` is an option defined by the ABP Framework to the column definitions to show a drop down button to take actions for a row in the table. @@ -260,4 +286,4 @@ Assuming that the possible values for a column data is `f` and `m`, the `gender` ## Other Data Grids -You can use any library you like. For example, [see this article](https://community.abp.io/articles/using-devextreme-components-with-the-abp-framework-zb8z7yqv) to learn how to use DevExtreme Data Grid in your applications. \ No newline at end of file +You can use any library you like. For example, [see this article](https://community.abp.io/articles/using-devextreme-components-with-the-abp-framework-zb8z7yqv) to learn how to use DevExtreme Data Grid in your applications. diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md index 3390ce99b3..4c63dcd818 100644 --- a/docs/zh-Hans/CLI.md +++ b/docs/zh-Hans/CLI.md @@ -126,6 +126,7 @@ abp update [options] * `--solution-path` 或 `-sp`: 指定解决方案路径/目录. 默认使用当前目录 * `--solution-name` 或 `-sn`: 指定解决方案名称. 默认在目录中搜索`*.sln`文件. * `--check-all`: 分别检查每个包的新版本. 默认是 `false`. +* `--version` or `-v`: 指定用于升级的版本. 如果没有指定,则使用最新版本. ### add-package diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js index 2bbd1aa7a3..b0c4873b59 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js @@ -325,7 +325,14 @@ * AJAX extension for datatables * *************************************************************************/ (function () { - datatables.createAjax = function (serverMethod, inputAction) { + datatables.createAjax = function (serverMethod, inputAction, responseCallback) { + responseCallback = responseCallback || function(result) { + return { + recordsTotal: result.totalCount, + recordsFiltered: result.totalCount, + data: result.items + }; + } return function (requestData, callback, settings) { var input = inputAction ? inputAction(requestData, settings) : {}; @@ -359,11 +366,7 @@ if (callback) { serverMethod(input).then(function (result) { - callback({ - recordsTotal: result.totalCount, - recordsFiltered: result.totalCount, - data: result.items - }); + callback(responseCallback(result)); }); } }; diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs index 62e79010cc..2635294323 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs @@ -17,7 +17,7 @@ namespace Volo.Abp.BackgroundJobs protected AbpBackgroundJobWorkerOptions WorkerOptions { get; } public BackgroundJobWorker( - AbpTimer timer, + AbpAsyncTimer timer, IOptions jobOptions, IOptions workerOptions, IServiceScopeFactory serviceScopeFactory) @@ -113,4 +113,4 @@ namespace Volo.Abp.BackgroundJobs return nextTryDate; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs index e6a65a2865..970beea3f0 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs @@ -34,7 +34,7 @@ namespace Volo.Abp.BackgroundWorkers.Quartz throw new ArgumentException($"{nameof(worker)} type is different from the generic type"); } - var timer = (AbpTimer) worker.GetType().GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker); + var timer = (AbpAsyncTimer) worker.GetType().GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker); period = timer?.Period; } else diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs index ed48ba69ee..094c6eb7eb 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs @@ -11,15 +11,15 @@ namespace Volo.Abp.BackgroundWorkers public abstract class AsyncPeriodicBackgroundWorkerBase : BackgroundWorkerBase { protected IServiceScopeFactory ServiceScopeFactory { get; } - protected AbpTimer Timer { get; } + protected AbpAsyncTimer Timer { get; } protected AsyncPeriodicBackgroundWorkerBase( - AbpTimer timer, + AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) { ServiceScopeFactory = serviceScopeFactory; Timer = timer; - Timer.Elapsed += Timer_Elapsed; + Timer.Elapsed = Timer_Elapsed; } public async override Task StartAsync(CancellationToken cancellationToken = default) @@ -34,23 +34,24 @@ namespace Volo.Abp.BackgroundWorkers await base.StopAsync(cancellationToken); } - private void Timer_Elapsed(object sender, System.EventArgs e) + private async Task Timer_Elapsed(AbpAsyncTimer timer) + { + await DoWorkAsync(); + } + + private async Task DoWorkAsync() { using (var scope = ServiceScopeFactory.CreateScope()) { try { - AsyncHelper.RunSync( - () => DoWorkAsync(new PeriodicBackgroundWorkerContext(scope.ServiceProvider)) - ); + await DoWorkAsync(new PeriodicBackgroundWorkerContext(scope.ServiceProvider)); } catch (Exception ex) { - AsyncHelper.RunSync( - () => scope.ServiceProvider - .GetRequiredService() - .NotifyAsync(new ExceptionNotificationContext(ex)) - ); + await scope.ServiceProvider + .GetRequiredService() + .NotifyAsync(new ExceptionNotificationContext(ex)); Logger.LogException(ex); } diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs index e29dbfec6e..6ac757fef5 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs @@ -43,14 +43,12 @@ namespace Volo.Abp.BackgroundWorkers { try { - DoWork(new PeriodicBackgroundWorkerContext(scope.ServiceProvider)); } catch (Exception ex) { - scope.ServiceProvider - .GetRequiredService() - .NotifyAsync(new ExceptionNotificationContext(ex)); + var exceptionNotifier = scope.ServiceProvider.GetRequiredService(); + AsyncHelper.RunSync(() => exceptionNotifier.NotifyAsync(new ExceptionNotificationContext(ex))); Logger.LogException(ex); } diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 3a46c3b18e..d62411628e 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -1029,7 +1029,7 @@ namespace Volo.Abp.Caching protected virtual void HandleException(Exception ex) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + _ = HandleExceptionAsync(ex); } protected virtual async Task HandleExceptionAsync(Exception ex) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs index 5fd30115e2..3173744bd0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs @@ -34,24 +34,25 @@ namespace Volo.Abp.Cli.Commands var directory = commandLineArgs.Options.GetOrNull(Options.SolutionPath.Short, Options.SolutionPath.Long) ?? Directory.GetCurrentDirectory(); + var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); if (updateNuget || !updateNpm) { - await UpdateNugetPackages(commandLineArgs, directory); + await UpdateNugetPackages(commandLineArgs, directory, version); } if (updateNpm || !updateNuget) { - await UpdateNpmPackages(directory); + await UpdateNpmPackages(directory, version); } } - private async Task UpdateNpmPackages(string directory) + private async Task UpdateNpmPackages(string directory, string version) { - await _npmPackagesUpdater.Update(directory); + await _npmPackagesUpdater.Update(directory, version: version); } - private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory) + private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory, string version) { var solution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long); @@ -66,7 +67,7 @@ namespace Volo.Abp.Cli.Commands { var solutionName = Path.GetFileName(solution).RemovePostFix(".sln"); - await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll); + await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll, version: version); Logger.LogInformation($"Volo packages are updated in {solutionName} solution."); return; @@ -78,7 +79,7 @@ namespace Volo.Abp.Cli.Commands { var projectName = Path.GetFileName(project).RemovePostFix(".csproj"); - await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll); + await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll, version: version); Logger.LogInformation($"Volo packages are updated in {projectName} project."); return; @@ -107,6 +108,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine("-sp|--solution-path (Specify the solution path)"); sb.AppendLine("-sn|--solution-name (Specify the solution name)"); sb.AppendLine("--check-all (Check the new version of each package separately)"); + sb.AppendLine("-v|--version (default: latest version)"); sb.AppendLine(""); sb.AppendLine("Some examples:"); sb.AppendLine(""); @@ -148,6 +150,12 @@ namespace Volo.Abp.Cli.Commands { public const string Long = "check-all"; } + + public static class Version + { + public const string Short = "v"; + public const string Long = "version"; + } } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index 275bf23c76..c499dd0ee0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -25,11 +25,11 @@ namespace Volo.Abp.Cli.ProjectModification Logger = NullLogger.Instance; } - public async Task UpdateSolutionAsync(string solutionPath, bool includePreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false) + public async Task UpdateSolutionAsync(string solutionPath, bool includePreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) { var projectPaths = ProjectFinder.GetProjectFiles(solutionPath); - if (checkAll) + if (checkAll && version.IsNullOrWhiteSpace()) { Task.WaitAll(projectPaths.Select(projectPath => UpdateInternalAsync(projectPath, includePreviews, includeReleaseCandidates, switchToStable)).ToArray()); } @@ -48,7 +48,8 @@ namespace Volo.Abp.Cli.ProjectModification switchToStable, latestVersionFromNuget, latestReleaseCandidateVersionFromNuget, - latestVersionFromMyGet); + latestVersionFromMyGet, + version); File.WriteAllText(filePath, updatedContent); } @@ -57,9 +58,9 @@ namespace Volo.Abp.Cli.ProjectModification } } - public async Task UpdateProjectAsync(string projectPath, bool includeNightlyPreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false) + public async Task UpdateProjectAsync(string projectPath, bool includeNightlyPreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, bool checkAll = false, string version = null) { - if (checkAll) + if (checkAll && version.IsNullOrWhiteSpace()) { await UpdateInternalAsync(projectPath, includeNightlyPreviews, includeReleaseCandidates, switchToStable); } @@ -77,7 +78,8 @@ namespace Volo.Abp.Cli.ProjectModification switchToStable, latestVersionFromNuget, latestReleaseCandidateVersionFromNuget, - latestVersionFromMyGet); + latestVersionFromMyGet, + version); File.WriteAllText(projectPath, updatedContent); } @@ -91,13 +93,26 @@ namespace Volo.Abp.Cli.ProjectModification File.WriteAllText(projectPath, updatedContent); } + protected virtual async Task SpecifiedVersionExists(string version, string packageId) + { + var versionList = await _nuGetService.GetPackageVersionListAsync(packageId); + + if (versionList.All(v => !v.Equals(version, StringComparison.OrdinalIgnoreCase))) + { + versionList = await _nuGetService.GetPackageVersionListAsync(packageId, true); + } + + return versionList.Any(v => v.Equals(version, StringComparison.OrdinalIgnoreCase)); + } + private async Task UpdateVoloPackagesAsync(string content, bool includeNightlyPreviews = false, bool includeReleaseCandidates = false, bool switchToStable = false, SemanticVersion latestNugetVersion = null, SemanticVersion latestNugetReleaseCandidateVersion = null, - string latestMyGetVersion = null) + string latestMyGetVersion = null, + string specifiedVersion = null) { string packageId = null; @@ -129,40 +144,63 @@ namespace Volo.Abp.Cli.ProjectModification Logger.LogDebug("Checking package: \"{0}\" - Current version: {1}", packageId, currentSemanticVersion); - if ((includeNightlyPreviews || (currentVersion.Contains("-preview") && !switchToStable)) && !includeReleaseCandidates) + if (!specifiedVersion.IsNullOrWhiteSpace()) { - var latestVersion = latestMyGetVersion ?? await GetLatestVersionFromMyGet(packageId); - - if (currentVersion != latestVersion) + if (await SpecifiedVersionExists(specifiedVersion, packageId)) { - Logger.LogInformation("Updating package \"{0}\" from v{1} to v{2}.", packageId, currentVersion, latestVersion); - versionAttribute.Value = latestVersion; + var specifiedSemanticVersion = SemanticVersion.Parse(specifiedVersion); + if (specifiedSemanticVersion > currentSemanticVersion) + { + Logger.LogInformation("Updating package \"{0}\" from v{1} to v{2}.", packageId, currentVersion, specifiedVersion); + versionAttribute.Value = specifiedVersion; + } + else + { + Logger.LogWarning("Unable to update package \"{0}\" version v{1} to v{2}.", packageId, currentVersion, specifiedVersion); + } } else { - Logger.LogDebug("Package: \"{0}-v{1}\" is up to date.", packageId, currentVersion); + Logger.LogWarning("Package \"{0}\" specified version v{1} does not exist.", packageId, specifiedVersion); } } else { - SemanticVersion latestVersion; - if (currentSemanticVersion.IsPrerelease && !switchToStable) - { - latestVersion = latestNugetReleaseCandidateVersion ?? await _nuGetService.GetLatestVersionOrNullAsync(packageId, includeReleaseCandidates: true); - } - else - { - latestVersion = latestNugetVersion ?? await _nuGetService.GetLatestVersionOrNullAsync(packageId, includeReleaseCandidates: includeReleaseCandidates); - } - - if (latestVersion != null && (currentSemanticVersion < latestVersion || (currentSemanticVersion.IsPrerelease && switchToStable))) + if ((includeNightlyPreviews || (currentVersion.Contains("-preview") && !switchToStable)) && !includeReleaseCandidates) { - Logger.LogInformation("Updating package \"{0}\" from v{1} to v{2}.", packageId, currentSemanticVersion.ToString(), latestVersion.ToString()); - versionAttribute.Value = latestVersion.ToString(); + var latestVersion = latestMyGetVersion ?? await GetLatestVersionFromMyGet(packageId); + + if (currentVersion != latestVersion) + { + Logger.LogInformation("Updating package \"{0}\" from v{1} to v{2}.", packageId, currentVersion, latestVersion); + versionAttribute.Value = latestVersion; + } + else + { + Logger.LogDebug("Package: \"{0}-v{1}\" is up to date.", packageId, currentVersion); + } } else { - Logger.LogInformation("Package: \"{0}-v{1}\" is up to date.", packageId, currentSemanticVersion); + SemanticVersion latestVersion; + if (currentSemanticVersion.IsPrerelease && !switchToStable) + { + latestVersion = latestNugetReleaseCandidateVersion ?? await _nuGetService.GetLatestVersionOrNullAsync(packageId, includeReleaseCandidates: true); + } + else + { + latestVersion = latestNugetVersion ?? await _nuGetService.GetLatestVersionOrNullAsync(packageId, includeReleaseCandidates: includeReleaseCandidates); + } + + if (latestVersion != null && (currentSemanticVersion < latestVersion || (currentSemanticVersion.IsPrerelease && switchToStable))) + { + Logger.LogInformation("Updating package \"{0}\" from v{1} to v{2}.", packageId, currentSemanticVersion.ToString(), latestVersion.ToString()); + versionAttribute.Value = latestVersion.ToString(); + } + else + { + Logger.LogInformation("Package: \"{0}-v{1}\" is up to date.", packageId, currentSemanticVersion); + } } } } 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 c78d5f93f3..886520e95c 100644 --- a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs +++ b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs @@ -3,13 +3,13 @@ using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Events; using System.IO; -using Volo.Abp.Threading; +using System.Threading.Tasks; namespace Volo.Abp.Cli { public class Program { - private static void Main(string[] args) + private static async Task Main(string[] args) { Console.OutputEncoding = System.Text.Encoding.UTF8; @@ -37,11 +37,9 @@ namespace Volo.Abp.Cli { application.Initialize(); - AsyncHelper.RunSync( - () => application.ServiceProvider - .GetRequiredService() - .RunAsync(args) - ); + await application.ServiceProvider + .GetRequiredService() + .RunAsync(args); application.Shutdown(); } diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs index 135156f7a8..fb1b0c7fe2 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs @@ -20,7 +20,7 @@ namespace Volo.Abp.RabbitMQ protected IExceptionNotifier ExceptionNotifier { get; } - protected AbpTimer Timer { get; } + protected AbpAsyncTimer Timer { get; } protected ExchangeDeclareConfiguration Exchange { get; private set; } @@ -38,7 +38,7 @@ namespace Volo.Abp.RabbitMQ public RabbitMqMessageConsumer( IConnectionPool connectionPool, - AbpTimer timer, + AbpAsyncTimer timer, IExceptionNotifier exceptionNotifier) { ConnectionPool = connectionPool; @@ -50,7 +50,7 @@ namespace Volo.Abp.RabbitMQ Callbacks = new ConcurrentBag>(); Timer.Period = 5000; //5 sec. - Timer.Elapsed += Timer_Elapsed; + Timer.Elapsed = Timer_Elapsed; Timer.RunOnStart = true; } @@ -77,7 +77,7 @@ namespace Volo.Abp.RabbitMQ await TrySendQueueBindCommandsAsync(); } - protected virtual void TrySendQueueBindCommands() + protected virtual async Task TrySendQueueBindCommandsAsync() { try { @@ -119,40 +119,33 @@ namespace Volo.Abp.RabbitMQ catch (Exception ex) { Logger.LogException(ex, LogLevel.Warning); - AsyncHelper.RunSync(() => ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning)); + await ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning); } } - protected virtual Task TrySendQueueBindCommandsAsync() - { - TrySendQueueBindCommands(); - return Task.CompletedTask; - } - public virtual void OnMessageReceived(Func callback) { Callbacks.Add(callback); } - protected virtual void Timer_Elapsed(object sender, EventArgs e) + protected virtual async Task Timer_Elapsed(AbpAsyncTimer timer) { if (Channel == null || Channel.IsOpen == false) { - TryCreateChannel(); - TrySendQueueBindCommands(); + await TryCreateChannelAsync(); + await TrySendQueueBindCommandsAsync(); } } - protected virtual void TryCreateChannel() + protected virtual async Task TryCreateChannelAsync() { - DisposeChannel(); + await DisposeChannelAsync(); try { var channel = ConnectionPool .Get(ConnectionName) .CreateModel(); - channel.ExchangeDeclare( exchange: Exchange.ExchangeName, type: Exchange.Type, @@ -172,7 +165,7 @@ namespace Volo.Abp.RabbitMQ var consumer = new EventingBasicConsumer(channel); consumer.Received += async (model, basicDeliverEventArgs) => { - await HandleIncomingMessage(channel, basicDeliverEventArgs); + await HandleIncomingMessageAsync(channel, basicDeliverEventArgs); }; channel.BasicConsume( @@ -186,11 +179,11 @@ namespace Volo.Abp.RabbitMQ catch (Exception ex) { Logger.LogException(ex, LogLevel.Warning); - AsyncHelper.RunSync(() => ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning)); + await ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning); } } - protected virtual async Task HandleIncomingMessage(IModel channel, BasicDeliverEventArgs basicDeliverEventArgs) + protected virtual async Task HandleIncomingMessageAsync(IModel channel, BasicDeliverEventArgs basicDeliverEventArgs) { try { @@ -208,6 +201,24 @@ namespace Volo.Abp.RabbitMQ } } + protected virtual async Task DisposeChannelAsync() + { + if (Channel == null) + { + return; + } + + try + { + Channel.Dispose(); + } + catch (Exception ex) + { + Logger.LogException(ex, LogLevel.Warning); + await ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning); + } + } + protected virtual void DisposeChannel() { if (Channel == null) diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpAsyncTimer.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpAsyncTimer.cs new file mode 100644 index 0000000000..8ce369ef66 --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpAsyncTimer.cs @@ -0,0 +1,127 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.ExceptionHandling; + +namespace Volo.Abp.Threading +{ + /// + /// A robust timer implementation that ensures no overlapping occurs. It waits exactly specified between ticks. + /// + public class AbpAsyncTimer : ITransientDependency + { + /// + /// This func is raised periodically according to Period of Timer. + /// + public Func Elapsed = _ => Task.CompletedTask; + + /// + /// Task period of timer (as milliseconds). + /// + public int Period { get; set; } + + /// + /// Indicates whether timer raises Elapsed event on Start method of Timer for once. + /// Default: False. + /// + public bool RunOnStart { get; set; } + + public ILogger Logger { get; set; } + + public IExceptionNotifier ExceptionNotifier { get; set; } + + private readonly Timer _taskTimer; + private volatile bool _performingTasks; + private volatile bool _isRunning; + + public AbpAsyncTimer() + { + ExceptionNotifier = NullExceptionNotifier.Instance; + Logger = NullLogger.Instance; + + _taskTimer = new Timer( + TimerCallBack, + null, + Timeout.Infinite, + Timeout.Infinite + ); + } + + public void Start(CancellationToken cancellationToken = default) + { + if (Period <= 0) + { + throw new AbpException("Period should be set before starting the timer!"); + } + + lock (_taskTimer) + { + _taskTimer.Change(RunOnStart ? 0 : Period, Timeout.Infinite); + _isRunning = true; + } + } + + public void Stop(CancellationToken cancellationToken = default) + { + lock (_taskTimer) + { + _taskTimer.Change(Timeout.Infinite, Timeout.Infinite); + while (_performingTasks) + { + Monitor.Wait(_taskTimer); + } + + _isRunning = false; + } + } + + /// + /// This method is called by _taskTimer. + /// + /// Not used argument + private void TimerCallBack(object state) + { + lock (_taskTimer) + { + if (!_isRunning || _performingTasks) + { + return; + } + + _taskTimer.Change(Timeout.Infinite, Timeout.Infinite); + _performingTasks = true; + } + + _ = Timer_Elapsed(); + } + + private async Task Timer_Elapsed() + { + try + { + await Elapsed(this); + } + catch(Exception ex) + { + Logger.LogException(ex); + await ExceptionNotifier.NotifyAsync(ex); + } + finally + { + lock (_taskTimer) + { + _performingTasks = false; + if (_isRunning) + { + _taskTimer.Change(Period, Timeout.Infinite); + } + + Monitor.Pulse(_taskTimer); + } + } + } + } +} diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs index 5c5041cdf6..e7e6efd5de 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs @@ -118,4 +118,4 @@ namespace Volo.Abp.Threading } } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.BlobStoring.Aws.Tests/Volo/Abp/BlobStoring/Aws/AbpBlobStoringAwsTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Aws.Tests/Volo/Abp/BlobStoring/Aws/AbpBlobStoringAwsTestModule.cs index d176a864e7..71a1cd8504 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aws.Tests/Volo/Abp/BlobStoring/Aws/AbpBlobStoringAwsTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aws.Tests/Volo/Abp/BlobStoring/Aws/AbpBlobStoringAwsTestModule.cs @@ -69,7 +69,7 @@ namespace Volo.Abp.BlobStoring.Aws private async Task DeleteBucketAsync(ApplicationShutdownContext context) { - var amazonS3Client = await context.ServiceProvider.GetService() + var amazonS3Client = await context.ServiceProvider.GetRequiredService() .GetAmazonS3Client(_configuration); if (await AmazonS3Util.DoesS3BucketExistV2Async(amazonS3Client, _randomContainerName)) @@ -89,4 +89,4 @@ namespace Volo.Abp.BlobStoring.Aws } } } -} \ No newline at end of file +} diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20201208084431_Added_CommitCount.Designer.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20201208084431_Added_CommitCount.Designer.cs new file mode 100644 index 0000000000..ff28c9f865 --- /dev/null +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20201208084431_Added_CommitCount.Designer.cs @@ -0,0 +1,987 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; +using VoloDocs.EntityFrameworkCore; + +namespace VoloDocs.EntityFrameworkCore.Migrations +{ + [DbContext(typeof(VoloDocsDbContext))] + [Migration("20201208084431_Added_CommitCount")] + partial class Added_CommitCount + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityColumns() + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.0"); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("SourceTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique() + .HasFilter("[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); + + b.ToTable("AbpLinkUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("bit") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("bit") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("bit") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SecurityStamp"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("nvarchar(196)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("nvarchar(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Docs.Documents.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("EditLink") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Format") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LanguageCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastCachedTime") + .HasColumnType("datetime2"); + + b.Property("LastSignificantUpdateTime") + .HasColumnType("datetime2"); + + b.Property("LastUpdatedTime") + .HasColumnType("datetime2"); + + b.Property("LocalDirectory") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("RawRootUrl") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("RootUrl") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("DocsDocuments"); + }); + + modelBuilder.Entity("Volo.Docs.Documents.DocumentContributor", b => + { + b.Property("DocumentId") + .HasColumnType("uniqueidentifier"); + + b.Property("Username") + .HasColumnType("nvarchar(450)"); + + b.Property("AvatarUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("CommitCount") + .HasColumnType("int"); + + b.Property("UserProfileUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("DocumentId", "Username"); + + b.ToTable("DocsDocumentContributors"); + }); + + modelBuilder.Entity("Volo.Docs.Projects.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("DefaultDocumentName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DocumentStoreType") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Format") + .HasColumnType("nvarchar(max)"); + + b.Property("LatestVersionBranchName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("MainWebsiteUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("MinimumVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("NavigationDocumentName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ParametersDocumentName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.HasKey("Id"); + + b.ToTable("DocsProjects"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Docs.Documents.DocumentContributor", b => + { + b.HasOne("Volo.Docs.Documents.Document", null) + .WithMany("Contributors") + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Docs.Documents.Document", b => + { + b.Navigation("Contributors"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20201208084431_Added_CommitCount.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20201208084431_Added_CommitCount.cs new file mode 100644 index 0000000000..1ed082ff34 --- /dev/null +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20201208084431_Added_CommitCount.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace VoloDocs.EntityFrameworkCore.Migrations +{ + public partial class Added_CommitCount : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CommitCount", + table: "DocsDocumentContributors", + type: "int", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CommitCount", + table: "DocsDocumentContributors"); + } + } +} diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs index 204023bf3c..112509c609 100644 --- a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .UseIdentityColumns() .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.0-rc.2.20475.6"); + .HasAnnotation("ProductVersion", "5.0.0"); modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => { @@ -782,6 +782,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("AvatarUrl") .HasColumnType("nvarchar(max)"); + b.Property("CommitCount") + .HasColumnType("int"); + b.Property("UserProfileUrl") .HasColumnType("nvarchar(max)"); diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentContributorDto.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentContributorDto.cs index 337faf3db1..fcd049d7c9 100644 --- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentContributorDto.cs +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentContributorDto.cs @@ -10,5 +10,7 @@ namespace Volo.Docs.Documents public string UserProfileUrl { get; set; } public string AvatarUrl { get; set; } + + public int CommitCount { get; set; } } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Document.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Document.cs index c2ec85f33b..967b5939f2 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Document.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Document.cs @@ -87,9 +87,9 @@ namespace Volo.Docs.Documents Contributors = new List(); } - public virtual void AddContributor(string username, string userProfileUrl, string avatarUrl) + public virtual void AddContributor(string username, string userProfileUrl, string avatarUrl, int commitCount = 1) { - Contributors.AddIfNotContains(new DocumentContributor(Id, username, userProfileUrl, avatarUrl)); + Contributors.AddIfNotContains(new DocumentContributor(Id, username, userProfileUrl, avatarUrl, commitCount)); } public virtual void RemoveAllContributors() diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs index 1351b0a4ca..876686b5ff 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/DocumentContributor.cs @@ -9,6 +9,8 @@ namespace Volo.Docs.Documents public string Username { get; set; } + public int CommitCount { get; set; } + public string UserProfileUrl { get; set; } public string AvatarUrl { get; set; } @@ -23,12 +25,13 @@ namespace Volo.Docs.Documents return DocumentId == documentId && Username == username; } - public DocumentContributor(Guid documentId, string username, string userProfileUrl, string avatarUrl) + public DocumentContributor(Guid documentId, string username, string userProfileUrl, string avatarUrl, int commitCount = 1) { DocumentId = documentId; Username = username; UserProfileUrl = userProfileUrl; AvatarUrl = avatarUrl; + CommitCount = commitCount; } public override object[] GetKeys() diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/DocumentAuthor.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/DocumentAuthor.cs new file mode 100644 index 0000000000..f1fef91713 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/DocumentAuthor.cs @@ -0,0 +1,13 @@ +namespace Volo.Docs.GitHub.Documents +{ + public class DocumentAuthor + { + public string Login { get; set; } + + public string HtmlUrl { get; set; } + + public string AvatarUrl { get; set; } + + public int CommitCount { get; set; } + } +} diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs index ef8ea15e76..dfc79f7547 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs @@ -84,9 +84,11 @@ namespace Volo.Docs.GitHub.Documents } var authors = GetAuthors(commits); + + document.RemoveAllContributors(); foreach (var author in authors) { - document.AddContributor(author.Login, author.HtmlUrl, author.AvatarUrl); + document.AddContributor(author.Login, author.HtmlUrl, author.AvatarUrl, author.CommitCount); } return document; @@ -114,20 +116,35 @@ namespace Volo.Docs.GitHub.Documents : null; } - private static List GetAuthors(IReadOnlyList commits) + private static List GetAuthors(IReadOnlyList commits) { if (commits == null || !commits.Any()) { - return new List(); + return new List(); } - return commits + var authorsOrderedAndGrouped = commits .Where(x => x.Author != null) .Select(x => x.Author) .GroupBy(x => x.Id) - .OrderByDescending(x => x.Count()) - .Select(x => x.FirstOrDefault()) - .ToList(); + .OrderByDescending(x => x.Count()); + + var documentAuthors = new List(); + + foreach (var authorGroup in authorsOrderedAndGrouped) + { + var author = authorGroup.FirstOrDefault(); + var documentAuthor = new DocumentAuthor + { + CommitCount = authorGroup.Count(), + AvatarUrl = author.AvatarUrl, + HtmlUrl = author.HtmlUrl, + Login = author.Login + }; + documentAuthors.Add(documentAuthor); + } + + return documentAuthors; } private static DateTime GetLastCommitDate(IReadOnlyList commits) diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml index a7897eddfe..1cdfe7ca2e 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml @@ -217,7 +217,7 @@ @(L["Contributors"].Value) - @foreach (var contributor in Model.Document.Contributors) + @foreach (var contributor in Model.Document.Contributors.OrderByDescending(c=> c.CommitCount).ToList()) { options) : base( - timer, + timer, serviceScopeFactory) { Options = options.Value; @@ -27,8 +27,7 @@ namespace Volo.Abp.IdentityServer.Tokens await workerContext .ServiceProvider .GetRequiredService() - .CleanAsync() - ; + .CleanAsync(); } } }