├── .gitignore ├── Demos ├── Servers │ ├── OrderServer │ │ ├── OrderServer.csproj │ │ └── OrderServicesBuilderOptionsExtension.cs │ ├── TicketServer │ │ ├── TicketServer.csproj │ │ └── TicketServicesBuilderOptionsExtension.cs │ └── VipCardServer │ │ ├── VipCardServer.csproj │ │ └── VipCardServicesBuilderOptionsExtension.cs ├── Xpress.Demo.Api │ ├── Controllers │ │ └── ValuesController.cs │ ├── Filters │ │ └── JobAuthorizationFilter.cs │ ├── Models │ │ └── TestCacheItem.cs │ ├── NLog.Exceptionless.config │ ├── NLog.config │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── Xpress.Demo.Api.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ ├── favicon.ico │ │ ├── swagger │ │ └── webpack.config.js │ │ └── webpack.config.js ├── Xpress.Demo.Application │ ├── ApplicationServicesBuilderOptionsExtension.cs │ ├── BackgroundJobs │ │ ├── TestBackGroundJob.cs │ │ └── TestBackgroundEventArgs.cs │ ├── CapSubscribers │ │ └── CapEventSubscriber.cs │ ├── DemoService.cs │ ├── IDemoService.cs │ ├── LocalEventHandlers │ │ └── TestLocalEventHandler.cs │ ├── RecurringJobs │ │ └── TestRecurringJob.cs │ └── Xpress.Demo.Application.csproj ├── Xpress.Demo.Core │ ├── CoreServicesBuilderOptionsExtension.cs │ ├── Events │ │ ├── TestCapEvent.cs │ │ └── TestLocalEvent.cs │ └── Xpress.Demo.Core.csproj └── Xpress.Demo.EntityFramework │ ├── DemoDbContext.cs │ └── Xpress.Demo.EntityFramework.csproj ├── README.md ├── Source ├── Xpress.AspNetCore │ ├── AspNetCoreServicesBuilderOptionsExtension.cs │ ├── ExceptionHandling │ │ ├── DefaultExceptionToErrorInfoConverter.cs │ │ ├── DefaultHttpExceptionStatusCodeFinder.cs │ │ ├── ExceptionHttpStatusCodeOptions.cs │ │ ├── IExceptionToErrorInfoConverter.cs │ │ ├── IHttpExceptionStatusCodeFinder.cs │ │ └── XpressExceptionFilter.cs │ ├── Extensions │ │ └── ActionDescriptorExtensions.cs │ ├── ModelValidation │ │ ├── IModelStateValidator.cs │ │ ├── IXpressValidationResult.cs │ │ ├── ModelStateValidator.cs │ │ ├── XpressModelValidationActionFilter.cs │ │ └── XpressValidationResult.cs │ ├── Runtime │ │ ├── HttpContextCancellationTokenProvider.cs │ │ └── HttpContextCurrentPrincipalAccessor.cs │ ├── Uow │ │ ├── UnitOfWorkMiddleware.cs │ │ └── UowActionFilter.cs │ ├── Utils │ │ └── ActionResultHelper.cs │ └── Xpress.AspNetCore.csproj ├── Xpress.AutoMapper │ ├── AutoMapAttribute.cs │ ├── AutoMapAttributeBase.cs │ ├── AutoMapExtensions.cs │ ├── AutoMapFromAttribute.cs │ ├── AutoMapToAttribute.cs │ ├── AutoMapperAppBuilderOptionsExtension.cs │ ├── AutoMapperObjectMapper.cs │ ├── IObjectMapper.cs │ └── Xpress.AutoMapper.csproj ├── Xpress.Autofac │ ├── AutofacIocRegister.cs │ ├── AutofacIocResolver.cs │ ├── AutofacServicesBuilderOptionsExtension.cs │ ├── RegistrationBuilderExtensions.cs │ └── Xpress.Autofac.csproj ├── Xpress.CastleWindsor │ ├── ComponentRegistrationExtensions.cs │ ├── LocalLifetimeScopeAccessor.cs │ ├── ThreadSafeDefaultLifetimeScope.cs │ ├── WindsorIocRegister.cs │ ├── WindsorIocResolver.cs │ ├── WindsorServicesBuilderExtension.cs │ └── Xpress.CastleWindsor.csproj └── Xpress.Core │ ├── AppBuilderOptions.cs │ ├── Application │ ├── ApplicationService.cs │ └── IApplicationService.cs │ ├── BackgroundJobs │ ├── BackgroundEventArgsBase.cs │ ├── BackgroundJobArgsHelper.cs │ ├── BackgroundJobConfiguration.cs │ ├── BackgroundJobExecuter.cs │ ├── BackgroundJobExecutionException.cs │ ├── BackgroundJobManagerExtensions.cs │ ├── BackgroundJobNameAttribute.cs │ ├── BackgroundJobOptions.cs │ ├── BackgroundJobPriority.cs │ ├── HangfireBackgroundJobManager.cs │ ├── HangfireJobExecutionAdapter.cs │ ├── IBackgroundEventArgs.cs │ ├── IBackgroundJob.cs │ ├── IBackgroundJobExecuter.cs │ ├── IBackgroundJobManager.cs │ ├── IBackgroundJobNameProvider.cs │ ├── JobExecutionContext.cs │ └── NullBackgroundJobManager.cs │ ├── Caching │ ├── CacheNameAttribute.cs │ ├── DistributedCache.cs │ └── IDistributedCache.cs │ ├── Collections │ ├── ITypeList.cs │ └── TypeList.cs │ ├── DependencyInjection │ ├── DependencyLifeStyle.cs │ ├── IIocResolver.cs │ ├── IScopedDependency.cs │ ├── IServiceProviderAccessor.cs │ ├── ISingletonDependency.cs │ ├── ITransientDependency.cs │ ├── InterceptorBase.cs │ ├── IocRegisterBase.cs │ └── IocResolverBase.cs │ ├── Domain │ ├── Aggregates │ │ ├── AggregateEvent.cs │ │ ├── AggregateName.cs │ │ ├── AggregateNameAttribute.cs │ │ ├── AggregateRoot.cs │ │ ├── AggregateState.cs │ │ ├── IAggregateEvent.cs │ │ ├── IAggregateName.cs │ │ ├── IAggregateRoot.cs │ │ ├── IEmit.cs │ │ └── IEventApplier.cs │ ├── Entities │ │ ├── Entity.cs │ │ ├── IEntity.cs │ │ ├── IHasCreationTime.cs │ │ ├── IHasDeletionTime.cs │ │ ├── IHasModificationTime.cs │ │ ├── IPassivable.cs │ │ └── ISoftDelete.cs │ └── Services │ │ ├── DomainService.cs │ │ └── IDomainService.cs │ ├── Dtos │ ├── DisplayItem.cs │ ├── DisplayProperty.cs │ ├── EntityDto.cs │ ├── ErrorResult.cs │ ├── IEntityDto.cs │ ├── IPagingResult.cs │ ├── IResult.cs │ ├── PagingResult.cs │ ├── Result.cs │ └── ResultCode.cs │ ├── EntityFramework │ ├── EfDbContextBase.cs │ ├── EfDbContextProvider.cs │ ├── EfRepositoryBaseOfTEntityAndTPrimaryKey.cs │ ├── IEfDbContextProvider.cs │ └── RepositoryExtensions.cs │ ├── EventBus │ ├── Cap │ │ ├── CapEventPublisher.cs │ │ ├── CapTransactionExtensions.cs │ │ ├── DbContextBasedCapTransaction.cs │ │ ├── ICapEventPublisher.cs │ │ ├── ICapEventSubscriber.cs │ │ └── NullCapPublisher.cs │ ├── Distributed │ │ ├── IDistributedEventBus.cs │ │ ├── IDistributedEventHandler.cs │ │ └── NullDistributedEventBus.cs │ ├── EventBase.cs │ ├── EventNameAttribute.cs │ ├── GenericEventNameAttribute.cs │ ├── IEvent.cs │ ├── IEventNameProvider.cs │ └── Local │ │ ├── ActionEventHandler.cs │ │ ├── EventHandlerDisposeWrapper.cs │ │ ├── EventHandlerFactoryUnregistrar.cs │ │ ├── IEventDataWithInheritableGenericArgument.cs │ │ ├── IEventHandlerDisposeWrapper.cs │ │ ├── IEventHandlerFactory.cs │ │ ├── ILocalEventBus.cs │ │ ├── ILocalEventHandler.cs │ │ ├── ILocalEventPublisher.cs │ │ ├── ILocalEventSubscriber.cs │ │ ├── IocEventHandlerFactory.cs │ │ ├── LocalEventBus.cs │ │ ├── LocalEventBusOptions.cs │ │ ├── NullLocalEventBus.cs │ │ ├── SingleInstanceHandlerFactory.cs │ │ └── TransientEventHandlerFactory.cs │ ├── Exceptions │ ├── BusinessException.cs │ ├── EntityNotFoundException.cs │ ├── HttpRequestExtensions.cs │ ├── IBusinessException.cs │ ├── IUserFriendlyException.cs │ ├── OptimisticConcurrencyException.cs │ ├── UserFriendlyException.cs │ ├── XpressAuthorizationException.cs │ ├── XpressException.cs │ └── XpressValidationException.cs │ ├── Extensions │ ├── CancellationTokenProviderExtensions.cs │ ├── CollectionExtensions.cs │ ├── ComparisonOperator.cs │ ├── DictionaryExtensions.cs │ ├── EnumerableExtensions.cs │ ├── EventHandlerExtensions.cs │ ├── ExceptionExtensions.cs │ ├── ExpressionExtensions.cs │ ├── LockExtensions.cs │ ├── LoggerExtensions.cs │ ├── ObjectExtensions.cs │ ├── QueryableExtensions.cs │ ├── StringExtensions.cs │ └── TypeExtensions.cs │ ├── Http │ ├── MimeTypes.cs │ ├── RemoteServiceErrorInfo.cs │ ├── RemoteServiceErrorResponse.cs │ ├── RemoteServiceValidationErrorInfo.cs │ └── XpressHttpConsts.cs │ ├── IHasErrorCode.cs │ ├── IHasErrorDetails.cs │ ├── IHasLogLevel.cs │ ├── IHasValidationErrors.cs │ ├── Identity │ ├── GuidProvider.cs │ ├── IGuidGenerator.cs │ ├── IIdentity.cs │ ├── Identity.cs │ ├── SequentialGuidGenerator.cs │ └── SequentialGuidType.cs │ ├── NullDisposable.cs │ ├── Queries │ ├── IPagingInfo.cs │ ├── IPagingQuery.cs │ ├── IPagingSortInfo.cs │ ├── IQuery.cs │ ├── ISortInfo.cs │ ├── PagingInfo.cs │ ├── PagingQuery.cs │ ├── PagingSortInfo.cs │ ├── Query.cs │ ├── QueryAttribute.cs │ ├── QueryExtensions.cs │ └── SortQuery.cs │ ├── RecurringJobs │ ├── IReccuringJobManager.cs │ └── ReccuringJobManager.cs │ ├── Repositories │ ├── AbsRepositoryBase.cs │ ├── IRepository.cs │ └── IRepositoryOfTEntityAndTPrimaryKey.cs │ ├── RetryStrategies │ ├── IOptimisticConcurrencyRetryStrategy.cs │ ├── IRetryStrategy.cs │ ├── OptimisticConcurrencyRetryOption.cs │ ├── OptimisticConcurrencyRetryStrategy.cs │ └── Retry.cs │ ├── Runtime │ ├── ClaimsExtensions.cs │ ├── ClaimsUserSession.cs │ ├── ICurrentPrincipalAccessor.cs │ ├── IUserSession.cs │ ├── NullUserSession.cs │ ├── ThreadCurrentPrincipalAccessor.cs │ └── XpressClaimTypes.cs │ ├── Serialization │ ├── BinarySerializationHelper.cs │ ├── DefaultObjectSerializer.cs │ ├── IJsonSerializer.cs │ ├── IObjectSerializer.cs │ ├── JsonIsoDateTimeConverter.cs │ └── NewtonsoftJsonSerializer.cs │ ├── ServicesBuilderOptions.cs │ ├── Specifications │ ├── AllSpecifications.cs │ ├── AndSpeficication.cs │ ├── AtLeastSpecification.cs │ ├── ISpecification.cs │ ├── NotSpecification.cs │ ├── OrSpecification.cs │ └── Specification.cs │ ├── Threading │ ├── AsyncHelper.cs │ ├── AsyncLocalObjectProvider.cs │ ├── IAsyncLocalObjectProvider.cs │ ├── ICancellationTokenProvider.cs │ ├── InternalAsyncHelper.cs │ └── NullCancellationTokenProvider.cs │ ├── Timing │ ├── Clock.cs │ ├── ClockOptions.cs │ └── IClock.cs │ ├── Uow │ ├── ChildUnitOfWork.cs │ ├── DefaultUnitOfWorkOptions.cs │ ├── ISupportsRollback.cs │ ├── ISupportsSavingChanges.cs │ ├── IUnitOfWork.cs │ ├── IUnitOfWorkAccessor.cs │ ├── IUnitOfWorkEnabled.cs │ ├── IUnitOfWorkManager.cs │ ├── IUnitOfWorkOptions.cs │ ├── UnitOfWork.cs │ ├── UnitOfWorkAccessor.cs │ ├── UnitOfWorkAttribute.cs │ ├── UnitOfWorkEventArgs.cs │ ├── UnitOfWorkExtensions.cs │ ├── UnitOfWorkFailedEventArgs.cs │ ├── UnitOfWorkHelper.cs │ ├── UnitOfWorkInterceptor.cs │ ├── UnitOfWorkManager.cs │ ├── UnitOfWorkOptions.cs │ └── UnitOfWorkTransactionBehavior.cs │ ├── Utils │ ├── CastleProxyHelper.cs │ ├── Check.cs │ ├── EntityHelper.cs │ ├── EnumHelper.cs │ ├── ReflectionHelper.cs │ └── XpressAsyncHelper.cs │ ├── ValueObjects │ ├── ISingleValueObject.cs │ ├── SingleValueObject.cs │ ├── SingleValueObjectConverter.cs │ └── ValueObject.cs │ ├── Xpress.Core.csproj │ ├── XpressAppBuilderExtensions.cs │ └── XpressServiceCollectionExtensions.cs └── Xpress.sln /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #ignore thumbnails created by windows 3 | Thumbs.db 4 | #Ignore files build by Visual Studio 5 | *.obj 6 | *.exe 7 | *.pdb 8 | *.user 9 | *.aps 10 | *.pch 11 | *.vspscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.cache 20 | *.ilk 21 | *.log 22 | [Bb]in 23 | [Dd]ebug*/ 24 | *.lib 25 | *.sbr 26 | obj/ 27 | [Rr]elease*/ 28 | _ReSharper*/ 29 | [Tt]est[Rr]esult* 30 | 31 | #Ignore thumbnails created by Windows 32 | #Ignore files built by Visual Studio 33 | .vs/ 34 | #Nuget packages folder 35 | packages/ 36 | -------------------------------------------------------------------------------- /Demos/Servers/OrderServer/OrderServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Demos/Servers/OrderServer/OrderServicesBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xpress.Core; 3 | 4 | namespace OrderServer 5 | { 6 | /// 7 | /// Order server extension methods for . 8 | /// 9 | public static class OrderServicesBuilderOptionsExtension 10 | { 11 | /// 12 | /// Add a order server module 13 | /// 14 | public static ServicesBuilderOptions AddOrderServer(this ServicesBuilderOptions servicesBuilderOptions) 15 | { 16 | servicesBuilderOptions.IocRegister.RegisterAssemblyByBasicInterface(servicesBuilderOptions,typeof(OrderServicesBuilderOptionsExtension).Assembly); 17 | 18 | return servicesBuilderOptions; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Demos/Servers/TicketServer/TicketServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Demos/Servers/TicketServer/TicketServicesBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xpress.Core; 3 | 4 | namespace TicketServer 5 | { 6 | /// 7 | /// ticket server extension methods for . 8 | /// 9 | public static class TicketServicesBuilderOptionsExtension 10 | { 11 | /// 12 | /// Add a ticket server module 13 | /// 14 | public static ServicesBuilderOptions AddTicketServer(this ServicesBuilderOptions servicesBuilderOptions) 15 | { 16 | servicesBuilderOptions.IocRegister.RegisterAssemblyByBasicInterface(servicesBuilderOptions, typeof(TicketServicesBuilderOptionsExtension).Assembly); 17 | return servicesBuilderOptions; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Demos/Servers/VipCardServer/VipCardServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Demos/Servers/VipCardServer/VipCardServicesBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xpress.Core; 3 | 4 | namespace VipCardServer 5 | { 6 | /// 7 | /// vip card extension methods for . 8 | /// 9 | public static class VipCardServicesBuilderOptionsExtension 10 | { 11 | /// 12 | /// Add a vip card module 13 | /// 14 | public static ServicesBuilderOptions AddVipCardServer(this ServicesBuilderOptions servicesBuilderOptions) 15 | { 16 | servicesBuilderOptions.IocRegister.RegisterAssemblyByBasicInterface(servicesBuilderOptions, typeof(VipCardServicesBuilderOptionsExtension).Assembly); 17 | return servicesBuilderOptions; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/Filters/JobAuthorizationFilter.cs: -------------------------------------------------------------------------------- 1 | using Exceptionless; 2 | using Exceptionless.Logging; 3 | using Hangfire.Dashboard; 4 | using Microsoft.AspNetCore.Hosting; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace Xpress.Demo.Api.Filters 11 | { 12 | /// 13 | public class JobAuthorizationFilter : IDashboardAuthorizationFilter 14 | { 15 | private readonly IHostingEnvironment _hostingEnvironment; 16 | 17 | /// 18 | public JobAuthorizationFilter(IHostingEnvironment hostingEnvironment) 19 | { 20 | _hostingEnvironment = hostingEnvironment; 21 | } 22 | 23 | /// 24 | public bool Authorize(DashboardContext context) 25 | { 26 | if (_hostingEnvironment.IsProduction()) //非测试环境 27 | return false; 28 | else 29 | { 30 | ExceptionlessClient.Default 31 | .CreateLog(nameof(JobAuthorizationFilter), $"job请求访问地址:{context.Request.RemoteIpAddress}", LogLevel.Fatal).Submit(); 32 | return true; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/Models/TestCacheItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Xpress.Demo.Api.Models 7 | { 8 | [Serializable] 9 | public class TestCacheItem 10 | { 11 | public string Name { get; private set; } 12 | 13 | 14 | public TestCacheItem(string name) 15 | { 16 | Name = name; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/NLog.Exceptionless.config: -------------------------------------------------------------------------------- 1 |  2 | 4 | 5 |       6 | 7 | 8 |   9 | 10 |     11 |       12 |           13 | 14 | 15 | 16 | 17 |   18 | 19 | 20 | 21 |   22 | 23 | 24 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/NLog.config: -------------------------------------------------------------------------------- 1 |  2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |   35 | 36 | 37 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:32308", 8 | "sslPort": 44304 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger/index.html", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Xpress.Demo.Api": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger/index.html", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "ConnectionStrings": { 10 | //"Default": "server=10.99.59.183,1433;database=XpresssDemo;uid=sa;pwd=LZL1314525?", 11 | "Default": "server=10.99.59.183;port=3306;database=XpressDemo;uid=root;pwd=lzl1314525?", 12 | "CacheRedis": "10.99.59.183:6379,password=LZL1314525?,defaultDatabase=10,connectTimeout=1000,connectRetry=3,syncTimeout=10000", 13 | "JobRedis": "10.99.59.183:6379,password=LZL1314525?,allowAdmin=true,defaultDatabase=11,connectTimeout=1000,connectRetry=3,syncTimeout=10000" 14 | }, 15 | "ApolloConfig": { 16 | "AppId": "Xpress-Demo-Api", 17 | "MetaServer": "http://10.98.0.232:9100" 18 | }, 19 | "Exceptionless": { 20 | "ServerUrl": "http://10.98.98.175:9001", 21 | "ApiKey": "QI7Mokkw5mnRfWs1ka4WsSpB4gFbS8E0XezwWizH" 22 | }, 23 | "RabbitMq": { 24 | "HostName": "10.98.0.233", 25 | "VirtualHost": "/", 26 | "UserName": "ThemePark", 27 | "Password": "123456", 28 | "Port": 5673 29 | }, 30 | //"RabbitMq": { 31 | // "HostName": "127.0.0.1", 32 | // "VirtualHost": "/", 33 | // "UserName": "guest", 34 | // "Password": "guest", 35 | // "Port": 5672 36 | //} 37 | } 38 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XF1314/Xpress/b99841d14e8dc03128afbdad68310da2be3aa435/Demos/Xpress.Demo.Api/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/wwwroot/swagger/webpack.config.js: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Api/wwwroot/webpack.config.js: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/ApplicationServicesBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xpress.Core; 3 | 4 | namespace Xpress.Demo.Application 5 | { 6 | /// 7 | /// Demo application module extension methods for . 8 | /// 9 | public static class ApplicationServicesBuilderOptionsExtension 10 | { 11 | /// 12 | /// Add a application demo module 13 | /// 14 | public static ServicesBuilderOptions AddDemoApplication(this ServicesBuilderOptions servicesBuilderOptions) 15 | { 16 | servicesBuilderOptions.IocRegister.RegisterAssemblyByBasicInterface(servicesBuilderOptions, typeof(ApplicationServicesBuilderOptionsExtension).Assembly); 17 | return servicesBuilderOptions; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/BackgroundJobs/TestBackGroundJob.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Xpress.Core.BackgroundJobs; 6 | using Xpress.Core.DependencyInjection; 7 | 8 | namespace Xpress.Demo.Application.BackgroundJobs 9 | { 10 | /// 11 | public class TestBackGroundJob : IBackgroundJob, ITransientDependency 12 | { 13 | private readonly ILogger _logger; 14 | 15 | /// 16 | public TestBackGroundJob(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | /// 22 | public void Execute(TestBackgroundEventArgs args) 23 | { 24 | _logger.LogInformation(args.Message); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/BackgroundJobs/TestBackgroundEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.BackgroundJobs; 5 | 6 | namespace Xpress.Demo.Application.BackgroundJobs 7 | { 8 | public class TestBackgroundEventArgs : BackgroundEventArgsBase 9 | { 10 | public string Message { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/CapSubscribers/CapEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | using DotNetCore.CAP; 2 | using Exceptionless; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Logging.Abstractions; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Xpress.Core.DependencyInjection; 10 | using Xpress.Core.EventBus.Cap; 11 | using Xpress.Core.Uow; 12 | using Xpress.Demo.Core.Events; 13 | 14 | namespace Xpress.Demo.Application.CapSubscribers 15 | { 16 | public class CapEventSubscriber : ICapEventSubscriber 17 | { 18 | private readonly IServiceProvider _serviceProvider; 19 | private readonly ILogger _logger; 20 | private readonly IUnitOfWorkManager _unitOfWorkManager; 21 | 22 | public CapEventSubscriber(IServiceProvider serviceProvider, ILogger logger ,IUnitOfWorkManager unitOfWorkManager) 23 | { 24 | _logger = logger; 25 | _serviceProvider = serviceProvider; 26 | _unitOfWorkManager = unitOfWorkManager; 27 | } 28 | 29 | [CapSubscribe("TestCap")] 30 | public async Task TestCap(TestCapEvent testCapEvent) 31 | { 32 | _logger.LogInformation(testCapEvent.Message); 33 | ExceptionlessClient.Default.CreateLog(nameof(CapEventSubscriber), testCapEvent.Message, Exceptionless.Logging.LogLevel.Fatal).Submit(); 34 | 35 | await Task.CompletedTask; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/DemoService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Logging.Abstractions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using Xpress.Core.Application; 7 | 8 | namespace Xpress.Demo.Application 9 | { 10 | public class DemoService : ApplicationService, IDemoService 11 | { 12 | public ILogger Logger { get; set; } 13 | 14 | public DemoService(ILogger logger) 15 | { 16 | Logger = NullLogger.Instance; 17 | } 18 | 19 | public void WriteMessage(string message) 20 | { 21 | Logger.LogCritical(message); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/IDemoService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Application; 5 | 6 | namespace Xpress.Demo.Application 7 | { 8 | public interface IDemoService : IApplicationService 9 | { 10 | void WriteMessage(string message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/LocalEventHandlers/TestLocalEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Exceptionless; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Xpress.Core.DependencyInjection; 8 | using Xpress.Core.EventBus.Local; 9 | using Xpress.Demo.Application.CapSubscribers; 10 | using Xpress.Demo.Core.Events; 11 | 12 | namespace Xpress.Demo.Application.LocalEventHandlers 13 | { 14 | public class TestLocalEventHandler : ILocalEventHandler, ITransientDependency 15 | { 16 | private readonly IServiceProvider _serviceProvider; 17 | private readonly ILogger _logger; 18 | 19 | public TestLocalEventHandler(IServiceProvider serviceProvider, ILogger logger) 20 | { 21 | _logger = logger; 22 | _serviceProvider = serviceProvider; 23 | } 24 | 25 | public async Task HandleEventAsync(TestLocalEvent @event) 26 | { 27 | _logger.LogCritical(@event.Message); 28 | //ExceptionlessClient.Default.CreateLog(nameof(TestLocalEventHandler), @event.Message, Exceptionless.Logging.LogLevel.Fatal).Submit(); 29 | 30 | await Task.CompletedTask; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/RecurringJobs/TestRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire.RecurringJobExtensions; 2 | using Hangfire.Server; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using Xpress.Core.DependencyInjection; 8 | 9 | namespace Xpress.Demo.Application.RecurringJobs 10 | { 11 | /// 12 | public class TestRecurringJob : IRecurringJob, ITransientDependency 13 | { 14 | private readonly ILogger _logger; 15 | 16 | /// 17 | public TestRecurringJob(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | /// 23 | /// Job Excution 24 | /// 25 | [RecurringJob("00 02 * * *", "China Standard Time", "default")] 26 | public void Execute(PerformContext context) 27 | { 28 | _logger.LogInformation("RecuringJobTest..."); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Application/Xpress.Demo.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | bin\Debug\netcoreapp2.2\Xpress.Demo.Application.xml 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Core/CoreServicesBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xpress.Core; 3 | 4 | namespace Xpress.Demo.Core 5 | { 6 | /// 7 | /// Demo core extension methods for . 8 | /// 9 | public static class CoreServicesBuilderOptionsExtension 10 | { 11 | /// 12 | /// Add a core demo module 13 | /// 14 | public static ServicesBuilderOptions AddDemoCore(this ServicesBuilderOptions servicesBuilderOptions) 15 | { 16 | servicesBuilderOptions.IocRegister.RegisterAssemblyByBasicInterface(servicesBuilderOptions, typeof(CoreServicesBuilderOptionsExtension).Assembly); 17 | return servicesBuilderOptions; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Core/Events/TestCapEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.EventBus; 5 | 6 | namespace Xpress.Demo.Core.Events 7 | { 8 | public class TestCapEvent : EventBase 9 | { 10 | public string Message { get; set; } 11 | 12 | public TestCapEvent(string eventName= default(string)) : base(eventName) 13 | { 14 | 15 | } 16 | 17 | public override string GetEventName() 18 | { 19 | return "TestCap"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Core/Events/TestLocalEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.EventBus; 5 | 6 | namespace Xpress.Demo.Core.Events 7 | { 8 | public class TestLocalEvent : EventBase 9 | { 10 | public string Message { get; set; } 11 | 12 | public TestLocalEvent(string eventName = default(string)) : base(eventName) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.Core/Xpress.Demo.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.EntityFramework/DemoDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Xpress.Core.EntityFramework; 6 | 7 | namespace Xpress.Demo.EntityFramework 8 | { 9 | public class DemoDbContext : EfDbContextBase 10 | { 11 | /// 12 | /// 唯一标识 13 | /// 14 | public string Id { get; private set; } 15 | 16 | public DemoDbContext(DbContextOptions options) 17 | : base(options) 18 | { 19 | Id = Guid.NewGuid().ToString("N"); 20 | } 21 | 22 | public override void Dispose() 23 | { 24 | base.Dispose(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Demos/Xpress.Demo.EntityFramework/Xpress.Demo.EntityFramework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xpress 2 | 一个轻量级面向服务的Asp.Net Core项目框架,基于Ioc容器(Autofac或者Castle)实现了服务自动注册, 集成了Apollo配置中心,CAP的事件总线,内存事件总线,Hangfire后台任务&定时任务,Exceptionless&Nlog日志组件,Redis分布式缓存具备工作单元、领域驱动等特性。框架的远景是集成Ids4身份认证,Consul服务注册等组件,以完成对基于Ocelot+Consul+Ids4的微服务解决方案支持。 3 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/AspNetCoreServicesBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core; 5 | 6 | namespace Xpress.AspNetCore 7 | { 8 | /// 9 | ///AspNetCore extension methods for . 10 | /// 11 | public static class AspNetCoreServicesBuilderOptionsExtension 12 | { 13 | /// 14 | /// Add a aspNetCore module 15 | /// 16 | public static ServicesBuilderOptions AddAspNetCore(this ServicesBuilderOptions servicesBuilderOptions) 17 | { 18 | servicesBuilderOptions.IocRegister.RegisterAssemblyByBasicInterface(servicesBuilderOptions, typeof(AspNetCoreServicesBuilderOptionsExtension).Assembly); 19 | return servicesBuilderOptions; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ExceptionHandling/ExceptionHttpStatusCodeOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Text; 5 | 6 | namespace Xpress.AspNetCore.ExceptionHandling 7 | { 8 | public class ExceptionHttpStatusCodeOptions 9 | { 10 | public IDictionary ErrorCodeToHttpStatusCodeMappings { get; } 11 | 12 | public ExceptionHttpStatusCodeOptions() 13 | { 14 | ErrorCodeToHttpStatusCodeMappings = new Dictionary(); 15 | } 16 | 17 | public void Map(string errorCode, HttpStatusCode httpStatusCode) 18 | { 19 | ErrorCodeToHttpStatusCodeMappings[errorCode] = httpStatusCode; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ExceptionHandling/IExceptionToErrorInfoConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Http; 5 | 6 | namespace Xpress.AspNetCore.ExceptionHandling 7 | { 8 | /// 9 | /// This interface can be implemented to convert an object to an object. 10 | /// Implements Chain Of Responsibility pattern. 11 | /// 12 | public interface IExceptionToErrorInfoConverter 13 | { 14 | /// 15 | /// Converter method. 16 | /// 17 | /// The exception 18 | /// Error info or null 19 | RemoteServiceErrorInfo Convert(Exception exception); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ExceptionHandling/IHttpExceptionStatusCodeFinder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace Xpress.AspNetCore.ExceptionHandling 8 | { 9 | public interface IHttpExceptionStatusCodeFinder 10 | { 11 | HttpStatusCode GetStatusCode(HttpContext httpContext, Exception exception); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/Extensions/ActionDescriptorExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Abstractions; 2 | using Microsoft.AspNetCore.Mvc.Controllers; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Text; 7 | using Xpress.AspNetCore.Utils; 8 | using Xpress.Core.Exceptions; 9 | 10 | namespace Xpress.AspNetCore.Extensions 11 | { 12 | public static class ActionDescriptorExtensions 13 | { 14 | public static ControllerActionDescriptor AsControllerActionDescriptor(this ActionDescriptor actionDescriptor) 15 | { 16 | if (!actionDescriptor.IsControllerAction()) 17 | { 18 | throw new XpressException($"{nameof(actionDescriptor)} should be type of {typeof(ControllerActionDescriptor).AssemblyQualifiedName}"); 19 | } 20 | 21 | return actionDescriptor as ControllerActionDescriptor; 22 | } 23 | 24 | public static MethodInfo GetMethodInfo(this ActionDescriptor actionDescriptor) 25 | { 26 | return actionDescriptor.AsControllerActionDescriptor().MethodInfo; 27 | } 28 | 29 | public static Type GetReturnType(this ActionDescriptor actionDescriptor) 30 | { 31 | return actionDescriptor.GetMethodInfo().ReturnType; 32 | } 33 | 34 | public static bool HasObjectResult(this ActionDescriptor actionDescriptor) 35 | { 36 | return ActionResultHelper.IsObjectResult(actionDescriptor.GetReturnType()); 37 | } 38 | 39 | public static bool IsControllerAction(this ActionDescriptor actionDescriptor) 40 | { 41 | return actionDescriptor is ControllerActionDescriptor; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ModelValidation/IModelStateValidator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ModelBinding; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Xpress.AspNetCore.ModelValidation 7 | { 8 | public interface IModelStateValidator 9 | { 10 | void Validate(ModelStateDictionary modelState); 11 | 12 | void AddErrors(IXpressValidationResult validationResult, ModelStateDictionary modelState); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ModelValidation/IXpressValidationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace Xpress.AspNetCore.ModelValidation 7 | { 8 | public interface IXpressValidationResult 9 | { 10 | List Errors { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ModelValidation/ModelStateValidator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ModelBinding; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Linq; 6 | using System.Text; 7 | using Xpress.Core.DependencyInjection; 8 | using Xpress.Core.Exceptions; 9 | 10 | namespace Xpress.AspNetCore.ModelValidation 11 | { 12 | public class ModelStateValidator : IModelStateValidator, ITransientDependency 13 | { 14 | public virtual void Validate(ModelStateDictionary modelState) 15 | { 16 | var validationResult = new XpressValidationResult(); 17 | 18 | AddErrors(validationResult, modelState); 19 | 20 | if (validationResult.Errors.Any()) 21 | { 22 | throw new XpressValidationException( 23 | "ModelState 验证未通过,具体原因看详情。", 24 | validationResult.Errors 25 | ); 26 | } 27 | } 28 | 29 | public virtual void AddErrors(IXpressValidationResult validationResult, ModelStateDictionary modelState) 30 | { 31 | if (modelState.IsValid) 32 | { 33 | return; 34 | } 35 | 36 | foreach (var state in modelState) 37 | { 38 | foreach (var error in state.Value.Errors) 39 | { 40 | validationResult.Errors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key })); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ModelValidation/XpressModelValidationActionFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Filters; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xpress.AspNetCore.Extensions; 7 | using Xpress.Core.DependencyInjection; 8 | 9 | namespace Xpress.AspNetCore.ModelValidation 10 | { 11 | public class XpressModelValidationActionFilter : IAsyncActionFilter, ITransientDependency 12 | { 13 | private readonly IModelStateValidator _validator; 14 | 15 | public XpressModelValidationActionFilter(IModelStateValidator validator) 16 | { 17 | _validator = validator; 18 | } 19 | 20 | public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 21 | { 22 | //TODO: Configuration to disable validation for controllers..? 23 | 24 | if (context.ActionDescriptor.IsControllerAction() && context.ActionDescriptor.HasObjectResult()) 25 | { 26 | _validator.Validate(context.ModelState); 27 | } 28 | 29 | await next(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/ModelValidation/XpressValidationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace Xpress.AspNetCore.ModelValidation 7 | { 8 | public class XpressValidationResult : IXpressValidationResult 9 | { 10 | public List Errors { get; } 11 | 12 | public XpressValidationResult() 13 | { 14 | Errors = new List(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/Runtime/HttpContextCancellationTokenProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading; 6 | using Xpress.Core.DependencyInjection; 7 | using Xpress.Core.Threading; 8 | 9 | namespace Xpress.AspNetCore.Runtime 10 | { 11 | public class HttpContextCancellationTokenProvider : ICancellationTokenProvider, ITransientDependency 12 | { 13 | public CancellationToken Token => _httpContextAccessor.HttpContext?.RequestAborted ?? default(CancellationToken); 14 | 15 | private readonly IHttpContextAccessor _httpContextAccessor; 16 | 17 | public HttpContextCancellationTokenProvider(IHttpContextAccessor httpContextAccessor) 18 | { 19 | _httpContextAccessor = httpContextAccessor; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/Runtime/HttpContextCurrentPrincipalAccessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Security.Claims; 5 | using System.Text; 6 | using Xpress.Core.Runtime; 7 | 8 | namespace Xpress.AspNetCore.Runtime 9 | { 10 | public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor 11 | { 12 | public override ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal; 13 | 14 | private readonly IHttpContextAccessor _httpContextAccessor; 15 | 16 | public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor) 17 | { 18 | _httpContextAccessor = httpContextAccessor; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/Uow/UnitOfWorkMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xpress.Core.Uow; 7 | 8 | namespace Xpress.AspNetCore.Uow 9 | { 10 | public class UnitOfWorkMiddleware 11 | { 12 | public const string UnitOfWorkReservationName = "_ActionUnitOfWork"; 13 | 14 | private readonly RequestDelegate _next; 15 | private readonly IUnitOfWorkManager _unitOfWorkManager; 16 | 17 | public UnitOfWorkMiddleware(RequestDelegate next, IUnitOfWorkManager unitOfWorkManager) 18 | { 19 | _next = next; 20 | _unitOfWorkManager = unitOfWorkManager; 21 | } 22 | 23 | public async Task Invoke(HttpContext httpContext) 24 | { 25 | using (var uow = _unitOfWorkManager.Reserve(UnitOfWorkReservationName)) 26 | { 27 | await _next(httpContext); 28 | await uow.CompleteAsync(httpContext.RequestAborted); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/Utils/ActionResultHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Xpress.Core.Threading; 7 | using Xpress.Core; 8 | using Xpress.Core.Utils; 9 | 10 | namespace Xpress.AspNetCore.Utils 11 | { 12 | public static class ActionResultHelper 13 | { 14 | public static List ObjectResultTypes { get; } 15 | 16 | static ActionResultHelper() 17 | { 18 | ObjectResultTypes = new List 19 | { 20 | typeof(JsonResult), 21 | typeof(ObjectResult), 22 | typeof(NoContentResult) 23 | }; 24 | } 25 | 26 | public static bool IsObjectResult(Type returnType) 27 | { 28 | returnType = XpressAsyncHelper.UnwrapTask(returnType); 29 | 30 | if (!typeof(IActionResult).IsAssignableFrom(returnType)) 31 | { 32 | return true; 33 | } 34 | 35 | return ObjectResultTypes.Any(t => t.IsAssignableFrom(returnType)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Source/Xpress.AspNetCore/Xpress.AspNetCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/AutoMapAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | 4 | namespace Xpress.AutoMapper 5 | { 6 | /// 7 | /// Mark as a two-way mapping 8 | /// 9 | public class AutoMapAttribute : AutoMapAttributeBase 10 | { 11 | /// 12 | /// Create a two-way entity mapping relationship 13 | /// 14 | public override void CreateMap(IMapperConfigurationExpression configuration, Type type) 15 | { 16 | CreateMap(configuration, type, MemberList.Destination); 17 | CreateMap(configuration, type, MemberList.Source); 18 | } 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/AutoMapAttributeBase.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using System; 3 | 4 | namespace Xpress.AutoMapper 5 | { 6 | /// 7 | /// Mapping relationships need to inherit abstract base classes 8 | /// 9 | public abstract class AutoMapAttributeBase : Attribute 10 | { 11 | /// 12 | /// Target type 13 | /// 14 | public Type[] TargetTypes { get; private set; } 15 | 16 | /// 17 | protected AutoMapAttributeBase(params Type[] targetTypes) 18 | { 19 | TargetTypes = targetTypes; 20 | } 21 | 22 | /// 23 | /// Create a specified mapping 24 | /// 25 | protected void CreateMap(IMapperConfigurationExpression configuration, Type type, MemberList memberList) 26 | { 27 | if (TargetTypes == null) 28 | { 29 | return; 30 | } 31 | 32 | foreach (var targetType in TargetTypes) 33 | { 34 | configuration.CreateMap(targetType, type, memberList); 35 | } 36 | } 37 | 38 | /// 39 | /// Create mappings 40 | /// 41 | public abstract void CreateMap(IMapperConfigurationExpression configuration, Type type); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/AutoMapExtensions.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | namespace Xpress.AutoMapper 4 | { 5 | /// 6 | /// Object mapping extension 7 | /// 8 | public static class ObjectMapperExtensions 9 | { 10 | /// 11 | /// Map out new target objects 12 | /// 13 | public static TDestination MapTo(this object source) 14 | { 15 | return Mapper.Map(source); 16 | } 17 | 18 | /// 19 | /// Map attribute content to target object 20 | /// 21 | public static TDestination MapTo(this TSource source, TDestination destination) 22 | { 23 | return Mapper.Map(source, destination); 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/AutoMapFromAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | 4 | namespace Xpress.AutoMapper 5 | { 6 | /// 7 | /// Mark target object mapped to this object 8 | /// 9 | public class AutoMapFromAttribute : AutoMapAttributeBase 10 | { 11 | /// 12 | public override void CreateMap(IMapperConfigurationExpression configuration, Type type) 13 | { 14 | CreateMap(configuration, type, MemberList.Destination); 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/AutoMapToAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoMapper; 3 | 4 | namespace Xpress.AutoMapper 5 | { 6 | /// 7 | /// Tag mapping to target object 8 | /// 9 | public class AutoMapToAttribute : AutoMapAttributeBase 10 | { 11 | /// 12 | public override void CreateMap(IMapperConfigurationExpression configuration, Type type) 13 | { 14 | CreateMap(configuration, type, MemberList.Source); 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/AutoMapperAppBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using System; 3 | using Xpress.Core; 4 | 5 | namespace Xpress.AutoMapper 6 | { 7 | /// 8 | /// AutoMapper specific extension methods for . 9 | /// 10 | public static class AutoMapperAppBuilderOptionsExtension 11 | { 12 | /// 13 | /// Use AutoMapper 14 | /// 15 | public static AppBuilderOptions UseAutoMapper(this AppBuilderOptions builder, Action config = null) 16 | { 17 | Mapper.Initialize(options => 18 | { 19 | options.ValidateInlineMaps = false; 20 | config?.Invoke(options); 21 | }); 22 | 23 | return builder; 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/AutoMapperObjectMapper.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | namespace Xpress.AutoMapper 4 | { 5 | /// 6 | /// Use Automapper mapping 7 | /// 8 | public class AutoMapperObjectMapper : IObjectMapper 9 | { 10 | private readonly IMapper _mapper; 11 | 12 | /// 13 | public AutoMapperObjectMapper(IMapper mapper) 14 | { 15 | _mapper = mapper; 16 | } 17 | 18 | /// 19 | public TDestination Map(object source) 20 | { 21 | return _mapper.Map(source); 22 | } 23 | 24 | /// 25 | public TDestination Map(TSource source, TDestination destination) 26 | { 27 | return _mapper.Map(source, destination); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/IObjectMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.AutoMapper 6 | { 7 | /// 8 | /// Defines a simple interface to map objects. 9 | /// 10 | public interface IObjectMapper 11 | { 12 | /// 13 | /// Converts an object to another. 14 | /// 15 | TDestination Map(object source); 16 | 17 | /// 18 | /// Execute a mapping from the source object to the existing destination object 19 | /// 20 | TDestination Map(TSource source, TDestination destination); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.AutoMapper/Xpress.AutoMapper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Source/Xpress.Autofac/AutofacServicesBuilderOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core; 5 | using Xpress.Core.DependencyInjection; 6 | 7 | namespace Xpress.Autofac 8 | { 9 | /// 10 | /// Autofac specific extension methods for . 11 | /// 12 | public static class AutofacServicesBuilderOptionsExtension 13 | { 14 | /// 15 | /// Use Autofac as an injection container 16 | /// 17 | public static ServicesBuilderOptions UseAutofac(this ServicesBuilderOptions servicesBuilderOptions) 18 | { 19 | servicesBuilderOptions.IocRegister = new AutofacIocRegister(); 20 | servicesBuilderOptions.IocRegister.Register(DependencyLifeStyle.Transient); 21 | return servicesBuilderOptions; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xpress.Autofac/RegistrationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Autofac.Builder; 2 | using System; 3 | using Xpress.Core.DependencyInjection; 4 | 5 | namespace Xpress.Autofac 6 | { 7 | /// 8 | /// Autofac RegistrationBuilder extensions 9 | /// 10 | public static class RegistrationBuilderExtensions 11 | { 12 | /// 13 | /// Lifestyle conversion and application 14 | /// 15 | public static void AddLifeStyle(this IRegistrationBuilder registration, 16 | DependencyLifeStyle lifeStyle) 17 | { 18 | switch (lifeStyle) 19 | { 20 | case DependencyLifeStyle.Transient: 21 | registration.InstancePerDependency(); 22 | break; 23 | case DependencyLifeStyle.Scoped: 24 | registration.InstancePerLifetimeScope(); 25 | break; 26 | case DependencyLifeStyle.Singleton: 27 | registration.SingleInstance(); 28 | break; 29 | default: 30 | throw new ArgumentException(nameof(lifeStyle)); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Xpress.Autofac/Xpress.Autofac.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Source/Xpress.CastleWindsor/ComponentRegistrationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Castle.MicroKernel.Registration; 2 | using System; 3 | using Xpress.Core.DependencyInjection; 4 | 5 | namespace Xpress.CastleWindsor 6 | { 7 | /// 8 | /// Windsor ComponentRegistration extensions 9 | /// 10 | public static class ComponentRegistrationExtensions 11 | { 12 | /// 13 | /// Lifestyle conversion and application 14 | /// 15 | public static ComponentRegistration ApplyLifestyle(this ComponentRegistration registration, DependencyLifeStyle lifeStyle) 16 | where T : class 17 | { 18 | switch (lifeStyle) 19 | { 20 | case DependencyLifeStyle.Transient: 21 | return registration.LifestyleTransient(); 22 | case DependencyLifeStyle.Scoped: 23 | return registration.LifestyleScoped(); 24 | case DependencyLifeStyle.Singleton: 25 | return registration.LifestyleSingleton(); 26 | default: 27 | throw new ArgumentException(nameof(lifeStyle)); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Xpress.CastleWindsor/LocalLifetimeScopeAccessor.cs: -------------------------------------------------------------------------------- 1 | using Castle.MicroKernel.Context; 2 | using Castle.MicroKernel.Lifestyle.Scoped; 3 | using System.Threading; 4 | 5 | namespace Xpress.CastleWindsor 6 | { 7 | /// 8 | /// Implement a lifetimescope like Autofac 9 | /// 10 | public class LocalLifetimeScopeAccessor : IScopeAccessor 11 | { 12 | private static readonly AsyncLocal AsyncLocalScope = new AsyncLocal(); 13 | 14 | /// 15 | public ILifetimeScope GetScope(CreationContext context) 16 | { 17 | if (AsyncLocalScope.Value == null) 18 | { 19 | AsyncLocalScope.Value = new ThreadSafeDefaultLifetimeScope(); 20 | } 21 | return AsyncLocalScope.Value; 22 | } 23 | 24 | /// 25 | public void Dispose() 26 | { 27 | AsyncLocalScope.Value.Dispose(); 28 | AsyncLocalScope.Value = null; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Xpress.CastleWindsor/ThreadSafeDefaultLifetimeScope.cs: -------------------------------------------------------------------------------- 1 | using Castle.Core; 2 | using Castle.MicroKernel; 3 | using Castle.MicroKernel.Lifestyle.Scoped; 4 | using System; 5 | 6 | namespace Xpress.CastleWindsor 7 | { 8 | /// 9 | /// Default lifetimescope within the scope of a secure thread 10 | /// 11 | public class ThreadSafeDefaultLifetimeScope : ILifetimeScope 12 | { 13 | private static readonly Action _emptyOnAfterCreated = delegate { }; 14 | private readonly object _syncLock = new object(); 15 | private readonly Action _onAfterCreated; 16 | private IScopeCache _scopeCache; 17 | 18 | /// 19 | public ThreadSafeDefaultLifetimeScope(IScopeCache scopeCache = null, Action onAfterCreated = null) 20 | { 21 | this._scopeCache = scopeCache ?? new ScopeCache(); 22 | this._onAfterCreated = onAfterCreated ?? _emptyOnAfterCreated; 23 | } 24 | 25 | /// 26 | public void Dispose() 27 | { 28 | lock (_syncLock) 29 | { 30 | if (_scopeCache == null) return; 31 | if (_scopeCache is IDisposable disposableCache) 32 | { 33 | disposableCache.Dispose(); 34 | } 35 | _scopeCache = null; 36 | } 37 | } 38 | 39 | /// 40 | public Burden GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance) 41 | { 42 | lock (_syncLock) 43 | { 44 | var burden = _scopeCache[model]; 45 | if (burden == null) 46 | { 47 | _scopeCache[model] = burden = createInstance(_onAfterCreated); 48 | } 49 | return burden; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Source/Xpress.CastleWindsor/WindsorIocResolver.cs: -------------------------------------------------------------------------------- 1 | using Castle.Windsor; 2 | using System; 3 | using Xpress.Core.DependencyInjection; 4 | 5 | namespace Xpress.CastleWindsor 6 | { 7 | /// 8 | public class WindsorIocResolver : IocResolverBase 9 | { 10 | private readonly IWindsorContainer _container; 11 | 12 | /// 13 | public WindsorIocResolver(IWindsorContainer container) 14 | { 15 | _container = container; 16 | } 17 | 18 | /// 19 | public override object Resolve(Type serviceType, object argumentsAsAnonymousType = null) 20 | { 21 | if (argumentsAsAnonymousType == null) 22 | { 23 | return _container.Resolve(serviceType); 24 | } 25 | return _container.Resolve(serviceType, argumentsAsAnonymousType); 26 | } 27 | 28 | /// 29 | public override bool IsRegistered(Type serviceType) 30 | { 31 | return _container.Kernel.HasComponent(serviceType); 32 | } 33 | 34 | /// 35 | public override void Release(object obj) 36 | { 37 | _container.Release(obj); 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Source/Xpress.CastleWindsor/WindsorServicesBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core; 5 | using Xpress.Core.DependencyInjection; 6 | 7 | namespace Xpress.CastleWindsor 8 | { 9 | /// 10 | /// Castle Windsor specific extension methods for . 11 | /// 12 | public static class WindsorServicesBuilderExtension 13 | { 14 | /// 15 | /// Use Castle Windsor as an injection container 16 | /// 17 | public static ServicesBuilderOptions UseWindsor(this ServicesBuilderOptions servicesBuilderOptions) 18 | { 19 | servicesBuilderOptions.IocRegister = new WindsorIocRegister(); 20 | servicesBuilderOptions.IocRegister.Register(DependencyLifeStyle.Transient); 21 | return servicesBuilderOptions; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xpress.CastleWindsor/Xpress.CastleWindsor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Source/Xpress.Core/AppBuilderOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.DependencyInjection; 5 | 6 | namespace Xpress.Core 7 | { 8 | /// 9 | /// Application framework usage 10 | /// 11 | public class AppBuilderOptions 12 | { 13 | /// 14 | /// Ioc resolver 15 | /// 16 | public readonly IIocResolver IocResolver; 17 | 18 | /// 19 | public AppBuilderOptions(IIocResolver iocResolver) 20 | { 21 | IocResolver = iocResolver; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Application/ApplicationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Application 6 | { 7 | /// 8 | public abstract class ApplicationService : IApplicationService 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Application/IApplicationService.cs: -------------------------------------------------------------------------------- 1 | using Xpress.Core.DependencyInjection; 2 | 3 | namespace Xpress.Core.Application 4 | { 5 | /// 6 | /// Application layer service interface, application services should inherit this interface 7 | /// 8 | public interface IApplicationService : ITransientDependency 9 | { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/BackgroundEventArgsBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Identity; 5 | 6 | namespace Xpress.Core.BackgroundJobs 7 | { 8 | public class BackgroundEventArgsBase : IBackgroundEventArgs 9 | { 10 | /// 11 | /// 事件Id 12 | /// 13 | public string EventId { get; set; } = GuidProvider.Comb.Create().ToString("N"); 14 | 15 | /// 16 | /// 事件发布时间 17 | /// 18 | public DateTime EventTime { get; set; } = DateTime.Now; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/BackgroundJobArgsHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Exceptions; 5 | 6 | namespace Xpress.Core.BackgroundJobs 7 | { 8 | public static class BackgroundJobArgsHelper 9 | { 10 | public static Type GetJobArgsType(Type jobType) 11 | { 12 | foreach (var @interface in jobType.GetInterfaces()) 13 | { 14 | if (!@interface.IsGenericType) 15 | { 16 | continue; 17 | } 18 | 19 | if (@interface.GetGenericTypeDefinition() != typeof(IBackgroundJob<>)) 20 | { 21 | continue; 22 | } 23 | 24 | var genericArgs = @interface.GetGenericArguments(); 25 | if (genericArgs.Length != 1) 26 | { 27 | continue; 28 | } 29 | 30 | return genericArgs[0]; 31 | } 32 | 33 | throw new XpressException($"Could not find type of the job args. Ensure that given type implements the {typeof(IBackgroundJob<>).AssemblyQualifiedName} interface. Given job type: {jobType.AssemblyQualifiedName}"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/BackgroundJobConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.BackgroundJobs 6 | { 7 | public class BackgroundJobConfiguration 8 | { 9 | public Type ArgsType { get; } 10 | 11 | public Type JobType { get; } 12 | 13 | public string JobName { get; } 14 | 15 | public BackgroundJobConfiguration(Type jobType) 16 | { 17 | JobType = jobType; 18 | ArgsType = BackgroundJobArgsHelper.GetJobArgsType(jobType); 19 | JobName = BackgroundJobNameAttribute.GetName(ArgsType); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/BackgroundJobExecutionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Text; 5 | using Xpress.Core.Exceptions; 6 | 7 | namespace Xpress.Core.BackgroundJobs 8 | { 9 | [Serializable] 10 | public class BackgroundJobExecutionException : XpressException 11 | { 12 | public string JobType { get; set; } 13 | 14 | public object JobArgs { get; set; } 15 | 16 | public BackgroundJobExecutionException() 17 | { 18 | 19 | } 20 | 21 | /// 22 | /// Creates a new object. 23 | /// 24 | public BackgroundJobExecutionException(SerializationInfo serializationInfo, StreamingContext context) 25 | : base(serializationInfo, context) 26 | { 27 | 28 | } 29 | 30 | /// 31 | /// Creates a new object. 32 | /// 33 | /// Exception message 34 | /// Inner exception 35 | public BackgroundJobExecutionException(string message, Exception innerException) 36 | : base(message, innerException) 37 | { 38 | 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/BackgroundJobManagerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Threading; 5 | using Xpress.Core.Utils; 6 | 7 | namespace Xpress.Core.BackgroundJobs 8 | { 9 | /// 10 | /// Some extension methods for . 11 | /// 12 | public static class BackgroundJobManagerExtensions 13 | { 14 | /// 15 | /// Enqueues a job to be executed. 16 | /// 17 | /// Type of the arguments of job. 18 | /// Background job manager reference 19 | /// Job arguments. 20 | /// Job priority. 21 | /// Job delay (wait duration before first try). 22 | public static string Enqueue(this IBackgroundJobManager backgroundJobManager, TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) 23 | where TArgs : IBackgroundEventArgs 24 | { 25 | return AsyncHelper.RunSync(() => backgroundJobManager.EnqueueAsync(args, priority, delay)); 26 | } 27 | 28 | /// 29 | /// Checks if background job system has a real implementation. 30 | /// It returns false if the current implementation is . 31 | /// 32 | /// 33 | /// 34 | public static bool IsAvailable(this IBackgroundJobManager backgroundJobManager) 35 | { 36 | return !(CastleProxyHelper.UnProxy(backgroundJobManager) is NullBackgroundJobManager); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/BackgroundJobNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using Hangfire.Annotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Xpress.Core.Utils; 7 | 8 | namespace Xpress.Core.BackgroundJobs 9 | { 10 | public class BackgroundJobNameAttribute : Attribute, IBackgroundJobNameProvider 11 | { 12 | public string Name { get; } 13 | 14 | public BackgroundJobNameAttribute([NotNull] string name) 15 | { 16 | Name = Check.NotNullOrWhiteSpace(name, nameof(name)); 17 | } 18 | 19 | public static string GetName() where TArgs : IBackgroundEventArgs 20 | { 21 | return GetName(typeof(TArgs)); 22 | } 23 | 24 | public static string GetName([NotNull] Type jobArgsType) 25 | { 26 | Check.NotNull(jobArgsType, nameof(jobArgsType)); 27 | 28 | return jobArgsType 29 | .GetCustomAttributes(true) 30 | .OfType() 31 | .FirstOrDefault() 32 | ?.Name 33 | ?? jobArgsType.FullName; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/BackgroundJobPriority.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.BackgroundJobs 6 | { 7 | /// 8 | /// Priority of a background job. 9 | /// 10 | public enum BackgroundJobPriority : byte 11 | { 12 | /// 13 | /// Low. 14 | /// 15 | Low = 5, 16 | 17 | /// 18 | /// Below normal. 19 | /// 20 | BelowNormal = 10, 21 | 22 | /// 23 | /// Normal (default). 24 | /// 25 | Normal = 15, 26 | 27 | /// 28 | /// Above normal. 29 | /// 30 | AboveNormal = 20, 31 | 32 | /// 33 | /// High. 34 | /// 35 | High = 25 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/HangfireBackgroundJobManager.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xpress.Core.DependencyInjection; 7 | 8 | namespace Xpress.Core.BackgroundJobs 9 | { 10 | public class HangfireBackgroundJobManager : IBackgroundJobManager, ISingletonDependency 11 | { 12 | public Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) 13 | where TArgs : IBackgroundEventArgs 14 | { 15 | if (!delay.HasValue) 16 | { 17 | return Task.FromResult( 18 | BackgroundJob.Enqueue>(adapter => adapter.Execute(args)) 19 | ); 20 | } 21 | else 22 | { 23 | return Task.FromResult( 24 | BackgroundJob.Schedule>(adapter => adapter.Execute(args), delay.Value) 25 | ); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/HangfireJobExecutionAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Options; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace Xpress.Core.BackgroundJobs 8 | { 9 | public class HangfireJobExecutionAdapter where TArgs : IBackgroundEventArgs 10 | { 11 | protected BackgroundJobOptions Options { get; } 12 | protected IServiceScopeFactory ServiceScopeFactory { get; } 13 | protected IBackgroundJobExecuter JobExecuter { get; } 14 | 15 | public HangfireJobExecutionAdapter(IOptions options, 16 | IBackgroundJobExecuter jobExecuter, IServiceScopeFactory serviceScopeFactory) 17 | { 18 | JobExecuter = jobExecuter; 19 | ServiceScopeFactory = serviceScopeFactory; 20 | Options = options.Value; 21 | } 22 | 23 | public void Execute(TArgs args) 24 | { 25 | using (var scope = ServiceScopeFactory.CreateScope()) 26 | { 27 | var jobType = Options.GetJob(typeof(TArgs)).JobType; 28 | var context = new JobExecutionContext(scope.ServiceProvider, jobType, args); 29 | JobExecuter.Execute(context); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/IBackgroundEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.BackgroundJobs 6 | { 7 | public interface IBackgroundEventArgs 8 | { 9 | /// 10 | /// 事件Id 11 | /// 12 | string EventId { get; set; } 13 | 14 | /// 15 | /// 事件发布时间 16 | /// 17 | DateTime EventTime { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/IBackgroundJob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.BackgroundJobs 6 | { 7 | public interface IBackgroundJob 8 | { } 9 | 10 | /// 11 | /// Defines interface of a background job. 12 | /// 13 | public interface IBackgroundJob: IBackgroundJob where TArgs : IBackgroundEventArgs 14 | { 15 | /// 16 | /// Executes the job with the . 17 | /// 18 | /// Job arguments. 19 | void Execute(TArgs args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/IBackgroundJobExecuter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.BackgroundJobs 6 | { 7 | public interface IBackgroundJobExecuter 8 | { 9 | void Execute(JobExecutionContext context); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/IBackgroundJobManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Xpress.Core.BackgroundJobs 7 | { 8 | /// 9 | /// Defines interface of a job manager. 10 | /// 11 | public interface IBackgroundJobManager 12 | { 13 | /// 14 | /// Enqueues a job to be executed. 15 | /// 16 | /// Type of the arguments of job. 17 | /// Job arguments. 18 | /// Job priority. 19 | /// Job delay (wait duration before first try). 20 | /// Unique identifier of a background job. 21 | Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) where TArgs : IBackgroundEventArgs; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/IBackgroundJobNameProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.BackgroundJobs 6 | { 7 | public interface IBackgroundJobNameProvider 8 | { 9 | string Name { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/JobExecutionContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.DependencyInjection; 5 | 6 | namespace Xpress.Core.BackgroundJobs 7 | { 8 | public class JobExecutionContext : IServiceProviderAccessor 9 | { 10 | public IServiceProvider ServiceProvider { get; } 11 | 12 | public Type JobType { get; } 13 | 14 | public object JobArgs { get; } 15 | 16 | public JobExecutionContext(IServiceProvider serviceProvider, Type jobType, object jobArgs) 17 | { 18 | ServiceProvider = serviceProvider; 19 | JobType = jobType; 20 | JobArgs = jobArgs; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Xpress.Core/BackgroundJobs/NullBackgroundJobManager.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Logging.Abstractions; 3 | using System; 4 | using System.Threading.Tasks; 5 | using Xpress.Core.DependencyInjection; 6 | 7 | namespace Xpress.Core.BackgroundJobs 8 | { 9 | public class NullBackgroundJobManager : IBackgroundJobManager 10 | { 11 | public ILogger Logger { get; set; } 12 | 13 | public static NullBackgroundJobManager Instance => new NullBackgroundJobManager(); 14 | 15 | public NullBackgroundJobManager() 16 | { 17 | Logger = NullLogger.Instance; 18 | } 19 | 20 | public virtual async Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) 21 | where TArgs : IBackgroundEventArgs 22 | { 23 | Logger.LogInformation("Background job system has not a real implementation. If it's mandatory, use an implementation (either the default provider or a 3rd party implementation). If it's optional, check IBackgroundJobManager.IsAvailable() extension method and act based on it."); 24 | 25 | return await Task.FromResult(string.Empty); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Caching/CacheNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using System; 3 | using Xpress.Core.Utils; 4 | 5 | namespace Xpress.Core.Caching 6 | { 7 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct)] 8 | public class CacheNameAttribute : Attribute 9 | { 10 | public string Name { get; } 11 | 12 | public CacheNameAttribute([NotNull] string name) 13 | { 14 | Check.NotNull(name, nameof(name)); 15 | 16 | Name = name; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Caching/IDistributedCache.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.Extensions.Caching.Distributed; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Xpress.Core.Caching 10 | { 11 | public interface IDistributedCache 12 | where TCacheItem : class 13 | { 14 | TCacheItem Get(string key); 15 | 16 | Task GetAsync([NotNull] string key, CancellationToken token = default(CancellationToken)); 17 | 18 | TCacheItem GetOrAdd(string key, Func factory, Func optionsFactory = null); 19 | 20 | Task GetOrAddAsync( 21 | [NotNull] string key, 22 | Func> factory, 23 | Func optionsFactory = null, 24 | CancellationToken token = default(CancellationToken) 25 | ); 26 | 27 | void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null); 28 | 29 | Task SetAsync( 30 | [NotNull] string key, 31 | [NotNull] TCacheItem value, 32 | [CanBeNull] DistributedCacheEntryOptions options = null, 33 | CancellationToken token = default(CancellationToken) 34 | ); 35 | 36 | void Refresh(string key); 37 | 38 | Task RefreshAsync(string key, CancellationToken token = default(CancellationToken)); 39 | 40 | void Remove(string key); 41 | 42 | Task RemoveAsync(string key, CancellationToken token = default(CancellationToken)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Collections/ITypeList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Collections 6 | { 7 | /// 8 | /// A shortcut for to use object as base type. 9 | /// 10 | public interface ITypeList : ITypeList 11 | { 12 | 13 | } 14 | 15 | /// 16 | /// Extends to add restriction a specific base type. 17 | /// 18 | /// Base Type of s in this list 19 | public interface ITypeList : IList 20 | { 21 | /// 22 | /// Adds a type to list. 23 | /// 24 | /// Type 25 | void Add() where T : TBaseType; 26 | 27 | /// 28 | /// Adds a type to list if it's not already in the list. 29 | /// 30 | /// Type 31 | void TryAdd() where T : TBaseType; 32 | 33 | /// 34 | /// Checks if a type exists in the list. 35 | /// 36 | /// Type 37 | /// 38 | bool Contains() where T : TBaseType; 39 | 40 | /// 41 | /// Removes a type from list 42 | /// 43 | /// 44 | void Remove() where T : TBaseType; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/DependencyLifeStyle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.DependencyInjection 6 | { 7 | /// 8 | /// Dependent service lifecycle approach 9 | /// 10 | public enum DependencyLifeStyle 11 | { 12 | /// 13 | /// Singleton, follow the application life cycle 14 | /// 15 | Singleton = 0, 16 | 17 | /// 18 | /// Scoped, follow the life cycle of Http per request 19 | /// 20 | Scoped = 1, 21 | 22 | /// 23 | /// Transient, an instance of the scope 24 | /// 25 | Transient = 2 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/IIocResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.DependencyInjection 6 | { 7 | /// 8 | /// Define a resolve interface 9 | /// 10 | public interface IIocResolver : IDisposable 11 | { 12 | /// 13 | /// Get injected generic instance 14 | /// 15 | T Resolve(); 16 | 17 | /// 18 | /// Get the injected generic instance and pass in the constructor 19 | /// 20 | T Resolve(object argumentsAsAnonymousType); 21 | 22 | /// 23 | /// Get an instance of the specified type 24 | /// 25 | object Resolve(Type serviceType); 26 | 27 | /// 28 | /// Get an instance of the specified type and pass parameters to the constructor 29 | /// 30 | object Resolve(Type serviceType, object argumentsAsAnonymousType); 31 | 32 | /// 33 | /// Is the service type registered? 34 | /// 35 | bool IsRegistered(Type serviceType); 36 | 37 | /// 38 | /// Is the service generic registered? 39 | /// 40 | bool IsRegistered(); 41 | 42 | /// 43 | /// Release the parsed object 44 | /// 45 | void Release(object obj); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/IScopedDependency.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.DependencyInjection 2 | { 3 | /// 4 | /// Inherit this interface, the dependent service follows the life cycle of the Http request 5 | /// 6 | public interface IScopedDependency 7 | { 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/IServiceProviderAccessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.DependencyInjection 6 | { 7 | public interface IServiceProviderAccessor 8 | { 9 | IServiceProvider ServiceProvider { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/ISingletonDependency.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.DependencyInjection 2 | { 3 | /// 4 | /// Inherit this interface, relying on the service to follow the application's life cycle 5 | /// 6 | public interface ISingletonDependency 7 | { 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/ITransientDependency.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.DependencyInjection 2 | { 3 | /// 4 | /// Inheriting this interface, the dependent service will return a new object each time it is requested in different scopes. 5 | /// 6 | public interface ITransientDependency 7 | { 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/InterceptorBase.cs: -------------------------------------------------------------------------------- 1 | using Castle.DynamicProxy; 2 | using System.Threading.Tasks; 3 | 4 | namespace Xpress.Core.DependencyInjection 5 | { 6 | /// 7 | /// Base class of interceptor 8 | /// 9 | public abstract class InterceptorBase : IInterceptor 10 | { 11 | /// 12 | /// Intercept execution method 13 | /// 14 | public abstract void Intercept(IInvocation invocation); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Source/Xpress.Core/DependencyInjection/IocResolverBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Xpress.Core.DependencyInjection 4 | { 5 | /// 6 | /// Defining a resolver abstract class 7 | /// 8 | public abstract class IocResolverBase : IIocResolver 9 | { 10 | /// 11 | public T Resolve() 12 | { 13 | return (T)Resolve(typeof(T)); 14 | } 15 | 16 | /// 17 | public T Resolve(object argumentsAsAnonymousType) 18 | { 19 | return (T)Resolve(typeof(T), argumentsAsAnonymousType); 20 | } 21 | 22 | /// 23 | public object Resolve(Type serviceType) 24 | { 25 | return Resolve(serviceType, null); 26 | } 27 | 28 | /// 29 | public abstract object Resolve(Type serviceType, object argumentsAsAnonymousType = null); 30 | 31 | /// 32 | public bool IsRegistered() 33 | { 34 | return IsRegistered(typeof(TService)); 35 | } 36 | 37 | /// 38 | public abstract bool IsRegistered(Type serviceType); 39 | 40 | /// 41 | public abstract void Release(object obj); 42 | 43 | /// 44 | public virtual void Dispose() { } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/AggregateEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Extensions; 5 | 6 | namespace Xpress.Core.Domain.Aggregates 7 | { 8 | public abstract class AggregateEvent : IAggregateEvent 9 | where TAggregate : IAggregateRoot 10 | where TIdentity : IIdentity 11 | { 12 | public override string ToString() 13 | { 14 | return $"{typeof(TAggregate).PrettyPrint()}/{GetType().PrettyPrint()}"; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/AggregateName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.ValueObjects; 5 | 6 | namespace Xpress.Core.Domain.Aggregates 7 | { 8 | public class AggregateName : SingleValueObject, IAggregateName 9 | { 10 | public AggregateName(string value) : base(value) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/AggregateNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Aggregates 6 | { 7 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 8 | public class AggregateNameAttribute : Attribute 9 | { 10 | public string Name { get; } 11 | 12 | public AggregateNameAttribute(string name) 13 | { 14 | if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); 15 | 16 | Name = name; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/AggregateState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Extensions; 5 | 6 | namespace Xpress.Core.Domain.Aggregates 7 | { 8 | public abstract class AggregateState : IEventApplier 9 | where TEventApplier : class, IEventApplier 10 | where TAggregate : IAggregateRoot 11 | where TIdentity : IIdentity 12 | { 13 | private static readonly IReadOnlyDictionary> ApplyMethods; 14 | 15 | static AggregateState() 16 | { 17 | ApplyMethods = typeof(TEventApplier).GetAggregateEventApplyMethods(); 18 | } 19 | 20 | protected AggregateState() 21 | { 22 | var me = this as TEventApplier; 23 | if (me == null) 24 | { 25 | throw new InvalidOperationException( 26 | $"Event applier of type '{GetType().PrettyPrint()}' has a wrong generic argument '{typeof(TEventApplier).PrettyPrint()}'"); 27 | } 28 | } 29 | 30 | public bool Apply(TAggregate aggregate, IAggregateEvent aggregateEvent) 31 | { 32 | var aggregateEventType = aggregateEvent.GetType(); 33 | Action applier; 34 | if (!ApplyMethods.TryGetValue(aggregateEventType, out applier)) 35 | return false; 36 | else 37 | { 38 | applier((TEventApplier)(object)this, aggregateEvent); 39 | return true; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/IAggregateEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Aggregates 6 | { 7 | public interface IAggregateEvent 8 | { 9 | } 10 | 11 | public interface IAggregateEvent : IAggregateEvent 12 | where TAggregate : IAggregateRoot 13 | where TIdentity : IIdentity 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/IAggregateName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Aggregates 6 | { 7 | public interface IAggregateName : IIdentity 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Aggregates 6 | { 7 | public interface IAggregateRoot 8 | { 9 | 10 | } 11 | 12 | public interface IAggregateRoot : IAggregateRoot 13 | where TIdentity : IIdentity 14 | { 15 | TIdentity Id { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/IEmit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Aggregates 6 | { 7 | public interface IEmit 8 | where TAggregateEvent : IAggregateEvent 9 | { 10 | void Apply(TAggregateEvent aggregateEvent); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Aggregates/IEventApplier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Aggregates 6 | { 7 | public interface IEventApplier 8 | where TAggregate : IAggregateRoot 9 | where TIdentity : IIdentity 10 | { 11 | bool Apply(TAggregate aggregate, IAggregateEvent aggregateEvent); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Entities/IEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Entities 6 | { 7 | /// 8 | /// Defines an entity. It's primary key may not be "Id" or it may have a composite primary key. 9 | /// Use where possible for better integration to repositories and other structures in the framework. 10 | /// 11 | public interface IEntity 12 | { 13 | /// 14 | /// Returns an array of ordered keys for this entity. 15 | /// 16 | /// 17 | object[] GetKeys(); 18 | } 19 | 20 | /// 21 | /// Defines an entity with a single primary key with "Id" property. 22 | /// 23 | /// Type of the primary key of the entity 24 | public interface IEntity : IEntity 25 | { 26 | /// 27 | /// Unique identifier for this entity. 28 | /// 29 | TKey Id { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Entities/IHasCreationTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Xpress.Core.Domain.Entities 4 | { 5 | /// 6 | /// Inherit from this interface must have a creation time 7 | /// 8 | public interface IHasCreationTime 9 | { 10 | /// 11 | /// Creation time of this entity. 12 | /// 13 | DateTime CreationTime { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Entities/IHasDeletionTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Xpress.Core.Domain.Entities 4 | { 5 | /// 6 | /// Inherit from this interface must contain delete time 7 | /// 8 | public interface IHasDeletionTime : ISoftDelete 9 | { 10 | /// 11 | /// Deletion time of this entity. 12 | /// 13 | DateTime? DeletionTime { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Entities/IHasModificationTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Xpress.Core.Domain.Entities 4 | { 5 | /// 6 | /// Inherit from this interface must have modification time 7 | /// 8 | public interface IHasModificationTime 9 | { 10 | /// 11 | /// The last modified time for this entity. 12 | /// 13 | DateTime? LastModificationTime { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Entities/IPassivable.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.Domain.Entities 2 | { 3 | /// 4 | /// Object activation status 5 | /// 6 | public interface IPassivable 7 | { 8 | /// 9 | /// Is active 10 | /// 11 | bool IsActive { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Entities/ISoftDelete.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.Domain.Entities 2 | { 3 | /// 4 | /// Soft delete interface 5 | /// 6 | public interface ISoftDelete 7 | { 8 | /// 9 | /// Is deleted 10 | /// 11 | bool IsDeleted { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Services/DomainService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Domain.Services 6 | { 7 | public class DomainService: IDomainService 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Domain/Services/IDomainService.cs: -------------------------------------------------------------------------------- 1 | using Xpress.Core.DependencyInjection; 2 | 3 | namespace Xpress.Core.Domain.Services 4 | { 5 | /// 6 | /// This interface must be implemented by all domain services to identify them by convention. 7 | /// 8 | public interface IDomainService : ITransientDependency 9 | { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/DisplayItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Dtos 6 | { 7 | public class DisplayItem 8 | { 9 | /// 10 | /// Id标识 11 | /// 12 | public object Id { get; set; } 13 | 14 | /// 15 | /// 键值 16 | /// 17 | public string Value { get; set; } 18 | 19 | /// 20 | /// 名称 21 | /// 22 | public string Name { get; set; } 23 | 24 | /// 25 | /// 数据类型(string,bool,int,html,decimal) 26 | /// 27 | public string Type { get; set; } 28 | 29 | /// 30 | /// 简称 31 | /// 32 | public string ShortName { get; set; } 33 | 34 | /// 35 | /// 显示说明 36 | /// 37 | public string Description { get; set; } 38 | 39 | /// 40 | /// 分组 41 | /// 42 | public string GroupName { get; set; } 43 | 44 | /// 45 | /// 列的排序权重 46 | /// 47 | public int? Order { get; set; } 48 | } 49 | 50 | /// 51 | public class DisplayItem : DisplayItem 52 | { 53 | /// 54 | /// 值 55 | /// 56 | public new TValue Id 57 | { 58 | get => (TValue)base.Id; 59 | set => base.Id = value; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/DisplayProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Dtos 6 | { 7 | public enum DisplayProperty 8 | { 9 | /// 10 | /// 名称 11 | /// 12 | Name, 13 | 14 | /// 15 | /// 短名称 16 | /// 17 | ShortName, 18 | 19 | /// 20 | /// 分组名称 21 | /// 22 | GroupName, 23 | 24 | /// 25 | /// 说明 26 | /// 27 | Description, 28 | 29 | /// 30 | /// 排序 31 | /// 32 | Order, 33 | 34 | /// 35 | /// 水印信息 36 | /// 37 | Prompt, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/EntityDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Dtos 6 | { 7 | 8 | [Serializable] 9 | public abstract class EntityDto : IEntityDto 10 | where TIdentity : IIdentity 11 | { 12 | /// 13 | /// Id of the entity. 14 | /// 15 | public TIdentity Id { get; set; } 16 | 17 | public override string ToString() 18 | { 19 | return $"[DTO: {GetType().Name}] Id = {Id}"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/ErrorResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Dtos 6 | { 7 | /// 8 | /// 错误结果 9 | /// 10 | public class ErrorResult : Result 11 | { 12 | /// 13 | public ErrorResult() : base(ResultCode.Fail) 14 | { 15 | Error = new Dictionary(); 16 | } 17 | 18 | /// 19 | public ErrorResult(IDictionary error, ResultCode code = ResultCode.Fail, string message = null) 20 | { 21 | Code = code; 22 | Message = message; 23 | Error = error; 24 | } 25 | 26 | /// 27 | /// 错误信息 28 | /// 29 | public IDictionary Error { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/IEntityDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Dtos 6 | { 7 | 8 | public interface IEntityDto 9 | where TIdentity : IIdentity 10 | { 11 | TIdentity Id { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/IPagingResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Queries; 5 | 6 | namespace Xpress.Core.Dtos 7 | { 8 | public interface IPagingResult : IPagingInfo 9 | { 10 | /// 11 | /// 总数 12 | /// 13 | int TotalCount { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/IResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Dtos 6 | { 7 | public interface IResult 8 | { 9 | /// 10 | /// 结果状态码 11 | /// 12 | ResultCode Code { get; set; } 13 | 14 | /// 15 | /// 提示信息 16 | /// 17 | /// 操作成功 18 | string Message { get; set; } 19 | 20 | /// 21 | /// 是否成功 22 | /// 23 | bool IsSuccess { get; } 24 | } 25 | 26 | /// 27 | /// 返回结果 28 | /// 29 | public interface IResult : IResult 30 | { 31 | /// 32 | /// 结果状态码 33 | /// 34 | TData Data { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Dtos/ResultCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Dtos 7 | { 8 | public enum ResultCode 9 | { /// 10 | /// 操作成功 11 | /// 12 | [Display(Name = "操作成功", GroupName = Result.SuccessCode)] 13 | Ok = 0, 14 | 15 | /// 16 | /// 操作失败 17 | /// 18 | [Display(Name = "操作失败")] 19 | Fail = 1, 20 | 21 | /// 22 | /// 服务数据异常 23 | /// 24 | [Display(Name = "服务数据异常")] 25 | ServerError = 10, 26 | 27 | /// 28 | /// 未登录 29 | /// 30 | [Display(Name = "未登录")] 31 | Unauthorized = 20, 32 | 33 | /// 34 | /// 未授权 35 | /// 36 | [Display(Name = "未授权")] 37 | Forbidden = 21, 38 | 39 | /// 40 | /// Token 失效 41 | /// 42 | [Display(Name = "Token 失效")] 43 | InvalidToken = 22 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EntityFramework/EfDbContextBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.EntityFramework 7 | { 8 | /// 9 | public abstract class EfDbContextBase : DbContext 10 | { 11 | /// 12 | /// Mark if DbContext has been released 13 | /// 14 | public bool IsDisposed { get; private set; } 15 | 16 | /// 17 | public EfDbContextBase(DbContextOptions options) : base(options) 18 | { 19 | 20 | } 21 | 22 | /// 23 | public override void Dispose() 24 | { 25 | IsDisposed = true; 26 | base.Dispose(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EntityFramework/EfDbContextProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using Xpress.Core.DependencyInjection; 3 | using Xpress.Core.Threading; 4 | 5 | namespace Xpress.Core.EntityFramework 6 | { 7 | /// 8 | public class EfDbContextProvider : IEfDbContextProvider 9 | { 10 | private class LocalDbContextWapper 11 | { 12 | public EfDbContextBase DbContext { get; set; } 13 | 14 | public IDbContextTransaction DbContextTransaction { get; set; } 15 | } 16 | 17 | private readonly IAsyncLocalObjectProvider _asyncLocalObjectProvider; 18 | private readonly IIocResolver _iocResolver; 19 | 20 | /// 21 | public EfDbContextProvider(IAsyncLocalObjectProvider asyncLocalObjectProvider, IIocResolver iocResolver) 22 | { 23 | _asyncLocalObjectProvider = asyncLocalObjectProvider; 24 | _iocResolver = iocResolver; 25 | } 26 | 27 | /// 28 | public EfDbContextBase GetDbContext() 29 | { 30 | var localDbContext = _asyncLocalObjectProvider.GetCurrent(); 31 | if (localDbContext == null || localDbContext.DbContext == null || localDbContext.DbContext.IsDisposed) 32 | { 33 | localDbContext = new LocalDbContextWapper() 34 | { 35 | DbContext = _iocResolver.Resolve() 36 | }; 37 | _asyncLocalObjectProvider.SetCurrent(localDbContext); 38 | } 39 | return localDbContext.DbContext; 40 | } 41 | 42 | /// 43 | public IDbContextTransaction DbContextTransaction 44 | { 45 | get => _asyncLocalObjectProvider.GetCurrent()?.DbContextTransaction; 46 | set => _asyncLocalObjectProvider.GetCurrent().DbContextTransaction = value; 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EntityFramework/IEfDbContextProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using Xpress.Core.DependencyInjection; 3 | 4 | namespace Xpress.Core.EntityFramework 5 | { 6 | /// 7 | /// DbContext provider 8 | /// 9 | public interface IEfDbContextProvider : ITransientDependency 10 | { 11 | /// 12 | /// Get data context operation object 13 | /// 14 | EfDbContextBase GetDbContext(); 15 | 16 | /// 17 | /// DbContext transaction 18 | /// 19 | IDbContextTransaction DbContextTransaction { get; set; } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Cap/CapEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using DotNetCore.CAP; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xpress.Core.DependencyInjection; 7 | 8 | namespace Xpress.Core.EventBus.Cap 9 | { 10 | public class CapEventPublisher : ICapEventPublisher, ITransientDependency 11 | { 12 | private readonly ICapPublisher _capPublisher; 13 | 14 | public CapEventPublisher(ICapPublisher capPublisher) 15 | { 16 | _capPublisher = capPublisher; 17 | } 18 | 19 | public async Task PublishAsync(TEvent eventData) where TEvent : IEvent 20 | { 21 | var eventName = eventData.GetEventName(); 22 | if (string.IsNullOrWhiteSpace(eventName)) 23 | { 24 | throw new ArgumentException($"{typeof(TEvent).FullName}.{nameof(IEvent.GetEventName)}() is null or empty"); 25 | } 26 | 27 | await _capPublisher.PublishAsync(eventName, eventData); 28 | } 29 | 30 | public async Task PublishAsync(Type eventType, IEvent eventData) 31 | { 32 | var eventName = eventData.GetEventName(); 33 | if (string.IsNullOrWhiteSpace(eventName)) 34 | { 35 | throw new ArgumentException($"{eventType.FullName}.{nameof(IEvent.GetEventName)}() is null or empty"); 36 | } 37 | 38 | await _capPublisher.PublishAsync(eventName, eventData); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Cap/CapTransactionExtensions.cs: -------------------------------------------------------------------------------- 1 | using DotNetCore.CAP; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.EventBus.Cap 7 | { 8 | public static class CapTransactionExtensions 9 | { 10 | public static ICapTransaction Begin(this ICapTransaction transaction, bool autoCommit = false) 11 | { 12 | transaction.AutoCommit = autoCommit; 13 | 14 | return transaction; 15 | } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Cap/DbContextBasedCapTransaction.cs: -------------------------------------------------------------------------------- 1 | using DotNetCore.CAP; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | using System.Diagnostics; 7 | using System.Text; 8 | using Xpress.Core.EntityFramework; 9 | 10 | namespace Xpress.Core.EventBus.Cap 11 | { 12 | public class DbContextBasedCapTransaction : CapTransactionBase 13 | { 14 | private readonly IEfDbContextProvider _dbContextProvider; 15 | 16 | public DbContextBasedCapTransaction(IDispatcher dispatcher, IEfDbContextProvider dbContextProvider) : base(dispatcher) 17 | { 18 | _dbContextProvider = dbContextProvider; 19 | } 20 | 21 | public override void Commit() 22 | { 23 | DbTransaction = DbTransaction ?? _dbContextProvider.DbContextTransaction; 24 | switch (DbTransaction) 25 | { 26 | case IDbTransaction dbTransaction: 27 | dbTransaction.Commit(); 28 | break; 29 | case IDbContextTransaction dbContextTransaction: 30 | dbContextTransaction.Commit(); 31 | break; 32 | } 33 | 34 | Flush(); 35 | } 36 | 37 | public override void Rollback() 38 | { 39 | DbTransaction = DbTransaction ?? _dbContextProvider.DbContextTransaction; 40 | switch (DbTransaction) 41 | { 42 | case IDbTransaction dbTransaction: 43 | dbTransaction.Rollback(); 44 | break; 45 | case IDbContextTransaction dbContextTransaction: 46 | dbContextTransaction.Rollback(); 47 | break; 48 | } 49 | } 50 | 51 | public override void Dispose() 52 | { 53 | (DbTransaction as IDbTransaction)?.Dispose(); 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Cap/ICapEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Xpress.Core.EventBus.Cap 7 | { 8 | public interface ICapEventPublisher 9 | { 10 | /// 11 | /// Triggers an event. 12 | /// 13 | /// Event type 14 | /// Related data for the event 15 | /// The task to handle async operation 16 | Task PublishAsync(TEvent @event) 17 | where TEvent : IEvent; 18 | 19 | /// 20 | /// Triggers an event. 21 | /// 22 | /// Event type 23 | /// Related data for the event 24 | /// The task to handle async operation 25 | Task PublishAsync(Type eventType, IEvent @event); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Cap/ICapEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | using DotNetCore.CAP; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.EventBus.Cap 7 | { 8 | public interface ICapEventSubscriber : ICapSubscribe 9 | { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Cap/NullCapPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Xpress.Core.EventBus.Cap 7 | { 8 | public class NullCapPublisher : ICapEventPublisher 9 | { 10 | public static NullCapPublisher Instance { get; } = new NullCapPublisher(); 11 | 12 | public async Task PublishAsync(TEvent @event) where TEvent : IEvent 13 | { 14 | await Task.CompletedTask; 15 | } 16 | 17 | public async Task PublishAsync(Type eventType, IEvent @event) 18 | { 19 | await Task.CompletedTask; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Distributed/IDistributedEventBus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Distributed 6 | { 7 | public interface IDistributedEventBus : IEventBus 8 | { 9 | /// 10 | /// Registers to an event. 11 | /// Same (given) instance of the handler is used for all event occurrences. 12 | /// 13 | /// Event type 14 | /// Object to handle the event 15 | IDisposable Subscribe(IDistributedEventHandler handler) 16 | where TEvent : class; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Distributed/IDistributedEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Xpress.Core.EventBus.Distributed 7 | { 8 | public interface IDistributedEventHandler : IEventHandler 9 | { 10 | /// 11 | /// Handler handles the event by implementing this method. 12 | /// 13 | /// Event data 14 | Task HandleEventAsync(TEvent eventData); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/EventBase.cs: -------------------------------------------------------------------------------- 1 | using Castle.Core.Internal; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Xpress.Core.Identity; 6 | 7 | namespace Xpress.Core.EventBus 8 | { 9 | public class EventBase : IEvent 10 | { 11 | /// 12 | /// 事件名称 13 | /// 14 | private readonly string _eventName; 15 | 16 | /// 17 | /// 事件Id 18 | /// 19 | public string EventId { get; set; } = GuidProvider.Comb.Create().ToString("N"); 20 | 21 | /// 22 | /// 事件发布时间 23 | /// 24 | public DateTime EventTime { get; set; } = DateTime.Now; 25 | 26 | public EventBase(string eventName= default(string)) 27 | { 28 | _eventName = eventName; 29 | } 30 | 31 | public virtual string GetEventName() 32 | { 33 | var eventName = _eventName; 34 | if (string.IsNullOrWhiteSpace(eventName)) 35 | { 36 | var eventType = base.GetType(); 37 | if (!eventType.IsGenericType) 38 | eventName = EventNameAttribute.GetNameOrDefault(eventType); 39 | else 40 | { 41 | var eventNameAttribute = GetType().GetAttribute(); 42 | eventName = eventNameAttribute.GetName(eventType); 43 | } 44 | } 45 | 46 | return eventName; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/EventNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Xpress.Core.Utils; 7 | 8 | namespace Xpress.Core.EventBus 9 | { 10 | [AttributeUsage(AttributeTargets.Class)] 11 | public class EventNameAttribute : Attribute, IEventNameProvider 12 | { 13 | public virtual string Name { get; } 14 | 15 | public EventNameAttribute([NotNull] string name) 16 | { 17 | Name = Check.NotNullOrWhiteSpace(name, nameof(name)); 18 | } 19 | 20 | public static string GetNameOrDefault() 21 | { 22 | return GetNameOrDefault(typeof(TEvent)); 23 | } 24 | 25 | public static string GetNameOrDefault([NotNull] Type eventType) 26 | { 27 | Check.NotNull(eventType, nameof(eventType)); 28 | 29 | return eventType.GetCustomAttributes(true) 30 | .OfType() 31 | .FirstOrDefault() 32 | ?.GetName(eventType) 33 | ?? eventType.FullName; 34 | } 35 | 36 | public string GetName(Type eventType) 37 | { 38 | return Name; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/GenericEventNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Exceptions; 5 | using Xpress.Core.Extensions; 6 | 7 | namespace Xpress.Core.EventBus 8 | { 9 | [AttributeUsage(AttributeTargets.Class)] 10 | public class GenericEventNameAttribute : Attribute, IEventNameProvider 11 | { 12 | public string Prefix { get; set; } 13 | 14 | public string Postfix { get; set; } 15 | 16 | public virtual string GetName(Type eventType) 17 | { 18 | if (!eventType.IsGenericType) 19 | { 20 | throw new XpressException($"Given type is not generic: {eventType.AssemblyQualifiedName}"); 21 | } 22 | 23 | var genericArguments = eventType.GetGenericArguments(); 24 | if (genericArguments.Length > 1) 25 | { 26 | throw new XpressException($"Given type has more than one generic argument: {eventType.AssemblyQualifiedName}"); 27 | } 28 | 29 | var eventName = EventNameAttribute.GetNameOrDefault(genericArguments[0]); 30 | 31 | if (!Prefix.IsNullOrEmpty()) 32 | { 33 | eventName = Prefix + eventName; 34 | } 35 | 36 | if (!Postfix.IsNullOrEmpty()) 37 | { 38 | eventName = eventName + Postfix; 39 | } 40 | 41 | return eventName; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/IEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus 6 | { 7 | public interface IEvent 8 | { 9 | /// 10 | /// 事件Id 11 | /// 12 | string EventId { get; set; } 13 | 14 | /// 15 | /// 事件发布时间 16 | /// 17 | DateTime EventTime { get; set; } 18 | 19 | 20 | /// 21 | /// 获取事件名称 22 | /// 23 | string GetEventName(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/IEventNameProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus 6 | { 7 | public interface IEventNameProvider 8 | { 9 | string GetName(Type eventType); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/ActionEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Xpress.Core.DependencyInjection; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | /// 8 | /// This event handler is an adapter to be able to use an action as implementation. 9 | /// 10 | /// Event type 11 | public class ActionEventHandler : ILocalEventHandler, ITransientDependency 12 | where TEvent : IEvent 13 | { 14 | /// 15 | /// Function to handle the event. 16 | /// 17 | public Func Action { get; } 18 | 19 | /// 20 | /// Creates a new instance of . 21 | /// 22 | /// Action to handle the event 23 | public ActionEventHandler(Func handler) 24 | { 25 | Action = handler; 26 | } 27 | 28 | /// 29 | /// Handles the event. 30 | /// 31 | /// 32 | public async Task HandleEventAsync(TEvent @event) 33 | { 34 | await Action(@event); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/EventHandlerDisposeWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | public class EventHandlerDisposeWrapper : IEventHandlerDisposeWrapper 8 | { 9 | public ILocalEventHandler EventHandler { get; } 10 | 11 | private readonly Action _disposeAction; 12 | 13 | public EventHandlerDisposeWrapper(ILocalEventHandler eventHandler, Action disposeAction = null) 14 | { 15 | _disposeAction = disposeAction; 16 | EventHandler = eventHandler; 17 | } 18 | 19 | public void Dispose() 20 | { 21 | _disposeAction?.Invoke(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/EventHandlerFactoryUnregistrar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | /// 8 | /// Used to unregister a on method. 9 | /// 10 | public class EventHandlerFactoryUnregistrar : IDisposable 11 | { 12 | private readonly ILocalEventBus _eventBus; 13 | private readonly Type _eventType; 14 | private readonly IEventHandlerFactory _factory; 15 | 16 | public EventHandlerFactoryUnregistrar(ILocalEventBus eventBus, Type eventType, IEventHandlerFactory factory) 17 | { 18 | _eventBus = eventBus; 19 | _eventType = eventType; 20 | _factory = factory; 21 | } 22 | 23 | public void Dispose() 24 | { 25 | _eventBus.Unsubscribe(_eventType, _factory); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/IEventDataWithInheritableGenericArgument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | /// 8 | /// This interface must be implemented by event data classes that 9 | /// has a single generic argument and this argument will be used by inheritance. 10 | /// 11 | /// For example; 12 | /// Assume that Student inherits From Person. When trigger an EntityCreatedEventData{Student}, 13 | /// EntityCreatedEventData{Person} is also triggered if EntityCreatedEventData implements 14 | /// this interface. 15 | /// 16 | public interface IEventDataWithInheritableGenericArgument 17 | { 18 | /// 19 | /// Gets arguments to create this class since a new instance of this class is created. 20 | /// 21 | /// Constructor arguments 22 | object[] GetConstructorArgs(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/IEventHandlerDisposeWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | public interface IEventHandlerDisposeWrapper : IDisposable 8 | { 9 | ILocalEventHandler EventHandler { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/IEventHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | public interface IEventHandlerFactory 8 | { 9 | /// 10 | /// Gets an event handler. 11 | /// 12 | /// The event handler 13 | IEventHandlerDisposeWrapper GetHandler(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/ILocalEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Xpress.Core.EventBus.Local 4 | { 5 | public interface ILocalEventHandler 6 | { 7 | } 8 | 9 | public interface ILocalEventHandler : ILocalEventHandler 10 | where TEvent : IEvent 11 | { 12 | /// 13 | /// Handler handles the event by implementing this method. 14 | /// 15 | /// Event data 16 | Task HandleEventAsync(TEvent @event); 17 | } 18 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/ILocalEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Xpress.Core.EventBus.Local 7 | { 8 | public interface ILocalEventPublisher 9 | { 10 | /// 11 | /// Triggers an event. 12 | /// 13 | /// Event type 14 | /// Related data for the event 15 | /// The task to handle async operation 16 | Task PublishAsync(TEvent @event) 17 | where TEvent : IEvent; 18 | 19 | /// 20 | /// Triggers an event. 21 | /// 22 | /// Event type 23 | /// Related data for the event 24 | /// The task to handle async operation 25 | Task PublishAsync(Type eventType, IEvent @event); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/IocEventHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Xpress.Core.DependencyInjection; 6 | 7 | namespace Xpress.Core.EventBus.Local 8 | { 9 | /// 10 | /// This implementation is used to get/release 11 | /// handlers using Ioc. 12 | /// 13 | public class IocEventHandlerFactory : IEventHandlerFactory, IDisposable 14 | { 15 | public Type HandlerType { get; } 16 | 17 | protected IServiceScope ServiceScope { get; } 18 | 19 | public IocEventHandlerFactory(IServiceScopeFactory scopeFactory, Type handlerType) 20 | { 21 | HandlerType = handlerType; 22 | ServiceScope = scopeFactory.CreateScope(); 23 | } 24 | 25 | /// 26 | /// Resolves handler object from Ioc container. 27 | /// 28 | /// Resolved handler object 29 | public IEventHandlerDisposeWrapper GetHandler() 30 | { 31 | var scope = ServiceScope.ServiceProvider.CreateScope(); 32 | return new EventHandlerDisposeWrapper( 33 | (ILocalEventHandler)scope.ServiceProvider.GetRequiredService(HandlerType), 34 | () => scope.Dispose() 35 | ); 36 | } 37 | 38 | public void Dispose() 39 | { 40 | ServiceScope.Dispose(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/LocalEventBusOptions.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | using Xpress.Core.Collections; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | public class LocalEventBusOptions 8 | { 9 | public ITypeList Handlers { get; } 10 | 11 | public LocalEventBusOptions() 12 | { 13 | Handlers = new TypeList(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/SingleInstanceHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | /// 8 | /// This implementation is used to handle events 9 | /// by a single instance object. 10 | /// 11 | /// 12 | /// This class always gets the same single instance of handler. 13 | /// 14 | public class SingleInstanceHandlerFactory : IEventHandlerFactory 15 | { 16 | /// 17 | /// The event handler instance. 18 | /// 19 | public ILocalEventHandler HandlerInstance { get; } 20 | 21 | /// 22 | /// 23 | /// 24 | /// 25 | public SingleInstanceHandlerFactory(ILocalEventHandler handler) 26 | { 27 | HandlerInstance = handler; 28 | } 29 | 30 | public IEventHandlerDisposeWrapper GetHandler() 31 | { 32 | return new EventHandlerDisposeWrapper(HandlerInstance); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Xpress.Core/EventBus/Local/TransientEventHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.EventBus.Local 6 | { 7 | /// 8 | /// This implementation is used to handle events 9 | /// by a transient instance object. 10 | /// 11 | /// 12 | /// This class always creates a new transient instance of handler. 13 | /// 14 | public class TransientEventHandlerFactory : IEventHandlerFactory 15 | where THandler : ILocalEventHandler, new() 16 | { 17 | /// 18 | /// Creates a new instance of the handler object. 19 | /// 20 | /// The handler object 21 | public IEventHandlerDisposeWrapper GetHandler() 22 | { 23 | var handler = new THandler(); 24 | return new EventHandlerDisposeWrapper(handler, () => (handler as IDisposable)?.Dispose()); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/BusinessException.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using Xpress.Core.Application; 7 | 8 | namespace Xpress.Core.Exceptions 9 | { 10 | [Serializable] 11 | public class BusinessException : Exception, 12 | IBusinessException, 13 | IHasErrorCode, 14 | IHasErrorDetails, 15 | IHasLogLevel 16 | { 17 | public string Code { get; set; } 18 | 19 | public string Details { get; set; } 20 | 21 | public LogLevel LogLevel { get; set; } 22 | 23 | public BusinessException( 24 | string code = null, 25 | string message = null, 26 | string details = null, 27 | Exception innerException = null, 28 | LogLevel logLevel = LogLevel.Warning) 29 | : base(message, innerException) 30 | { 31 | Code = code; 32 | Details = details; 33 | LogLevel = logLevel; 34 | } 35 | 36 | /// 37 | /// Constructor for serializing. 38 | /// 39 | public BusinessException(SerializationInfo serializationInfo, StreamingContext context) 40 | : base(serializationInfo, context) 41 | { 42 | 43 | } 44 | 45 | public BusinessException WithData(string name, object value) 46 | { 47 | Data[name] = value; 48 | return this; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/HttpRequestExtensions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.AspNetCore.Http; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using Xpress.Core.Utils; 7 | 8 | namespace Xpress.Core.Exceptions 9 | { 10 | public static class HttpRequestExtensions 11 | { 12 | private const string RequestedWithHeader = "X-Requested-With"; 13 | private const string XmlHttpRequest = "XMLHttpRequest"; 14 | 15 | public static bool IsAjax([NotNull]this HttpRequest request) 16 | { 17 | Check.NotNull(request, nameof(request)); 18 | 19 | if (request.Headers == null) 20 | { 21 | return false; 22 | } 23 | 24 | return request.Headers[RequestedWithHeader] == XmlHttpRequest; 25 | } 26 | 27 | public static bool CanAccept([NotNull]this HttpRequest request, [NotNull] string contentType) 28 | { 29 | Check.NotNull(request, nameof(request)); 30 | Check.NotNull(contentType, nameof(contentType)); 31 | 32 | return request.Headers["Accept"].ToString().Contains(contentType); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/IBusinessException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Exceptions 6 | { 7 | public interface IBusinessException 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/IUserFriendlyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Application; 5 | 6 | namespace Xpress.Core.Exceptions 7 | { 8 | public interface IUserFriendlyException : IBusinessException 9 | { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/OptimisticConcurrencyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Exceptions 6 | { 7 | public class OptimisticConcurrencyException : Exception 8 | { 9 | public OptimisticConcurrencyException(string message) 10 | : base(message) 11 | { 12 | } 13 | 14 | public OptimisticConcurrencyException(string message, Exception innerException) 15 | : base(message, innerException) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/UserFriendlyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Text; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Xpress.Core.Exceptions 8 | { 9 | /// 10 | /// This exception type is directly shown to the user. 11 | /// 12 | [Serializable] 13 | public class UserFriendlyException : BusinessException, IUserFriendlyException 14 | { 15 | public UserFriendlyException( 16 | string message, 17 | string code = null, 18 | string details = null, 19 | Exception innerException = null, 20 | LogLevel logLevel = LogLevel.Warning) 21 | : base( 22 | code, 23 | message, 24 | details, 25 | innerException, 26 | logLevel) 27 | { 28 | Details = details; 29 | } 30 | 31 | /// 32 | /// Constructor for serializing. 33 | /// 34 | public UserFriendlyException(SerializationInfo serializationInfo, StreamingContext context) 35 | : base(serializationInfo, context) 36 | { 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/XpressAuthorizationException.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | 7 | namespace Xpress.Core.Exceptions 8 | { 9 | /// 10 | /// This exception is thrown on an unauthorized request. 11 | /// 12 | [Serializable] 13 | public class XpressAuthorizationException : XpressException, IHasLogLevel 14 | { 15 | /// 16 | /// Severity of the exception. 17 | /// Default: Warn. 18 | /// 19 | public LogLevel LogLevel { get; set; } 20 | 21 | /// 22 | /// Creates a new object. 23 | /// 24 | public XpressAuthorizationException() 25 | { 26 | LogLevel = LogLevel.Warning; 27 | } 28 | 29 | /// 30 | /// Creates a new object. 31 | /// 32 | public XpressAuthorizationException(SerializationInfo serializationInfo, StreamingContext context) 33 | : base(serializationInfo, context) 34 | { 35 | 36 | } 37 | 38 | /// 39 | /// Creates a new object. 40 | /// 41 | /// Exception message 42 | public XpressAuthorizationException(string message) 43 | : base(message) 44 | { 45 | LogLevel = LogLevel.Warning; 46 | } 47 | 48 | /// 49 | /// Creates a new object. 50 | /// 51 | /// Exception message 52 | /// Inner exception 53 | public XpressAuthorizationException(string message, Exception innerException) 54 | : base(message, innerException) 55 | { 56 | LogLevel = LogLevel.Warning; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Exceptions/XpressException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Exceptions 7 | { 8 | /// 9 | /// Base exception type for those are thrown by Abp system for Abp specific exceptions. 10 | /// 11 | public class XpressException : Exception 12 | { 13 | /// 14 | /// Creates a new object. 15 | /// 16 | public XpressException() 17 | { 18 | 19 | } 20 | 21 | /// 22 | /// Creates a new object. 23 | /// 24 | /// Exception message 25 | public XpressException(string message) 26 | : base(message) 27 | { 28 | 29 | } 30 | 31 | /// 32 | /// Creates a new object. 33 | /// 34 | /// Exception message 35 | /// Inner exception 36 | public XpressException(string message, Exception innerException) 37 | : base(message, innerException) 38 | { 39 | 40 | } 41 | 42 | /// 43 | /// Constructor for serializing. 44 | /// 45 | public XpressException(SerializationInfo serializationInfo, StreamingContext context) 46 | : base(serializationInfo, context) 47 | { 48 | 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Extensions/CancellationTokenProviderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using Xpress.Core.Threading; 6 | 7 | namespace Xpress.Core.Extensions 8 | { 9 | public static class CancellationTokenProviderExtensions 10 | { 11 | public static CancellationToken FallbackToProvider(this ICancellationTokenProvider provider, CancellationToken prefferedValue = default(CancellationToken)) 12 | { 13 | return prefferedValue == default(CancellationToken) || prefferedValue == CancellationToken.None 14 | ? provider.Token 15 | : prefferedValue; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Extensions/EventHandlerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Extensions 6 | { 7 | public static class EventHandlerExtensions 8 | { 9 | /// 10 | /// Raises given event safely with given arguments. 11 | /// 12 | /// The event handler 13 | /// Source of the event 14 | public static void InvokeSafely(this EventHandler eventHandler, object sender) 15 | { 16 | eventHandler.InvokeSafely(sender, EventArgs.Empty); 17 | } 18 | 19 | /// 20 | /// Raises given event safely with given arguments. 21 | /// 22 | /// The event handler 23 | /// Source of the event 24 | /// Event argument 25 | public static void InvokeSafely(this EventHandler eventHandler, object sender, EventArgs e) 26 | { 27 | eventHandler?.Invoke(sender, e); 28 | } 29 | 30 | /// 31 | /// Raises given event safely with given arguments. 32 | /// 33 | /// Type of the 34 | /// The event handler 35 | /// Source of the event 36 | /// Event argument 37 | public static void InvokeSafely(this EventHandler eventHandler, object sender, TEventArgs e) 38 | where TEventArgs : EventArgs 39 | { 40 | eventHandler?.Invoke(sender, e); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Extensions/ExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Runtime.ExceptionServices; 5 | using System.Text; 6 | 7 | namespace Xpress.Core.Extensions 8 | { 9 | /// 10 | /// Extension methods for class. 11 | /// 12 | public static class ExceptionExtensions 13 | { 14 | /// 15 | /// Uses method to re-throws exception 16 | /// while preserving stack trace. 17 | /// 18 | /// Exception to be re-thrown 19 | public static void ReThrow(this Exception exception) 20 | { 21 | ExceptionDispatchInfo.Capture(exception).Throw(); 22 | } 23 | 24 | /// 25 | /// Try to get a log level from the given 26 | /// if it implements the interface. 27 | /// Otherwise, returns the . 28 | /// 29 | /// 30 | /// 31 | /// 32 | public static LogLevel GetLogLevel(this Exception exception, LogLevel defaultLevel = LogLevel.Error) 33 | { 34 | return (exception as IHasLogLevel)?.LogLevel ?? defaultLevel; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Xpress.Core.Extensions 8 | { 9 | /// 10 | /// Extension methods for all objects. 11 | /// 12 | public static class ObjectExtensions 13 | { 14 | /// 15 | /// Used to simplify and beautify casting an object to a type. 16 | /// 17 | /// Type to be casted 18 | /// Object to cast 19 | /// Casted object 20 | public static T As(this object obj) 21 | where T : class 22 | { 23 | return (T)obj; 24 | } 25 | 26 | /// 27 | /// Converts given object to a value type using method. 28 | /// 29 | /// Object to be converted 30 | /// Type of the target object 31 | /// Converted object 32 | public static T To(this object obj) 33 | where T : struct 34 | { 35 | return (T)Convert.ChangeType(obj, typeof(T), CultureInfo.InvariantCulture); 36 | } 37 | 38 | /// 39 | /// Check if an item is in a list. 40 | /// 41 | /// Item to check 42 | /// List of items 43 | /// Type of the items 44 | public static bool IsIn(this T item, params T[] list) 45 | { 46 | return list.Contains(item); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Http/RemoteServiceErrorInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Http 6 | { 7 | /// 8 | /// Used to store information about an error. 9 | /// 10 | [Serializable] 11 | public class RemoteServiceErrorInfo 12 | { 13 | /// 14 | /// Error code. 15 | /// 16 | public string Code { get; set; } 17 | 18 | /// 19 | /// Error message. 20 | /// 21 | public string Message { get; set; } 22 | 23 | /// 24 | /// Error details. 25 | /// 26 | public string Details { get; set; } 27 | 28 | /// 29 | /// Validation errors if exists. 30 | /// 31 | public RemoteServiceValidationErrorInfo[] ValidationErrors { get; set; } 32 | 33 | /// 34 | /// Creates a new instance of . 35 | /// 36 | public RemoteServiceErrorInfo() 37 | { 38 | 39 | } 40 | 41 | /// 42 | /// Creates a new instance of . 43 | /// 44 | /// Error code 45 | /// Error details 46 | /// Error message 47 | public RemoteServiceErrorInfo(string message, string details = null, string code = null) 48 | { 49 | Message = message; 50 | Details = details; 51 | Code = code; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Http/RemoteServiceErrorResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Http 6 | { 7 | public class RemoteServiceErrorResponse 8 | { 9 | public RemoteServiceErrorInfo Error { get; set; } 10 | 11 | public RemoteServiceErrorResponse(RemoteServiceErrorInfo error) 12 | { 13 | Error = error; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Http/RemoteServiceValidationErrorInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Http 6 | { 7 | /// 8 | /// Used to store information about a validation error. 9 | /// 10 | [Serializable] 11 | public class RemoteServiceValidationErrorInfo 12 | { 13 | /// 14 | /// Validation error message. 15 | /// 16 | public string Message { get; set; } 17 | 18 | /// 19 | /// Relate invalid members (fields/properties). 20 | /// 21 | public string[] Members { get; set; } 22 | 23 | /// 24 | /// Creates a new instance of . 25 | /// 26 | public RemoteServiceValidationErrorInfo() 27 | { 28 | 29 | } 30 | 31 | /// 32 | /// Creates a new instance of . 33 | /// 34 | /// Validation error message 35 | public RemoteServiceValidationErrorInfo(string message) 36 | { 37 | Message = message; 38 | } 39 | 40 | /// 41 | /// Creates a new instance of . 42 | /// 43 | /// Validation error message 44 | /// Related invalid members 45 | public RemoteServiceValidationErrorInfo(string message, string[] members) 46 | : this(message) 47 | { 48 | Members = members; 49 | } 50 | 51 | /// 52 | /// Creates a new instance of . 53 | /// 54 | /// Validation error message 55 | /// Related invalid member 56 | public RemoteServiceValidationErrorInfo(string message, string member) 57 | : this(message, new[] { member }) 58 | { 59 | 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Http/XpressHttpConsts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Http 6 | { 7 | public static class XpressHttpConsts 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Source/Xpress.Core/IHasErrorCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core 6 | { 7 | public interface IHasErrorCode 8 | { 9 | string Code { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/IHasErrorDetails.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core 6 | { 7 | public interface IHasErrorDetails 8 | { 9 | string Details { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/IHasLogLevel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Xpress.Core 7 | { 8 | /// 9 | /// Interface to define a property (see ). 10 | /// 11 | public interface IHasLogLevel 12 | { 13 | /// 14 | /// Log severity. 15 | /// 16 | LogLevel LogLevel { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Xpress.Core/IHasValidationErrors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace Xpress.Core 7 | { 8 | public interface IHasValidationErrors 9 | { 10 | IList ValidationErrors { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Identity/IGuidGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Identity 6 | { 7 | /// 8 | /// Used to generate Ids. 9 | /// 10 | public interface IGuidGenerator 11 | { 12 | /// 13 | /// Creates a new . 14 | /// 15 | Guid Create(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Identity/IIdentity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core 6 | { 7 | public interface IIdentity 8 | { 9 | string Value { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Identity/SequentialGuidType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Identity 6 | { 7 | /// 8 | /// Describes the type of a sequential GUID value. 9 | /// 10 | public enum SequentialGuidType 11 | { 12 | /// 13 | /// The GUID should be sequential when formatted using the method. 14 | /// Used by MySql and PostgreSql. 15 | /// 16 | SequentialAsString, 17 | 18 | /// 19 | /// The GUID should be sequential when formatted using the method. 20 | /// Used by Oracle. 21 | /// 22 | SequentialAsBinary, 23 | 24 | /// 25 | /// The sequential portion of the GUID should be located at the end of the Data4 block. 26 | /// Used by SqlServer. 27 | /// 28 | SequentialAtEnd 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Xpress.Core/NullDisposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core 6 | { 7 | public sealed class NullDisposable : IDisposable 8 | { 9 | public static NullDisposable Instance { get; } = new NullDisposable(); 10 | 11 | private NullDisposable() 12 | { 13 | 14 | } 15 | 16 | public void Dispose() 17 | { 18 | 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/IPagingInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Queries 6 | { 7 | public interface IPagingInfo 8 | { 9 | /// 10 | /// 页号,从 1 开始 11 | /// 12 | int PageIndex { get; set; } 13 | 14 | /// 15 | /// 分页大小 16 | /// 17 | int PageSize { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/IPagingQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Queries 6 | { 7 | public interface IPagingQuery : IPagingSortInfo, IQuery 8 | { 9 | 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/IPagingSortInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Queries 6 | { 7 | public interface IPagingSortInfo : IPagingInfo, ISortInfo 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/IQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Queries 7 | { 8 | public interface IQuery 9 | { 10 | /// 11 | /// 获取查询条件 12 | /// 13 | /// 要查询的实体类型 14 | Expression> GetFilter() where TEntity : class; 15 | 16 | void And(Expression> filter) where TEntity : class; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/ISortInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Queries 6 | { 7 | /// 8 | /// 排序信息 9 | /// 10 | public interface ISortInfo 11 | { 12 | /// 13 | /// 排序字段 14 | /// 15 | /// ['SortNo desc', 'CreateTime desc'] 16 | IList SortFields { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/PagingInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Queries 6 | { 7 | public class PagingInfo : IPagingInfo 8 | { 9 | /// 10 | /// 页号,从 1 开始 11 | /// 12 | public int PageIndex { get; set; } 13 | 14 | /// 15 | /// 分页大小 16 | /// 17 | public int PageSize { get; set; } 18 | 19 | /// 20 | /// 初始化 类的新实例。 21 | /// 22 | public PagingInfo(int pageIndex = 1, int pageSize = 10) 23 | { 24 | PageIndex = pageIndex; 25 | PageSize = pageSize; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/PagingQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Text; 5 | using Xpress.Core.Extensions; 6 | 7 | namespace Xpress.Core.Queries 8 | { 9 | public class PagingQuery : PagingSortInfo, IPagingQuery 10 | { 11 | /// 12 | /// 指定查询条件 13 | /// 14 | protected LambdaExpression Filter { get; set; } 15 | 16 | /// 17 | /// 并且 18 | /// 19 | public virtual void And(Expression> filter) where TEntity : class 20 | { 21 | Filter = (Filter as Expression>).And(filter); 22 | } 23 | 24 | /// 25 | /// 获取查询条件 26 | /// 27 | /// 要查询的实体类型 28 | public Expression> GetFilter() where TEntity : class 29 | { 30 | return (Filter as Expression>).And(this.GetQueryExpression()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/PagingSortInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Queries 6 | { 7 | /// 8 | /// 分页排序信息 9 | /// 10 | public class PagingSortInfo : PagingInfo, IPagingSortInfo 11 | { 12 | /// 13 | /// 分页排序信息 14 | /// 15 | public PagingSortInfo() 16 | { 17 | SortFields = new List(); 18 | } 19 | 20 | /// 21 | /// 分页排序信息 22 | /// 23 | public PagingSortInfo(int pageIndex, int pageSize = 10, IEnumerable sort = null) 24 | { 25 | PageSize = pageSize; 26 | PageIndex = pageIndex; 27 | 28 | var sortFields = new List(); 29 | if (sort != null) sortFields.AddRange(sort); 30 | 31 | SortFields = sortFields; 32 | } 33 | 34 | /// 35 | /// 排序字段 36 | /// 37 | /// ['SortNo desc', 'CreateTime desc'] 38 | public IList SortFields { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/Query.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Text; 5 | using Xpress.Core.Extensions; 6 | 7 | namespace Xpress.Core.Queries 8 | { 9 | public class Query : IQuery 10 | { 11 | /// 12 | /// 指定查询条件 13 | /// 14 | protected LambdaExpression Filter { get; set; } 15 | 16 | /// 17 | /// 并且 18 | /// 19 | public virtual void And(Expression> filter) where TEntity : class 20 | { 21 | Filter = (Filter as Expression>).And(filter); 22 | } 23 | 24 | /// 25 | /// 获取查询条件 26 | /// 27 | /// 要查询的实体类型 28 | public Expression> GetFilter() where TEntity : class 29 | { 30 | return (Filter as Expression>).And(this.GetQueryExpression()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/QueryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Extensions; 5 | 6 | namespace Xpress.Core.Queries 7 | { 8 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] 9 | public class QueryAttribute : Attribute 10 | { 11 | /// 12 | /// 比较操作符 13 | /// 14 | public ComparisonOperator ComparisonOperator { get; set; } 15 | 16 | /// 17 | /// 对应属性路径 18 | /// 19 | public string[] PropertyPath { get; set; } 20 | 21 | /// 22 | /// 或条件组 23 | /// 24 | public string OrGroup { get; set; } 25 | 26 | /// 27 | /// 查询字段 28 | /// 29 | public QueryAttribute(params string[] propertyPath) 30 | { 31 | PropertyPath = propertyPath; 32 | } 33 | 34 | /// 35 | /// 查询字段 36 | /// 37 | public QueryAttribute(ComparisonOperator comparisonOperator, params string[] propertyPath) 38 | { 39 | PropertyPath = propertyPath; 40 | ComparisonOperator = comparisonOperator; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Queries/SortQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Queries 6 | { 7 | /// 8 | /// 排序查询 9 | /// 10 | public class SortQuery : Query, ISortInfo 11 | { 12 | /// 13 | public IList SortFields { get; set; } = new List(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/Xpress.Core/RecurringJobs/IReccuringJobManager.cs: -------------------------------------------------------------------------------- 1 | using Hangfire.RecurringJobExtensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.RecurringJobs 7 | { 8 | public interface IReccuringJobManager 9 | { 10 | /// 11 | /// Builds Hangfire.RecurringJob automatically within specified interface or class. 12 | /// 13 | void AddOrUpdate() where TJob : IRecurringJob; 14 | 15 | /// 16 | /// Builds Hangfire.RecurringJob automatically within specified interface or class. 17 | /// 18 | void AddOrUpdate(params Type[] types); 19 | 20 | /// 21 | /// Builds Hangfire.RecurringJob automatically within specified interface or class. 22 | /// 23 | void AddOrUpdate(Func> typesProvider); 24 | 25 | /// 26 | /// Builds Hangfire.RecurringJob automatically by using a JSON configuration. 27 | /// 28 | void AddOrUpdate(string[] jsonFiles, bool reloadOnChange = true); 29 | 30 | /// 31 | /// Builds Hangfire.RecurringJob automatically with the array of Hangfire.RecurringJobExtensions.RecurringJobInfo. 32 | /// 33 | void AddOrUpdate(params RecurringJobInfo[] recurringJobInfos); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Xpress.Core/RecurringJobs/ReccuringJobManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Hangfire.RecurringJobExtensions; 5 | using Xpress.Core.DependencyInjection; 6 | 7 | namespace Xpress.Core.RecurringJobs 8 | { 9 | public class ReccuringJobManager : IReccuringJobManager, ISingletonDependency 10 | { 11 | /// 12 | /// Builds Hangfire.RecurringJob automatically within specified interface or class. 13 | /// 14 | public void AddOrUpdate() where TJob : IRecurringJob 15 | { 16 | CronJob.AddOrUpdate(typeof(TJob)); 17 | } 18 | 19 | /// 20 | /// Builds Hangfire.RecurringJob automatically within specified interface or class. 21 | /// 22 | public void AddOrUpdate(params Type[] types) 23 | { 24 | CronJob.AddOrUpdate(types); 25 | } 26 | 27 | /// 28 | /// Builds Hangfire.RecurringJob automatically within specified interface or class. 29 | /// 30 | public void AddOrUpdate(Func> typesProvider) 31 | { 32 | CronJob.AddOrUpdate(typesProvider); 33 | } 34 | 35 | /// 36 | /// Builds Hangfire.RecurringJob automatically by using a JSON configuration. 37 | /// 38 | public void AddOrUpdate(string[] jsonFiles, bool reloadOnChange = true) 39 | { 40 | CronJob.AddOrUpdate(jsonFiles,reloadOnChange); 41 | } 42 | 43 | /// 44 | /// Builds Hangfire.RecurringJob automatically with the array of Hangfire.RecurringJobExtensions.RecurringJobInfo. 45 | /// 46 | public void AddOrUpdate(params RecurringJobInfo[] recurringJobInfos) 47 | { 48 | CronJob.AddOrUpdate(recurringJobInfos); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Repositories/IRepository.cs: -------------------------------------------------------------------------------- 1 | using Xpress.Core.DependencyInjection; 2 | 3 | namespace Xpress.Core.Repositories 4 | { 5 | /// 6 | /// This interface must be implemented by all repositories to identify them by convention. 7 | /// Implement generic version instead of this one. 8 | /// 9 | public interface IRepository : ITransientDependency 10 | { 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/RetryStrategies/IOptimisticConcurrencyRetryStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.RetryStrategies 6 | { 7 | public interface IOptimisticConcurrencyRetryStrategy : IRetryStrategy 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Source/Xpress.Core/RetryStrategies/IRetryStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.RetryStrategies 6 | { 7 | public interface IRetryStrategy 8 | { 9 | Retry ShouldThisBeRetried(Exception exception, TimeSpan totalExecutionTime, int currentRetryCount); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/Xpress.Core/RetryStrategies/OptimisticConcurrencyRetryOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.RetryStrategies 6 | { 7 | public class OptimisticConcurrencyRetryOption 8 | { 9 | public int NumberOfRetriesOnOptimisticConcurrency { get; set; } 10 | 11 | public TimeSpan DelayBeforeRetryOnOptimisticConcurrency { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/Xpress.Core/RetryStrategies/OptimisticConcurrencyRetryStrategy.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Xpress.Core.Exceptions; 6 | 7 | namespace Xpress.Core.RetryStrategies 8 | { 9 | public class OptimisticConcurrencyRetryStrategy : IOptimisticConcurrencyRetryStrategy 10 | { 11 | private readonly OptimisticConcurrencyRetryOption _optimisticConcurrencyRetryOption; 12 | 13 | public OptimisticConcurrencyRetryStrategy(IOptions optimisticConcurrencyRetryOption) 14 | { 15 | _optimisticConcurrencyRetryOption = optimisticConcurrencyRetryOption.Value; 16 | } 17 | 18 | public Retry ShouldThisBeRetried(Exception exception, TimeSpan totalExecutionTime, int currentRetryCount) 19 | { 20 | if (!(exception is OptimisticConcurrencyException)) 21 | { 22 | return Retry.No; 23 | } 24 | 25 | return _optimisticConcurrencyRetryOption.NumberOfRetriesOnOptimisticConcurrency >= currentRetryCount 26 | ? Retry.YesAfter(_optimisticConcurrencyRetryOption.DelayBeforeRetryOnOptimisticConcurrency) 27 | : Retry.No; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Xpress.Core/RetryStrategies/Retry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.ValueObjects; 5 | 6 | namespace Xpress.Core.RetryStrategies 7 | { 8 | public class Retry : ValueObject 9 | { 10 | public static Retry Yes { get; } = new Retry(true, TimeSpan.Zero); 11 | public static Retry YesAfter(TimeSpan retryAfter) => new Retry(true, retryAfter); 12 | public static Retry No { get; } = new Retry(false, TimeSpan.Zero); 13 | 14 | public bool ShouldBeRetried { get; } 15 | public TimeSpan RetryAfter { get; } 16 | 17 | private Retry(bool shouldBeRetried, TimeSpan retryAfter) 18 | { 19 | if (retryAfter != TimeSpan.Zero && retryAfter != retryAfter.Duration()) 20 | throw new ArgumentOutOfRangeException(nameof(retryAfter)); 21 | if (!shouldBeRetried && retryAfter != TimeSpan.Zero) 22 | throw new ArgumentException("Invalid combination. Should not be retried and retry after set"); 23 | 24 | ShouldBeRetried = shouldBeRetried; 25 | RetryAfter = retryAfter; 26 | } 27 | 28 | protected override IEnumerable GetEqualityComponents() 29 | { 30 | yield return ShouldBeRetried; 31 | yield return RetryAfter; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Runtime/ClaimsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Claims; 5 | using System.Text; 6 | 7 | namespace Xpress.Core.Runtime 8 | { 9 | /// 10 | /// 扩展方法 11 | /// 12 | public static class ClaimsExtensions 13 | { 14 | /// 15 | /// 获取单个值 16 | /// 17 | public static string GetValue(this IEnumerable claims, string type) 18 | { 19 | return claims.FirstOrDefault(p => p.Type == type)?.Value; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Runtime/ClaimsUserSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Claims; 4 | using System.Text; 5 | using Xpress.Core.DependencyInjection; 6 | 7 | namespace Xpress.Core.Runtime 8 | { 9 | /// 10 | /// 使用 获取当前用户 11 | /// 12 | public class ClaimsUserSession : IUserSession, ITransientDependency 13 | { 14 | /// 15 | public string UserId => _principalAccessor.Principal?.Claims.GetValue(XpressClaimTypes.UserIdentity); 16 | 17 | /// 18 | public string UserName => _principalAccessor.Principal?.Claims.GetValue(ClaimTypes.GivenName); 19 | 20 | /// 21 | public string UserType => _principalAccessor.Principal?.Claims.GetValue(XpressClaimTypes.UserType); 22 | 23 | 24 | private readonly ICurrentPrincipalAccessor _principalAccessor; 25 | 26 | /// 27 | /// 创建 28 | /// 29 | public ClaimsUserSession(ICurrentPrincipalAccessor principalAccessor) 30 | { 31 | _principalAccessor = principalAccessor; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Runtime/ICurrentPrincipalAccessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Claims; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Runtime 7 | { 8 | /// 9 | /// 获取当前身份凭证 接口 10 | /// 11 | public interface ICurrentPrincipalAccessor 12 | { 13 | /// 14 | /// 获取当前身份凭证 15 | /// 16 | ClaimsPrincipal Principal { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Runtime/IUserSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Runtime 6 | { 7 | public interface IUserSession 8 | { 9 | /// 10 | /// 获取当前用户 11 | /// 12 | string UserId { get; } 13 | 14 | /// 15 | /// 获取当前用户名称 16 | /// 17 | string UserName { get; } 18 | 19 | /// 20 | /// 获取当前用户类型 21 | /// 22 | string UserType { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Runtime/NullUserSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Runtime 6 | { 7 | /// 8 | public class NullUserSession : IUserSession 9 | { 10 | /// 11 | /// 单例模式 12 | /// 13 | public static NullUserSession Instanse = new NullUserSession(); 14 | 15 | /// 16 | public string UserId { get; } = null; 17 | 18 | /// 19 | public string UserName { get; } = null; 20 | 21 | /// 22 | public string UserType { get; } = null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Runtime/ThreadCurrentPrincipalAccessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Claims; 4 | using System.Text; 5 | using System.Threading; 6 | using Xpress.Core.DependencyInjection; 7 | 8 | namespace Xpress.Core.Runtime 9 | { 10 | public class ThreadCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency 11 | { 12 | public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Runtime/XpressClaimTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Runtime 6 | { 7 | public static class XpressClaimTypes 8 | { 9 | /// 10 | /// 用户类型 11 | /// 12 | public static string UserType = "Xpress.UserType"; 13 | 14 | /// 15 | /// 用户标识 16 | /// 17 | public static string UserIdentity = "Xpress.UserIdentity"; 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Serialization/IJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Serialization 6 | { 7 | public interface IJsonSerializer 8 | { 9 | string Serialize(object obj, bool camelCase = true, bool indented = false); 10 | 11 | T Deserialize(string jsonString, bool camelCase = true); 12 | 13 | object Deserialize(Type type, string jsonString, bool camelCase = true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Serialization/IObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Serialization 6 | { 7 | public interface IObjectSerializer 8 | { 9 | byte[] Serialize(T obj); 10 | 11 | T Deserialize(byte[] bytes); 12 | } 13 | 14 | public interface IObjectSerializer 15 | { 16 | byte[] Serialize(T obj); 17 | 18 | T Deserialize(byte[] bytes); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Serialization/JsonIsoDateTimeConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using Xpress.Core.DependencyInjection; 7 | using Xpress.Core.Timing; 8 | 9 | namespace Xpress.Core.Serialization 10 | { 11 | public class JsonIsoDateTimeConverter : IsoDateTimeConverter, ITransientDependency 12 | { 13 | private readonly IClock _clock; 14 | 15 | public JsonIsoDateTimeConverter(IClock clock) 16 | { 17 | _clock = clock; 18 | } 19 | 20 | public override bool CanConvert(Type objectType) 21 | { 22 | if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) 23 | { 24 | return true; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 31 | { 32 | var date = base.ReadJson(reader, objectType, existingValue, serializer) as DateTime?; 33 | 34 | if (date.HasValue) 35 | { 36 | return _clock.Normalize(date.Value); 37 | } 38 | 39 | return null; 40 | } 41 | 42 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 43 | { 44 | var date = value as DateTime?; 45 | base.WriteJson(writer, date.HasValue ? _clock.Normalize(date.Value) : value, serializer); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Specifications/AllSpecifications.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Specifications 7 | { 8 | /// 9 | /// 全部满足 10 | /// 11 | /// 12 | public class AllSpecifications : Specification 13 | { 14 | private readonly IReadOnlyList> _specifications; 15 | 16 | public AllSpecifications(IEnumerable> specifications) 17 | { 18 | var specificationList = (specifications ?? Enumerable.Empty>()).ToList(); 19 | 20 | if (!specificationList.Any()) throw new ArgumentException("Please provide some specifications", nameof(specifications)); 21 | 22 | _specifications = specificationList; 23 | } 24 | 25 | protected override IEnumerable IsNotSatisfiedBecause(T obj) 26 | { 27 | return _specifications.SelectMany(s => s.WhyIsNotSatisfiedBy(obj)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Specifications/AndSpeficication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Specifications 7 | { 8 | public class AndSpeficication : Specification 9 | { 10 | private readonly ISpecification _specification1; 11 | private readonly ISpecification _specification2; 12 | 13 | public AndSpeficication( 14 | ISpecification specification1, 15 | ISpecification specification2) 16 | { 17 | _specification1 = specification1 ?? throw new ArgumentNullException(nameof(specification1)); 18 | _specification2 = specification2 ?? throw new ArgumentNullException(nameof(specification2)); 19 | } 20 | 21 | protected override IEnumerable IsNotSatisfiedBecause(T obj) 22 | { 23 | return _specification1.WhyIsNotSatisfiedBy(obj).Concat(_specification2.WhyIsNotSatisfiedBy(obj)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Specifications/ISpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Xpress.Core.Specifications 6 | { 7 | public interface ISpecification 8 | { 9 | bool IsSatisfiedBy(T obj); 10 | 11 | IEnumerable WhyIsNotSatisfiedBy(T obj); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Specifications/NotSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xpress.Core.Extensions; 5 | 6 | namespace Xpress.Core.Specifications 7 | { 8 | public class NotSpecification : Specification 9 | { 10 | private readonly ISpecification _specification; 11 | 12 | public NotSpecification( 13 | ISpecification specification) 14 | { 15 | _specification = specification ?? throw new ArgumentNullException(nameof(specification)); 16 | } 17 | 18 | protected override IEnumerable IsNotSatisfiedBecause(T obj) 19 | { 20 | if (_specification.IsSatisfiedBy(obj)) 21 | { 22 | yield return $"Specification '{_specification.GetType().PrettyPrint()}' should not be satisfied"; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Specifications/OrSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Specifications 7 | { 8 | public class OrSpecification : Specification 9 | { 10 | private readonly ISpecification _specification1; 11 | private readonly ISpecification _specification2; 12 | 13 | public OrSpecification( 14 | ISpecification specification1, 15 | ISpecification specification2) 16 | { 17 | _specification1 = specification1 ?? throw new ArgumentNullException(nameof(specification1)); 18 | _specification2 = specification2 ?? throw new ArgumentNullException(nameof(specification2)); 19 | } 20 | 21 | protected override IEnumerable IsNotSatisfiedBecause(T obj) 22 | { 23 | var reasons1 = _specification1.WhyIsNotSatisfiedBy(obj).ToList(); 24 | var reasons2 = _specification2.WhyIsNotSatisfiedBy(obj).ToList(); 25 | 26 | if (!reasons1.Any() || !reasons2.Any()) 27 | { 28 | return Enumerable.Empty(); 29 | } 30 | 31 | return reasons1.Concat(reasons2); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Specifications/Specification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Specifications 7 | { 8 | public abstract class Specification : ISpecification 9 | { 10 | public bool IsSatisfiedBy(T obj) 11 | { 12 | return !IsNotSatisfiedBecause(obj).Any(); 13 | } 14 | 15 | public IEnumerable WhyIsNotSatisfiedBy(T obj) 16 | { 17 | return IsNotSatisfiedBecause(obj); 18 | } 19 | 20 | protected abstract IEnumerable IsNotSatisfiedBecause(T obj); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Threading/AsyncHelper.cs: -------------------------------------------------------------------------------- 1 | using Nito.AsyncEx; 2 | using System; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | 6 | namespace Xpress.Core.Threading 7 | { 8 | /// 9 | /// Asynchronous method call helper class 10 | /// 11 | public static class AsyncHelper 12 | { 13 | /// 14 | /// Determine if it is an asynchronous method 15 | /// 16 | public static bool IsAsync(this MethodInfo method) 17 | { 18 | return ( 19 | method.ReturnType == typeof(Task) || 20 | (method.ReturnType.GetTypeInfo().IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) 21 | ); 22 | } 23 | 24 | /// 25 | /// Synchronously run asynchronous method 26 | /// 27 | /// Generic object type 28 | /// asynchronous method 29 | /// Return generic object 30 | public static TResult RunSync(Func> func) 31 | { 32 | return AsyncContext.Run(func); 33 | } 34 | 35 | /// /// Synchronously run asynchronous method 36 | /// 37 | public static void RunSync(Func action) 38 | { 39 | AsyncContext.Run(action); 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Threading/AsyncLocalObjectProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | 5 | namespace Xpress.Core.Threading 6 | { 7 | /// 8 | public class AsyncLocalObjectProvider : IAsyncLocalObjectProvider 9 | { 10 | private static readonly AsyncLocal> _asyncLocalObjects = new AsyncLocal>(); 11 | 12 | private List GetOrInitialize() 13 | { 14 | if (_asyncLocalObjects.Value == null) 15 | { 16 | _asyncLocalObjects.Value = new List(); 17 | } 18 | return _asyncLocalObjects.Value.Where(value => value != null).ToList(); 19 | } 20 | 21 | /// 22 | public T GetCurrent() where T : class 23 | { 24 | var item = GetOrInitialize().SingleOrDefault(o => o.GetType() == typeof(T)); 25 | if (item == null) 26 | { 27 | item = default(T); 28 | } 29 | return (T)item; 30 | } 31 | 32 | /// 33 | public void SetCurrent(T value) where T : class 34 | { 35 | var item = GetOrInitialize().SingleOrDefault(o => o.GetType() == typeof(T)); 36 | if (item != null) 37 | { 38 | _asyncLocalObjects.Value.Remove(item); 39 | } 40 | _asyncLocalObjects.Value.Add(value); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Threading/IAsyncLocalObjectProvider.cs: -------------------------------------------------------------------------------- 1 | using Xpress.Core.DependencyInjection; 2 | 3 | namespace Xpress.Core.Threading 4 | { 5 | /// 6 | /// Define an AsyncLocal based on the thread storage object 7 | /// 8 | public interface IAsyncLocalObjectProvider : ISingletonDependency 9 | { 10 | /// 11 | /// Get the T object in the current thread 12 | /// 13 | T GetCurrent() where T : class; 14 | 15 | /// 16 | /// Set the T type object to the current thread 17 | /// 18 | void SetCurrent(T value) where T : class; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Threading/ICancellationTokenProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace Xpress.Core.Threading 7 | { 8 | public interface ICancellationTokenProvider 9 | { 10 | CancellationToken Token { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Threading/NullCancellationTokenProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace Xpress.Core.Threading 7 | { 8 | public class NullCancellationTokenProvider : ICancellationTokenProvider 9 | { 10 | public static NullCancellationTokenProvider Instance { get; } = new NullCancellationTokenProvider(); 11 | 12 | public CancellationToken Token { get; } = default(CancellationToken); 13 | 14 | private NullCancellationTokenProvider() 15 | { 16 | 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Timing/Clock.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using System; 3 | using Xpress.Core.DependencyInjection; 4 | 5 | namespace Xpress.Core.Timing 6 | { 7 | public class Clock : IClock, ITransientDependency 8 | { 9 | protected ClockOptions Options { get; } 10 | 11 | public Clock(IOptions options) 12 | { 13 | Options = options.Value; 14 | } 15 | 16 | public virtual DateTime Now => Options.Kind == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.Now; 17 | 18 | public virtual DateTimeKind Kind => Options.Kind; 19 | 20 | public virtual bool SupportsMultipleTimezone => Options.Kind == DateTimeKind.Utc; 21 | 22 | public virtual DateTime Normalize(DateTime dateTime) 23 | { 24 | if (Kind == DateTimeKind.Unspecified || Kind == dateTime.Kind) 25 | { 26 | return dateTime; 27 | } 28 | 29 | if (Kind == DateTimeKind.Local && dateTime.Kind == DateTimeKind.Utc) 30 | { 31 | return dateTime.ToLocalTime(); 32 | } 33 | 34 | if (Kind == DateTimeKind.Utc && dateTime.Kind == DateTimeKind.Local) 35 | { 36 | return dateTime.ToUniversalTime(); 37 | } 38 | 39 | return DateTime.SpecifyKind(dateTime, Kind); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Timing/ClockOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Xpress.Core.Timing 4 | { 5 | public class ClockOptions 6 | { 7 | /// 8 | /// Default: 9 | /// 10 | public DateTimeKind Kind { get; set; } 11 | 12 | public ClockOptions() 13 | { 14 | Kind = DateTimeKind.Unspecified; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Timing/IClock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Xpress.Core.Timing 4 | { 5 | public interface IClock 6 | { 7 | /// 8 | /// Gets Now. 9 | /// 10 | DateTime Now { get; } 11 | 12 | /// 13 | /// Gets kind. 14 | /// 15 | DateTimeKind Kind { get; } 16 | 17 | /// 18 | /// Is that provider supports multiple time zone. 19 | /// 20 | bool SupportsMultipleTimezone { get; } 21 | 22 | /// 23 | /// Normalizes given . 24 | /// 25 | /// DateTime to be normalized. 26 | /// Normalized DateTime 27 | DateTime Normalize(DateTime dateTime); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/DefaultUnitOfWorkOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using Xpress.Core.Application; 5 | using Xpress.Core.Domain.Services; 6 | using Xpress.Core.Exceptions; 7 | using Xpress.Core.Repositories; 8 | 9 | namespace Xpress.Core.Uow 10 | { 11 | public class DefaultUnitOfWorkOptions : IUnitOfWorkOptions 12 | { 13 | /// 14 | /// Default: false. 15 | /// 16 | public bool IsTransactional { get; set; } 17 | 18 | 19 | public TimeSpan? Timeout { get; set; } 20 | 21 | /// 22 | /// If this UOW is transactional, this option indicated the isolation level of the transaction. 23 | /// Uses default value if not supplied. 24 | /// 25 | public IsolationLevel IsolationLevel { get; set; } = IsolationLevel.ReadCommitted; 26 | 27 | /// 28 | /// Apply uow type 29 | /// 30 | public List> ConventionalUowSelectors { get; set; } 31 | 32 | /// 33 | public DefaultUnitOfWorkOptions() 34 | { 35 | ConventionalUowSelectors = new List> 36 | { 37 | type => typeof(IRepository).IsAssignableFrom(type), 38 | type => typeof(IDomainService).IsAssignableFrom(type), 39 | type => typeof(IApplicationService).IsAssignableFrom(type) 40 | }; 41 | } 42 | 43 | public UnitOfWorkOptions Clone() 44 | { 45 | return new UnitOfWorkOptions 46 | { 47 | IsTransactional = IsTransactional, 48 | IsolationLevel = IsolationLevel, 49 | Timeout = Timeout 50 | }; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/ISupportsRollback.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Xpress.Core.Uow 5 | { 6 | public interface ISupportsRollback 7 | { 8 | void Rollback(); 9 | 10 | Task RollbackAsync(CancellationToken cancellationToken); 11 | } 12 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/ISupportsSavingChanges.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Xpress.Core.Uow 5 | { 6 | public interface ISupportsSavingChanges 7 | { 8 | void SaveChanges(); 9 | 10 | Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); 11 | } 12 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using JetBrains.Annotations; 5 | 6 | namespace Xpress.Core.Uow 7 | { 8 | public interface IUnitOfWork : IDisposable 9 | { 10 | Guid Id { get; } 11 | 12 | event EventHandler Failed; 13 | 14 | event EventHandler Disposed; 15 | 16 | IUnitOfWorkOptions UnitOfWorkOptions { get; } 17 | 18 | IUnitOfWork Outer { get; } 19 | 20 | void Reserve([NotNull] string reservationName); 21 | 22 | bool IsReserved { get; } 23 | 24 | string ReservationName { get; } 25 | 26 | void SetOuter([CanBeNull] IUnitOfWork outer); 27 | 28 | void Initialize([NotNull] IUnitOfWorkOptions unitOfWorkOptions); 29 | 30 | void SaveChanges(); 31 | 32 | Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); 33 | 34 | void Complete(); 35 | 36 | Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken)); 37 | 38 | void OnCompleted(Func handler); 39 | 40 | bool IsCompleted { get; } 41 | 42 | void Rollback(); 43 | 44 | bool IsDisposed { get; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/IUnitOfWorkAccessor.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Xpress.Core.Uow 4 | { 5 | public interface IUnitOfWorkAccessor 6 | { 7 | [CanBeNull] 8 | IUnitOfWork UnitOfWork { get; } 9 | 10 | void SetUnitOfWork([CanBeNull] IUnitOfWork unitOfWork); 11 | } 12 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/IUnitOfWorkEnabled.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.Uow 2 | { 3 | public interface IUnitOfWorkEnabled 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/IUnitOfWorkManager.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Xpress.Core.Uow 4 | { 5 | public interface IUnitOfWorkManager 6 | { 7 | [CanBeNull] 8 | IUnitOfWork Current { get; } 9 | 10 | [NotNull] 11 | IUnitOfWork Begin([NotNull] IUnitOfWorkOptions unitOfWorkOptions, bool requiresNew = false); 12 | 13 | [NotNull] 14 | IUnitOfWork Reserve([NotNull] string reservationName, bool requiresNew = false); 15 | 16 | void BeginReserved([NotNull] string reservationName, [NotNull] IUnitOfWorkOptions unitOfWorkOptions); 17 | 18 | bool TryBeginReserved([NotNull] string reservationName, [NotNull] IUnitOfWorkOptions unitOfWorkOptions); 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/IUnitOfWorkOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | 4 | namespace Xpress.Core.Uow 5 | { 6 | public interface IUnitOfWorkOptions 7 | { 8 | bool IsTransactional { get; set; } 9 | 10 | IsolationLevel IsolationLevel { get; set; } 11 | 12 | TimeSpan? Timeout { get; set; } 13 | 14 | UnitOfWorkOptions Clone(); 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/UnitOfWorkAccessor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using Xpress.Core.DependencyInjection; 3 | 4 | namespace Xpress.Core.Uow 5 | { 6 | public class UnitOfWorkAccessor : IUnitOfWorkAccessor, ISingletonDependency 7 | { 8 | public IUnitOfWork UnitOfWork => _currentUow.Value; 9 | 10 | private readonly AsyncLocal _currentUow; 11 | 12 | public UnitOfWorkAccessor() 13 | { 14 | _currentUow = new AsyncLocal(); 15 | } 16 | 17 | public void SetUnitOfWork(IUnitOfWork unitOfWork) 18 | { 19 | _currentUow.Value = unitOfWork; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/UnitOfWorkEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using Xpress.Core.Utils; 4 | 5 | namespace Xpress.Core.Uow 6 | { 7 | public class UnitOfWorkEventArgs : EventArgs 8 | { 9 | /// 10 | /// Reference to the unit of work related to this event. 11 | /// 12 | public IUnitOfWork UnitOfWork { get; } 13 | 14 | public UnitOfWorkEventArgs([NotNull] IUnitOfWork unitOfWork) 15 | { 16 | Check.NotNull(unitOfWork, nameof(unitOfWork)); 17 | 18 | UnitOfWork = unitOfWork; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/UnitOfWorkExtensions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Xpress.Core.Utils; 3 | 4 | namespace Xpress.Core.Uow 5 | { 6 | public static class UnitOfWorkExtensions 7 | { 8 | public static bool IsReservedFor([NotNull] this IUnitOfWork unitOfWork, string reservationName) 9 | { 10 | Check.NotNull(unitOfWork, nameof(unitOfWork)); 11 | 12 | return unitOfWork.IsReserved && unitOfWork.ReservationName == reservationName; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/UnitOfWorkFailedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace Xpress.Core.Uow 5 | { 6 | /// 7 | /// Used as event arguments on event. 8 | /// 9 | public class UnitOfWorkFailedEventArgs : UnitOfWorkEventArgs 10 | { 11 | /// 12 | /// Exception that caused failure. This is set only if an error occured during . 13 | /// Can be null if there is no exception, but is not called. 14 | /// Can be null if another exception occurred during the UOW. 15 | /// 16 | [CanBeNull] 17 | public Exception Exception { get; } 18 | 19 | /// 20 | /// True, if the unit of work is manually rolled back. 21 | /// 22 | public bool IsRolledback { get; } 23 | 24 | /// 25 | /// Creates a new object. 26 | /// 27 | public UnitOfWorkFailedEventArgs([NotNull] IUnitOfWork unitOfWork, [CanBeNull] Exception exception, bool isRolledback) 28 | : base(unitOfWork) 29 | { 30 | Exception = exception; 31 | IsRolledback = isRolledback; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/UnitOfWorkOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Text; 5 | 6 | namespace Xpress.Core.Uow 7 | { 8 | public class UnitOfWorkOptions : IUnitOfWorkOptions 9 | { 10 | /// 11 | /// Default: false. 12 | /// 13 | public bool IsTransactional { get; set; } 14 | 15 | 16 | public TimeSpan? Timeout { get; set; } 17 | 18 | /// 19 | /// If this UOW is transactional, this option indicated the isolation level of the transaction. 20 | /// Uses default value if not supplied. 21 | /// 22 | public IsolationLevel IsolationLevel { get; set; } = IsolationLevel.ReadCommitted; 23 | 24 | public UnitOfWorkOptions Clone() 25 | { 26 | return new UnitOfWorkOptions 27 | { 28 | IsTransactional = IsTransactional, 29 | IsolationLevel = IsolationLevel, 30 | Timeout = Timeout 31 | }; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Xpress.Core/Uow/UnitOfWorkTransactionBehavior.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.Uow 2 | { 3 | public enum UnitOfWorkTransactionBehavior 4 | { 5 | Auto, 6 | 7 | Enabled, 8 | 9 | Disabled 10 | } 11 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Utils/CastleProxyHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | 7 | namespace Xpress.Core.Utils 8 | { 9 | public static class CastleProxyHelper 10 | { 11 | /// 12 | /// Returns dynamic proxy target object if this is a proxied object, otherwise returns the given object. 13 | /// It supports Castle Dynamic Proxies. 14 | /// 15 | public static object UnProxy(object obj) 16 | { 17 | if (obj.GetType().Namespace != "Castle.Proxies") 18 | { 19 | return obj; 20 | } 21 | 22 | var targetField = obj.GetType() 23 | .GetFields(BindingFlags.Instance | BindingFlags.NonPublic) 24 | .FirstOrDefault(f => f.Name == "__target"); 25 | 26 | if (targetField == null) 27 | { 28 | return obj; 29 | } 30 | 31 | return targetField.GetValue(obj); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Xpress.Core/ValueObjects/ISingleValueObject.cs: -------------------------------------------------------------------------------- 1 | namespace Xpress.Core.ValueObjects 2 | { 3 | public interface ISingleValueObject 4 | { 5 | object GetValue(); 6 | } 7 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/ValueObjects/SingleValueObject.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using Xpress.Core.Extensions; 6 | 7 | namespace Xpress.Core.ValueObjects 8 | 9 | { 10 | public abstract class SingleValueObject : ValueObject, IComparable, ISingleValueObject 11 | where T : IComparable 12 | { 13 | private static readonly Type Type = typeof(T); 14 | private static readonly TypeInfo TypeInfo = typeof(T).GetTypeInfo(); 15 | 16 | public T Value { get; } 17 | 18 | protected SingleValueObject(T value) 19 | { 20 | if (TypeInfo.IsEnum && !Enum.IsDefined(Type, value)) 21 | { 22 | throw new ArgumentException($"The value '{value}' isn't defined in enum '{Type.PrettyPrint()}'"); 23 | } 24 | 25 | Value = value; 26 | } 27 | 28 | public int CompareTo(object obj) 29 | { 30 | if (ReferenceEquals(null, obj)) 31 | { 32 | throw new ArgumentNullException(nameof(obj)); 33 | } 34 | 35 | var other = obj as SingleValueObject; 36 | if (other == null) 37 | { 38 | throw new ArgumentException($"Cannot compare '{GetType().PrettyPrint()}' and '{obj.GetType().PrettyPrint()}'"); 39 | } 40 | 41 | return Value.CompareTo(other.Value); 42 | } 43 | 44 | protected override IEnumerable GetEqualityComponents() 45 | { 46 | yield return Value; 47 | } 48 | 49 | public override string ToString() 50 | { 51 | return ReferenceEquals(Value, null) 52 | ? string.Empty 53 | : Value.ToString(); 54 | } 55 | 56 | public object GetValue() 57 | { 58 | return Value; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Source/Xpress.Core/ValueObjects/SingleValueObjectConverter.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Newtonsoft.Json; 7 | 8 | namespace Xpress.Core.ValueObjects 9 | { 10 | public class SingleValueObjectConverter : JsonConverter 11 | { 12 | private static readonly ConcurrentDictionary ConstructorArgumenTypes = new ConcurrentDictionary(); 13 | 14 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 15 | { 16 | var singleValueObject = value as ISingleValueObject; 17 | if (singleValueObject == null) 18 | { 19 | return; 20 | } 21 | serializer.Serialize(writer, singleValueObject.GetValue()); 22 | } 23 | 24 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 25 | { 26 | var parameterType = ConstructorArgumenTypes.GetOrAdd( 27 | objectType, 28 | t => 29 | { 30 | var constructorInfo = objectType.GetTypeInfo().GetConstructors(BindingFlags.Public | BindingFlags.Instance).Single(); 31 | var parameterInfo = constructorInfo.GetParameters().Single(); 32 | return parameterInfo.ParameterType; 33 | }); 34 | 35 | var value = serializer.Deserialize(reader, parameterType); 36 | return Activator.CreateInstance(objectType, new[] { value }); 37 | } 38 | 39 | public override bool CanConvert(Type objectType) 40 | { 41 | return typeof(ISingleValueObject).GetTypeInfo().IsAssignableFrom(objectType); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Source/Xpress.Core/Xpress.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Source/Xpress.Core/XpressAppBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using Xpress.Core; 5 | using Xpress.Core.DependencyInjection; 6 | using Xpress.Core.EventBus.Local; 7 | 8 | namespace Xpress.Core 9 | { 10 | /// 11 | /// Xpress application service usage 12 | /// 13 | public static class XpressAppBuilderExtensions 14 | { 15 | /// 16 | /// Use application framework service 17 | /// 18 | public static void UseXpress(this IApplicationBuilder applicationBuilder, Action options = null) 19 | { 20 | if (applicationBuilder == null) 21 | { 22 | throw new ArgumentNullException(nameof(applicationBuilder)); 23 | } 24 | 25 | var iocResolver = applicationBuilder.ApplicationServices.GetService(); 26 | //var localEventBus = iocResolver.Resolve(); 27 | var builder = new AppBuilderOptions(iocResolver); 28 | options?.Invoke(builder); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Xpress.Core/XpressServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using Xpress.Core; 4 | 5 | namespace Xpress.Core 6 | { 7 | /// 8 | /// Xpress application service injection 9 | /// 10 | public static class XpressServiceCollectionExtensions 11 | { 12 | /// 13 | /// Add application framework service 14 | /// 15 | public static IServiceProvider AddXpress(this IServiceCollection serviceCollection, Action options = null) 16 | { 17 | if (serviceCollection == null) 18 | { 19 | throw new ArgumentNullException(nameof(serviceCollection)); 20 | } 21 | var servicesBuilderOptions = new ServicesBuilderOptions(); 22 | options?.Invoke(servicesBuilderOptions); 23 | return servicesBuilderOptions.Build(serviceCollection); 24 | } 25 | } 26 | } 27 | 28 | --------------------------------------------------------------------------------