122 changed files with 3417 additions and 3244 deletions
@ -1,117 +0,0 @@ |
|||
namespace Lion.AbpPro.CAP |
|||
{ |
|||
[Dependency(ServiceLifetime.Singleton, ReplaceServices = true)] |
|||
[ExposeServices(typeof(IConsumerServiceSelector))] |
|||
public sealed class AbpProAbpCapConsumerServiceSelector : ConsumerServiceSelector |
|||
{ |
|||
private AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; } |
|||
private IServiceProvider ServiceProvider { get; } |
|||
|
|||
/// <summary>
|
|||
/// Creates a new <see cref="T:DotNetCore.CAP.Internal.ConsumerServiceSelector" />.
|
|||
/// </summary>
|
|||
public AbpProAbpCapConsumerServiceSelector( |
|||
IServiceProvider serviceProvider, |
|||
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions) |
|||
: base(serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
AbpDistributedEventBusOptions = distributedEventBusOptions.Value; |
|||
} |
|||
|
|||
protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider) |
|||
{ |
|||
var executorDescriptorList = base.FindConsumersFromInterfaceTypes(provider).ToList(); |
|||
|
|||
//handlers
|
|||
var handlers = AbpDistributedEventBusOptions.Handlers; |
|||
|
|||
foreach (var handler in handlers) |
|||
{ |
|||
var interfaces = handler.GetInterfaces(); |
|||
foreach (var @interface in interfaces) |
|||
{ |
|||
if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(@interface)) |
|||
{ |
|||
continue; |
|||
} |
|||
var genericArgs = @interface.GetGenericArguments(); |
|||
|
|||
if (genericArgs.Length != 1) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var descriptors = GetHandlerDescription(genericArgs[0], handler); |
|||
|
|||
foreach (var descriptor in descriptors) |
|||
{ |
|||
var count = executorDescriptorList.Count(x => |
|||
x.Attribute.Name == descriptor.Attribute.Name); |
|||
|
|||
descriptor.Attribute.Group = descriptor.Attribute.Group.Insert( |
|||
descriptor.Attribute.Group.LastIndexOf(".", StringComparison.Ordinal), $".{count}"); |
|||
|
|||
executorDescriptorList.Add(descriptor); |
|||
} |
|||
|
|||
//Subscribe(genericArgs[0], new IocEventHandlerFactory(ServiceScopeFactory, handler));
|
|||
} |
|||
} |
|||
return executorDescriptorList; |
|||
} |
|||
|
|||
private IEnumerable<ConsumerExecutorDescriptor> GetHandlerDescription(Type eventType,Type typeInfo) |
|||
{ |
|||
var serviceTypeInfo = typeof(IDistributedEventHandler<>) |
|||
.MakeGenericType(eventType); |
|||
var method = typeInfo |
|||
.GetMethod( |
|||
nameof(IDistributedEventHandler<object>.HandleEventAsync), |
|||
new[] { eventType } |
|||
); |
|||
var eventName = EventNameAttribute.GetNameOrDefault(eventType); |
|||
var topicAttr = method.GetCustomAttributes<TopicAttribute>(true); |
|||
var topicAttributes = topicAttr.ToList(); |
|||
|
|||
if (topicAttributes.Count == 0) |
|||
{ |
|||
topicAttributes.Add(new CapSubscribeAttribute(eventName)); |
|||
} |
|||
|
|||
foreach (var attr in topicAttributes) |
|||
{ |
|||
SetSubscribeAttribute(attr); |
|||
|
|||
var parameters = method.GetParameters() |
|||
.Select(parameter => new ParameterDescriptor |
|||
{ |
|||
Name = parameter.Name, |
|||
ParameterType = parameter.ParameterType, |
|||
IsFromCap = parameter.GetCustomAttributes(typeof(FromCapAttribute)).Any() |
|||
}).ToList(); |
|||
|
|||
yield return InitDescriptor(attr, method, typeInfo.GetTypeInfo(), serviceTypeInfo.GetTypeInfo(), parameters); |
|||
} |
|||
} |
|||
|
|||
private static ConsumerExecutorDescriptor InitDescriptor( |
|||
TopicAttribute attr, |
|||
MethodInfo methodInfo, |
|||
TypeInfo implType, |
|||
TypeInfo serviceTypeInfo, |
|||
IList<ParameterDescriptor> parameters) |
|||
{ |
|||
var descriptor = new ConsumerExecutorDescriptor |
|||
{ |
|||
Attribute = attr, |
|||
MethodInfo = methodInfo, |
|||
ImplTypeInfo = implType, |
|||
ServiceTypeInfo = serviceTypeInfo, |
|||
Parameters = parameters |
|||
}; |
|||
|
|||
return descriptor; |
|||
} |
|||
} |
|||
} |
|||
@ -1,161 +0,0 @@ |
|||
namespace Lion.AbpPro.CAP |
|||
{ |
|||
public class AbpProAbpCapDistributedEventBus : |
|||
EventBusBase, |
|||
IDistributedEventBus, |
|||
ISingletonDependency |
|||
{ |
|||
private AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; } |
|||
private ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; } |
|||
private ConcurrentDictionary<string, Type> EventTypes { get; } |
|||
|
|||
private readonly ICapPublisher CapPublisher; |
|||
|
|||
|
|||
public AbpProAbpCapDistributedEventBus(IServiceScopeFactory serviceScopeFactory, |
|||
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions, |
|||
ICapPublisher capPublisher, |
|||
ICurrentTenant currentTenant, |
|||
UnitOfWorkManager unitOfWorkManager, |
|||
IEventHandlerInvoker eventHandlerInvoker) |
|||
: base(serviceScopeFactory, currentTenant,unitOfWorkManager,eventHandlerInvoker) |
|||
{ |
|||
CapPublisher = capPublisher; |
|||
AbpDistributedEventBusOptions = distributedEventBusOptions.Value; |
|||
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>(); |
|||
EventTypes = new ConcurrentDictionary<string, Type>(); |
|||
} |
|||
|
|||
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory) |
|||
{ |
|||
//This is handled by CAP ConsumerServiceSelector
|
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public override void Unsubscribe<TEvent>(Func<TEvent, Task> action) |
|||
{ |
|||
Check.NotNull(action, nameof(action)); |
|||
|
|||
GetOrCreateHandlerFactories(typeof(TEvent)) |
|||
.Locking(factories => |
|||
{ |
|||
factories.RemoveAll( |
|||
factory => |
|||
{ |
|||
var singleInstanceFactory = factory as SingleInstanceHandlerFactory; |
|||
if (singleInstanceFactory == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var actionHandler = singleInstanceFactory.HandlerInstance as ActionEventHandler<TEvent>; |
|||
if (actionHandler == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return actionHandler.Action == action; |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
public override void Unsubscribe(Type eventType, IEventHandler handler) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType) |
|||
.Locking(factories => |
|||
{ |
|||
factories.RemoveAll( |
|||
factory => |
|||
factory is SingleInstanceHandlerFactory && |
|||
(factory as SingleInstanceHandlerFactory).HandlerInstance == handler |
|||
); |
|||
}); |
|||
} |
|||
|
|||
public override void Unsubscribe(Type eventType, IEventHandlerFactory factory) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory)); |
|||
} |
|||
|
|||
public override void UnsubscribeAll(Type eventType) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear()); |
|||
} |
|||
|
|||
protected override Task PublishToEventBusAsync(Type eventType, object eventData) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
protected override void AddToUnitOfWork(IUnitOfWork unitOfWork, UnitOfWorkEventRecord eventRecord) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IDisposable Subscribe<TEvent>(IDistributedEventHandler<TEvent> handler) where TEvent : class |
|||
{ |
|||
return Subscribe(typeof(TEvent), handler); |
|||
} |
|||
|
|||
public async Task PublishAsync<TEvent>(TEvent eventData, bool onUnitOfWorkComplete = true, |
|||
bool useOutbox = true) where TEvent : class |
|||
{ |
|||
var eventName = EventNameAttribute.GetNameOrDefault(typeof(TEvent)); |
|||
await CapPublisher.PublishAsync(eventName, eventData); |
|||
|
|||
} |
|||
|
|||
public async Task PublishAsync(Type eventType, object eventData, bool onUnitOfWorkComplete = true, |
|||
bool useOutbox = true) |
|||
{ |
|||
var eventName = EventNameAttribute.GetNameOrDefault(eventType); |
|||
await CapPublisher.PublishAsync(eventName, eventData); |
|||
} |
|||
|
|||
|
|||
protected override IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType) |
|||
{ |
|||
var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>(); |
|||
|
|||
foreach (var handlerFactory in |
|||
HandlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key))) |
|||
{ |
|||
handlerFactoryList.Add( |
|||
new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value)); |
|||
} |
|||
|
|||
return handlerFactoryList.ToArray(); |
|||
} |
|||
|
|||
private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType) |
|||
{ |
|||
return HandlerFactories.GetOrAdd( |
|||
eventType, |
|||
type => |
|||
{ |
|||
var eventName = EventNameAttribute.GetNameOrDefault(type); |
|||
EventTypes[eventName] = type; |
|||
return new List<IEventHandlerFactory>(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
private static bool ShouldTriggerEventForHandler(Type targetEventType, Type handlerEventType) |
|||
{ |
|||
//Should trigger same type
|
|||
if (handlerEventType == targetEventType) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
//TODO: Support inheritance? But it does not support on subscription to RabbitMq!
|
|||
//Should trigger for inherited types
|
|||
if (handlerEventType.IsAssignableFrom(targetEventType)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +0,0 @@ |
|||
namespace Lion.AbpPro.CAP |
|||
{ |
|||
[DependsOn(typeof(AbpEventBusModule))] |
|||
public class AbpProAbpCapModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
namespace Lion.AbpPro.CAP |
|||
{ |
|||
public static class AbpProAbpCapServiceCollectionExtensions |
|||
{ |
|||
public static ServiceConfigurationContext AddAbpCap( |
|||
this ServiceConfigurationContext context, |
|||
Action<CapOptions> capAction) |
|||
{ |
|||
context.Services.AddCap(capAction); |
|||
context.Services.AddSingleton<IConsumerServiceSelector, AbpProAbpCapConsumerServiceSelector>(); |
|||
context.Services.AddSingleton<IDistributedEventBus, AbpProAbpCapDistributedEventBus>(); |
|||
return context; |
|||
} |
|||
} |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.1</TargetFramework> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Core" /> |
|||
<PackageReference Include="Volo.Abp.EventBus" /> |
|||
<PackageReference Include="DotNetCore.CAP" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,23 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Dtos |
|||
{ |
|||
[Serializable] |
|||
public class CustomeListResultDto<T> |
|||
{ |
|||
public IReadOnlyList<T> Items |
|||
{ |
|||
get { return _items ??= new List<T>(); } |
|||
set => _items = value; |
|||
} |
|||
|
|||
private IReadOnlyList<T> _items; |
|||
|
|||
public CustomeListResultDto() |
|||
{ |
|||
} |
|||
|
|||
public CustomeListResultDto(IReadOnlyList<T> items) |
|||
{ |
|||
Items = items; |
|||
} |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Dtos |
|||
{ |
|||
[Serializable] |
|||
public class CustomePagedResultDto<T> : CustomeListResultDto<T> |
|||
{ |
|||
public long TotalCount { get; set; } |
|||
|
|||
public CustomePagedResultDto() |
|||
{ |
|||
} |
|||
|
|||
public CustomePagedResultDto(long totalCount, IReadOnlyList<T> items) |
|||
: base(items) |
|||
{ |
|||
TotalCount = totalCount; |
|||
} |
|||
} |
|||
} |
|||
@ -1,41 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Dtos |
|||
{ |
|||
|
|||
|
|||
public abstract class FromSelectorBase |
|||
{ |
|||
protected FromSelectorBase(int value, string label) |
|||
{ |
|||
Value = value; |
|||
Label = label; |
|||
} |
|||
public int Value { get; protected set; } |
|||
public string Label { get; protected set; } |
|||
} |
|||
|
|||
public abstract class FromSelectorBase<TValue, TLabel> |
|||
{ |
|||
protected FromSelectorBase(TValue value, TLabel label) |
|||
{ |
|||
Value = value; |
|||
Label = label; |
|||
} |
|||
|
|||
public TValue Value { get; protected set; } |
|||
public TLabel Label { get; protected set; } |
|||
} |
|||
|
|||
public class FromSelector : FromSelectorBase |
|||
{ |
|||
public FromSelector(int value, string label) : base(value, label) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public class FromSelector<TValue, TLabel> : FromSelectorBase<TValue, TLabel> |
|||
{ |
|||
public FromSelector(TValue value, TLabel label) : base(value, label) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Dtos |
|||
{ |
|||
public class IdInput |
|||
{ |
|||
public Guid Id { get; set; } |
|||
} |
|||
} |
|||
@ -1,56 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Dtos |
|||
{ |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 分页查询时使用的Dto类型
|
|||
/// </summary>
|
|||
public class PagingBase : IValidatableObject |
|||
{ |
|||
public const int MaxPageSize = 100000; |
|||
|
|||
/// <summary>
|
|||
/// 当前页面.默认从1开始
|
|||
/// </summary>
|
|||
public int PageIndex { get; set; } = 1; |
|||
|
|||
/// <summary>
|
|||
/// 每页多少条.每页显示多少记录
|
|||
/// </summary>
|
|||
public int PageSize { get; set; } = 10; |
|||
|
|||
/// <summary>
|
|||
/// 跳过多少条
|
|||
/// </summary>
|
|||
public int SkipCount => (PageIndex - 1) * PageSize; |
|||
|
|||
protected PagingBase() |
|||
{ |
|||
} |
|||
|
|||
public PagingBase(int pageIndex = 1, int pageSize = 10) |
|||
{ |
|||
PageIndex = pageIndex; |
|||
PageSize = pageSize; |
|||
} |
|||
|
|||
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) |
|||
{ |
|||
if (PageIndex < 1) |
|||
{ |
|||
yield return new ValidationResult( |
|||
"起始页必须大于等于1", |
|||
new[] { "PageIndex"} |
|||
); |
|||
} |
|||
|
|||
if (PageSize > MaxPageSize) |
|||
{ |
|||
yield return new ValidationResult( |
|||
$"每页最大记录数不能超过'{MaxPageSize}'", |
|||
new[] { "PageSize"} |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,36 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Dtos |
|||
{ |
|||
public class WrapResult<T> |
|||
{ |
|||
public bool Success { get; private set; } |
|||
|
|||
public string Message { get; private set; } |
|||
|
|||
public T Data { get; private set; } |
|||
|
|||
public int Code { get; private set; } |
|||
|
|||
public WrapResult() |
|||
{ |
|||
Success = true; |
|||
Message = "Success"; |
|||
Data = default; |
|||
Code = 200; |
|||
} |
|||
|
|||
public void SetSuccess(T data, string message = "Success", int code = 200) |
|||
{ |
|||
Success = true; |
|||
Data = data; |
|||
Code = code; |
|||
} |
|||
|
|||
public void SetFail(string message = "Fail", int code = 500) |
|||
{ |
|||
Success = false; |
|||
Message = message; |
|||
Code = code; |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -1,6 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Dtos |
|||
{ |
|||
public class WrapResultAttribute : Attribute |
|||
{ |
|||
} |
|||
} |
|||
@ -1,265 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs |
|||
{ |
|||
/// <summary>
|
|||
/// 参数合法性检查类
|
|||
/// </summary>
|
|||
[DebuggerStepThrough] |
|||
public static class Guard |
|||
{ |
|||
/// <summary>
|
|||
/// 检查参数不能为空引用,
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称</param>
|
|||
public static T NotNull<T>(T value, string valueName) |
|||
{ |
|||
if (null == value) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查字符串不能为空引用或空字符串,
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="maxLength">字符串允许的最大长度。</param>
|
|||
/// <param name="minLength">字符串允许的最小长度。0表示不限制最小长度</param>
|
|||
public static string NotNullOrEmpty(string value, string valueName, int maxLength = int.MaxValue, |
|||
int minLength = 0) |
|||
{ |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
if (value.Length > maxLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
if (minLength > 0 && value.Length < minLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查字符串不能为空引用或全部为空白,
|
|||
/// </summary>
|
|||
/// <param name="value">需检查的字符串</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="maxLength">字符串允许的最大长度。</param>
|
|||
/// <param name="minLength">字符串允许的最小长度。0表示不限制最小长度</param>
|
|||
public static string NotNullOrWhiteSpace( |
|||
string value, |
|||
string valueName, |
|||
int maxLength = int.MaxValue, |
|||
int minLength = 0) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(value)) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
if (value.Length > maxLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
if (minLength > 0 && value.Length < minLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查字符串长度是否超过最大长度,或低于最小长度,
|
|||
/// </summary>
|
|||
/// <param name="value">需检查的字符串。</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="maxLength">字符串允许的最大长度。</param>
|
|||
/// <param name="minLength">字符串要求的最小长度。0表示不限制最小长度</param>
|
|||
public static string Length(string value, string valueName, int maxLength = int.MaxValue, |
|||
int minLength = 0) |
|||
{ |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
if (value.Length > maxLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
if (minLength > 0 && value.Length < minLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查Guid值不能为Guid.Empty
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
public static Guid NotEmpty( |
|||
Guid value, |
|||
string valueName) |
|||
{ |
|||
if (value == Guid.Empty) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查集合不能为空引用或空集合,
|
|||
/// </summary>
|
|||
/// <typeparam name="T">集合项的类型。</typeparam>
|
|||
/// <param name="list"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
public static void NotNullOrEmpty<T>( |
|||
IReadOnlyList<T> list, |
|||
string valueName) |
|||
{ |
|||
if (null == list || !list.Any()) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查参数必须小于[或可等于,参数<paramref name="canEqual"/>]指定值,
|
|||
/// </summary>
|
|||
/// <typeparam name="T">参数类型。</typeparam>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="target">要比较的值。</param>
|
|||
/// <param name="canEqual">是否可等于。</param>
|
|||
public static void LessThan<T>( |
|||
T value, |
|||
string valueName, |
|||
T target, |
|||
bool canEqual = false) |
|||
where T : IComparable<T> |
|||
{ |
|||
var flag = canEqual ? value.CompareTo(target) <= 0 : value.CompareTo(target) < 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查参数必须大于[或可等于,参数<paramref name="canEqual"/>]指定值,
|
|||
/// </summary>
|
|||
/// <typeparam name="T">参数类型。</typeparam>
|
|||
/// <param name="value">需检查的参数。</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="target">要比较的值。</param>
|
|||
/// <param name="canEqual">是否可等于。</param>
|
|||
public static void GreaterThan<T>( |
|||
T value, |
|||
string valueName, |
|||
T target, |
|||
bool canEqual = false) |
|||
where T : IComparable<T> |
|||
{ |
|||
var flag = canEqual ? value.CompareTo(target) >= 0 : value.CompareTo(target) > 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查参数必须在指定范围之间
|
|||
/// </summary>
|
|||
/// <typeparam name="T">参数类型。</typeparam>
|
|||
/// <param name="value">需检查的参数。</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="start">比较范围的起始值。</param>
|
|||
/// <param name="end">比较范围的结束值。</param>
|
|||
/// <param name="startEqual">是否可等于起始值</param>
|
|||
/// <param name="endEqual">是否可等于结束值</param>
|
|||
public static void Between<T>( |
|||
T value, |
|||
string valueName, |
|||
T start, |
|||
T end, |
|||
bool startEqual = false, |
|||
bool endEqual = false) |
|||
where T : IComparable<T> |
|||
{ |
|||
var flag = startEqual ? value.CompareTo(start) >= 0 : value.CompareTo(start) > 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
flag = endEqual ? value.CompareTo(end) <= 0 : value.CompareTo(end) < 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查指定路径的文件夹必须存在,
|
|||
/// </summary>
|
|||
/// <param name="directory">需检查的路径。</param>
|
|||
/// <param name="parameterName">参数名称。</param>
|
|||
public static string DirectoryExists( |
|||
string directory, |
|||
string parameterName) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(directory)) |
|||
{ |
|||
throw new DirectoryNotFoundException(parameterName); |
|||
} |
|||
|
|||
if (!Directory.Exists(directory)) |
|||
{ |
|||
throw new DirectoryNotFoundException(directory); |
|||
} |
|||
|
|||
return directory; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查指定路径的文件必须存在,否则抛出
|
|||
/// </summary>
|
|||
/// <param name="filename"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
|
|||
public static string FileExists( |
|||
string filename, |
|||
string valueName) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(filename)) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
if (!File.Exists(filename)) |
|||
{ |
|||
throw new FileNotFoundException(filename); |
|||
} |
|||
|
|||
return filename; |
|||
} |
|||
} |
|||
} |
|||
@ -1,156 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.Customs.Http |
|||
{ |
|||
/// <summary>
|
|||
/// 基于IHttpClientFactory二次封装httpclient
|
|||
/// </summary>
|
|||
public static class HttpClientHelper |
|||
{ |
|||
|
|||
public static async Task<TResult> GetAsync<TResult>(this IHttpClientFactory _httpClientFactory, string clientName, string url, Dictionary<string, string> headers = null) where TResult : class |
|||
{ |
|||
try |
|||
{ |
|||
var client = _httpClientFactory.CreateClient(clientName); |
|||
if (headers != null && headers.Count > 0) |
|||
{ |
|||
foreach (var item in headers) |
|||
{ |
|||
client.DefaultRequestHeaders.Add(item.Key, item.Value); |
|||
} |
|||
|
|||
} |
|||
|
|||
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache"); |
|||
|
|||
//执行请求
|
|||
var response = await client.GetAsync(url); |
|||
var result = await response.Content.ReadAsStringAsync(); |
|||
if (response.IsSuccessStatusCode) |
|||
{ |
|||
if (result != null && !string.IsNullOrEmpty(result)) |
|||
return JsonConvert.DeserializeObject<TResult>(result); |
|||
else |
|||
return default(TResult); |
|||
} |
|||
else |
|||
{ |
|||
if (string.IsNullOrEmpty(result)) |
|||
result = response.ReasonPhrase; |
|||
throw new Exception(result); |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new Exception(e.Message); |
|||
} |
|||
} |
|||
|
|||
public static async Task<TResult> PostAsync<T, TResult>(this IHttpClientFactory _httpClientFactory, string clientName, string url, T obj, Dictionary<string, string> headers = null) where T : class where TResult : class |
|||
{ |
|||
var data = typeof(T).Name.ToLower() == "string" ? obj.ToString() : JsonConvert.SerializeObject(obj); |
|||
var client = _httpClientFactory.CreateClient(clientName); |
|||
if (headers != null && headers.Count > 0) |
|||
{ |
|||
foreach (var item in headers) |
|||
{ |
|||
client.DefaultRequestHeaders.Add(item.Key, item.Value); |
|||
} |
|||
} |
|||
|
|||
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache"); |
|||
//post 参数
|
|||
var content = new StringContent(data, Encoding.UTF8, "application/json"); |
|||
//执行请求
|
|||
var response = await client.PostAsync(url, content); |
|||
|
|||
var result = await response.Content.ReadAsStringAsync(); |
|||
if (response.IsSuccessStatusCode) |
|||
{ |
|||
if (result != null && !string.IsNullOrEmpty(result)) |
|||
return JsonConvert.DeserializeObject<TResult>(result); |
|||
else |
|||
return default(TResult); |
|||
} |
|||
else |
|||
{ |
|||
if (string.IsNullOrEmpty(result)) |
|||
result = response.ReasonPhrase; |
|||
|
|||
throw new Exception(result); |
|||
} |
|||
} |
|||
|
|||
public static async Task<TResult> PutAsync<T, TResult>(this IHttpClientFactory _httpClientFactory, string clientName, string url, T obj, Dictionary<string, string> headers = null) where T : class where TResult : class |
|||
{ |
|||
var data = typeof(T).Name.ToLower() == "string" ? obj.ToString() : JsonConvert.SerializeObject(obj); |
|||
var client = _httpClientFactory.CreateClient(clientName); |
|||
if (headers != null && headers.Count > 0) |
|||
{ |
|||
foreach (var item in headers) |
|||
{ |
|||
client.DefaultRequestHeaders.Add(item.Key, item.Value); |
|||
} |
|||
} |
|||
|
|||
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache"); |
|||
//post 参数
|
|||
var content = new StringContent(data, Encoding.UTF8, "application/json"); |
|||
//执行请求
|
|||
var response = await client.PutAsync(url, content); |
|||
|
|||
var result = await response.Content.ReadAsStringAsync(); |
|||
if (response.IsSuccessStatusCode) |
|||
{ |
|||
if (result != null && !string.IsNullOrEmpty(result)) |
|||
return JsonConvert.DeserializeObject<TResult>(result); |
|||
else |
|||
return default(TResult); |
|||
} |
|||
else |
|||
{ |
|||
if (string.IsNullOrEmpty(result)) |
|||
result = response.ReasonPhrase; |
|||
|
|||
throw new Exception(result); |
|||
} |
|||
} |
|||
|
|||
public static async Task<TResult> DeleteAsync<TResult>(this IHttpClientFactory _httpClientFactory, string clientName, string url, Dictionary<string, string> headers = null) where TResult : class |
|||
{ |
|||
try |
|||
{ |
|||
var client = _httpClientFactory.CreateClient(clientName); |
|||
if (headers != null && headers.Count > 0) |
|||
{ |
|||
foreach (var item in headers) |
|||
{ |
|||
client.DefaultRequestHeaders.Add(item.Key, item.Value); |
|||
} |
|||
} |
|||
|
|||
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache"); |
|||
|
|||
//执行请求
|
|||
var response = await client.DeleteAsync(url); |
|||
var result = await response.Content.ReadAsStringAsync(); |
|||
if (response.IsSuccessStatusCode) |
|||
{ |
|||
if (result != null && !string.IsNullOrEmpty(result)) |
|||
return JsonConvert.DeserializeObject<TResult>(result); |
|||
else |
|||
return default(TResult); |
|||
} |
|||
else |
|||
{ |
|||
if (string.IsNullOrEmpty(result)) |
|||
result = response.ReasonPhrase; |
|||
throw new Exception(result); |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new Exception(e.Message); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// Global using directives
|
|||
|
|||
global using System; |
|||
global using System.Collections.Generic; |
|||
global using System.Collections.Specialized; |
|||
global using System.ComponentModel; |
|||
global using System.ComponentModel.DataAnnotations; |
|||
global using System.Diagnostics; |
|||
global using System.Globalization; |
|||
global using System.IO; |
|||
global using System.Linq; |
|||
global using System.Linq.Expressions; |
|||
global using System.Net.Http; |
|||
global using System.Net.Http.Headers; |
|||
global using System.Reflection; |
|||
global using System.Runtime.ExceptionServices; |
|||
global using System.Text; |
|||
global using System.Text.RegularExpressions; |
|||
global using System.Threading.Tasks; |
|||
global using System.Web; |
|||
global using Lion.AbpPro.Extension.Customs; |
|||
global using Lion.AbpPro.Extension.System.Collections.Generic; |
|||
global using Lion.AbpPro.Extension.System.Reflection; |
|||
global using Newtonsoft.Json; |
|||
@ -1,19 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.1</TargetFramework> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.DependencyModel" /> |
|||
<PackageReference Include="Microsoft.Extensions.Http" /> |
|||
<PackageReference Include="Newtonsoft.Json" /> |
|||
<PackageReference Include="System.ComponentModel.Annotations" /> |
|||
<PackageReference Include="Volo.Abp.Autofac" /> |
|||
<PackageReference Include="Volo.Abp.Json" /> |
|||
<PackageReference Include="Volo.Abp.Validation" /> |
|||
<PackageReference Include="Volo.Abp.Ddd.Domain" /> |
|||
<PackageReference Include="Volo.Abp.AutoMapper" /> |
|||
<PackageReference Include="Volo.Abp.ObjectMapping" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,27 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System |
|||
{ |
|||
/// <summary>
|
|||
/// 布尔值<see cref="Boolean"/>类型的扩展辅助操作类
|
|||
/// </summary>
|
|||
public static class BooleanExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 把布尔值转换为小写字符串
|
|||
/// </summary>
|
|||
public static string ToLower(this bool value) |
|||
{ |
|||
return value.ToString().ToLower(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,则抛出异常
|
|||
/// </summary>
|
|||
public static void TrueThrow(this bool flag, Exception exception) |
|||
{ |
|||
if (flag) |
|||
{ |
|||
throw exception; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,74 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Collections.Generic |
|||
{ |
|||
/// <summary>
|
|||
/// 集合扩展方法
|
|||
/// </summary>
|
|||
public static class CollectionExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 如果条件成立,添加项
|
|||
/// </summary>
|
|||
public static void AddIf<T>(this ICollection<T> collection, T value, bool flag) |
|||
{ |
|||
Guard.NotNull(collection, nameof(collection)); |
|||
if (flag) |
|||
{ |
|||
collection.Add(value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,添加项
|
|||
/// </summary>
|
|||
public static void AddIf<T>(this ICollection<T> collection, T value, Func<bool> func) |
|||
{ |
|||
Guard.NotNull(collection, nameof(collection)); |
|||
if (func()) |
|||
{ |
|||
collection.Add(value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取对象,不存在对使用委托添加对象
|
|||
/// </summary>
|
|||
public static T GetOrAdd<T>(this ICollection<T> collection, Func<T, bool> selector, Func<T> factory) |
|||
{ |
|||
Guard.NotNull(collection, nameof(collection)); |
|||
T item = collection.FirstOrDefault(selector); |
|||
if (item == null) |
|||
{ |
|||
item = factory(); |
|||
collection.Add(item); |
|||
} |
|||
|
|||
return item; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 判断集合是否为null或空集合
|
|||
/// </summary>
|
|||
public static bool IsNullOrEmpty<T>(this ICollection<T> collection) |
|||
{ |
|||
return collection == null || collection.Count == 0; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 交换两项的位置
|
|||
/// </summary>
|
|||
public static void Swap<T>(this List<T> list, int index1, int index2) |
|||
{ |
|||
Guard.Between(index1, nameof(index1), 0, list.Count, true); |
|||
Guard.Between(index2, nameof(index2), 0, list.Count, true); |
|||
|
|||
if (index1 == index2) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
T tmp = list[index1]; |
|||
list[index1] = list[index2]; |
|||
list[index2] = tmp; |
|||
} |
|||
} |
|||
} |
|||
@ -1,196 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Collections.Generic |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerable集合扩展方法
|
|||
/// </summary>
|
|||
public static class EnumerableExtensions |
|||
{ |
|||
/// <summary>断言集合中的元素符合指定表达式,否则抛出异常。</summary>
|
|||
/// <typeparam name="T">集合项类型</typeparam>
|
|||
/// <param name="source">源集合</param>
|
|||
/// <param name="predicate">元素判断表达式</param>
|
|||
/// <param name="errorSelector">异常选择器</param>
|
|||
/// <returns>筛选过的集合</returns>
|
|||
public static IEnumerable<T> Assert<T>(this IEnumerable<T> source, Func<T, bool> predicate, |
|||
Func<T, Exception> errorSelector = null) |
|||
{ |
|||
foreach (var item in source) |
|||
{ |
|||
var success = predicate(item); |
|||
if (!success) |
|||
{ |
|||
throw errorSelector?.Invoke(item) ?? |
|||
new InvalidOperationException("集合中包含无效的元素。"); |
|||
} |
|||
|
|||
yield return item; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 打乱一个集合的项顺序,将一个集合洗牌
|
|||
/// </summary>
|
|||
public static IEnumerable<TSource> Shuffle<TSource>(this IEnumerable<TSource> source) |
|||
{ |
|||
// ReSharper disable PossibleMultipleEnumeration
|
|||
|
|||
Guard.NotNull(source, nameof(source)); |
|||
return source.OrderBy(m => Guid.NewGuid()); |
|||
|
|||
// ReSharper restore PossibleMultipleEnumeration
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将集合展开并分别转换成字符串,再以指定的分隔符衔接,拼成一个字符串返回。默认分隔符为逗号。
|
|||
/// </summary>
|
|||
/// <param name="collection">要处理的集合</param>
|
|||
/// <param name="separator">分隔符,默认为逗号</param>
|
|||
/// <returns>拼接后的字符串</returns>
|
|||
public static string ExpandAndToString<T>(this IEnumerable<T> collection, string separator = ",") |
|||
{ |
|||
return collection.ExpandAndToString(item => item?.ToString() ?? string.Empty, separator); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 循环集合的每一项,调用委托生成字符串,返回合并后的字符串。默认分隔符为逗号
|
|||
/// </summary>
|
|||
/// <param name="collection">待处理的集合</param>
|
|||
/// <param name="itemFormatFunc">单个集合项的转换委托</param>
|
|||
/// <param name="separator">分隔符,默认为逗号</param>
|
|||
/// <typeparam name="T">泛型类型</typeparam>
|
|||
/// <returns></returns>
|
|||
public static string ExpandAndToString<T>( |
|||
this IEnumerable<T> collection, |
|||
Func<T, string> itemFormatFunc, |
|||
string separator = ",") |
|||
{ |
|||
collection = collection as IList<T> ?? collection.ToList(); |
|||
Guard.NotNull(itemFormatFunc, nameof(itemFormatFunc)); |
|||
|
|||
if (!collection.Any()) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
var sb = new StringBuilder(); |
|||
var i = 0; |
|||
var count = collection.Count(); |
|||
foreach (var item in collection) |
|||
{ |
|||
if (i == count - 1) |
|||
{ |
|||
sb.Append(itemFormatFunc(item)); |
|||
} |
|||
else |
|||
{ |
|||
sb.Append(itemFormatFunc(item) + separator); |
|||
} |
|||
|
|||
i++; |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 集合是否为空
|
|||
/// </summary>
|
|||
/// <param name="collection"> 要处理的集合 </param>
|
|||
/// <typeparam name="T"> 动态类型 </typeparam>
|
|||
/// <returns> 为空返回True,不为空返回False </returns>
|
|||
public static bool IsEmpty<T>(this IEnumerable<T> collection) |
|||
{ |
|||
collection = collection as IList<T> ?? collection.ToList(); |
|||
return !collection.Any(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 根据第三方条件是否为真来决定是否执行指定条件的查询
|
|||
/// </summary>
|
|||
/// <param name="source"> 要查询的源 </param>
|
|||
/// <param name="predicate"> 查询条件 </param>
|
|||
/// <param name="condition"> 第三方条件 </param>
|
|||
/// <typeparam name="T"> 动态类型 </typeparam>
|
|||
/// <returns> 查询的结果 </returns>
|
|||
public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool condition) |
|||
{ |
|||
Guard.NotNull(predicate, nameof(predicate)); |
|||
source = source as IList<T> ?? source.ToList(); |
|||
|
|||
return condition ? source.Where(predicate) : source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串集合按指定前缀排序
|
|||
/// </summary>
|
|||
public static IEnumerable<T> OrderByPrefixes<T>(this IEnumerable<T> source, Func<T, string> keySelector, |
|||
params string[] prefixes) |
|||
{ |
|||
var all = source.OrderBy(keySelector).ToList(); |
|||
var result = new List<T>(); |
|||
foreach (var prefix in prefixes) |
|||
{ |
|||
var tmpList = all.Where(m => keySelector(m).StartsWith(prefix)).OrderBy(keySelector).ToList(); |
|||
all = all.Except(tmpList).ToList(); |
|||
result.AddRange(tmpList); |
|||
} |
|||
|
|||
result.AddRange(all); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 根据指定条件返回集合中不重复的元素
|
|||
/// </summary>
|
|||
/// <typeparam name="T">动态类型</typeparam>
|
|||
/// <typeparam name="TKey">动态筛选条件类型</typeparam>
|
|||
/// <param name="source">要操作的源</param>
|
|||
/// <param name="keySelector">重复数据筛选条件</param>
|
|||
/// <returns>不重复元素的集合</returns>
|
|||
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) |
|||
{ |
|||
Guard.NotNull(keySelector, nameof(keySelector)); |
|||
source = source as IList<T> ?? source.ToList(); |
|||
|
|||
return source.GroupBy(keySelector).Select(group => group.First()); |
|||
} |
|||
|
|||
#region Internal
|
|||
|
|||
internal static int? TryGetCollectionCount<T>(this IEnumerable<T> source) |
|||
{ |
|||
switch (source) |
|||
{ |
|||
case null: |
|||
throw new ArgumentNullException(nameof(source)); |
|||
case ICollection<T> collection: |
|||
return collection.Count; |
|||
case IReadOnlyCollection<T> collection: |
|||
return collection.Count; |
|||
default: |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
static int CountUpTo<T>(this IEnumerable<T> source, int max) |
|||
{ |
|||
if (source == null) throw new ArgumentNullException(nameof(source)); |
|||
if (max < 0) |
|||
throw new ArgumentOutOfRangeException(nameof(max), "最大计数参数不能为负。"); |
|||
|
|||
var count = 0; |
|||
|
|||
using (var e = source.GetEnumerator()) |
|||
{ |
|||
while (count < max && e.MoveNext()) |
|||
{ |
|||
count++; |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -1,100 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System |
|||
{ |
|||
/// <summary>
|
|||
/// 时间扩展操作类
|
|||
/// </summary>
|
|||
public static class DateTimeExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 当前时间是否周末
|
|||
/// </summary>
|
|||
/// <param name="dateTime">时间点</param>
|
|||
/// <returns></returns>
|
|||
public static bool IsWeekend(this DateTime dateTime) |
|||
{ |
|||
DayOfWeek[] weeks = { DayOfWeek.Saturday, DayOfWeek.Sunday }; |
|||
return weeks.Contains(dateTime.DayOfWeek); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 当前时间是否工作日
|
|||
/// </summary>
|
|||
/// <param name="dateTime">时间点</param>
|
|||
/// <returns></returns>
|
|||
public static bool IsWeekday(this DateTime dateTime) |
|||
{ |
|||
DayOfWeek[] weeks = { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }; |
|||
return weeks.Contains(dateTime.DayOfWeek); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取时间相对唯一字符串
|
|||
/// </summary>
|
|||
/// <param name="dateTime"></param>
|
|||
/// <param name="millisecond">是否使用毫秒</param>
|
|||
/// <returns></returns>
|
|||
public static string ToUniqueString(this DateTime dateTime, bool millisecond = false) |
|||
{ |
|||
var seconds = dateTime.Hour * 3600 + dateTime.Minute * 60 + dateTime.Second; |
|||
var value = $"{dateTime:yyyy}{dateTime.DayOfYear}{seconds}"; |
|||
if (millisecond) |
|||
{ |
|||
return value + dateTime.ToString("fff"); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将时间转换为JS时间格式(Date.getTime())
|
|||
/// </summary>
|
|||
/// <param name="dateTime"></param>
|
|||
/// <param name="millisecond">是否使用毫秒</param>
|
|||
public static string ToJsGetTime(this DateTime dateTime, bool millisecond = true) |
|||
{ |
|||
var utc = dateTime.ToUniversalTime(); |
|||
var span = utc.Subtract(new DateTime(1970, 1, 1)); |
|||
return Math.Round(millisecond ? span.TotalMilliseconds : span.TotalSeconds).ToString(CultureInfo.InvariantCulture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将JS时间格式的数值转换为时间
|
|||
/// </summary>
|
|||
public static DateTime FromJsGetTime(this long jsTime) |
|||
{ |
|||
var length = jsTime.ToString().Length; |
|||
if (!(length == 10 || length == 13)) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(null, "JS时间数值的长度不正确,必须为10位或13位"); |
|||
} |
|||
var start = new DateTime(1970, 1, 1); |
|||
var result = length == 10 ? start.AddSeconds(jsTime) : start.AddMilliseconds(jsTime); |
|||
return result.ToUniversalTime(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取指定日期 当天的最大时间
|
|||
/// 例如 2021-09-10 11:22:33.123456 转换后 2021-09-10 23:59:59.9999999
|
|||
/// </summary>
|
|||
public static DateTime? ToCurrentDateMaxDateTime(this DateTime? dateTime) |
|||
{ |
|||
return dateTime?.Date.AddDays(1).AddTicks(-1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取指定时间的下一秒
|
|||
/// 例如 2021-09-10 11:11:11.1234567 转换后 2021-09-10 11:11:12.0000000
|
|||
/// </summary>
|
|||
public static DateTime? ToNextSecondDateTime(this DateTime? dateTime) |
|||
{ |
|||
if (!dateTime.HasValue) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return new DateTime(dateTime.Value.Year, dateTime.Value.Month, dateTime.Value.Day, dateTime.Value.Hour, |
|||
dateTime.Value.Minute, dateTime.Value.Second) |
|||
.AddSeconds(1); |
|||
} |
|||
} |
|||
} |
|||
@ -1,192 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System |
|||
{ |
|||
/// <summary>
|
|||
/// 枚举<see cref="Enum"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class EnumExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 获取枚举项上的<see cref="DescriptionAttribute"/>特性的文字描述
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToDescription(this Enum value) |
|||
{ |
|||
var type = value.GetType(); |
|||
var member = type.GetMember(value.ToString()).FirstOrDefault(); |
|||
|
|||
return member != null ? member.GetDescription() : value.ToString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 枚举遍历,返回枚举的名称、值、特性
|
|||
/// </summary>
|
|||
/// <param name="enumType">枚举类型</param>
|
|||
/// <param name="action">回调函数</param>
|
|||
private static void Each(this Type enumType, Action<string, string, string, object> action) |
|||
{ |
|||
if (enumType.BaseType != typeof(Enum)) |
|||
{ |
|||
return; |
|||
} |
|||
var arr = Enum.GetValues(enumType); |
|||
foreach (var name in arr) |
|||
{ |
|||
var currentEnum = Enum.Parse(enumType, name.ToString()); |
|||
var value = Convert.ToInt32(Enum.Parse(enumType, name.ToString())); |
|||
var fieldInfo = enumType.GetField(name.ToString()); |
|||
var description = ""; |
|||
if (fieldInfo != null) |
|||
{ |
|||
var attr = Attribute.GetCustomAttribute(fieldInfo, |
|||
typeof(DescriptionAttribute), false) as DescriptionAttribute; |
|||
if (attr != null) |
|||
{ |
|||
description = attr.Description; |
|||
} |
|||
} |
|||
action(name.ToString(), value.ToString(), description, currentEnum); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 根据枚举类型值返回枚举定义Description属性
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="enumType"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToEnumDescriptionString(this short value, Type enumType) |
|||
{ |
|||
var nvc = new NameValueCollection(); |
|||
var typeDescription = typeof(DescriptionAttribute); |
|||
var fields = enumType.GetFields(); |
|||
foreach (var field in fields) |
|||
{ |
|||
if (field.FieldType.IsEnum) |
|||
{ |
|||
var strValue = ((int)enumType.InvokeMember(field.Name, BindingFlags.GetField, null, null, null)).ToString(); |
|||
var arr = field.GetCustomAttributes(typeDescription, true); |
|||
string strText; |
|||
if (arr.Length > 0) |
|||
{ |
|||
var aa = (DescriptionAttribute)arr[0]; |
|||
strText = aa.Description; |
|||
} |
|||
else |
|||
{ |
|||
strText = ""; |
|||
} |
|||
nvc.Add(strValue, strText); |
|||
} |
|||
} |
|||
return nvc[value.ToString()]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举的Value为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
private static List<KeyValuePair<string, string>> GetEnumTypeValueList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, string>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => |
|||
items.Add(new KeyValuePair<string, string>(description, value))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
private static List<KeyValuePair<string, T>> GetEnumTypeList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, T>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => |
|||
items.Add(new KeyValuePair<string, T>(description, (T)enumObj))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举的Name为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, string>> GetEnumTypeDescriptionNameList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, string>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => items.Add(new KeyValuePair<string, string>(description, name))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Name为字典的Key,枚举的Description为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, string>> GetEnumTypeValueNameList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, string>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => items.Add(new KeyValuePair<string, string>(name, description))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Name为字典的Key,枚举的Description为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="TModel">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, string>> GetStringKeyValueList<TModel>() where TModel : Enum |
|||
{ |
|||
var keyValuePairList = new List<KeyValuePair<string, string>>(); |
|||
var values = Enum.GetValues(typeof(TModel)); |
|||
var modelArray = new TModel[values.Length]; |
|||
values.CopyTo(modelArray, 0); |
|||
foreach (TModel model in modelArray) |
|||
keyValuePairList.Add(new KeyValuePair<string, string>(model.ToString(), model.ToString())); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="TModel">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, TModel>> GetEnumKeyValueList<TModel>() where TModel : Enum |
|||
{ |
|||
var enumTypeList = GetEnumTypeList<TModel>(); |
|||
var keyValuePairList = new List<KeyValuePair<string, TModel>>(); |
|||
foreach (KeyValuePair<string, TModel> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<string, TModel>(keyValuePair.Key, keyValuePair.Value)); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
public static List<KeyValuePair<string, string>> GetEntityDoubleStringKeyValueList<TModel>() |
|||
{ |
|||
var enumTypeList = GetEnumTypeValueList<TModel>(); |
|||
var keyValuePairList = new List<KeyValuePair<string, string>>(); |
|||
foreach (KeyValuePair<string, string> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<string, string>(keyValuePair.Key, keyValuePair.Value)); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
public static List<KeyValuePair<string, int>> GetEntityStringIntKeyValueList<TModel>() |
|||
{ |
|||
List<KeyValuePair<string, string>> enumTypeList = GetEnumTypeValueList<TModel>(); |
|||
List<KeyValuePair<string, int>> keyValuePairList = new List<KeyValuePair<string, int>>(); |
|||
foreach (KeyValuePair<string, string> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<string, int>(keyValuePair.Key, Convert.ToInt32(keyValuePair.Value))); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
public static List<KeyValuePair<int, int>> GetEntityDoubleIntKeyValueList<TModel>() |
|||
{ |
|||
List<KeyValuePair<string, string>> enumTypeList = GetEnumTypeValueList<TModel>(); |
|||
List<KeyValuePair<int, int>> keyValuePairList = new List<KeyValuePair<int, int>>(); |
|||
foreach (KeyValuePair<string, string> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<int, int>(Convert.ToInt32(keyValuePair.Key), Convert.ToInt32(keyValuePair.Value))); |
|||
return keyValuePairList; |
|||
} |
|||
} |
|||
} |
|||
@ -1,73 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System |
|||
{ |
|||
/// <summary>
|
|||
/// 异常操作扩展
|
|||
/// </summary>
|
|||
public static class ExceptionExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 格式化异常消息
|
|||
/// </summary>
|
|||
/// <param name="e">异常对象</param>
|
|||
/// <param name="isHideStackTrace">是否隐藏异常规模信息</param>
|
|||
/// <returns>格式化后的异常信息字符串</returns>
|
|||
public static string FormatMessage(this Exception e, bool isHideStackTrace = false) |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
var count = 0; |
|||
var appString = string.Empty; |
|||
while (e != null) |
|||
{ |
|||
if (count > 0) |
|||
{ |
|||
appString += " "; |
|||
} |
|||
sb.AppendLine($"{appString}异常消息:{e.Message}"); |
|||
sb.AppendLine($"{appString}异常类型:{e.GetType().FullName}"); |
|||
sb.AppendLine($"{appString}异常方法:{(e.TargetSite == null ? null : e.TargetSite.Name)}"); |
|||
sb.AppendLine($"{appString}异常源:{e.Source}"); |
|||
if (!isHideStackTrace && e.StackTrace != null) |
|||
{ |
|||
sb.AppendLine($"{appString}异常堆栈:{e.StackTrace}"); |
|||
} |
|||
if (e.InnerException != null) |
|||
{ |
|||
sb.AppendLine($"{appString}内部异常:"); |
|||
count++; |
|||
e = e.InnerException; |
|||
} |
|||
} |
|||
return sb.ToString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将异常重新抛出
|
|||
/// </summary>
|
|||
public static void ReThrow(this Exception exception) |
|||
{ |
|||
ExceptionDispatchInfo.Capture(exception).Throw(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,则抛出异常
|
|||
/// </summary>
|
|||
public static void ThrowIf(this Exception exception, bool isThrow) |
|||
{ |
|||
if (isThrow) |
|||
{ |
|||
throw exception; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,则抛出异常
|
|||
/// </summary>
|
|||
public static void ThrowIf(this Exception exception, Func<bool> isThrowFunc) |
|||
{ |
|||
if (isThrowFunc()) |
|||
{ |
|||
throw exception; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Linq |
|||
{ |
|||
/// <summary>
|
|||
/// IQueryable集合扩展方法
|
|||
/// </summary>
|
|||
public static class QueryableExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 根据第三方条件是否为真来决定是否执行指定条件的查询
|
|||
/// </summary>
|
|||
/// <param name="source"> 要查询的源 </param>
|
|||
/// <param name="predicate"> 查询条件 </param>
|
|||
/// <param name="condition"> 第三方条件 </param>
|
|||
/// <typeparam name="T"> 动态类型 </typeparam>
|
|||
/// <returns> 查询的结果 </returns>
|
|||
public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, |
|||
bool condition) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
Guard.NotNull(predicate, nameof(predicate)); |
|||
|
|||
return condition ? source.Where(predicate) : source; |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Reflection |
|||
{ |
|||
/// <summary>
|
|||
/// 程序集扩展操作类
|
|||
/// </summary>
|
|||
public static class AssemblyExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 获取程序集的产品版本
|
|||
/// </summary>
|
|||
public static string GetProductVersion(this Assembly assembly) |
|||
{ |
|||
Guard.NotNull(assembly, nameof(assembly)); |
|||
var info = FileVersionInfo.GetVersionInfo(assembly.Location); |
|||
var version = info.ProductVersion; |
|||
if (version.Contains("+")) |
|||
{ |
|||
version = version.ReplaceRegex(@"\+(\w+)?", ""); |
|||
} |
|||
return version; |
|||
} |
|||
} |
|||
} |
|||
@ -1,69 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Reflection |
|||
{ |
|||
/// <summary>
|
|||
/// 成员<see cref="MemberInfo"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class MemberInfoExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 获取成员元数据的Description特性描述信息。
|
|||
/// </summary>
|
|||
/// <param name="member">成员元数据对象。</param>
|
|||
/// <param name="inherit">是否搜索成员的继承链以查找描述特性。</param>
|
|||
/// <returns>返回Description特性描述信息,如不存在则返回成员的名称。</returns>
|
|||
public static string GetDescription(this MemberInfo member, bool inherit = true) |
|||
{ |
|||
var desc = member.GetAttribute<DescriptionAttribute>(inherit); |
|||
if (desc != null) |
|||
{ |
|||
return desc.Description; |
|||
} |
|||
|
|||
var displayName = member.GetAttribute<DisplayNameAttribute>(inherit); |
|||
if (displayName != null) |
|||
{ |
|||
return displayName.DisplayName; |
|||
} |
|||
|
|||
var display = member.GetAttribute<DisplayAttribute>(inherit); |
|||
return display != null ? display.Name : member.Name; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查指定指定类型成员中是否存在指定的Attribute特性。
|
|||
/// </summary>
|
|||
/// <typeparam name="T">要检查的Attribute特性类型。</typeparam>
|
|||
/// <param name="memberInfo">要检查的类型成员</param>
|
|||
/// <param name="inherit">是否从继承中查找</param>
|
|||
/// <returns>是否存在</returns>
|
|||
public static bool HasAttribute<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute |
|||
{ |
|||
return memberInfo.IsDefined(typeof(T), inherit); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 从类型成员获取指定Attribute特性
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Attribute特性类型</typeparam>
|
|||
/// <param name="memberInfo">类型类型成员</param>
|
|||
/// <param name="inherit">是否从继承中查找</param>
|
|||
/// <returns>存在返回第一个,不存在返回null</returns>
|
|||
public static T GetAttribute<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute |
|||
{ |
|||
var attributes = memberInfo.GetCustomAttributes(typeof(T), inherit); |
|||
return attributes.FirstOrDefault() as T; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 从类型成员获取指定Attribute特性。
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Attribute特性类型。</typeparam>
|
|||
/// <param name="memberInfo">类型类型成员。</param>
|
|||
/// <param name="inherit">是否从继承中查找。</param>
|
|||
/// <returns>返回所有指定Attribute特性的数组。</returns>
|
|||
public static T[] GetAttributes<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute |
|||
{ |
|||
return memberInfo.GetCustomAttributes(typeof(T), inherit).Cast<T>().ToArray(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Reflection |
|||
{ |
|||
/// <summary>
|
|||
/// 方法<see cref="MethodInfo"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class MethodInfoExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 方法是否是异步
|
|||
/// </summary>
|
|||
public static bool IsAsync(this MethodInfo method) |
|||
{ |
|||
return (method.ReturnType == typeof(Task<>) |
|||
|| method.ReturnType.IsGenericType |
|||
&& method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) |
|||
|| method.ReturnType == typeof(Task); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 返回当前方法信息是否是重写方法
|
|||
/// </summary>
|
|||
/// <param name="method">要判断的方法信息</param>
|
|||
/// <returns>是否是重写方法</returns>
|
|||
public static bool IsOverridden(this MethodInfo method) |
|||
{ |
|||
return method.GetBaseDefinition().DeclaringType != method.DeclaringType; |
|||
} |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Reflection |
|||
{ |
|||
/// <summary>
|
|||
/// 属性<see cref="MethodInfo"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class PropertyInfoExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 返回当前属性信息是否为virtual
|
|||
/// </summary>
|
|||
public static bool IsVirtual(this PropertyInfo property) |
|||
{ |
|||
var accessor = property.GetAccessors().FirstOrDefault(); |
|||
if (accessor == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return accessor.IsVirtual && !accessor.IsFinal; |
|||
} |
|||
} |
|||
} |
|||
@ -1,971 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System |
|||
{ |
|||
/// <summary>
|
|||
/// 字符串<see cref="string"/>类型的扩展辅助操作类
|
|||
/// </summary>
|
|||
[DebuggerStepThrough] |
|||
public static class StringExtensions |
|||
{ |
|||
#region 正则表达式
|
|||
|
|||
/// <summary>
|
|||
/// 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项
|
|||
/// </summary>
|
|||
/// <param name="value">要搜索匹配项的字符串</param>
|
|||
/// <param name="pattern">要匹配的正则表达式模式</param>
|
|||
/// <param name="isContains">是否包含,否则全匹配</param>
|
|||
/// <returns>如果正则表达式找到匹配项,则为 true;否则,为 false</returns>
|
|||
public static bool IsMatch(this string value, string pattern, bool isContains = true) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return isContains |
|||
? Regex.IsMatch(value, pattern) |
|||
: Regex.Match(value, pattern).Success; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中搜索指定的正则表达式的第一个匹配项
|
|||
/// </summary>
|
|||
/// <param name="value">要搜索匹配项的字符串</param>
|
|||
/// <param name="pattern">要匹配的正则表达式模式</param>
|
|||
/// <returns>一个对象,包含有关匹配项的信息</returns>
|
|||
public static string Match(this string value, string pattern) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return Regex.Match(value, pattern).Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中匹配并替换符合指定正则表达式的子串
|
|||
/// </summary>
|
|||
public static string ReplaceRegex(this string value, string pattern, string replacement) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return Regex.Replace(value, pattern, replacement); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中搜索指定的正则表达式的所有匹配项的字符串集合
|
|||
/// </summary>
|
|||
/// <param name="value"> 要搜索匹配项的字符串 </param>
|
|||
/// <param name="pattern"> 要匹配的正则表达式模式 </param>
|
|||
/// <returns> 一个集合,包含有关匹配项的字符串值 </returns>
|
|||
public static IEnumerable<string> Matches(this string value, string pattern) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return new string[] { }; |
|||
} |
|||
|
|||
var matches = Regex.Matches(value, pattern); |
|||
return from Match match in matches select match.Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中匹配第一个数字字符串
|
|||
/// </summary>
|
|||
public static string MatchFirstNumber(this string value) |
|||
{ |
|||
var matches = Regex.Matches(value, @"\d+"); |
|||
if (matches.Count == 0) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
return matches[0].Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定字符串中匹配最后一个数字字符串
|
|||
/// </summary>
|
|||
public static string MatchLastNumber(this string value) |
|||
{ |
|||
var matches = Regex.Matches(value, @"\d+"); |
|||
if (matches.Count == 0) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
return matches[matches.Count - 1].Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定字符串中匹配所有数字字符串
|
|||
/// </summary>
|
|||
public static IEnumerable<string> MatchNumbers(this string value) |
|||
{ |
|||
return Matches(value, @"\d+"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检测指定字符串中是否包含数字
|
|||
/// </summary>
|
|||
public static bool IsMatchNumber(this string value) |
|||
{ |
|||
return IsMatch(value, @"\d"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检测指定字符串是否全部为数字并且长度等于指定长度
|
|||
/// </summary>
|
|||
public static bool IsMatchNumber(this string value, int length) |
|||
{ |
|||
var regex = new Regex(@"^\d{" + length + "}$"); |
|||
return regex.IsMatch(value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 截取指定字符串之间的字符串
|
|||
/// </summary>
|
|||
/// <param name="source"></param>
|
|||
/// <param name="startString">起始字符串</param>
|
|||
/// <param name="endStrings">结束字符串,可多个</param>
|
|||
/// <returns>返回的中间字符串</returns>
|
|||
public static string Substring(this string source, string startString, params string[] endStrings) |
|||
{ |
|||
if (source.IsMissing()) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
var startIndex = 0; |
|||
if (!string.IsNullOrEmpty(startString)) |
|||
{ |
|||
startIndex = source.IndexOf(startString, StringComparison.OrdinalIgnoreCase); |
|||
if (startIndex < 0) |
|||
{ |
|||
throw new InvalidOperationException($"在源字符串中无法找到“{startString}”的子串位置"); |
|||
} |
|||
|
|||
startIndex += startString.Length; |
|||
} |
|||
|
|||
var endIndex = source.Length; |
|||
endStrings = endStrings.OrderByDescending(m => m.Length).ToArray(); |
|||
foreach (var endString in endStrings) |
|||
{ |
|||
if (string.IsNullOrEmpty(endString)) |
|||
{ |
|||
endIndex = source.Length; |
|||
break; |
|||
} |
|||
|
|||
endIndex = source.IndexOf(endString, startIndex, StringComparison.OrdinalIgnoreCase); |
|||
if (endIndex < 0 || endIndex < startIndex) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
if (endIndex < 0 || endIndex < startIndex) |
|||
{ |
|||
throw new InvalidOperationException($"在源字符串中无法找到“{endStrings.ExpandAndToString()}”的子串位置"); |
|||
} |
|||
|
|||
var length = endIndex - startIndex; |
|||
|
|||
return source.Substring(startIndex, length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 用正则表达式截取字符串
|
|||
/// </summary>
|
|||
public static string Substring2(this string source, string startString, string endString) |
|||
{ |
|||
return source.Substring2(startString, endString, false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 用正则表达式截取字符串
|
|||
/// </summary>
|
|||
public static string Substring2(this string source, string startString, string endString, bool containsEmpty) |
|||
{ |
|||
if (source.IsMissing()) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
var inner = containsEmpty ? "\\s\\S" : "\\S"; |
|||
var result = source.Match($"(?<={startString})([{inner}]+?)(?={endString})"); |
|||
return result.IsMissing() ? null : result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否电子邮件
|
|||
/// </summary>
|
|||
public static bool IsEmail(this string value) |
|||
{ |
|||
const string pattern = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否是IP地址
|
|||
/// </summary>
|
|||
public static bool IsIpAddress(this string value) |
|||
{ |
|||
const string pattern = |
|||
@"^((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否是整数
|
|||
/// </summary>
|
|||
public static bool IsNumeric(this string value) |
|||
{ |
|||
const string pattern = @"^\-?[0-9]+$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否是Unicode字符串
|
|||
/// </summary>
|
|||
public static bool IsUnicode(this string value) |
|||
{ |
|||
const string pattern = @"^[\u4E00-\u9FA5\uE815-\uFA29]+$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否Url字符串
|
|||
/// </summary>
|
|||
public static bool IsUrl(this string value) |
|||
{ |
|||
try |
|||
{ |
|||
if (string.IsNullOrEmpty(value) || value.Contains(' ')) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var uri = new Uri(value); |
|||
return true; |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否身份证号,验证如下3种情况:
|
|||
/// 1.身份证号码为15位数字;
|
|||
/// 2.身份证号码为18位数字;
|
|||
/// 3.身份证号码为17位数字+1个字母
|
|||
/// </summary>
|
|||
public static bool IsIdentityCardId(this string value) |
|||
{ |
|||
if (value.Length != 15 && value.Length != 18) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
Regex regex; |
|||
string[] array; |
|||
if (value.Length == 15) |
|||
{ |
|||
regex = new Regex(@"^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})_"); |
|||
if (!regex.Match(value).Success) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
array = regex.Split(value); |
|||
return DateTime.TryParse(string.Format("{0}-{1}-{2}", "19" + array[2], array[3], array[4]), out _); |
|||
} |
|||
|
|||
regex = new Regex(@"^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9Xx])$"); |
|||
if (!regex.Match(value).Success) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
array = regex.Split(value); |
|||
if (!DateTime.TryParse(string.Format("{0}-{1}-{2}", array[2], array[3], array[4]), out _)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
//校验最后一位
|
|||
var chars = value.ToCharArray().Select(m => m.ToString()).ToArray(); |
|||
int[] weights = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; |
|||
var sum = 0; |
|||
for (var i = 0; i < 17; i++) |
|||
{ |
|||
var num = int.Parse(chars[i]); |
|||
sum += num * weights[i]; |
|||
} |
|||
|
|||
var mod = sum % 11; |
|||
var vCode = "10X98765432"; // 检验码字符串
|
|||
var last = vCode.ToCharArray().ElementAt(mod).ToString(); |
|||
return chars.Last().ToUpper() == last; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否手机号码
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="isRestrict">是否按严格格式验证</param>
|
|||
public static bool IsMobileNumber(this string value, bool isRestrict = false) |
|||
{ |
|||
var pattern = isRestrict ? @"^[1][3-8]\d{9}$" : @"^[1]\d{10}$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region 其他操作
|
|||
|
|||
/// <summary>
|
|||
/// 判断指定的字符串不是 null、空。
|
|||
/// </summary>
|
|||
public static bool IsNotNullOrEmpty(this string str) |
|||
{ |
|||
return !string.IsNullOrEmpty(str); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 判断指定的字符串不是 null、空或者仅由空白字符组成
|
|||
/// </summary>
|
|||
public static bool IsNotNullOrWhiteSpace(this string str) |
|||
{ |
|||
return !string.IsNullOrWhiteSpace(str); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 指示指定的字符串是 null、空或者仅由空白字符组成
|
|||
/// </summary>
|
|||
public static bool IsMissing(this string value) |
|||
{ |
|||
return string.IsNullOrWhiteSpace(value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 为指定格式的字符串填充相应对象来生成字符串
|
|||
/// </summary>
|
|||
/// <param name="format">字符串格式,占位符以{n}表示</param>
|
|||
/// <param name="args">用于填充占位符的参数</param>
|
|||
/// <returns>格式化后的字符串</returns>
|
|||
public static string FormatWith(this string format, params object[] args) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
return string.Format(CultureInfo.CurrentCulture, format, args); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串反转
|
|||
/// </summary>
|
|||
/// <param name="value">要反转的字符串</param>
|
|||
public static string ReverseString(this string value) |
|||
{ |
|||
Guard.NotNull(value, nameof(value)); |
|||
return new string(value.Reverse().ToArray()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 单词复数变成单数形式
|
|||
/// </summary>
|
|||
/// <param name="word"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToSingular(this string word) |
|||
{ |
|||
var plural1 = new Regex("(?<keep>[^aeiou])ies$"); |
|||
var plural2 = new Regex("(?<keep>[aeiou]y)s$"); |
|||
var plural3 = new Regex("(?<keep>[sxzh])es$"); |
|||
var plural4 = new Regex("(?<keep>[^sxzhyu])s$"); |
|||
|
|||
if (plural1.IsMatch(word)) |
|||
{ |
|||
return plural1.Replace(word, "${keep}y"); |
|||
} |
|||
|
|||
if (plural2.IsMatch(word)) |
|||
{ |
|||
return plural2.Replace(word, "${keep}"); |
|||
} |
|||
|
|||
if (plural3.IsMatch(word)) |
|||
{ |
|||
return plural3.Replace(word, "${keep}"); |
|||
} |
|||
|
|||
if (plural4.IsMatch(word)) |
|||
{ |
|||
return plural4.Replace(word, "${keep}"); |
|||
} |
|||
|
|||
return word; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 单词单数变成复数形式
|
|||
/// </summary>
|
|||
/// <param name="word"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToPlural(this string word) |
|||
{ |
|||
var plural1 = new Regex("(?<keep>[^aeiou])y$"); |
|||
var plural2 = new Regex("(?<keep>[aeiou]y)$"); |
|||
var plural3 = new Regex("(?<keep>[sxzh])$"); |
|||
var plural4 = new Regex("(?<keep>[^sxzhy])$"); |
|||
|
|||
if (plural1.IsMatch(word)) |
|||
{ |
|||
return plural1.Replace(word, "${keep}ies"); |
|||
} |
|||
|
|||
if (plural2.IsMatch(word)) |
|||
{ |
|||
return plural2.Replace(word, "${keep}s"); |
|||
} |
|||
|
|||
if (plural3.IsMatch(word)) |
|||
{ |
|||
return plural3.Replace(word, "${keep}es"); |
|||
} |
|||
|
|||
if (plural4.IsMatch(word)) |
|||
{ |
|||
return plural4.Replace(word, "${keep}s"); |
|||
} |
|||
|
|||
return word; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 以指定字符串作为分隔符将指定字符串分隔成数组
|
|||
/// </summary>
|
|||
/// <param name="value">要分割的字符串</param>
|
|||
/// <param name="strSplit">字符串类型的分隔符</param>
|
|||
/// <param name="removeEmptyEntries">是否移除数据中元素为空字符串的项</param>
|
|||
/// <returns>分割后的数据</returns>
|
|||
public static string[] Split(this string value, string strSplit, bool removeEmptyEntries = false) |
|||
{ |
|||
return value.Split(new[] {strSplit}, |
|||
removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 支持汉字的字符串长度,汉字长度计为2
|
|||
/// </summary>
|
|||
/// <param name="value">参数字符串</param>
|
|||
/// <returns>当前字符串的长度,汉字长度为2</returns>
|
|||
public static int TextLength(this string value) |
|||
{ |
|||
var ascii = new ASCIIEncoding(); |
|||
var tempLen = 0; |
|||
var bytes = ascii.GetBytes(value); |
|||
foreach (var b in bytes) |
|||
{ |
|||
if (b == 63) |
|||
{ |
|||
tempLen += 2; |
|||
} |
|||
else |
|||
{ |
|||
tempLen += 1; |
|||
} |
|||
} |
|||
|
|||
return tempLen; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将JSON字符串还原为对象
|
|||
/// </summary>
|
|||
/// <typeparam name="T">要转换的目标类型</typeparam>
|
|||
/// <param name="json">JSON字符串</param>
|
|||
/// <returns></returns>
|
|||
public static T FromJsonString<T>(this string json) |
|||
{ |
|||
Guard.NotNull(json, nameof(json)); |
|||
return JsonConvert.DeserializeObject<T>(json); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将JSON字符串还原为对象
|
|||
/// </summary>
|
|||
/// <param name="json">JSON字符串 </param>
|
|||
/// <param name="type">数据类型</param>
|
|||
public static object FromJsonString(this string json, Type type) |
|||
{ |
|||
return JsonConvert.DeserializeObject(json, type); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 给URL添加查询参数
|
|||
/// </summary>
|
|||
/// <param name="url">URL字符串</param>
|
|||
/// <param name="queries">要添加的参数,形如:"id=1,cid=2"</param>
|
|||
/// <returns></returns>
|
|||
public static string AddUrlQuery(this string url, params string[] queries) |
|||
{ |
|||
foreach (var query in queries) |
|||
{ |
|||
if (!url.Contains("?")) |
|||
{ |
|||
url += "?"; |
|||
} |
|||
else if (!url.EndsWith("&")) |
|||
{ |
|||
url += "&"; |
|||
} |
|||
|
|||
url += query; |
|||
} |
|||
|
|||
return url; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取URL中指定参数的值,不存在返回空字符串
|
|||
/// </summary>
|
|||
public static string GetUrlQuery(this string url, string key) |
|||
{ |
|||
var uri = new Uri(url); |
|||
var query = uri.Query; |
|||
if (string.IsNullOrEmpty(query)) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
query = query.TrimStart('?'); |
|||
var dict = (from m in query.Split("&", true) |
|||
let strs = m.Split("=") |
|||
select new KeyValuePair<string, string>(strs[0], strs[1])) |
|||
.ToDictionary(m => m.Key, m => m.Value); |
|||
if (dict.ContainsKey(key)) |
|||
{ |
|||
return dict[key]; |
|||
} |
|||
|
|||
return string.Empty; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 给URL添加 # 参数
|
|||
/// </summary>
|
|||
/// <param name="url">URL字符串</param>
|
|||
/// <param name="query">要添加的参数</param>
|
|||
/// <returns></returns>
|
|||
public static string AddHashFragment(this string url, string query) |
|||
{ |
|||
Guard.NotNull(url, nameof(url)); |
|||
Guard.NotNull(query, nameof(query)); |
|||
|
|||
if (!url.Contains("#")) |
|||
{ |
|||
url += "#"; |
|||
} |
|||
|
|||
return url + query; |
|||
} |
|||
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// 将<see cref="byte"/>[]数组转换为Base64字符串
|
|||
/// </summary>
|
|||
public static string ToBase64String(this byte[] bytes) |
|||
{ |
|||
Guard.NotNull(bytes, nameof(bytes)); |
|||
|
|||
return Convert.ToBase64String(bytes); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串转换为Base64字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
/// <param name="source">正常的字符串</param>
|
|||
/// <param name="encoding">编码</param>
|
|||
/// <returns>Base64字符串</returns>
|
|||
public static string ToBase64String(this string source, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
return Convert.ToBase64String(encoding.GetBytes(source)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将Base64字符串转换为正常字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
/// <param name="base64String">Base64字符串</param>
|
|||
/// <param name="encoding">编码</param>
|
|||
/// <returns>正常字符串</returns>
|
|||
public static string FromBase64String(this string base64String, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(base64String, nameof(base64String)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
var bytes = Convert.FromBase64String(base64String); |
|||
return encoding.GetString(bytes); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行UrlDecode解码
|
|||
/// </summary>
|
|||
/// <param name="source">待UrlDecode解码的字符串</param>
|
|||
/// <returns>UrlDecode解码后的字符串</returns>
|
|||
public static string ToUrlDecode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.UrlDecode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行UrlEncode编码
|
|||
/// </summary>
|
|||
/// <param name="source">待UrlEncode编码的字符串</param>
|
|||
/// <returns>UrlEncode编码后的字符串</returns>
|
|||
public static string ToUrlEncode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.UrlEncode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行HtmlDecode解码
|
|||
/// </summary>
|
|||
/// <param name="source">待HtmlDecode解码的字符串</param>
|
|||
/// <returns>HtmlDecode解码后的字符串</returns>
|
|||
public static string ToHtmlDecode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.HtmlDecode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行HtmlEncode编码
|
|||
/// </summary>
|
|||
/// <param name="source">待HtmlEncode编码的字符串</param>
|
|||
/// <returns>HtmlEncode编码后的字符串</returns>
|
|||
public static string ToHtmlEncode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.HtmlEncode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串转换为十六进制字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
public static string ToHexString(this string source, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
byte[] bytes = encoding.GetBytes(source); |
|||
return bytes.ToHexString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将十六进制字符串转换为常规字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
public static string FromHexString(this string hexString, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(hexString, nameof(hexString)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
var bytes = hexString.ToHexBytes(); |
|||
return encoding.GetString(bytes); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将byte[]编码为十六进制字符串
|
|||
/// </summary>
|
|||
/// <param name="bytes">byte[]数组</param>
|
|||
/// <returns>十六进制字符串</returns>
|
|||
public static string ToHexString(this byte[] bytes) |
|||
{ |
|||
Guard.NotNull(bytes, nameof(bytes)); |
|||
|
|||
return bytes.Aggregate(string.Empty, (current, t) => current + t.ToString("X2")); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将十六进制字符串转换为byte[]
|
|||
/// </summary>
|
|||
/// <param name="hexString">十六进制字符串</param>
|
|||
/// <returns>byte[]数组</returns>
|
|||
public static byte[] ToHexBytes(this string hexString) |
|||
{ |
|||
hexString = hexString ?? ""; |
|||
hexString = hexString.Replace(" ", ""); |
|||
byte[] bytes = new byte[hexString.Length / 2]; |
|||
for (int i = 0; i < bytes.Length; i++) |
|||
{ |
|||
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); |
|||
} |
|||
|
|||
return bytes; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行Unicode编码,变成形如“\u7f16\u7801”的形式
|
|||
/// </summary>
|
|||
/// <param name="source">要进行编号的字符串</param>
|
|||
public static string ToUnicodeString(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
var regex = new Regex(@"[^\u0000-\u00ff]"); |
|||
return regex.Replace(source, m => string.Format(@"\u{0:x4}", (short) m.Value[0])); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将形如“\u7f16\u7801”的Unicode字符串解码
|
|||
/// </summary>
|
|||
public static string FromUnicodeString(this string source) |
|||
{ |
|||
var regex = new Regex(@"\\u([0-9a-fA-F]{4})", RegexOptions.Compiled); |
|||
return regex.Replace(source, |
|||
m => |
|||
{ |
|||
short s; |
|||
if (short.TryParse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InstalledUICulture, |
|||
out s)) |
|||
{ |
|||
return "" + (char) s; |
|||
} |
|||
|
|||
return m.Value; |
|||
}); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将驼峰字符串按单词拆分并转换成小写,再以特定字符串分隔
|
|||
/// </summary>
|
|||
/// <param name="str">待转换的字符串</param>
|
|||
/// <param name="splitStr">分隔符字符</param>
|
|||
/// <returns></returns>
|
|||
public static string UpperToLowerAndSplit(this string str, string splitStr = "-") |
|||
{ |
|||
if (string.IsNullOrEmpty(str)) |
|||
{ |
|||
return str; |
|||
} |
|||
|
|||
List<string> words = new List<string>(); |
|||
while (str.Length > 0) |
|||
{ |
|||
char c = str.FirstOrDefault(char.IsUpper); |
|||
if (c == default(char)) |
|||
{ |
|||
words.Add(str); |
|||
break; |
|||
} |
|||
|
|||
int upperIndex = str.IndexOf(c); |
|||
if (upperIndex < 0) //admin
|
|||
{ |
|||
return str; |
|||
} |
|||
|
|||
if (upperIndex > 0) //adminAdmin
|
|||
{ |
|||
string first = str.Substring(0, upperIndex); |
|||
words.Add(first); |
|||
str = str.Substring(upperIndex, str.Length - upperIndex); |
|||
continue; |
|||
} |
|||
|
|||
str = char.ToLower(str[0]) + str.Substring(1, str.Length - 1); |
|||
} |
|||
|
|||
return words.ExpandAndToString(splitStr); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将驼峰字符串的第一个字符小写
|
|||
/// </summary>
|
|||
public static string LowerFirstChar(this string str) |
|||
{ |
|||
if (string.IsNullOrEmpty(str) || !char.IsUpper(str[0])) |
|||
{ |
|||
return str; |
|||
} |
|||
|
|||
if (str.Length == 1) |
|||
{ |
|||
return char.ToLower(str[0]).ToString(); |
|||
} |
|||
|
|||
return char.ToLower(str[0]) + str.Substring(1, str.Length - 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将小驼峰字符串的第一个字符大写
|
|||
/// </summary>
|
|||
public static string UpperFirstChar(this string str) |
|||
{ |
|||
if (string.IsNullOrEmpty(str) || !char.IsLower(str[0])) |
|||
{ |
|||
return str; |
|||
} |
|||
|
|||
if (str.Length == 1) |
|||
{ |
|||
return char.ToUpper(str[0]).ToString(); |
|||
} |
|||
|
|||
return char.ToUpper(str[0]) + str.Substring(1, str.Length - 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 计算当前字符串与指定字符串的编辑距离(相似度)
|
|||
/// </summary>
|
|||
/// <param name="source">源字符串</param>
|
|||
/// <param name="target">目标字符串</param>
|
|||
/// <param name="similarity">输出相似度</param>
|
|||
/// <param name="ignoreCase">是否忽略大小写</param>
|
|||
/// <returns>编辑距离</returns>
|
|||
public static int LevenshteinDistance(this string source, string target, out double similarity, |
|||
bool ignoreCase = false) |
|||
{ |
|||
if (string.IsNullOrEmpty(source)) |
|||
{ |
|||
if (string.IsNullOrEmpty(target)) |
|||
{ |
|||
similarity = 1; |
|||
return 0; |
|||
} |
|||
|
|||
similarity = 0; |
|||
return target.Length; |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(target)) |
|||
{ |
|||
similarity = 0; |
|||
return source.Length; |
|||
} |
|||
|
|||
string from, to; |
|||
if (ignoreCase) |
|||
{ |
|||
from = source; |
|||
to = target; |
|||
} |
|||
else |
|||
{ |
|||
from = source.ToLower(); |
|||
to = source.ToLower(); |
|||
} |
|||
|
|||
int m = from.Length, n = to.Length; |
|||
int[,] mn = new int[m + 1, n + 1]; |
|||
for (int i = 0; i <= m; i++) |
|||
{ |
|||
mn[i, 0] = i; |
|||
} |
|||
|
|||
for (int j = 1; j <= n; j++) |
|||
{ |
|||
mn[0, j] = j; |
|||
} |
|||
|
|||
for (int i = 1; i <= m; i++) |
|||
{ |
|||
char c = from[i - 1]; |
|||
for (int j = 1; j <= n; j++) |
|||
{ |
|||
if (c == to[j - 1]) |
|||
{ |
|||
mn[i, j] = mn[i - 1, j - 1]; |
|||
} |
|||
else |
|||
{ |
|||
mn[i, j] = Math.Min(mn[i - 1, j - 1], Math.Min(mn[i - 1, j], mn[i, j - 1])) + 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
int maxLength = Math.Max(m, n); |
|||
similarity = (double) (maxLength - mn[m, n]) / maxLength; |
|||
return mn[m, n]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 计算两个字符串的相似度,应用公式:相似度=kq*q/(kq*q+kr*r+ks*s)(kq>0,kr>=0,ka>=0)
|
|||
/// 其中,q是字符串1和字符串2中都存在的单词的总数,s是字符串1中存在,字符串2中不存在的单词总数,r是字符串2中存在,字符串1中不存在的单词总数. kq,kr和ka分别是q,r,s的权重,根据实际的计算情况,我们设kq=2,kr=ks=1.
|
|||
/// </summary>
|
|||
/// <param name="source">源字符串</param>
|
|||
/// <param name="target">目标字符串</param>
|
|||
/// <param name="ignoreCase">是否忽略大小写</param>
|
|||
/// <returns>字符串相似度</returns>
|
|||
public static double GetSimilarityWith(this string source, string target, bool ignoreCase = false) |
|||
{ |
|||
if (string.IsNullOrEmpty(source) && string.IsNullOrEmpty(target)) |
|||
{ |
|||
return 1; |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(target)) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
const double kq = 2, kr = 1, ks = 1; |
|||
char[] sourceChars = source.ToCharArray(), targetChars = target.ToCharArray(); |
|||
|
|||
//获取交集数量
|
|||
int q = sourceChars.Intersect(targetChars).Count(), s = sourceChars.Length - q, r = targetChars.Length - q; |
|||
return kq * q / (kq * q + kr * r + ks * s); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 标准化Path字符串,将 \\ 转换为 /
|
|||
/// </summary>
|
|||
/// <param name="path">Path字符串</param>
|
|||
public static string NormalizePath(this string path) |
|||
{ |
|||
return path.Replace('\\', '/'); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// (Pascal) 命名法 的字符串 改为 短横线分隔式命名
|
|||
/// 例如UserName => user-name
|
|||
/// </summary>
|
|||
public static string PascalToKebabCase(this string value) |
|||
{ |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
return Regex.Replace( |
|||
value, |
|||
"(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])", |
|||
"-$1", |
|||
RegexOptions.Compiled) |
|||
.Trim() |
|||
.ToLower(); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -1,238 +0,0 @@ |
|||
namespace Lion.AbpPro.Extension.System.Text |
|||
{ |
|||
/// <summary>
|
|||
/// StringBuilder 扩展方法类
|
|||
/// </summary>
|
|||
public static class StringBuilderExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 去除<seealso cref="StringBuilder"/>开头的空格
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <returns>返回修改后的StringBuilder,主要用于链式操作</returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
|
|||
return stringBuilder.TrimStart(' '); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<seealso cref="StringBuilder"/>开头的指定<seealso cref="char"/>
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="c">要去掉的<seealso cref="char"/></param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder, char c) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
|
|||
if (stringBuilder.Length == 0) |
|||
return stringBuilder; |
|||
while (c.Equals(stringBuilder[0])) |
|||
{ |
|||
stringBuilder.Remove(0, 1); |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<seealso cref="StringBuilder"/>开头的指定字符数组
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="chars">要去掉的字符数组</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder, char[] chars) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
Guard.NotNull(chars, nameof(chars)); |
|||
|
|||
return stringBuilder.TrimStart(new string(chars)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>开头的指定的<seealso cref="string"/>
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="str">要去掉的<seealso cref="string"/></param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder, string str) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
|
|||
if (string.IsNullOrEmpty(str) |
|||
|| stringBuilder.Length == 0 |
|||
|| str.Length > stringBuilder.Length) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
|
|||
while (stringBuilder.SubString(0, str.Length).Equals(str)) |
|||
{ |
|||
stringBuilder.Remove(0, str.Length); |
|||
if (str.Length > stringBuilder.Length) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除StringBuilder结尾的空格
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder">StringBuilder</param>
|
|||
/// <returns>返回修改后的StringBuilder,主要用于链式操作</returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder) |
|||
{ |
|||
return stringBuilder.TrimEnd(' '); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>结尾指定字符
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="c">要去掉的字符</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder, char c) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
if (stringBuilder.Length == 0) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
|
|||
while (c.Equals(stringBuilder[stringBuilder.Length - 1])) |
|||
{ |
|||
stringBuilder.Remove(stringBuilder.Length - 1, 1); |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>结尾指定字符数组
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="chars">要去除的字符数组</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder, char[] chars) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
Guard.NotNull(chars, nameof(chars)); |
|||
|
|||
return stringBuilder.TrimEnd(new string(chars)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>结尾指定字符串
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="str">要去除的字符串</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder, string str) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
if (string.IsNullOrEmpty(str) |
|||
|| stringBuilder.Length == 0 |
|||
|| str.Length > stringBuilder.Length) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
while (stringBuilder.SubString(stringBuilder.Length - str.Length, str.Length).Equals(str)) |
|||
{ |
|||
stringBuilder.Remove(stringBuilder.Length - str.Length, str.Length); |
|||
if (stringBuilder.Length < str.Length) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除StringBuilder两端的空格
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder">StringBuilder</param>
|
|||
/// <returns>返回修改后的StringBuilder,主要用于链式操作</returns>
|
|||
public static StringBuilder Trim(this StringBuilder stringBuilder) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
|
|||
if (stringBuilder.Length == 0) |
|||
return stringBuilder; |
|||
return stringBuilder.TrimEnd().TrimStart(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 返回<see cref="StringBuilder"/>从起始位置指定长度的字符串
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="start">起始位置</param>
|
|||
/// <param name="length">长度</param>
|
|||
/// <returns>字符串</returns>
|
|||
/// <exception cref="IndexOutOfRangeException">超出字符串索引长度异常</exception>
|
|||
public static string SubString(this StringBuilder stringBuilder, int start, int length) |
|||
{ |
|||
Guard.NotNull(stringBuilder, nameof(stringBuilder)); |
|||
|
|||
if (start + length > stringBuilder.Length) |
|||
{ |
|||
throw new IndexOutOfRangeException("超出字符串索引长度"); |
|||
} |
|||
|
|||
var cs = new char[length]; |
|||
for (var i = 0; i < length; i++) |
|||
{ |
|||
cs[i] = stringBuilder[start + i]; |
|||
} |
|||
|
|||
return new string(cs); |
|||
} |
|||
|
|||
public static StringBuilder AppendLineWithControlChar(this StringBuilder stringBuilder, StringBuilder sb, string newLine) |
|||
{ |
|||
stringBuilder = AppendWithControlChar(stringBuilder, sb.ToString()); |
|||
return stringBuilder.Append(newLine); |
|||
} |
|||
|
|||
public static StringBuilder AppendLineWithControlChar(this StringBuilder stringBuilder, string str, string newLine) |
|||
{ |
|||
stringBuilder = AppendWithControlChar(stringBuilder, str); |
|||
return stringBuilder.Append(newLine); |
|||
} |
|||
|
|||
public static StringBuilder AppendWithControlChar(this StringBuilder stringBuilder, StringBuilder sb) |
|||
{ |
|||
return AppendWithControlChar(stringBuilder, sb.ToString()); |
|||
} |
|||
|
|||
public static StringBuilder AppendWithControlChar(this StringBuilder stringBuilder, string str) |
|||
{ |
|||
if (str.Contains('\b')) |
|||
{ |
|||
foreach (var c in str) |
|||
{ |
|||
if (c == '\b') |
|||
{ |
|||
stringBuilder.Length--; |
|||
} |
|||
else |
|||
{ |
|||
stringBuilder.Append(c); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
stringBuilder.Append(str); |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.1</TargetFramework> |
|||
<RootNamespace/> |
|||
<AssemblyName>Lion.AbpPro.CAP</AssemblyName> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Core"/> |
|||
<PackageReference Include="Volo.Abp.EventBus"/> |
|||
<PackageReference Include="DotNetCore.CAP"/> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,116 @@ |
|||
namespace Lion.AbpPro.CAP; |
|||
|
|||
[Dependency(ServiceLifetime.Singleton, ReplaceServices = true)] |
|||
[ExposeServices(typeof(IConsumerServiceSelector))] |
|||
public sealed class AbpProAbpCapConsumerServiceSelector : ConsumerServiceSelector |
|||
{ |
|||
private AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; } |
|||
private IServiceProvider ServiceProvider { get; } |
|||
|
|||
/// <summary>
|
|||
/// Creates a new <see cref="T:DotNetCore.CAP.Internal.ConsumerServiceSelector" />.
|
|||
/// </summary>
|
|||
public AbpProAbpCapConsumerServiceSelector( |
|||
IServiceProvider serviceProvider, |
|||
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions) |
|||
: base(serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
AbpDistributedEventBusOptions = distributedEventBusOptions.Value; |
|||
} |
|||
|
|||
protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider) |
|||
{ |
|||
var executorDescriptorList = base.FindConsumersFromInterfaceTypes(provider).ToList(); |
|||
|
|||
//handlers
|
|||
var handlers = AbpDistributedEventBusOptions.Handlers; |
|||
|
|||
foreach (var handler in handlers) |
|||
{ |
|||
var interfaces = handler.GetInterfaces(); |
|||
foreach (var @interface in interfaces) |
|||
{ |
|||
if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(@interface)) |
|||
{ |
|||
continue; |
|||
} |
|||
var genericArgs = @interface.GetGenericArguments(); |
|||
|
|||
if (genericArgs.Length != 1) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var descriptors = GetHandlerDescription(genericArgs[0], handler); |
|||
|
|||
foreach (var descriptor in descriptors) |
|||
{ |
|||
var count = executorDescriptorList.Count(x => |
|||
x.Attribute.Name == descriptor.Attribute.Name); |
|||
|
|||
descriptor.Attribute.Group = descriptor.Attribute.Group.Insert( |
|||
descriptor.Attribute.Group.LastIndexOf(".", StringComparison.Ordinal), $".{count}"); |
|||
|
|||
executorDescriptorList.Add(descriptor); |
|||
} |
|||
|
|||
//Subscribe(genericArgs[0], new IocEventHandlerFactory(ServiceScopeFactory, handler));
|
|||
} |
|||
} |
|||
return executorDescriptorList; |
|||
} |
|||
|
|||
private IEnumerable<ConsumerExecutorDescriptor> GetHandlerDescription(Type eventType,Type typeInfo) |
|||
{ |
|||
var serviceTypeInfo = typeof(IDistributedEventHandler<>) |
|||
.MakeGenericType(eventType); |
|||
var method = typeInfo |
|||
.GetMethod( |
|||
nameof(IDistributedEventHandler<object>.HandleEventAsync), |
|||
new[] { eventType } |
|||
); |
|||
var eventName = EventNameAttribute.GetNameOrDefault(eventType); |
|||
var topicAttr = method.GetCustomAttributes<TopicAttribute>(true); |
|||
var topicAttributes = topicAttr.ToList(); |
|||
|
|||
if (topicAttributes.Count == 0) |
|||
{ |
|||
topicAttributes.Add(new CapSubscribeAttribute(eventName)); |
|||
} |
|||
|
|||
foreach (var attr in topicAttributes) |
|||
{ |
|||
SetSubscribeAttribute(attr); |
|||
|
|||
var parameters = method.GetParameters() |
|||
.Select(parameter => new ParameterDescriptor |
|||
{ |
|||
Name = parameter.Name, |
|||
ParameterType = parameter.ParameterType, |
|||
IsFromCap = parameter.GetCustomAttributes(typeof(FromCapAttribute)).Any() |
|||
}).ToList(); |
|||
|
|||
yield return InitDescriptor(attr, method, typeInfo.GetTypeInfo(), serviceTypeInfo.GetTypeInfo(), parameters); |
|||
} |
|||
} |
|||
|
|||
private static ConsumerExecutorDescriptor InitDescriptor( |
|||
TopicAttribute attr, |
|||
MethodInfo methodInfo, |
|||
TypeInfo implType, |
|||
TypeInfo serviceTypeInfo, |
|||
IList<ParameterDescriptor> parameters) |
|||
{ |
|||
var descriptor = new ConsumerExecutorDescriptor |
|||
{ |
|||
Attribute = attr, |
|||
MethodInfo = methodInfo, |
|||
ImplTypeInfo = implType, |
|||
ServiceTypeInfo = serviceTypeInfo, |
|||
Parameters = parameters |
|||
}; |
|||
|
|||
return descriptor; |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
namespace Lion.AbpPro.CAP; |
|||
|
|||
public class AbpProAbpCapDistributedEventBus : |
|||
EventBusBase, |
|||
IDistributedEventBus, |
|||
ISingletonDependency |
|||
{ |
|||
private AbpDistributedEventBusOptions AbpDistributedEventBusOptions { get; } |
|||
private ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; } |
|||
private ConcurrentDictionary<string, Type> EventTypes { get; } |
|||
|
|||
private readonly ICapPublisher CapPublisher; |
|||
|
|||
|
|||
public AbpProAbpCapDistributedEventBus(IServiceScopeFactory serviceScopeFactory, |
|||
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions, |
|||
ICapPublisher capPublisher, |
|||
ICurrentTenant currentTenant, |
|||
UnitOfWorkManager unitOfWorkManager, |
|||
IEventHandlerInvoker eventHandlerInvoker) |
|||
: base(serviceScopeFactory, currentTenant,unitOfWorkManager,eventHandlerInvoker) |
|||
{ |
|||
CapPublisher = capPublisher; |
|||
AbpDistributedEventBusOptions = distributedEventBusOptions.Value; |
|||
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>(); |
|||
EventTypes = new ConcurrentDictionary<string, Type>(); |
|||
} |
|||
|
|||
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory) |
|||
{ |
|||
//This is handled by CAP ConsumerServiceSelector
|
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public override void Unsubscribe<TEvent>(Func<TEvent, Task> action) |
|||
{ |
|||
Check.NotNull(action, nameof(action)); |
|||
|
|||
GetOrCreateHandlerFactories(typeof(TEvent)) |
|||
.Locking(factories => |
|||
{ |
|||
factories.RemoveAll( |
|||
factory => |
|||
{ |
|||
var singleInstanceFactory = factory as SingleInstanceHandlerFactory; |
|||
if (singleInstanceFactory == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var actionHandler = singleInstanceFactory.HandlerInstance as ActionEventHandler<TEvent>; |
|||
if (actionHandler == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return actionHandler.Action == action; |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
public override void Unsubscribe(Type eventType, IEventHandler handler) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType) |
|||
.Locking(factories => |
|||
{ |
|||
factories.RemoveAll( |
|||
factory => |
|||
factory is SingleInstanceHandlerFactory && |
|||
(factory as SingleInstanceHandlerFactory).HandlerInstance == handler |
|||
); |
|||
}); |
|||
} |
|||
|
|||
public override void Unsubscribe(Type eventType, IEventHandlerFactory factory) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory)); |
|||
} |
|||
|
|||
public override void UnsubscribeAll(Type eventType) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear()); |
|||
} |
|||
|
|||
protected override Task PublishToEventBusAsync(Type eventType, object eventData) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
protected override void AddToUnitOfWork(IUnitOfWork unitOfWork, UnitOfWorkEventRecord eventRecord) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IDisposable Subscribe<TEvent>(IDistributedEventHandler<TEvent> handler) where TEvent : class |
|||
{ |
|||
return Subscribe(typeof(TEvent), handler); |
|||
} |
|||
|
|||
public async Task PublishAsync<TEvent>(TEvent eventData, bool onUnitOfWorkComplete = true, |
|||
bool useOutbox = true) where TEvent : class |
|||
{ |
|||
var eventName = EventNameAttribute.GetNameOrDefault(typeof(TEvent)); |
|||
await CapPublisher.PublishAsync(eventName, eventData); |
|||
|
|||
} |
|||
|
|||
public async Task PublishAsync(Type eventType, object eventData, bool onUnitOfWorkComplete = true, |
|||
bool useOutbox = true) |
|||
{ |
|||
var eventName = EventNameAttribute.GetNameOrDefault(eventType); |
|||
await CapPublisher.PublishAsync(eventName, eventData); |
|||
} |
|||
|
|||
|
|||
protected override IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType) |
|||
{ |
|||
var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>(); |
|||
|
|||
foreach (var handlerFactory in |
|||
HandlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key))) |
|||
{ |
|||
handlerFactoryList.Add( |
|||
new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value)); |
|||
} |
|||
|
|||
return handlerFactoryList.ToArray(); |
|||
} |
|||
|
|||
private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType) |
|||
{ |
|||
return HandlerFactories.GetOrAdd( |
|||
eventType, |
|||
type => |
|||
{ |
|||
var eventName = EventNameAttribute.GetNameOrDefault(type); |
|||
EventTypes[eventName] = type; |
|||
return new List<IEventHandlerFactory>(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
private static bool ShouldTriggerEventForHandler(Type targetEventType, Type handlerEventType) |
|||
{ |
|||
//Should trigger same type
|
|||
if (handlerEventType == targetEventType) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
//TODO: Support inheritance? But it does not support on subscription to RabbitMq!
|
|||
//Should trigger for inherited types
|
|||
if (handlerEventType.IsAssignableFrom(targetEventType)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Lion.AbpPro.CAP; |
|||
|
|||
[DependsOn(typeof(AbpEventBusModule))] |
|||
public class AbpProAbpCapModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
namespace Lion.AbpPro.CAP; |
|||
|
|||
public static class AbpProAbpCapServiceCollectionExtensions |
|||
{ |
|||
public static ServiceConfigurationContext AddAbpCap( |
|||
this ServiceConfigurationContext context, |
|||
Action<CapOptions> capAction) |
|||
{ |
|||
context.Services.AddCap(capAction); |
|||
context.Services.AddSingleton<IConsumerServiceSelector, AbpProAbpCapConsumerServiceSelector>(); |
|||
context.Services.AddSingleton<IDistributedEventBus, AbpProAbpCapDistributedEventBus>(); |
|||
return context; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
// Global using directives
|
|||
|
|||
global using System.ComponentModel.DataAnnotations; |
|||
global using System.Diagnostics; |
|||
global using System.Globalization; |
|||
global using System.Text; |
|||
global using System.Text.RegularExpressions; |
|||
global using System.Web; |
|||
global using Lion.AbpPro.Core; |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.1</TargetFramework> |
|||
<AssemblyName>Lion.AbpPro.Core</AssemblyName> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="System.ComponentModel.Annotations" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Lion.AbpPro.Localization\Lion.AbpPro.Localization.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,22 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
[Serializable] |
|||
public class CustomListResultDto<T> |
|||
{ |
|||
public IReadOnlyList<T> Items |
|||
{ |
|||
get { return _items ??= new List<T>(); } |
|||
set => _items = value; |
|||
} |
|||
|
|||
private IReadOnlyList<T> _items; |
|||
|
|||
public CustomListResultDto() |
|||
{ |
|||
} |
|||
|
|||
public CustomListResultDto(IReadOnlyList<T> items) |
|||
{ |
|||
Items = items; |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
[Serializable] |
|||
public class CustomPagedResultDto<T> : CustomListResultDto<T> |
|||
{ |
|||
public long TotalCount { get; set; } |
|||
|
|||
public CustomPagedResultDto() |
|||
{ |
|||
} |
|||
|
|||
public CustomPagedResultDto(long totalCount, IReadOnlyList<T> items) |
|||
: base(items) |
|||
{ |
|||
TotalCount = totalCount; |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
public abstract class FromSelectorBase |
|||
{ |
|||
protected FromSelectorBase(int value, string label) |
|||
{ |
|||
Value = value; |
|||
Label = label; |
|||
} |
|||
|
|||
public int Value { get; protected set; } |
|||
public string Label { get; protected set; } |
|||
} |
|||
|
|||
public abstract class FromSelectorBase<TValue, TLabel> |
|||
{ |
|||
protected FromSelectorBase(TValue value, TLabel label) |
|||
{ |
|||
Value = value; |
|||
Label = label; |
|||
} |
|||
|
|||
public TValue Value { get; protected set; } |
|||
public TLabel Label { get; protected set; } |
|||
} |
|||
|
|||
public class FromSelector : FromSelectorBase |
|||
{ |
|||
public FromSelector(int value, string label) : base(value, label) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public class FromSelector<TValue, TLabel> : FromSelectorBase<TValue, TLabel> |
|||
{ |
|||
public FromSelector(TValue value, TLabel label) : base(value, label) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,262 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
/// <summary>
|
|||
/// 参数合法性检查类
|
|||
/// </summary>
|
|||
public static class Guard |
|||
{ |
|||
/// <summary>
|
|||
/// 检查参数不能为空引用,
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称</param>
|
|||
public static T NotNull<T>(T value, string valueName) |
|||
{ |
|||
if (null == value) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查字符串不能为空引用或空字符串,
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="maxLength">字符串允许的最大长度。</param>
|
|||
/// <param name="minLength">字符串允许的最小长度。0表示不限制最小长度</param>
|
|||
public static string NotNullOrEmpty(string value, string valueName, int maxLength = int.MaxValue, |
|||
int minLength = 0) |
|||
{ |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
if (value.Length > maxLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
if (minLength > 0 && value.Length < minLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查字符串不能为空引用或全部为空白,
|
|||
/// </summary>
|
|||
/// <param name="value">需检查的字符串</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="maxLength">字符串允许的最大长度。</param>
|
|||
/// <param name="minLength">字符串允许的最小长度。0表示不限制最小长度</param>
|
|||
public static string NotNullOrWhiteSpace( |
|||
string value, |
|||
string valueName, |
|||
int maxLength = int.MaxValue, |
|||
int minLength = 0) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(value)) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
if (value.Length > maxLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
if (minLength > 0 && value.Length < minLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查字符串长度是否超过最大长度,或低于最小长度,
|
|||
/// </summary>
|
|||
/// <param name="value">需检查的字符串。</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="maxLength">字符串允许的最大长度。</param>
|
|||
/// <param name="minLength">字符串要求的最小长度。0表示不限制最小长度</param>
|
|||
public static string Length(string value, string valueName, int maxLength = int.MaxValue, |
|||
int minLength = 0) |
|||
{ |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
if (value.Length > maxLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
if (minLength > 0 && value.Length < minLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查Guid值不能为Guid.Empty
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
public static Guid NotEmpty( |
|||
Guid value, |
|||
string valueName) |
|||
{ |
|||
if (value == Guid.Empty) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查集合不能为空引用或空集合,
|
|||
/// </summary>
|
|||
/// <typeparam name="T">集合项的类型。</typeparam>
|
|||
/// <param name="list"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
public static void NotNullOrEmpty<T>( |
|||
IReadOnlyList<T> list, |
|||
string valueName) |
|||
{ |
|||
if (null == list || !list.Any()) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查参数必须小于[或可等于,参数<paramref name="canEqual"/>]指定值,
|
|||
/// </summary>
|
|||
/// <typeparam name="T">参数类型。</typeparam>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="target">要比较的值。</param>
|
|||
/// <param name="canEqual">是否可等于。</param>
|
|||
public static void LessThan<T>( |
|||
T value, |
|||
string valueName, |
|||
T target, |
|||
bool canEqual = false) |
|||
where T : IComparable<T> |
|||
{ |
|||
var flag = canEqual ? value.CompareTo(target) <= 0 : value.CompareTo(target) < 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查参数必须大于[或可等于,参数<paramref name="canEqual"/>]指定值,
|
|||
/// </summary>
|
|||
/// <typeparam name="T">参数类型。</typeparam>
|
|||
/// <param name="value">需检查的参数。</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="target">要比较的值。</param>
|
|||
/// <param name="canEqual">是否可等于。</param>
|
|||
public static void GreaterThan<T>( |
|||
T value, |
|||
string valueName, |
|||
T target, |
|||
bool canEqual = false) |
|||
where T : IComparable<T> |
|||
{ |
|||
var flag = canEqual ? value.CompareTo(target) >= 0 : value.CompareTo(target) > 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查参数必须在指定范围之间
|
|||
/// </summary>
|
|||
/// <typeparam name="T">参数类型。</typeparam>
|
|||
/// <param name="value">需检查的参数。</param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
/// <param name="start">比较范围的起始值。</param>
|
|||
/// <param name="end">比较范围的结束值。</param>
|
|||
/// <param name="startEqual">是否可等于起始值</param>
|
|||
/// <param name="endEqual">是否可等于结束值</param>
|
|||
public static void Between<T>( |
|||
T value, |
|||
string valueName, |
|||
T start, |
|||
T end, |
|||
bool startEqual = false, |
|||
bool endEqual = false) |
|||
where T : IComparable<T> |
|||
{ |
|||
var flag = startEqual ? value.CompareTo(start) >= 0 : value.CompareTo(start) > 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
|
|||
flag = endEqual ? value.CompareTo(end) <= 0 : value.CompareTo(end) < 0; |
|||
if (!flag) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(valueName); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查指定路径的文件夹必须存在,
|
|||
/// </summary>
|
|||
/// <param name="directory">需检查的路径。</param>
|
|||
/// <param name="parameterName">参数名称。</param>
|
|||
public static string DirectoryExists( |
|||
string directory, |
|||
string parameterName) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(directory)) |
|||
{ |
|||
throw new DirectoryNotFoundException(parameterName); |
|||
} |
|||
|
|||
if (!Directory.Exists(directory)) |
|||
{ |
|||
throw new DirectoryNotFoundException(directory); |
|||
} |
|||
|
|||
return directory; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查指定路径的文件必须存在,否则抛出
|
|||
/// </summary>
|
|||
/// <param name="filename"></param>
|
|||
/// <param name="valueName">参数名称。</param>
|
|||
public static string FileExists( |
|||
string filename, |
|||
string valueName) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(filename)) |
|||
{ |
|||
throw new ArgumentNullException(valueName); |
|||
} |
|||
|
|||
if (!File.Exists(filename)) |
|||
{ |
|||
throw new FileNotFoundException(filename); |
|||
} |
|||
|
|||
return filename; |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
public class IdInput |
|||
{ |
|||
public Guid Id { get; set; } |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
[DependsOn(typeof(LionAbpProLocalizationModule))] |
|||
public class LionAbpProCoreModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
/// <summary>
|
|||
/// 分页查询时使用的Dto类型
|
|||
/// </summary>
|
|||
public class PagingBase : IValidatableObject |
|||
{ |
|||
public const int MaxPageSize = 100000; |
|||
|
|||
/// <summary>
|
|||
/// 当前页面.默认从1开始
|
|||
/// </summary>
|
|||
public int PageIndex { get; set; } = 1; |
|||
|
|||
/// <summary>
|
|||
/// 每页多少条.每页显示多少记录
|
|||
/// </summary>
|
|||
public int PageSize { get; set; } = 10; |
|||
|
|||
/// <summary>
|
|||
/// 跳过多少条
|
|||
/// </summary>
|
|||
public int SkipCount => (PageIndex - 1) * PageSize; |
|||
|
|||
protected PagingBase() |
|||
{ |
|||
} |
|||
|
|||
public PagingBase(int pageIndex = 1, int pageSize = 10) |
|||
{ |
|||
PageIndex = pageIndex; |
|||
PageSize = pageSize; |
|||
} |
|||
|
|||
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) |
|||
{ |
|||
if (PageIndex < 1) |
|||
{ |
|||
yield return new ValidationResult( |
|||
"起始页必须大于等于1", |
|||
new[] { "PageIndex"} |
|||
); |
|||
} |
|||
|
|||
if (PageSize > MaxPageSize) |
|||
{ |
|||
yield return new ValidationResult( |
|||
$"每页最大记录数不能超过'{MaxPageSize}'", |
|||
new[] { "PageSize"} |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
public class WrapResult<T> |
|||
{ |
|||
public bool Success { get; private set; } |
|||
|
|||
public string Message { get; private set; } |
|||
|
|||
public T Data { get; private set; } |
|||
|
|||
public int Code { get; private set; } |
|||
|
|||
public WrapResult() |
|||
{ |
|||
Success = true; |
|||
Message = "Success"; |
|||
Data = default; |
|||
Code = 200; |
|||
} |
|||
|
|||
public void SetSuccess(T data, string message = "Success", int code = 200) |
|||
{ |
|||
Success = true; |
|||
Data = data; |
|||
Code = code; |
|||
} |
|||
|
|||
public void SetFail(string message = "Fail", int code = 500) |
|||
{ |
|||
Success = false; |
|||
Message = message; |
|||
Code = code; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
namespace Lion.AbpPro.Core; |
|||
|
|||
public class WrapResultAttribute : Attribute |
|||
{ |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
namespace System; |
|||
|
|||
/// <summary>
|
|||
/// 布尔值<see cref="Boolean"/>类型的扩展辅助操作类
|
|||
/// </summary>
|
|||
public static class BooleanExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 把布尔值转换为小写字符串
|
|||
/// </summary>
|
|||
public static string ToLower(this bool value) |
|||
{ |
|||
return value.ToString().ToLower(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,则抛出异常
|
|||
/// </summary>
|
|||
public static void TrueThrow(this bool flag, Exception exception) |
|||
{ |
|||
if (flag) |
|||
{ |
|||
throw exception; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
namespace System.Collections.Generic; |
|||
|
|||
/// <summary>
|
|||
/// 集合扩展方法
|
|||
/// </summary>
|
|||
public static class CollectionExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 如果条件成立,添加项
|
|||
/// </summary>
|
|||
public static void AddIf<T>(this ICollection<T> collection, T value, bool flag) |
|||
{ |
|||
if (collection is null) throw new ArgumentNullException(); |
|||
if (flag) |
|||
{ |
|||
collection.Add(value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,添加项
|
|||
/// </summary>
|
|||
public static void AddIf<T>(this ICollection<T> collection, T value, Func<bool> func) |
|||
{ |
|||
if (collection is null) throw new ArgumentNullException(); |
|||
if (func()) |
|||
{ |
|||
collection.Add(value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果不存在,添加项
|
|||
/// </summary>
|
|||
public static void AddIfNotExist<T>(this ICollection<T> collection, T value, Func<T, bool> existFunc = null) |
|||
{ |
|||
if (collection is null) throw new ArgumentNullException(); |
|||
var exists = existFunc == null ? collection!.Contains(value) : collection!.Any(existFunc); |
|||
if (!exists) |
|||
{ |
|||
collection!.Add(value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果不为空,添加项
|
|||
/// </summary>
|
|||
public static void AddIfNotNull<T>(this ICollection<T> collection, T value) where T : class |
|||
{ |
|||
if (collection is null) throw new ArgumentNullException(); |
|||
if (value != null) |
|||
{ |
|||
collection!.Add(value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取对象,不存在对使用委托添加对象
|
|||
/// </summary>
|
|||
public static T GetOrAdd<T>(this ICollection<T> collection, Func<T, bool> selector, Func<T> factory) |
|||
{ |
|||
if (collection is null) throw new ArgumentNullException(); |
|||
T item = collection.FirstOrDefault(selector); |
|||
if (item == null) |
|||
{ |
|||
item = factory(); |
|||
collection.Add(item); |
|||
} |
|||
|
|||
return item; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 判断数字集合是否是连续的
|
|||
/// </summary>
|
|||
/// <returns>如果参数集合为null或空集合,则返回false</returns>
|
|||
public static bool IsContinuous(this List<int> numList) |
|||
{ |
|||
if (numList == null || numList.Count == 0) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
numList.Sort((x, y) => -x.CompareTo(y)); //降序
|
|||
bool result = false; |
|||
var totalCount = numList.Count(); |
|||
for (int i = 0; i < totalCount - 1; i++) |
|||
{ |
|||
result = numList[i] - numList[i + 1] == 1; |
|||
|
|||
if (!result) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,237 @@ |
|||
namespace System; |
|||
|
|||
/// <summary>
|
|||
/// 时间扩展操作类
|
|||
/// </summary>
|
|||
public static class DateTimeExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 当前时间是否周末
|
|||
/// </summary>
|
|||
/// <param name="dateTime">时间点</param>
|
|||
/// <returns></returns>
|
|||
public static bool IsWeekend(this DateTime dateTime) |
|||
{ |
|||
DayOfWeek[] weeks = |
|||
{ |
|||
DayOfWeek.Saturday, |
|||
DayOfWeek.Sunday |
|||
}; |
|||
return weeks.Contains(dateTime.DayOfWeek); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 当前时间是否工作日
|
|||
/// </summary>
|
|||
/// <param name="dateTime">时间点</param>
|
|||
/// <returns></returns>
|
|||
public static bool IsWeekday(this DateTime dateTime) |
|||
{ |
|||
DayOfWeek[] weeks = |
|||
{ |
|||
DayOfWeek.Monday, |
|||
DayOfWeek.Tuesday, |
|||
DayOfWeek.Wednesday, |
|||
DayOfWeek.Thursday, |
|||
DayOfWeek.Friday |
|||
}; |
|||
return weeks.Contains(dateTime.DayOfWeek); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取时间相对唯一字符串
|
|||
/// </summary>
|
|||
/// <param name="dateTime"></param>
|
|||
/// <param name="millisecond">是否使用毫秒</param>
|
|||
/// <returns></returns>
|
|||
public static string ToUniqueString(this DateTime dateTime, bool millisecond = false) |
|||
{ |
|||
var seconds = dateTime.Hour * 3600 + dateTime.Minute * 60 + dateTime.Second; |
|||
var value = $"{dateTime:yyyy}{dateTime.DayOfYear}{seconds}"; |
|||
if (millisecond) |
|||
{ |
|||
return value + dateTime.ToString("fff"); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将时间转换为JS时间格式(Date.getTime())
|
|||
/// </summary>
|
|||
/// <param name="dateTime"></param>
|
|||
/// <param name="millisecond">是否使用毫秒</param>
|
|||
public static string ToJsGetTime(this DateTime dateTime, bool millisecond = true) |
|||
{ |
|||
var utc = dateTime.ToUniversalTime(); |
|||
var span = utc.Subtract(new DateTime(1970, 1, 1)); |
|||
return Math.Round(millisecond ? span.TotalMilliseconds : span.TotalSeconds).ToString(CultureInfo.InvariantCulture); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 将时间类型转成int类型. 例如 2021-09-01 => 20210901
|
|||
/// </summary>
|
|||
public static int ToYyyyMmDd(this DateTime dateTime) |
|||
{ |
|||
// 2021-09-01 => 2021*10000 + 09*100 + 1
|
|||
return dateTime.Year * 10000 + dateTime.Month * 100 + dateTime.Day; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将时间类型转成int类型. 例如 2021-09-01 => 202109
|
|||
/// </summary>
|
|||
public static int ToYyyyMm(this DateTime dateTime) |
|||
{ |
|||
// 2021-09-06 => 2021*100+09
|
|||
return dateTime.Year * 100 + dateTime.Month; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取指定日期 当天的最大时间
|
|||
/// 例如 2021-09-10 11:22:33.123456 转换后 2021-09-10 23:59:59.9999999
|
|||
/// </summary>
|
|||
public static DateTime? ToCurrentDateMaxDateTime(this DateTime? dateTime) |
|||
{ |
|||
return dateTime?.Date.AddDays(1).AddTicks(-1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取指定日期 当天的最大时间
|
|||
/// 例如 2021-09-10 11:22:33.123456 转换后 2021-09-10 23:59:59.9999999
|
|||
/// </summary>
|
|||
public static DateTime ToCurrentDateMaxDateTime(this DateTime dateTime) |
|||
{ |
|||
return dateTime.Date.AddDays(1).AddTicks(-1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取指定日期 当天的最小时间
|
|||
/// </summary>
|
|||
public static DateTime? ToCurrentDateMinDateTime(this DateTime? dateTime) |
|||
{ |
|||
return dateTime?.Date.AddTicks(1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取指定日期 当天的最小时间
|
|||
/// </summary>
|
|||
public static DateTime ToCurrentDateMinDateTime(this DateTime dateTime) |
|||
{ |
|||
return dateTime.Date.AddTicks(1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取指定时间的下一秒
|
|||
/// 例如 2021-09-10 11:11:11.1234567 转换后 2021-09-10 11:11:12.0000000
|
|||
/// </summary>
|
|||
public static DateTime? ToNextSecondDateTime(this DateTime? dateTime) |
|||
{ |
|||
if (!dateTime.HasValue) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return new DateTime(dateTime.Value.Year, dateTime.Value.Month, dateTime.Value.Day, dateTime.Value.Hour, dateTime.Value.Minute, dateTime.Value.Second).AddSeconds(1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 转为秒级时间戳
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static long ToSecondTimeStamp(this DateTime dateTime, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
long timeStamp = Convert.ToInt32((dateTime - dtStart).TotalSeconds); |
|||
return timeStamp; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 转为秒级时间戳
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static long? ToSecondTimeStamp(this DateTime? dateTime, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
if (!dateTime.HasValue) return null; |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
long timeStamp = Convert.ToInt32((dateTime.Value - dtStart).TotalSeconds); |
|||
return timeStamp; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 转为毫秒级时间戳
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static long ToMilliSecondTimeStamp(this DateTime dateTime, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
var timeStamp = Convert.ToInt64((dateTime - dtStart).TotalMilliseconds); |
|||
return timeStamp; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 转为毫秒级时间戳
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static long? ToMilliSecondTimeStamp(this DateTime? dateTime, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
if (!dateTime.HasValue) return null; |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
var timeStamp = Convert.ToInt64((dateTime.Value - dtStart).TotalMilliseconds); |
|||
return timeStamp; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 秒级时间戳转为时间
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static DateTime ToDateTimeBySecondTimeStamp(this long timestamp, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
var time = dtStart.AddSeconds(timestamp); |
|||
return time; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 秒级时间戳转为时间
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static DateTime? ToDateTimeBySecondTimeStamp(this long? timestamp, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
if (!timestamp.HasValue) return null; |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
var time = dtStart.AddSeconds(timestamp.Value); |
|||
return time; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 毫秒级时间戳转为时间
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static DateTime ToDateTimeByMilliSecondTimeStamp(this long timestamp, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
var time = dtStart.AddMilliseconds(timestamp); |
|||
return time; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 毫秒级时间戳转为时间
|
|||
/// <param name="timeZoneInfo">默认 TimeZoneInfo.Utc</param>
|
|||
/// </summary>
|
|||
public static DateTime? ToDateTimeByMilliSecondTimeStamp(this long? timestamp, TimeZoneInfo timeZoneInfo = null) |
|||
{ |
|||
if (!timestamp.HasValue) return null; |
|||
timeZoneInfo ??= TimeZoneInfo.Utc; |
|||
var dtStart = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0), timeZoneInfo); |
|||
var time = dtStart.AddMilliseconds(timestamp.Value); |
|||
return time; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
namespace System; |
|||
|
|||
/// <summary>
|
|||
/// Decimal扩展操作类
|
|||
/// </summary>
|
|||
public static class DecimalExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 移除小数点后面的零
|
|||
/// </summary>
|
|||
public static decimal TrimEndZero(this decimal number) |
|||
{ |
|||
var str = number.ToString(CultureInfo.InvariantCulture); |
|||
if (str.Contains('.')) |
|||
{ |
|||
return Convert.ToDecimal(str.TrimEnd('0').TrimEnd('.')); |
|||
} |
|||
|
|||
return Convert.ToDecimal(str); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 移除小数点后面的零
|
|||
/// </summary>
|
|||
public static decimal? TrimEndZero(this decimal? number) |
|||
{ |
|||
if (number.HasValue) |
|||
{ |
|||
return TrimEndZero(number.Value); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
@ -0,0 +1,195 @@ |
|||
using System.Collections.Specialized; |
|||
using System.ComponentModel; |
|||
using System.Reflection; |
|||
|
|||
namespace System; |
|||
|
|||
/// <summary>
|
|||
/// 枚举<see cref="Enum"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class EnumExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 获取枚举项上的<see cref="DescriptionAttribute"/>特性的文字描述
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToDescription(this Enum value) |
|||
{ |
|||
var type = value.GetType(); |
|||
var member = type.GetMember(value.ToString()).FirstOrDefault(); |
|||
|
|||
return member != null ? member.GetDescription() : value.ToString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 枚举遍历,返回枚举的名称、值、特性
|
|||
/// </summary>
|
|||
/// <param name="enumType">枚举类型</param>
|
|||
/// <param name="action">回调函数</param>
|
|||
private static void Each(this Type enumType, Action<string, string, string, object> action) |
|||
{ |
|||
if (enumType.BaseType != typeof(Enum)) |
|||
{ |
|||
return; |
|||
} |
|||
var arr = Enum.GetValues(enumType); |
|||
foreach (var name in arr) |
|||
{ |
|||
var currentEnum = Enum.Parse(enumType, name.ToString()); |
|||
var value = Convert.ToInt32(Enum.Parse(enumType, name.ToString())); |
|||
var fieldInfo = enumType.GetField(name.ToString()); |
|||
var description = ""; |
|||
if (fieldInfo != null) |
|||
{ |
|||
var attr = Attribute.GetCustomAttribute(fieldInfo, |
|||
typeof(DescriptionAttribute), false) as DescriptionAttribute; |
|||
if (attr != null) |
|||
{ |
|||
description = attr.Description; |
|||
} |
|||
} |
|||
action(name.ToString(), value.ToString(), description, currentEnum); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 根据枚举类型值返回枚举定义Description属性
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="enumType"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToEnumDescriptionString(this short value, Type enumType) |
|||
{ |
|||
var nvc = new NameValueCollection(); |
|||
var typeDescription = typeof(DescriptionAttribute); |
|||
var fields = enumType.GetFields(); |
|||
foreach (var field in fields) |
|||
{ |
|||
if (field.FieldType.IsEnum) |
|||
{ |
|||
var strValue = ((int)enumType.InvokeMember(field.Name, BindingFlags.GetField, null, null, null)).ToString(); |
|||
var arr = field.GetCustomAttributes(typeDescription, true); |
|||
string strText; |
|||
if (arr.Length > 0) |
|||
{ |
|||
var aa = (DescriptionAttribute)arr[0]; |
|||
strText = aa.Description; |
|||
} |
|||
else |
|||
{ |
|||
strText = ""; |
|||
} |
|||
nvc.Add(strValue, strText); |
|||
} |
|||
} |
|||
return nvc[value.ToString()]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举的Value为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
private static List<KeyValuePair<string, string>> GetEnumTypeValueList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, string>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => |
|||
items.Add(new KeyValuePair<string, string>(description, value))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
private static List<KeyValuePair<string, T>> GetEnumTypeList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, T>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => |
|||
items.Add(new KeyValuePair<string, T>(description, (T)enumObj))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举的Name为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, string>> GetEnumTypeDescriptionNameList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, string>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => items.Add(new KeyValuePair<string, string>(description, name))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Name为字典的Key,枚举的Description为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="T">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, string>> GetEnumTypeValueNameList<T>() |
|||
{ |
|||
var items = new List<KeyValuePair<string, string>>(); |
|||
typeof(T).Each((name, value, description, enumObj) => items.Add(new KeyValuePair<string, string>(name, description))); |
|||
return items; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Name为字典的Key,枚举的Description为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="TModel">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, string>> GetStringKeyValueList<TModel>() where TModel : Enum |
|||
{ |
|||
var keyValuePairList = new List<KeyValuePair<string, string>>(); |
|||
var values = Enum.GetValues(typeof(TModel)); |
|||
var modelArray = new TModel[values.Length]; |
|||
values.CopyTo(modelArray, 0); |
|||
foreach (TModel model in modelArray) |
|||
keyValuePairList.Add(new KeyValuePair<string, string>(model.ToString(), model.ToString())); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将指定枚举转换为字典.
|
|||
/// 枚举的Description为字典的Key,枚举为字典的Value
|
|||
/// </summary>
|
|||
/// <typeparam name="TModel">指定枚举</typeparam>
|
|||
public static List<KeyValuePair<string, TModel>> GetEnumKeyValueList<TModel>() where TModel : Enum |
|||
{ |
|||
var enumTypeList = GetEnumTypeList<TModel>(); |
|||
var keyValuePairList = new List<KeyValuePair<string, TModel>>(); |
|||
foreach (KeyValuePair<string, TModel> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<string, TModel>(keyValuePair.Key, keyValuePair.Value)); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
public static List<KeyValuePair<string, string>> GetEntityDoubleStringKeyValueList<TModel>() |
|||
{ |
|||
var enumTypeList = GetEnumTypeValueList<TModel>(); |
|||
var keyValuePairList = new List<KeyValuePair<string, string>>(); |
|||
foreach (KeyValuePair<string, string> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<string, string>(keyValuePair.Key, keyValuePair.Value)); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
public static List<KeyValuePair<string, int>> GetEntityStringIntKeyValueList<TModel>() |
|||
{ |
|||
List<KeyValuePair<string, string>> enumTypeList = GetEnumTypeValueList<TModel>(); |
|||
List<KeyValuePair<string, int>> keyValuePairList = new List<KeyValuePair<string, int>>(); |
|||
foreach (KeyValuePair<string, string> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<string, int>(keyValuePair.Key, Convert.ToInt32(keyValuePair.Value))); |
|||
return keyValuePairList; |
|||
} |
|||
|
|||
public static List<KeyValuePair<int, int>> GetEntityDoubleIntKeyValueList<TModel>() |
|||
{ |
|||
List<KeyValuePair<string, string>> enumTypeList = GetEnumTypeValueList<TModel>(); |
|||
List<KeyValuePair<int, int>> keyValuePairList = new List<KeyValuePair<int, int>>(); |
|||
foreach (KeyValuePair<string, string> keyValuePair in enumTypeList) |
|||
keyValuePairList.Add(new KeyValuePair<int, int>(Convert.ToInt32(keyValuePair.Key), Convert.ToInt32(keyValuePair.Value))); |
|||
return keyValuePairList; |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using System.Runtime.ExceptionServices; |
|||
|
|||
namespace System; |
|||
|
|||
/// <summary>
|
|||
/// 异常操作扩展
|
|||
/// </summary>
|
|||
public static class ExceptionExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 格式化异常消息
|
|||
/// </summary>
|
|||
/// <param name="e">异常对象</param>
|
|||
/// <param name="isHideStackTrace">是否隐藏异常规模信息</param>
|
|||
/// <returns>格式化后的异常信息字符串</returns>
|
|||
public static string FormatMessage(this Exception e, bool isHideStackTrace = false) |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
var count = 0; |
|||
var appString = string.Empty; |
|||
while (e != null) |
|||
{ |
|||
if (count > 0) |
|||
{ |
|||
appString += " "; |
|||
} |
|||
sb.AppendLine($"{appString}异常消息:{e.Message}"); |
|||
sb.AppendLine($"{appString}异常类型:{e.GetType().FullName}"); |
|||
sb.AppendLine($"{appString}异常方法:{(e.TargetSite == null ? null : e.TargetSite.Name)}"); |
|||
sb.AppendLine($"{appString}异常源:{e.Source}"); |
|||
if (!isHideStackTrace && e.StackTrace != null) |
|||
{ |
|||
sb.AppendLine($"{appString}异常堆栈:{e.StackTrace}"); |
|||
} |
|||
if (e.InnerException != null) |
|||
{ |
|||
sb.AppendLine($"{appString}内部异常:"); |
|||
count++; |
|||
e = e.InnerException; |
|||
} |
|||
} |
|||
return sb.ToString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将异常重新抛出
|
|||
/// </summary>
|
|||
public static void ReThrow(this Exception exception) |
|||
{ |
|||
ExceptionDispatchInfo.Capture(exception).Throw(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,则抛出异常
|
|||
/// </summary>
|
|||
public static void ThrowIf(this Exception exception, bool isThrow) |
|||
{ |
|||
if (isThrow) |
|||
{ |
|||
throw exception; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 如果条件成立,则抛出异常
|
|||
/// </summary>
|
|||
public static void ThrowIf(this Exception exception, Func<bool> isThrowFunc) |
|||
{ |
|||
if (isThrowFunc()) |
|||
{ |
|||
throw exception; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
namespace System; |
|||
|
|||
public static class GuidExtensions |
|||
{ |
|||
public static bool IsNullOrEmpty(this Guid? value) |
|||
{ |
|||
return value == null || value.Value == Guid.Empty; |
|||
} |
|||
|
|||
public static bool IsNullOrEmpty(this Guid value) |
|||
{ |
|||
return value == Guid.Empty; |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
namespace System; |
|||
|
|||
/// <summary>
|
|||
/// int 扩展方法
|
|||
/// </summary>
|
|||
public static class Int32Extensions |
|||
{ |
|||
/// <summary>
|
|||
/// 将 <see cref="DateTimeExtensions.ToYyyyMmDd"/> 反转换.
|
|||
/// 例如 20210826 => 2021-08-26
|
|||
/// </summary>
|
|||
public static DateTime YyyyMmDdToTime(this int value) |
|||
{ |
|||
var canParse = DateTime.TryParseExact(value.ToString(), "yyyyMMdd", null, DateTimeStyles.None, |
|||
out var result); |
|||
if (!canParse) |
|||
{ |
|||
throw new Exception("not can parse"); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
using System.ComponentModel; |
|||
|
|||
namespace System.Reflection; |
|||
|
|||
/// <summary>
|
|||
/// 成员<see cref="MemberInfo"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class MemberInfoExtensions |
|||
{ |
|||
|
|||
/// <summary>
|
|||
/// 获取成员元数据的Description特性描述信息。
|
|||
/// </summary>
|
|||
/// <param name="member">成员元数据对象。</param>
|
|||
/// <param name="inherit">是否搜索成员的继承链以查找描述特性。</param>
|
|||
/// <returns>返回Description特性描述信息,如不存在则返回成员的名称。</returns>
|
|||
public static string GetDescription(this MemberInfo member, bool inherit = true) |
|||
{ |
|||
var desc = member.GetAttribute<DescriptionAttribute>(inherit); |
|||
if (desc != null) |
|||
{ |
|||
return desc.Description; |
|||
} |
|||
|
|||
var displayName = member.GetAttribute<DisplayNameAttribute>(inherit); |
|||
if (displayName != null) |
|||
{ |
|||
return displayName.DisplayName; |
|||
} |
|||
|
|||
var display = member.GetAttribute<DisplayAttribute>(inherit); |
|||
return display != null ? display.Name : member.Name; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查指定指定类型成员中是否存在指定的Attribute特性。
|
|||
/// </summary>
|
|||
/// <typeparam name="T">要检查的Attribute特性类型。</typeparam>
|
|||
/// <param name="memberInfo">要检查的类型成员</param>
|
|||
/// <param name="inherit">是否从继承中查找</param>
|
|||
/// <returns>是否存在</returns>
|
|||
public static bool HasAttribute<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute |
|||
{ |
|||
return memberInfo.IsDefined(typeof(T), inherit); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 从类型成员获取指定Attribute特性
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Attribute特性类型</typeparam>
|
|||
/// <param name="memberInfo">类型类型成员</param>
|
|||
/// <param name="inherit">是否从继承中查找</param>
|
|||
/// <returns>存在返回第一个,不存在返回null</returns>
|
|||
public static T GetAttribute<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute |
|||
{ |
|||
var attributes = memberInfo.GetCustomAttributes(typeof(T), inherit); |
|||
return attributes.FirstOrDefault() as T; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 从类型成员获取指定Attribute特性。
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Attribute特性类型。</typeparam>
|
|||
/// <param name="memberInfo">类型类型成员。</param>
|
|||
/// <param name="inherit">是否从继承中查找。</param>
|
|||
/// <returns>返回所有指定Attribute特性的数组。</returns>
|
|||
public static T[] GetAttributes<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute |
|||
{ |
|||
return memberInfo.GetCustomAttributes(typeof(T), inherit).Cast<T>().ToArray(); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
namespace System.Reflection; |
|||
|
|||
/// <summary>
|
|||
/// 方法<see cref="MethodInfo"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class MethodInfoExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 方法是否是异步
|
|||
/// </summary>
|
|||
public static bool IsAsync(this MethodInfo method) |
|||
{ |
|||
return (method.ReturnType == typeof(Task<>) |
|||
|| method.ReturnType.IsGenericType |
|||
&& method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) |
|||
|| method.ReturnType == typeof(Task); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 返回当前方法信息是否是重写方法
|
|||
/// </summary>
|
|||
/// <param name="method">要判断的方法信息</param>
|
|||
/// <returns>是否是重写方法</returns>
|
|||
public static bool IsOverridden(this MethodInfo method) |
|||
{ |
|||
return method.GetBaseDefinition().DeclaringType != method.DeclaringType; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
namespace System.Reflection; |
|||
|
|||
/// <summary>
|
|||
/// 属性<see cref="MethodInfo"/>的扩展辅助操作方法
|
|||
/// </summary>
|
|||
public static class PropertyInfoExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 返回当前属性信息是否为virtual
|
|||
/// </summary>
|
|||
public static bool IsVirtual(this PropertyInfo property) |
|||
{ |
|||
var accessor = property.GetAccessors().FirstOrDefault(); |
|||
if (accessor == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return accessor.IsVirtual && !accessor.IsFinal; |
|||
} |
|||
} |
|||
@ -0,0 +1,850 @@ |
|||
namespace System; |
|||
|
|||
/// <summary>
|
|||
/// 字符串<see cref="string"/>类型的扩展辅助操作类
|
|||
/// </summary>
|
|||
[DebuggerStepThrough] |
|||
public static class StringExtensions |
|||
{ |
|||
#region 正则表达式
|
|||
|
|||
/// <summary>
|
|||
/// 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项
|
|||
/// </summary>
|
|||
/// <param name="value">要搜索匹配项的字符串</param>
|
|||
/// <param name="pattern">要匹配的正则表达式模式</param>
|
|||
/// <param name="isContains">是否包含,否则全匹配</param>
|
|||
/// <returns>如果正则表达式找到匹配项,则为 true;否则,为 false</returns>
|
|||
public static bool IsMatch(this string value, string pattern, bool isContains = true) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return isContains |
|||
? Regex.IsMatch(value, pattern) |
|||
: Regex.Match(value, pattern).Success; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中搜索指定的正则表达式的第一个匹配项
|
|||
/// </summary>
|
|||
/// <param name="value">要搜索匹配项的字符串</param>
|
|||
/// <param name="pattern">要匹配的正则表达式模式</param>
|
|||
/// <returns>一个对象,包含有关匹配项的信息</returns>
|
|||
public static string Match(this string value, string pattern) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return Regex.Match(value, pattern).Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中匹配并替换符合指定正则表达式的子串
|
|||
/// </summary>
|
|||
public static string ReplaceRegex(this string value, string pattern, string replacement) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return Regex.Replace(value, pattern, replacement); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中搜索指定的正则表达式的所有匹配项的字符串集合
|
|||
/// </summary>
|
|||
/// <param name="value"> 要搜索匹配项的字符串 </param>
|
|||
/// <param name="pattern"> 要匹配的正则表达式模式 </param>
|
|||
/// <returns> 一个集合,包含有关匹配项的字符串值 </returns>
|
|||
public static IEnumerable<string> Matches(this string value, string pattern) |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
return new string[] { }; |
|||
} |
|||
|
|||
var matches = Regex.Matches(value, pattern); |
|||
return from Match match in matches select match.Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定的输入字符串中匹配第一个数字字符串
|
|||
/// </summary>
|
|||
public static string MatchFirstNumber(this string value) |
|||
{ |
|||
var matches = Regex.Matches(value, @"\d+"); |
|||
if (matches.Count == 0) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
return matches[0].Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定字符串中匹配最后一个数字字符串
|
|||
/// </summary>
|
|||
public static string MatchLastNumber(this string value) |
|||
{ |
|||
var matches = Regex.Matches(value, @"\d+"); |
|||
if (matches.Count == 0) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
return matches[matches.Count - 1].Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 在指定字符串中匹配所有数字字符串
|
|||
/// </summary>
|
|||
public static IEnumerable<string> MatchNumbers(this string value) |
|||
{ |
|||
return Matches(value, @"\d+"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检测指定字符串中是否包含数字
|
|||
/// </summary>
|
|||
public static bool IsMatchNumber(this string value) |
|||
{ |
|||
return IsMatch(value, @"\d"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检测指定字符串是否全部为数字并且长度等于指定长度
|
|||
/// </summary>
|
|||
public static bool IsMatchNumber(this string value, int length) |
|||
{ |
|||
var regex = new Regex(@"^\d{" + length + "}$"); |
|||
return regex.IsMatch(value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 用正则表达式截取字符串
|
|||
/// </summary>
|
|||
public static string Substring2(this string source, string startString, string endString) |
|||
{ |
|||
return source.Substring2(startString, endString, false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 用正则表达式截取字符串
|
|||
/// </summary>
|
|||
public static string Substring2(this string source, string startString, string endString, bool containsEmpty) |
|||
{ |
|||
if (source.IsMissing()) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
var inner = containsEmpty ? "\\s\\S" : "\\S"; |
|||
var result = source.Match($"(?<={startString})([{inner}]+?)(?={endString})"); |
|||
return result.IsMissing() ? null : result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否电子邮件
|
|||
/// </summary>
|
|||
public static bool IsEmail(this string value) |
|||
{ |
|||
const string pattern = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否是IP地址
|
|||
/// </summary>
|
|||
public static bool IsIpAddress(this string value) |
|||
{ |
|||
const string pattern = |
|||
@"^((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否是整数
|
|||
/// </summary>
|
|||
public static bool IsNumeric(this string value) |
|||
{ |
|||
const string pattern = @"^\-?[0-9]+$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否是Unicode字符串
|
|||
/// </summary>
|
|||
public static bool IsUnicode(this string value) |
|||
{ |
|||
const string pattern = @"^[\u4E00-\u9FA5\uE815-\uFA29]+$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否Url字符串
|
|||
/// </summary>
|
|||
public static bool IsUrl(this string value) |
|||
{ |
|||
try |
|||
{ |
|||
if (string.IsNullOrEmpty(value) || value.Contains(' ')) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var uri = new Uri(value); |
|||
return true; |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否身份证号,验证如下3种情况:
|
|||
/// 1.身份证号码为15位数字;
|
|||
/// 2.身份证号码为18位数字;
|
|||
/// 3.身份证号码为17位数字+1个字母
|
|||
/// </summary>
|
|||
public static bool IsIdentityCardId(this string value) |
|||
{ |
|||
if (value.Length != 15 && value.Length != 18) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
Regex regex; |
|||
string[] array; |
|||
if (value.Length == 15) |
|||
{ |
|||
regex = new Regex(@"^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})_"); |
|||
if (!regex.Match(value).Success) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
array = regex.Split(value); |
|||
return DateTime.TryParse(string.Format("{0}-{1}-{2}", "19" + array[2], array[3], array[4]), out _); |
|||
} |
|||
|
|||
regex = new Regex(@"^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9Xx])$"); |
|||
if (!regex.Match(value).Success) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
array = regex.Split(value); |
|||
if (!DateTime.TryParse(string.Format("{0}-{1}-{2}", array[2], array[3], array[4]), out _)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
//校验最后一位
|
|||
var chars = value.ToCharArray().Select(m => m.ToString()).ToArray(); |
|||
int[] weights = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 }; |
|||
var sum = 0; |
|||
for (var i = 0; i < 17; i++) |
|||
{ |
|||
var num = int.Parse(chars[i]); |
|||
sum += num * weights[i]; |
|||
} |
|||
|
|||
var mod = sum % 11; |
|||
var vCode = "10X98765432"; // 检验码字符串
|
|||
var last = vCode.ToCharArray().ElementAt(mod).ToString(); |
|||
return chars.Last().ToUpper() == last; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 是否手机号码
|
|||
/// </summary>
|
|||
/// <param name="value"></param>
|
|||
/// <param name="isRestrict">是否按严格格式验证</param>
|
|||
public static bool IsMobileNumber(this string value, bool isRestrict = false) |
|||
{ |
|||
var pattern = isRestrict ? @"^[1][3-8]\d{9}$" : @"^[1]\d{10}$"; |
|||
return value.IsMatch(pattern); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region 其他操作
|
|||
|
|||
/// <summary>
|
|||
/// 判断指定的字符串不是 null、空。
|
|||
/// </summary>
|
|||
public static bool IsNotNullOrEmpty(this string str) |
|||
{ |
|||
return !string.IsNullOrEmpty(str); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 判断指定的字符串不是 null、空或者仅由空白字符组成
|
|||
/// </summary>
|
|||
public static bool IsNotNullOrWhiteSpace(this string str) |
|||
{ |
|||
return !string.IsNullOrWhiteSpace(str); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 指示指定的字符串是 null、空或者仅由空白字符组成
|
|||
/// </summary>
|
|||
public static bool IsMissing(this string value) |
|||
{ |
|||
return string.IsNullOrWhiteSpace(value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 为指定格式的字符串填充相应对象来生成字符串
|
|||
/// </summary>
|
|||
/// <param name="format">字符串格式,占位符以{n}表示</param>
|
|||
/// <param name="args">用于填充占位符的参数</param>
|
|||
/// <returns>格式化后的字符串</returns>
|
|||
public static string FormatWith(this string format, params object[] args) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
return string.Format(CultureInfo.CurrentCulture, format, args); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串反转
|
|||
/// </summary>
|
|||
/// <param name="value">要反转的字符串</param>
|
|||
public static string ReverseString(this string value) |
|||
{ |
|||
Guard.NotNull(value, nameof(value)); |
|||
return new string(value.Reverse().ToArray()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 单词复数变成单数形式
|
|||
/// </summary>
|
|||
/// <param name="word"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToSingular(this string word) |
|||
{ |
|||
var plural1 = new Regex("(?<keep>[^aeiou])ies$"); |
|||
var plural2 = new Regex("(?<keep>[aeiou]y)s$"); |
|||
var plural3 = new Regex("(?<keep>[sxzh])es$"); |
|||
var plural4 = new Regex("(?<keep>[^sxzhyu])s$"); |
|||
|
|||
if (plural1.IsMatch(word)) |
|||
{ |
|||
return plural1.Replace(word, "${keep}y"); |
|||
} |
|||
|
|||
if (plural2.IsMatch(word)) |
|||
{ |
|||
return plural2.Replace(word, "${keep}"); |
|||
} |
|||
|
|||
if (plural3.IsMatch(word)) |
|||
{ |
|||
return plural3.Replace(word, "${keep}"); |
|||
} |
|||
|
|||
if (plural4.IsMatch(word)) |
|||
{ |
|||
return plural4.Replace(word, "${keep}"); |
|||
} |
|||
|
|||
return word; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 单词单数变成复数形式
|
|||
/// </summary>
|
|||
/// <param name="word"></param>
|
|||
/// <returns></returns>
|
|||
public static string ToPlural(this string word) |
|||
{ |
|||
var plural1 = new Regex("(?<keep>[^aeiou])y$"); |
|||
var plural2 = new Regex("(?<keep>[aeiou]y)$"); |
|||
var plural3 = new Regex("(?<keep>[sxzh])$"); |
|||
var plural4 = new Regex("(?<keep>[^sxzhy])$"); |
|||
|
|||
if (plural1.IsMatch(word)) |
|||
{ |
|||
return plural1.Replace(word, "${keep}ies"); |
|||
} |
|||
|
|||
if (plural2.IsMatch(word)) |
|||
{ |
|||
return plural2.Replace(word, "${keep}s"); |
|||
} |
|||
|
|||
if (plural3.IsMatch(word)) |
|||
{ |
|||
return plural3.Replace(word, "${keep}es"); |
|||
} |
|||
|
|||
if (plural4.IsMatch(word)) |
|||
{ |
|||
return plural4.Replace(word, "${keep}s"); |
|||
} |
|||
|
|||
return word; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 以指定字符串作为分隔符将指定字符串分隔成数组
|
|||
/// </summary>
|
|||
/// <param name="value">要分割的字符串</param>
|
|||
/// <param name="strSplit">字符串类型的分隔符</param>
|
|||
/// <param name="removeEmptyEntries">是否移除数据中元素为空字符串的项</param>
|
|||
/// <returns>分割后的数据</returns>
|
|||
public static string[] Split(this string value, string strSplit, bool removeEmptyEntries = false) |
|||
{ |
|||
return value.Split(new[] { strSplit }, |
|||
removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 支持汉字的字符串长度,汉字长度计为2
|
|||
/// </summary>
|
|||
/// <param name="value">参数字符串</param>
|
|||
/// <returns>当前字符串的长度,汉字长度为2</returns>
|
|||
public static int TextLength(this string value) |
|||
{ |
|||
var ascii = new ASCIIEncoding(); |
|||
var tempLen = 0; |
|||
var bytes = ascii.GetBytes(value); |
|||
foreach (var b in bytes) |
|||
{ |
|||
if (b == 63) |
|||
{ |
|||
tempLen += 2; |
|||
} |
|||
else |
|||
{ |
|||
tempLen += 1; |
|||
} |
|||
} |
|||
|
|||
return tempLen; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 给URL添加查询参数
|
|||
/// </summary>
|
|||
/// <param name="url">URL字符串</param>
|
|||
/// <param name="queries">要添加的参数,形如:"id=1,cid=2"</param>
|
|||
/// <returns></returns>
|
|||
public static string AddUrlQuery(this string url, params string[] queries) |
|||
{ |
|||
foreach (var query in queries) |
|||
{ |
|||
if (!url.Contains("?")) |
|||
{ |
|||
url += "?"; |
|||
} |
|||
else if (!url.EndsWith("&")) |
|||
{ |
|||
url += "&"; |
|||
} |
|||
|
|||
url += query; |
|||
} |
|||
|
|||
return url; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取URL中指定参数的值,不存在返回空字符串
|
|||
/// </summary>
|
|||
public static string GetUrlQuery(this string url, string key) |
|||
{ |
|||
var uri = new Uri(url); |
|||
var query = uri.Query; |
|||
if (string.IsNullOrEmpty(query)) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
query = query.TrimStart('?'); |
|||
var dict = (from m in query.Split("&", true) |
|||
let strs = m.Split("=") |
|||
select new KeyValuePair<string, string>(strs[0], strs[1])) |
|||
.ToDictionary(m => m.Key, m => m.Value); |
|||
if (dict.ContainsKey(key)) |
|||
{ |
|||
return dict[key]; |
|||
} |
|||
|
|||
return string.Empty; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 给URL添加 # 参数
|
|||
/// </summary>
|
|||
/// <param name="url">URL字符串</param>
|
|||
/// <param name="query">要添加的参数</param>
|
|||
/// <returns></returns>
|
|||
public static string AddHashFragment(this string url, string query) |
|||
{ |
|||
Guard.NotNull(url, nameof(url)); |
|||
Guard.NotNull(query, nameof(query)); |
|||
|
|||
if (!url.Contains("#")) |
|||
{ |
|||
url += "#"; |
|||
} |
|||
|
|||
return url + query; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 将<see cref="byte"/>[]数组转换为Base64字符串
|
|||
/// </summary>
|
|||
public static string ToBase64String(this byte[] bytes) |
|||
{ |
|||
Guard.NotNull(bytes, nameof(bytes)); |
|||
|
|||
return Convert.ToBase64String(bytes); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串转换为Base64字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
/// <param name="source">正常的字符串</param>
|
|||
/// <param name="encoding">编码</param>
|
|||
/// <returns>Base64字符串</returns>
|
|||
public static string ToBase64String(this string source, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
return Convert.ToBase64String(encoding.GetBytes(source)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将Base64字符串转换为正常字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
/// <param name="base64String">Base64字符串</param>
|
|||
/// <param name="encoding">编码</param>
|
|||
/// <returns>正常字符串</returns>
|
|||
public static string FromBase64String(this string base64String, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(base64String, nameof(base64String)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
var bytes = Convert.FromBase64String(base64String); |
|||
return encoding.GetString(bytes); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行UrlDecode解码
|
|||
/// </summary>
|
|||
/// <param name="source">待UrlDecode解码的字符串</param>
|
|||
/// <returns>UrlDecode解码后的字符串</returns>
|
|||
public static string ToUrlDecode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.UrlDecode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行UrlEncode编码
|
|||
/// </summary>
|
|||
/// <param name="source">待UrlEncode编码的字符串</param>
|
|||
/// <returns>UrlEncode编码后的字符串</returns>
|
|||
public static string ToUrlEncode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.UrlEncode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行HtmlDecode解码
|
|||
/// </summary>
|
|||
/// <param name="source">待HtmlDecode解码的字符串</param>
|
|||
/// <returns>HtmlDecode解码后的字符串</returns>
|
|||
public static string ToHtmlDecode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.HtmlDecode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行HtmlEncode编码
|
|||
/// </summary>
|
|||
/// <param name="source">待HtmlEncode编码的字符串</param>
|
|||
/// <returns>HtmlEncode编码后的字符串</returns>
|
|||
public static string ToHtmlEncode(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return HttpUtility.HtmlEncode(source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串转换为十六进制字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
public static string ToHexString(this string source, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
byte[] bytes = encoding.GetBytes(source); |
|||
return bytes.ToHexString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将十六进制字符串转换为常规字符串,默认编码为<see cref="Encoding.UTF8"/>
|
|||
/// </summary>
|
|||
public static string FromHexString(this string hexString, Encoding encoding = null) |
|||
{ |
|||
Guard.NotNull(hexString, nameof(hexString)); |
|||
|
|||
if (encoding == null) encoding = Encoding.UTF8; |
|||
|
|||
var bytes = hexString.ToHexBytes(); |
|||
return encoding.GetString(bytes); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将byte[]编码为十六进制字符串
|
|||
/// </summary>
|
|||
/// <param name="bytes">byte[]数组</param>
|
|||
/// <returns>十六进制字符串</returns>
|
|||
public static string ToHexString(this byte[] bytes) |
|||
{ |
|||
Guard.NotNull(bytes, nameof(bytes)); |
|||
|
|||
return bytes.Aggregate(string.Empty, (current, t) => current + t.ToString("X2")); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将十六进制字符串转换为byte[]
|
|||
/// </summary>
|
|||
/// <param name="hexString">十六进制字符串</param>
|
|||
/// <returns>byte[]数组</returns>
|
|||
public static byte[] ToHexBytes(this string hexString) |
|||
{ |
|||
hexString = hexString ?? ""; |
|||
hexString = hexString.Replace(" ", ""); |
|||
byte[] bytes = new byte[hexString.Length / 2]; |
|||
for (int i = 0; i < bytes.Length; i++) |
|||
{ |
|||
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); |
|||
} |
|||
|
|||
return bytes; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将字符串进行Unicode编码,变成形如“\u7f16\u7801”的形式
|
|||
/// </summary>
|
|||
/// <param name="source">要进行编号的字符串</param>
|
|||
public static string ToUnicodeString(this string source) |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
var regex = new Regex(@"[^\u0000-\u00ff]"); |
|||
return regex.Replace(source, m => string.Format(@"\u{0:x4}", (short)m.Value[0])); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将形如“\u7f16\u7801”的Unicode字符串解码
|
|||
/// </summary>
|
|||
public static string FromUnicodeString(this string source) |
|||
{ |
|||
var regex = new Regex(@"\\u([0-9a-fA-F]{4})", RegexOptions.Compiled); |
|||
return regex.Replace(source, |
|||
m => |
|||
{ |
|||
short s; |
|||
if (short.TryParse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InstalledUICulture, |
|||
out s)) |
|||
{ |
|||
return "" + (char)s; |
|||
} |
|||
|
|||
return m.Value; |
|||
}); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将驼峰字符串的第一个字符小写
|
|||
/// </summary>
|
|||
public static string LowerFirstChar(this string str) |
|||
{ |
|||
if (string.IsNullOrEmpty(str) || !char.IsUpper(str[0])) |
|||
{ |
|||
return str; |
|||
} |
|||
|
|||
if (str.Length == 1) |
|||
{ |
|||
return char.ToLower(str[0]).ToString(); |
|||
} |
|||
|
|||
return char.ToLower(str[0]) + str.Substring(1, str.Length - 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将小驼峰字符串的第一个字符大写
|
|||
/// </summary>
|
|||
public static string UpperFirstChar(this string str) |
|||
{ |
|||
if (string.IsNullOrEmpty(str) || !char.IsLower(str[0])) |
|||
{ |
|||
return str; |
|||
} |
|||
|
|||
if (str.Length == 1) |
|||
{ |
|||
return char.ToUpper(str[0]).ToString(); |
|||
} |
|||
|
|||
return char.ToUpper(str[0]) + str.Substring(1, str.Length - 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 计算当前字符串与指定字符串的编辑距离(相似度)
|
|||
/// </summary>
|
|||
/// <param name="source">源字符串</param>
|
|||
/// <param name="target">目标字符串</param>
|
|||
/// <param name="similarity">输出相似度</param>
|
|||
/// <param name="ignoreCase">是否忽略大小写</param>
|
|||
/// <returns>编辑距离</returns>
|
|||
public static int LevenshteinDistance(this string source, string target, out double similarity, |
|||
bool ignoreCase = false) |
|||
{ |
|||
if (string.IsNullOrEmpty(source)) |
|||
{ |
|||
if (string.IsNullOrEmpty(target)) |
|||
{ |
|||
similarity = 1; |
|||
return 0; |
|||
} |
|||
|
|||
similarity = 0; |
|||
return target.Length; |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(target)) |
|||
{ |
|||
similarity = 0; |
|||
return source.Length; |
|||
} |
|||
|
|||
string from, to; |
|||
if (ignoreCase) |
|||
{ |
|||
from = source; |
|||
to = target; |
|||
} |
|||
else |
|||
{ |
|||
from = source.ToLower(); |
|||
to = source.ToLower(); |
|||
} |
|||
|
|||
int m = from.Length, n = to.Length; |
|||
int[,] mn = new int[m + 1, n + 1]; |
|||
for (int i = 0; i <= m; i++) |
|||
{ |
|||
mn[i, 0] = i; |
|||
} |
|||
|
|||
for (int j = 1; j <= n; j++) |
|||
{ |
|||
mn[0, j] = j; |
|||
} |
|||
|
|||
for (int i = 1; i <= m; i++) |
|||
{ |
|||
char c = from[i - 1]; |
|||
for (int j = 1; j <= n; j++) |
|||
{ |
|||
if (c == to[j - 1]) |
|||
{ |
|||
mn[i, j] = mn[i - 1, j - 1]; |
|||
} |
|||
else |
|||
{ |
|||
mn[i, j] = Math.Min(mn[i - 1, j - 1], Math.Min(mn[i - 1, j], mn[i, j - 1])) + 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
int maxLength = Math.Max(m, n); |
|||
similarity = (double)(maxLength - mn[m, n]) / maxLength; |
|||
return mn[m, n]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 计算两个字符串的相似度,应用公式:相似度=kq*q/(kq*q+kr*r+ks*s)(kq>0,kr>=0,ka>=0)
|
|||
/// 其中,q是字符串1和字符串2中都存在的单词的总数,s是字符串1中存在,字符串2中不存在的单词总数,r是字符串2中存在,字符串1中不存在的单词总数. kq,kr和ka分别是q,r,s的权重,根据实际的计算情况,我们设kq=2,kr=ks=1.
|
|||
/// </summary>
|
|||
/// <param name="source">源字符串</param>
|
|||
/// <param name="target">目标字符串</param>
|
|||
/// <param name="ignoreCase">是否忽略大小写</param>
|
|||
/// <returns>字符串相似度</returns>
|
|||
public static double GetSimilarityWith(this string source, string target, bool ignoreCase = false) |
|||
{ |
|||
if (string.IsNullOrEmpty(source) && string.IsNullOrEmpty(target)) |
|||
{ |
|||
return 1; |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(target)) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
const double kq = 2, kr = 1, ks = 1; |
|||
char[] sourceChars = source.ToCharArray(), targetChars = target.ToCharArray(); |
|||
|
|||
//获取交集数量
|
|||
int q = sourceChars.Intersect(targetChars).Count(), s = sourceChars.Length - q, r = targetChars.Length - q; |
|||
return kq * q / (kq * q + kr * r + ks * s); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 标准化Path字符串,将 \\ 转换为 /
|
|||
/// </summary>
|
|||
/// <param name="path">Path字符串</param>
|
|||
public static string NormalizePath(this string path) |
|||
{ |
|||
return path.Replace('\\', '/'); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// (Pascal) 命名法 的字符串 改为 短横线分隔式命名
|
|||
/// 例如UserName => user-name
|
|||
/// </summary>
|
|||
public static string PascalToKebabCase(this string value) |
|||
{ |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
return Regex.Replace( |
|||
value, |
|||
"(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])", |
|||
"-$1", |
|||
RegexOptions.Compiled) |
|||
.Trim() |
|||
.ToLower(); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
@ -0,0 +1,244 @@ |
|||
namespace System.Text; |
|||
|
|||
/// <summary>
|
|||
/// StringBuilder 扩展方法类
|
|||
/// </summary>
|
|||
public static class StringBuilderExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 去除<seealso cref="StringBuilder"/>开头的空格
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <returns>返回修改后的StringBuilder,主要用于链式操作</returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
|
|||
return stringBuilder.TrimStart(' '); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<seealso cref="StringBuilder"/>开头的指定<seealso cref="char"/>
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="c">要去掉的<seealso cref="char"/></param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder, char c) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
|
|||
if (stringBuilder.Length == 0) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
|
|||
while (c.Equals(stringBuilder[0])) |
|||
{ |
|||
stringBuilder.Remove(0, 1); |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<seealso cref="StringBuilder"/>开头的指定字符数组
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="chars">要去掉的字符数组</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder, char[] chars) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
|
|||
return stringBuilder.TrimStart(new string(chars)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>开头的指定的<seealso cref="string"/>
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="str">要去掉的<seealso cref="string"/></param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimStart(this StringBuilder stringBuilder, string str) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
|
|||
if (string.IsNullOrEmpty(str) |
|||
|| stringBuilder.Length == 0 |
|||
|| str.Length > stringBuilder.Length) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
|
|||
while (stringBuilder.SubString(0, str.Length).Equals(str)) |
|||
{ |
|||
stringBuilder.Remove(0, str.Length); |
|||
if (str.Length > stringBuilder.Length) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除StringBuilder结尾的空格
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder">StringBuilder</param>
|
|||
/// <returns>返回修改后的StringBuilder,主要用于链式操作</returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder) |
|||
{ |
|||
return stringBuilder.TrimEnd(' '); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>结尾指定字符
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="c">要去掉的字符</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder, char c) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
if (stringBuilder.Length == 0) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
|
|||
while (c.Equals(stringBuilder[stringBuilder.Length - 1])) |
|||
{ |
|||
stringBuilder.Remove(stringBuilder.Length - 1, 1); |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>结尾指定字符数组
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="chars">要去除的字符数组</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder, char[] chars) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
|
|||
return stringBuilder.TrimEnd(new string(chars)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除<see cref="StringBuilder"/>结尾指定字符串
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="str">要去除的字符串</param>
|
|||
/// <returns></returns>
|
|||
public static StringBuilder TrimEnd(this StringBuilder stringBuilder, string str) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
if (string.IsNullOrEmpty(str) |
|||
|| stringBuilder.Length == 0 |
|||
|| str!.Length > stringBuilder.Length) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
|
|||
while (stringBuilder.SubString(stringBuilder.Length - str.Length, str.Length).Equals(str)) |
|||
{ |
|||
stringBuilder.Remove(stringBuilder.Length - str.Length, str.Length); |
|||
if (stringBuilder.Length < str.Length) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 去除StringBuilder两端的空格
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder">StringBuilder</param>
|
|||
/// <returns>返回修改后的StringBuilder,主要用于链式操作</returns>
|
|||
public static StringBuilder Trim(this StringBuilder stringBuilder) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
|
|||
if (stringBuilder.Length == 0) |
|||
{ |
|||
return stringBuilder; |
|||
} |
|||
|
|||
return stringBuilder.TrimEnd().TrimStart(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 返回<see cref="StringBuilder"/>从起始位置指定长度的字符串
|
|||
/// </summary>
|
|||
/// <param name="stringBuilder"></param>
|
|||
/// <param name="start">起始位置</param>
|
|||
/// <param name="length">长度</param>
|
|||
/// <returns>字符串</returns>
|
|||
/// <exception cref="IndexOutOfRangeException">超出字符串索引长度异常</exception>
|
|||
public static string SubString(this StringBuilder stringBuilder, int start, int length) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
|
|||
if (start + length > stringBuilder.Length) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(); |
|||
} |
|||
|
|||
var cs = new char[length]; |
|||
for (var i = 0; i < length; i++) |
|||
{ |
|||
cs[i] = stringBuilder[start + i]; |
|||
} |
|||
|
|||
return new string(cs); |
|||
} |
|||
|
|||
public static StringBuilder AppendLineWithControlChar(this StringBuilder stringBuilder, StringBuilder sb, string newLine) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
stringBuilder = AppendWithControlChar(stringBuilder, sb.ToString()); |
|||
return stringBuilder.Append(newLine); |
|||
} |
|||
|
|||
public static StringBuilder AppendLineWithControlChar(this StringBuilder stringBuilder, string str, string newLine) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
stringBuilder = AppendWithControlChar(stringBuilder, str); |
|||
return stringBuilder.Append(newLine); |
|||
} |
|||
|
|||
public static StringBuilder AppendWithControlChar(this StringBuilder stringBuilder, StringBuilder sb) |
|||
{ |
|||
if (stringBuilder is null) throw new ArgumentNullException(); |
|||
return AppendWithControlChar(stringBuilder, sb.ToString()); |
|||
} |
|||
|
|||
public static StringBuilder AppendWithControlChar(this StringBuilder stringBuilder, string str) |
|||
{ |
|||
if (str.Contains('\b')) |
|||
{ |
|||
foreach (var c in str) |
|||
{ |
|||
if (c == '\b') |
|||
{ |
|||
stringBuilder.Length--; |
|||
} |
|||
else |
|||
{ |
|||
stringBuilder.Append(c); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
stringBuilder.Append(str); |
|||
} |
|||
|
|||
return stringBuilder; |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
// Global using directives
|
|||
|
|||
global using Lion.AbpPro.Localization; |
|||
global using Volo.Abp.Autofac; |
|||
global using Volo.Abp.Localization; |
|||
global using Volo.Abp.Localization.ExceptionHandling; |
|||
global using Volo.Abp.Modularity; |
|||
global using Volo.Abp.VirtualFileSystem; |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.1</TargetFramework> |
|||
<AssemblyName>Lion.AbpPro.Localization</AssemblyName> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" /> |
|||
<PackageReference Include="Volo.Abp.Autofac" /> |
|||
<PackageReference Include="Volo.Abp.Localization" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="Lion\AbpPro\Localization\Resources\*.json" /> |
|||
<EmbeddedResource Include="Lion\AbpPro\Localization\Resources\*.json" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,19 @@ |
|||
namespace Lion.AbpPro; |
|||
|
|||
public class LionAbpProLocalizationConsts |
|||
{ |
|||
/// <summary>
|
|||
/// 名称空间
|
|||
/// </summary>
|
|||
public const string NameSpace = "Lion.AbpPro"; |
|||
|
|||
/// <summary>
|
|||
/// 默认语言
|
|||
/// </summary>
|
|||
public const string DefaultCultureName = "zh-Hans"; |
|||
|
|||
/// <summary>
|
|||
/// 默认本地化文件虚拟路径
|
|||
/// </summary>
|
|||
public const string DefaultLocalizationResourceVirtualPath = "/Localization/Resources"; |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
namespace Lion.AbpPro; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAutofacModule), |
|||
typeof(AbpLocalizationModule) |
|||
)] |
|||
public class LionAbpProLocalizationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<LionAbpProLocalizationModule>(LionAbpProLocalizationConsts.NameSpace); }); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<LionAbpProLocalizationResource>(LionAbpProLocalizationConsts.DefaultCultureName) |
|||
.AddVirtualJson(LionAbpProLocalizationConsts.DefaultLocalizationResourceVirtualPath); |
|||
|
|||
options.DefaultResourceType = typeof(LionAbpProLocalizationResource); |
|||
}); |
|||
|
|||
Configure<AbpExceptionLocalizationOptions>(options => { options.MapCodeNamespace(LionAbpProLocalizationConsts.NameSpace, typeof(LionAbpProLocalizationResource)); }); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Lion.AbpPro.Localization; |
|||
|
|||
[LocalizationResourceName("LionAbpProLocalizationResource")] |
|||
public class LionAbpProLocalizationResource |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Welcome": "Welcome" |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Welcome": "欢迎" |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
// Global using directives
|
|||
|
|||
global using Shouldly; |
|||
global using Xunit; |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<IsPackable>false</IsPackable> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> |
|||
<PackageReference Include="NSubstitute" /> |
|||
<PackageReference Include="Shouldly" /> |
|||
<PackageReference Include="xunit" /> |
|||
<PackageReference Include="xunit.extensibility.execution" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" /> |
|||
<PackageReference Include="coverlet.collector" /> |
|||
<PackageReference Include="JunitXml.TestLogger" /> |
|||
<PackageReference Include="Volo.Abp.TestBase" /> |
|||
<PackageReference Include="Volo.Abp.Autofac" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Lion.AbpPro.Core\Lion.AbpPro.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Lion.AbpPro.Core |
|||
{ |
|||
public abstract class LionAbpProTestBase : AbpIntegratedTest<LionAbpProTestBaseModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Lion.AbpPro.Core |
|||
{ |
|||
[DependsOn(typeof(AbpTestBaseModule), |
|||
typeof(AbpAutofacModule))] |
|||
public class LionAbpProTestBaseModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
namespace System; |
|||
|
|||
public class BooleanExtensionsTests |
|||
{ |
|||
[Fact] |
|||
public void ToLowerTest() |
|||
{ |
|||
true.ToLower().ShouldBe("true"); |
|||
false.ToLower().ShouldBe("false"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TrueThrowTest() |
|||
{ |
|||
Should.Throw<Exception>(() => |
|||
{ |
|||
true.TrueThrow(new Exception()); |
|||
}); |
|||
|
|||
false.TrueThrow(new Exception()); |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
namespace System.Collections.Generic; |
|||
|
|||
public class CollectionExtensionsTests |
|||
{ |
|||
[Fact] |
|||
public void AddIfNotExistTest() |
|||
{ |
|||
var numbers = new List<int>() { 1, 2, 3 }; |
|||
numbers.AddIfNotExist(2); |
|||
numbers.Count(m => m == 2).ShouldBe(1); |
|||
numbers.AddIfNotExist(5); |
|||
numbers.Count(m => m == 5).ShouldBe(1); |
|||
|
|||
numbers = null; |
|||
Should.Throw<ArgumentNullException>(() => |
|||
{ |
|||
numbers.AddIfNotExist(3); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddIfNotNullTest() |
|||
{ |
|||
var strings = new List<string>() { "abc", "bcd", "cde" }; |
|||
strings.AddIfNotNull(null); |
|||
strings.Count.ShouldBe(3); |
|||
strings.AddIfNotNull("abc"); |
|||
strings.Count.ShouldBe(4); |
|||
|
|||
strings = null; |
|||
Should.Throw<ArgumentNullException>(() => |
|||
{ |
|||
strings.AddIfNotNull("abc"); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
namespace System; |
|||
|
|||
public class DateTimeExtensionsTests |
|||
{ |
|||
[Fact] |
|||
public void IsWeekendTest() |
|||
{ |
|||
var dt = new DateTime(2021, 4, 24); |
|||
dt.IsWeekend().ShouldBeTrue(); |
|||
dt = new DateTime(2021, 4, 25); |
|||
dt.IsWeekend().ShouldBeTrue(); |
|||
for (var i = 1; i <= 5; i++) |
|||
{ |
|||
dt = new DateTime(2021, 4, 25 + i); |
|||
dt.IsWeekend().ShouldBeFalse(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void IsWeekdayTest() |
|||
{ |
|||
var dt = new DateTime(2021, 4, 24); |
|||
dt.IsWeekday().ShouldBeFalse(); |
|||
dt = new DateTime(2021, 4, 25); |
|||
dt.IsWeekday().ShouldBeFalse(); |
|||
for (var i = 1; i <= 5; i++) |
|||
{ |
|||
dt = new DateTime(2021, 4, 25 + i); |
|||
dt.IsWeekday().ShouldBeTrue(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void ToUniqueStringTest() |
|||
{ |
|||
var dt = new DateTime(2021, 4, 24,21,30,23); |
|||
dt.ToUniqueString().ShouldBe("202111477423"); |
|||
dt = dt.AddMilliseconds(-1); |
|||
dt.ToUniqueString(true).ShouldBe("202111477422999"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ToYyyyMMddTest() |
|||
{ |
|||
var dt1 = new DateTime(2021, 9, 1); |
|||
dt1.ToYyyyMmDd().ShouldBe(20210901); |
|||
|
|||
var dt2 = new DateTime(2020, 12, 31); |
|||
dt2.ToYyyyMmDd().ShouldBe(20201231); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ToYyyyMMTest() |
|||
{ |
|||
var dt1 = new DateTime(2021, 9, 6); |
|||
dt1.ToYyyyMm().ShouldBe(202109); |
|||
|
|||
var dt2 = new DateTime(2020, 12, 31); |
|||
dt2.ToYyyyMm().ShouldBe(202012); |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
namespace System; |
|||
|
|||
public class DecimalExtensionsTest |
|||
{ |
|||
[Fact] |
|||
public void TrimEndZero() |
|||
{ |
|||
var d1 = 0.1M; |
|||
var d2 = 1M; |
|||
var d3 = 1.001M; |
|||
var d4 = 1.0000M; |
|||
var d5 = 0.1010M; |
|||
var result1 = d1.TrimEndZero(); |
|||
var result2 = d2.TrimEndZero(); |
|||
var result3 = d3.TrimEndZero(); |
|||
var result4 = d4.TrimEndZero(); |
|||
var result5 = d5.TrimEndZero(); |
|||
|
|||
result1.ToString().ShouldBe("0.1"); |
|||
result2.ToString().ShouldBe("1"); |
|||
result3.ToString().ShouldBe("1.001"); |
|||
result4.ToString().ShouldBe("1"); |
|||
result5.ToString().ShouldBe("0.101"); |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace System.Reflection; |
|||
|
|||
public class AssemblyExtensionsTests |
|||
{ |
|||
[Fact] |
|||
public void GetFileVersionTest() |
|||
{ |
|||
var assembly = typeof(AssemblyExtensionsTests).Assembly; |
|||
assembly.GetFileVersion().ShouldBe("1.0.0.0"); |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
namespace System.Text; |
|||
|
|||
public class StringBuilderExtensionsTest |
|||
{ |
|||
[Fact] |
|||
public void TrimTest() |
|||
{ |
|||
var sb = new StringBuilder(" hello world "); |
|||
sb.Trim().ToString().ShouldBe("hello world"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TrimStartTest() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
sb.TrimStart('a').ToString().ShouldBe(string.Empty); |
|||
|
|||
sb.Append("asdfgef"); |
|||
sb.TrimStart('a').ToString().ShouldBe("sdfgef"); |
|||
|
|||
sb.Insert(0, " "); |
|||
sb.TrimStart().ToString().ShouldBe("sdfgef"); |
|||
|
|||
sb.TrimStart("sdf").ToString().ShouldBe("gef"); |
|||
|
|||
sb.TrimStart("gef").ToString().ShouldBe(string.Empty); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TrimEndTest() |
|||
{ |
|||
var sb = new StringBuilder("asdfgef"); |
|||
|
|||
sb.TrimEnd((string)null).ToString().ShouldBe("asdfgef"); |
|||
|
|||
sb.TrimEnd('a').ToString().ShouldBe("asdfgef"); |
|||
|
|||
sb.TrimEnd('f').ToString().ShouldBe("asdfge"); |
|||
|
|||
sb.Append(" "); |
|||
sb.TrimEnd().ToString().ShouldBe("asdfge"); |
|||
|
|||
sb.TrimEnd(new[] { 'g', 'e' }).ToString().ShouldBe("asdf"); |
|||
sb.TrimEnd("asdf").ToString().ShouldBe(string.Empty); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SubStringTest() |
|||
{ |
|||
var sb = new StringBuilder("asdfgef"); |
|||
Should.Throw<ArgumentOutOfRangeException>(() => |
|||
{ |
|||
sb.SubString(0, 8); |
|||
}); |
|||
sb.SubString(0, 3).ToString().ShouldBe("asd"); |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<IsPackable>false</IsPackable> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> |
|||
<PackageReference Include="NSubstitute" /> |
|||
<PackageReference Include="Shouldly" /> |
|||
<PackageReference Include="xunit" /> |
|||
<PackageReference Include="xunit.extensibility.execution" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" /> |
|||
<PackageReference Include="coverlet.collector" /> |
|||
<PackageReference Include="JunitXml.TestLogger" /> |
|||
<PackageReference Include="Volo.Abp.TestBase" /> |
|||
</ItemGroup> |
|||
|
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Lion.AbpPro.Localization\Lion.AbpPro.Localization.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
|
|||
</Project> |
|||
@ -0,0 +1,14 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Lion.AbpPro |
|||
{ |
|||
|
|||
public abstract class LionAbpProLocalizationTestBase : AbpIntegratedTest<LionAbpProLocalizationTestBaseModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Lion.AbpPro |
|||
{ |
|||
|
|||
[DependsOn(typeof(LionAbpProLocalizationModule))] |
|||
[DependsOn(typeof(AbpTestBaseModule))] |
|||
public class LionAbpProLocalizationTestBaseModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using Lion.AbpPro.Localization; |
|||
using Microsoft.Extensions.Localization; |
|||
using Shouldly; |
|||
using Volo.Abp.Localization; |
|||
using Xunit; |
|||
|
|||
namespace Lion.AbpPro |
|||
{ |
|||
public sealed class LionAbpProLocalizationTests : LionAbpProLocalizationTestBase |
|||
{ |
|||
private readonly IStringLocalizer<LionAbpProLocalizationResource> _stringLocalizer; |
|||
|
|||
public LionAbpProLocalizationTests() |
|||
{ |
|||
_stringLocalizer = GetRequiredService<IStringLocalizer<LionAbpProLocalizationResource>>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Test() |
|||
{ |
|||
using (CultureHelper.Use("en")) |
|||
{ |
|||
_stringLocalizer["Welcome"].Value |
|||
.ShouldBe("Welcome"); |
|||
} |
|||
|
|||
using (CultureHelper.Use("zh-Hans")) |
|||
{ |
|||
_stringLocalizer["Welcome"].Value |
|||
.ShouldBe("欢迎"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,87 +1,84 @@ |
|||
using Lion.AbpPro.BasicManagement.Roles.Dtos; |
|||
namespace Lion.AbpPro.BasicManagement.Roles; |
|||
|
|||
namespace Lion.AbpPro.BasicManagement.Roles |
|||
[Authorize] |
|||
public class RoleAppService : BasicManagementAppService, IRoleAppService |
|||
{ |
|||
[Authorize] |
|||
public class RoleAppService : BasicManagementAppService, IRoleAppService |
|||
{ |
|||
private readonly IIdentityRoleAppService _identityRoleAppService; |
|||
private readonly IIdentityRoleAppService _identityRoleAppService; |
|||
|
|||
private readonly IIdentityRoleRepository _roleRepository; |
|||
private readonly IIdentityRoleRepository _roleRepository; |
|||
|
|||
public RoleAppService( |
|||
IIdentityRoleAppService identityRoleAppService, |
|||
IIdentityRoleRepository roleRepository) |
|||
{ |
|||
_identityRoleAppService = identityRoleAppService; |
|||
public RoleAppService( |
|||
IIdentityRoleAppService identityRoleAppService, |
|||
IIdentityRoleRepository roleRepository) |
|||
{ |
|||
_identityRoleAppService = identityRoleAppService; |
|||
|
|||
_roleRepository = roleRepository; |
|||
} |
|||
_roleRepository = roleRepository; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取所有角色
|
|||
/// </summary>
|
|||
/// <summary>
|
|||
/// 获取所有角色
|
|||
/// </summary>
|
|||
|
|||
public async Task<ListResultDto<IdentityRoleDto>> AllListAsync() |
|||
{ |
|||
List<IdentityRole> source = |
|||
await _roleRepository.GetListAsync() |
|||
.ConfigureAwait(continueOnCapturedContext: false); |
|||
return new ListResultDto<IdentityRoleDto>( |
|||
ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(source)); |
|||
} |
|||
public async Task<ListResultDto<IdentityRoleDto>> AllListAsync() |
|||
{ |
|||
List<IdentityRole> source = |
|||
await _roleRepository.GetListAsync() |
|||
.ConfigureAwait(continueOnCapturedContext: false); |
|||
return new ListResultDto<IdentityRoleDto>( |
|||
ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(source)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 分页查询角色
|
|||
/// </summary>
|
|||
/// <param name="input"></param>
|
|||
/// <returns></returns>
|
|||
public async Task<PagedResultDto<IdentityRoleDto>> ListAsync(PagingRoleListInput input) |
|||
/// <summary>
|
|||
/// 分页查询角色
|
|||
/// </summary>
|
|||
/// <param name="input"></param>
|
|||
/// <returns></returns>
|
|||
public async Task<PagedResultDto<IdentityRoleDto>> ListAsync(PagingRoleListInput input) |
|||
{ |
|||
var request = new GetIdentityRolesInput |
|||
{ |
|||
var request = new GetIdentityRolesInput |
|||
{ |
|||
Filter = input.Filter?.Trim(), MaxResultCount = input.PageSize, |
|||
SkipCount = input.SkipCount |
|||
}; |
|||
List<IdentityRole> list = await _roleRepository |
|||
.GetListAsync(request.Sorting, request.MaxResultCount, request.SkipCount, |
|||
request.Filter) |
|||
.ConfigureAwait(continueOnCapturedContext: false); |
|||
return new PagedResultDto<IdentityRoleDto>( |
|||
await _roleRepository.GetCountAsync(request.Filter) |
|||
.ConfigureAwait(continueOnCapturedContext: false), |
|||
ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(list)); |
|||
} |
|||
Filter = input.Filter?.Trim(), MaxResultCount = input.PageSize, |
|||
SkipCount = input.SkipCount |
|||
}; |
|||
List<IdentityRole> list = await _roleRepository |
|||
.GetListAsync(request.Sorting, request.MaxResultCount, request.SkipCount, |
|||
request.Filter) |
|||
.ConfigureAwait(continueOnCapturedContext: false); |
|||
return new PagedResultDto<IdentityRoleDto>( |
|||
await _roleRepository.GetCountAsync(request.Filter) |
|||
.ConfigureAwait(continueOnCapturedContext: false), |
|||
ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(list)); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 创建角色
|
|||
/// </summary>
|
|||
/// <param name="input"></param>
|
|||
/// <returns></returns>
|
|||
[Authorize(IdentityPermissions.Roles.Create)] |
|||
public async Task<IdentityRoleDto> CreateAsync(IdentityRoleCreateDto input) |
|||
{ |
|||
return await _identityRoleAppService.CreateAsync(input); |
|||
} |
|||
/// <summary>
|
|||
/// 创建角色
|
|||
/// </summary>
|
|||
/// <param name="input"></param>
|
|||
/// <returns></returns>
|
|||
[Authorize(IdentityPermissions.Roles.Create)] |
|||
public async Task<IdentityRoleDto> CreateAsync(IdentityRoleCreateDto input) |
|||
{ |
|||
return await _identityRoleAppService.CreateAsync(input); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 更新角色
|
|||
/// </summary>
|
|||
[Authorize(IdentityPermissions.Roles.Update)] |
|||
public async Task<IdentityRoleDto> UpdateAsync(UpdateRoleInput input) |
|||
{ |
|||
return await _identityRoleAppService.UpdateAsync(input.RoleId, input.RoleInfo); |
|||
} |
|||
/// <summary>
|
|||
/// 更新角色
|
|||
/// </summary>
|
|||
[Authorize(IdentityPermissions.Roles.Update)] |
|||
public async Task<IdentityRoleDto> UpdateAsync(UpdateRoleInput input) |
|||
{ |
|||
return await _identityRoleAppService.UpdateAsync(input.RoleId, input.RoleInfo); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 删除角色
|
|||
/// </summary>
|
|||
[Authorize(IdentityPermissions.Roles.Delete)] |
|||
public async Task DeleteAsync(IdInput input) |
|||
{ |
|||
await _identityRoleAppService.DeleteAsync(input.Id); |
|||
} |
|||
/// <summary>
|
|||
/// 删除角色
|
|||
/// </summary>
|
|||
[Authorize(IdentityPermissions.Roles.Delete)] |
|||
public async Task DeleteAsync(IdInput input) |
|||
{ |
|||
await _identityRoleAppService.DeleteAsync(input.Id); |
|||
} |
|||
} |
|||
@ -1,116 +1,115 @@ |
|||
namespace Lion.AbpPro.DataDictionaryManagement.DataDictionaries.Aggregates |
|||
namespace Lion.AbpPro.DataDictionaryManagement.DataDictionaries.Aggregates; |
|||
|
|||
/// <summary>
|
|||
/// 数据字典
|
|||
/// </summary>
|
|||
public class DataDictionary : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
/// <summary>
|
|||
/// 数据字典
|
|||
/// 租户id
|
|||
/// </summary>
|
|||
public class DataDictionary : FullAuditedAggregateRoot<Guid>, IMultiTenant |
|||
{ |
|||
/// <summary>
|
|||
/// 租户id
|
|||
/// </summary>
|
|||
public Guid? TenantId { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// 字典编码
|
|||
/// </summary>
|
|||
[Required] |
|||
[MaxLength(DataDictionaryMaxLengths.Code)] |
|||
public string Code { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// 显示名
|
|||
/// </summary>
|
|||
[Required] |
|||
[MaxLength(DataDictionaryMaxLengths.DisplayText)] |
|||
public string DisplayText { get; private set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 描述
|
|||
/// </summary>
|
|||
[Required] |
|||
[MaxLength(DataDictionaryMaxLengths.Description)] |
|||
public string Description { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// 字典明细集合
|
|||
/// </summary>
|
|||
public List<DataDictionaryDetail> Details { get; private set; } |
|||
|
|||
private DataDictionary() |
|||
{ |
|||
Details = new List<DataDictionaryDetail>(); |
|||
} |
|||
public Guid? TenantId { get; private set; } |
|||
|
|||
public DataDictionary( |
|||
Guid id, |
|||
string code, |
|||
string displayText, |
|||
string description = null, |
|||
Guid? tenantId = null) : base(id) |
|||
{ |
|||
SetProperties(code, displayText, description, tenantId); |
|||
Details = new List<DataDictionaryDetail>(); |
|||
} |
|||
/// <summary>
|
|||
/// 字典编码
|
|||
/// </summary>
|
|||
[Required] |
|||
[MaxLength(DataDictionaryMaxLengths.Code)] |
|||
public string Code { get; private set; } |
|||
|
|||
private void SetProperties(string code, string displayText, string description, Guid? tenantId) |
|||
{ |
|||
SetCode(code); |
|||
SetDisplayText(displayText); |
|||
SetDescription(description); |
|||
SetTenantId(tenantId); |
|||
} |
|||
/// <summary>
|
|||
/// 显示名
|
|||
/// </summary>
|
|||
[Required] |
|||
[MaxLength(DataDictionaryMaxLengths.DisplayText)] |
|||
public string DisplayText { get; private set; } |
|||
|
|||
public void SetTenantId(Guid? tenantId) |
|||
{ |
|||
TenantId = tenantId; |
|||
} |
|||
|
|||
public void SetCode(string code) |
|||
{ |
|||
Guard.NotNullOrWhiteSpace(code, nameof(code), DataDictionaryMaxLengths.Code); |
|||
Code = code; |
|||
} |
|||
/// <summary>
|
|||
/// 描述
|
|||
/// </summary>
|
|||
[Required] |
|||
[MaxLength(DataDictionaryMaxLengths.Description)] |
|||
public string Description { get; private set; } |
|||
|
|||
private void SetDisplayText(string displayText) |
|||
{ |
|||
Guard.NotNullOrWhiteSpace(displayText, nameof(displayText), DataDictionaryMaxLengths.DisplayText); |
|||
DisplayText = displayText; |
|||
} |
|||
/// <summary>
|
|||
/// 字典明细集合
|
|||
/// </summary>
|
|||
public List<DataDictionaryDetail> Details { get; private set; } |
|||
|
|||
private void SetDescription(string description) |
|||
{ |
|||
Guard.Length(description, nameof(description), DataDictionaryMaxLengths.Description); |
|||
Description = description ?? string.Empty; |
|||
} |
|||
private DataDictionary() |
|||
{ |
|||
Details = new List<DataDictionaryDetail>(); |
|||
} |
|||
|
|||
public void AddDetail(Guid dataDictionayDetailId, string code, string displayText, int order = 1, |
|||
string description = "", bool isEnabled = true) |
|||
{ |
|||
if (Details.Any(e => e.Code == code.Trim())) |
|||
{ |
|||
throw new DataDictionaryDomainException(message: "数据字典项已存在"); |
|||
} |
|||
public DataDictionary( |
|||
Guid id, |
|||
string code, |
|||
string displayText, |
|||
string description = null, |
|||
Guid? tenantId = null) : base(id) |
|||
{ |
|||
SetProperties(code, displayText, description, tenantId); |
|||
Details = new List<DataDictionaryDetail>(); |
|||
} |
|||
|
|||
Details.Add(new DataDictionaryDetail(dataDictionayDetailId, Id, code, displayText, order, isEnabled, |
|||
description)); |
|||
} |
|||
private void SetProperties(string code, string displayText, string description, Guid? tenantId) |
|||
{ |
|||
SetCode(code); |
|||
SetDisplayText(displayText); |
|||
SetDescription(description); |
|||
SetTenantId(tenantId); |
|||
} |
|||
|
|||
public void RemoveDetail(string detailCode) |
|||
{ |
|||
var detail = Details.FirstOrDefault(item => item.Code == detailCode); |
|||
if (null == detail) |
|||
{ |
|||
throw new DataDictionaryDomainException(message: "数据字典项不存在"); |
|||
} |
|||
public void SetTenantId(Guid? tenantId) |
|||
{ |
|||
TenantId = tenantId; |
|||
} |
|||
|
|||
public void SetCode(string code) |
|||
{ |
|||
Guard.NotNullOrWhiteSpace(code, nameof(code), DataDictionaryMaxLengths.Code); |
|||
Code = code; |
|||
} |
|||
|
|||
private void SetDisplayText(string displayText) |
|||
{ |
|||
Guard.NotNullOrWhiteSpace(displayText, nameof(displayText), DataDictionaryMaxLengths.DisplayText); |
|||
DisplayText = displayText; |
|||
} |
|||
|
|||
private void SetDescription(string description) |
|||
{ |
|||
Guard.Length(description, nameof(description), DataDictionaryMaxLengths.Description); |
|||
Description = description ?? string.Empty; |
|||
} |
|||
|
|||
Details.Remove(detail); |
|||
public void AddDetail(Guid dataDictionayDetailId, string code, string displayText, int order = 1, |
|||
string description = "", bool isEnabled = true) |
|||
{ |
|||
if (Details.Any(e => e.Code == code.Trim())) |
|||
{ |
|||
throw new DataDictionaryDomainException(message: "数据字典项已存在"); |
|||
} |
|||
|
|||
public void Update(Guid dataDictionayDetailId,string displayText,string description) |
|||
Details.Add(new DataDictionaryDetail(dataDictionayDetailId, Id, code, displayText, order, isEnabled, |
|||
description)); |
|||
} |
|||
|
|||
public void RemoveDetail(string detailCode) |
|||
{ |
|||
var detail = Details.FirstOrDefault(item => item.Code == detailCode); |
|||
if (null == detail) |
|||
{ |
|||
SetDescription(description); |
|||
SetDisplayText(displayText); |
|||
throw new DataDictionaryDomainException(message: "数据字典项不存在"); |
|||
} |
|||
|
|||
Details.Remove(detail); |
|||
} |
|||
|
|||
public void Update(Guid dataDictionayDetailId,string displayText,string description) |
|||
{ |
|||
SetDescription(description); |
|||
SetDisplayText(displayText); |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue