Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cnblogs.Architecture.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=cachable/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
2 changes: 2 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
<Product>Cnblogs.Architecture</Product>
<PackageProjectUrl>https://github.com/cnblogs/Architecture</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/cnblogs/Architecture</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
namespace Cnblogs.Architecture.Ddd.Cqrs.Abstractions;

/// <summary>
/// 缓存行为定义。
/// Options for handing <see cref="ICachableRequest"/>.
/// </summary>
public enum CacheBehavior
{
/// <summary>
/// 不存在时获取新的。
/// Update cache after cache missed, this is the default behavior.
/// </summary>
UpdateCacheIfMiss = 1,

/// <summary>
/// 不使用缓存。
/// Do not cache this request.
/// </summary>
DisabledCache = 2
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
namespace Cnblogs.Architecture.Ddd.Cqrs.Abstractions;

/// <summary>
/// 对实现了 <see cref="ICacheableRequest" /> 的请求进行处理。
/// Handler for <see cref="ICachableRequest" />.
/// </summary>
/// <typeparam name="TRequest">实现了 <see cref="ICacheableRequest" /> 的请求。</typeparam>
/// <typeparam name="TResponse"><typeparamref name="TRequest" /> 请求的结果。</typeparam>
/// <typeparam name="TRequest">Request that implements <see cref="ICachableRequest" />.</typeparam>
/// <typeparam name="TResponse">Cached result for <typeparamref name="TRequest" />.</typeparam>
public class CacheableRequestBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : ICacheableRequest, IRequest<TResponse>
where TRequest : ICachableRequest, IRequest<TResponse>
{
private readonly IDateTimeProvider _dateTimeProvider;
private readonly ILocalCacheProvider? _local;
Expand All @@ -23,12 +23,12 @@ public class CacheableRequestBehavior<TRequest, TResponse> : IPipelineBehavior<T
private readonly ILogger<CacheableRequestBehavior<TRequest, TResponse>> _logger;

/// <summary>
/// 构建一个 <see cref="CacheableRequestBehavior{TRequest,TResponse}" />
/// Create <see cref="CacheableRequestBehavior{TRequest,TResponse}" />.
/// </summary>
/// <param name="providers">缓存提供器。</param>
/// <param name="dateTimeProvider">时间提供器。</param>
/// <param name="options">缓存配置项。</param>
/// <param name="logger">日志记录器。</param>
/// <param name="providers">Cache providers.</param>
/// <param name="dateTimeProvider">Datetime provider.</param>
/// <param name="options">Options for cache behavior.</param>
/// <param name="logger">logger.</param>
public CacheableRequestBehavior(
IEnumerable<ICacheProvider> providers,
IDateTimeProvider dateTimeProvider,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
namespace Cnblogs.Architecture.Ddd.Cqrs.Abstractions;

/// <summary>
/// 缓存配置。
/// Options for handling <see cref="ICachableRequest"/>.
/// </summary>
public class CacheableRequestOptions
{
/// <summary>
/// 如果获取失败抛出异常。
/// Rethrow exception if getting cached result failed.
/// </summary>
public bool ThrowIfFailedOnGet { get; set; }

/// <summary>
/// 如果更新失败则抛出异常。
/// Rethrow exception if updating cache failed.
/// </summary>
public bool ThrowIfFailedOnUpdate { get; set; }

/// <summary>
/// 如果清除缓存失败则抛出异常,可能被 <see cref="InvalidCacheRequest"/> 中的 <see cref="InvalidCacheRequest.ThrowIfFailed"/> 覆盖。
/// Rethrow exception if removing cache failed, this option can be overriden by <see cref="InvalidCacheRequest.ThrowIfFailed"/> for specific type of request.
/// </summary>
public bool ThrowIfFailedOnRemove { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Mapster" Version="7.3.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
</ItemGroup>
<PropertyGroup>
<Description>
Provides building blocks to archive CQRS pattern, including ICommand, IQuery, IPageableQuery, etc.
</Description>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions\Cnblogs.Architecture.Ddd.Infrastructure.Abstractions.csproj"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Mapster" Version="7.3.0"/>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0"/>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0"/>
</ItemGroup>

</Project>
78 changes: 42 additions & 36 deletions src/Cnblogs.Architecture.Ddd.Cqrs.Abstractions/CommandResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@
namespace Cnblogs.Architecture.Ddd.Cqrs.Abstractions;

/// <summary>
/// 命令返回的结果。
/// Response returned by <see cref="ICommand{TError}"/>.
/// </summary>
public abstract record CommandResponse : IValidationResponse, ILockableResponse
{
/// <summary>
/// 是否出现验证错误。
/// Check if validation fails.
/// </summary>
public bool IsValidationError { get; init; }

/// <summary>
/// 是否出现并发错误。
/// Check if concurrent error happened.
/// </summary>
public bool IsConcurrentError { get; init; }

/// <summary>
/// 错误信息。
/// The error message returned by handler, return empty if no error or no error message.
/// </summary>
/// <remarks>
/// Do not rely on this property to determine if executed successful, use <see cref="IsSuccess"/> for this purpose.
/// </remarks>
public string ErrorMessage { get; init; } = string.Empty;

/// <inheritdoc />
Expand All @@ -29,55 +32,55 @@ public abstract record CommandResponse : IValidationResponse, ILockableResponse
public bool LockAcquired { get; set; }

/// <summary>
/// 执行是否成功。
/// Check if command executed successfully.
/// </summary>
/// <returns></returns>
/// <returns>Return true if executed successfully, else return false.</returns>
public virtual bool IsSuccess()
{
return IsValidationError == false && string.IsNullOrEmpty(ErrorMessage) && IsConcurrentError == false;
}

/// <summary>
/// 获取错误信息。
/// Get error message.
/// </summary>
/// <returns></returns>
/// <returns>The error message, return <see cref="string.Empty"/> if no error.</returns>
public virtual string GetErrorMessage() => ErrorMessage;
}

/// <summary>
/// 命令返回的结果。
/// Response returned by <see cref="ICommand{TError}"/>.
/// </summary>
/// <typeparam name="TError">错误枚举类型。</typeparam>
/// <typeparam name="TError">The enumeration presenting errors.</typeparam>
public record CommandResponse<TError> : CommandResponse
where TError : Enumeration
{
/// <summary>
/// 构造一个 <see cref="CommandResponse{TError}" />
/// Create a successful <see cref="CommandResponse{TError}" />.
/// </summary>
public CommandResponse()
{
ErrorCode = default;
}

/// <summary>
/// 构造一个 <see cref="CommandResponse{TError}" />
/// Create a <see cref="CommandResponse{TError}" /> with given error.
/// </summary>
/// <param name="errorCode">错误码。</param>
/// <param name="errorCode">The error.</param>
public CommandResponse(TError errorCode)
{
ErrorCode = errorCode;
}

/// <summary>
/// 错误码。
/// The error returned by handler, can be null if execution succeeded.
/// </summary>
public TError? ErrorCode { get; set; }

/// <summary>
/// 构造一个代表命令执行失败的 <see cref="CommandResponse{TError}" />
/// Create a failed <see cref="CommandResponse{TError}" /> with given error.
/// </summary>
/// <param name="errorCode">错误码。</param>
/// <returns>代表命令执行失败的 <see cref="CommandResponse{TError}" /></returns>
/// <param name="errorCode">The error.</param>
/// <returns>A failed <see cref="CommandResponse{TError}" /> with given error.</returns>
public static CommandResponse<TError> Fail(TError errorCode)
{
return new CommandResponse<TError>(errorCode);
Expand All @@ -96,77 +99,80 @@ public override string GetErrorMessage()
}

/// <summary>
/// 构造一个代表命令执行成功的 <see cref="CommandResponse{TError}" />
/// Create a successful <see cref="CommandResponse{TError}" />.
/// </summary>
/// <returns>代表命令执行成功的 <see cref="CommandResponse{TError}" /></returns>
/// <returns>A successful <see cref="CommandResponse{TError}" />.</returns>
public static CommandResponse<TError> Success()
{
return new CommandResponse<TError>();
}
}

/// <summary>
/// 命令返回的结果。
/// Response returned by <see cref="ICommand{TError}"/>.
/// </summary>
/// <typeparam name="TView">命令执行成功时返回的结果类型。</typeparam>
/// <typeparam name="TError">错误类型。</typeparam>
/// <typeparam name="TView">The model type been returned if execution completed without error.</typeparam>
/// <typeparam name="TError">The enumeration type representing errors.</typeparam>
public record CommandResponse<TView, TError> : CommandResponse<TError>, IObjectResponse
where TError : Enumeration
{
/// <summary>
/// 构造一个 <see cref="CommandResponse{TView,TError}" />
/// Create a <see cref="CommandResponse{TView,TError}" />.
/// </summary>
public CommandResponse()
{
}

/// <summary>
/// 构造一个 <see cref="CommandResponse{TError}" />
/// Create a <see cref="CommandResponse{TError}" /> with given error.
/// </summary>
/// <param name="errorCode">错误码。</param>
/// <param name="errorCode">The error.</param>
public CommandResponse(TError errorCode)
: base(errorCode)
{
}

/// <summary>
/// 构造一个 <see cref="CommandResponse{TError}" />
/// Create a <see cref="CommandResponse{TError}" /> with given model.
/// </summary>
/// <param name="response">命令返回结果。</param>
/// <param name="response">The execution result.</param>
private CommandResponse(TView response)
{
Response = response;
}

/// <summary>
/// 命令执行结果。
/// The result been returned by command handler.
/// </summary>
/// <remarks>
/// This property can be null even if execution completed with no error.
/// </remarks>
public TView? Response { get; }

/// <summary>
/// 构造一个代表执行失败的 <see cref="CommandResponse{TView,TError}" />
/// Create a <see cref="CommandResponse{TView,TError}" /> with given error.
/// </summary>
/// <param name="errorCode">错误码。</param>
/// <returns></returns>
/// <param name="errorCode">The error.</param>
/// <returns>A <see cref="CommandResponse{TView, TError}"/> with given error.</returns>
public static new CommandResponse<TView, TError> Fail(TError errorCode)
{
return new CommandResponse<TView, TError>(errorCode);
}

/// <summary>
/// 构造一个代表执行成功的 <see cref="CommandResponse{TView,TError}" />
/// Create a <see cref="CommandResponse{TView,TError}" /> with no result nor error.
/// </summary>
/// <returns>代表执行成功的 <see cref="CommandResponse{TView,TError}" />。</returns>
/// <returns>The <see cref="CommandResponse{TView,TError}" />。</returns>
public static new CommandResponse<TView, TError> Success()
{
return new CommandResponse<TView, TError>();
}

/// <summary>
/// 构造一个代表执行成功的 <see cref="CommandResponse{TView,TError}" />
/// Create a <see cref="CommandResponse{TView,TError}" /> with given result.
/// </summary>
/// <param name="view">执行结果。</param>
/// <returns></returns>
/// <param name="view">The model to return.</param>
/// <returns>A <see cref="CommandResponse{TView, TError}"/> with given result.</returns>
public static CommandResponse<TView, TError> Success(TView view)
{
return new CommandResponse<TView, TError>(view);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
namespace Cnblogs.Architecture.Ddd.Cqrs.Abstractions;

/// <summary>
/// 定义可缓存的请求
/// Definition for cachable request.
/// </summary>
public interface ICacheableRequest
public interface ICachableRequest
{
/// <summary>
/// 本地缓存配置。
/// Configuration for local cache provider.
/// </summary>
CacheBehavior LocalCacheBehavior { get; set; }

/// <summary>
/// 远程缓存配置。
/// Configuration for remote cache provider.
/// </summary>
CacheBehavior RemoteCacheBehavior { get; set; }

/// <summary>
/// 本地缓存过期时间。
/// The expire time for local cache.
/// </summary>
TimeSpan? LocalExpires { get; set; }

/// <summary>
/// 远程缓存过期时间。
/// The expire time for remote cache.
/// </summary>
TimeSpan? RemoteExpires { get; set; }

/// <summary>
/// 获取缓存分组键,<c>null</c> 代表不分组。
/// Generate key for cache group, return <c>null</c> for no group.
/// </summary>
/// <returns></returns>
string? CacheGroupKey();

/// <summary>
/// 获取缓存键。
/// Generate cache key for each request.
/// </summary>
/// <returns></returns>
/// <returns>The cache key for current request.</returns>
string CacheKey()
{
return string.Join('-', GetCacheKeyParameters().Select(p => p?.ToString()?.ToLower()));
}

/// <summary>
/// 获取组成缓存键的参数。
/// Get parameters for generating cache key, will call <see cref="object.ToString"/> to each object been provided.
/// </summary>
/// <returns></returns>
/// <returns>The parameter array.</returns>
object?[] GetCacheKeyParameters();
}
Loading