├── Doc
├── diInst.png
├── netLog.png
├── packT4.png
├── sunny.png
├── packApi.png
├── commentXml.png
├── myDbContext.png
├── offlineLog.png
├── pageRequest.png
├── swaggerXml.png
├── T4Repository.png
├── pageResponse.png
└── packRepository.png
├── UseDemo
├── ApiDemo
│ ├── appsettings.Development.json
│ ├── DTO
│ │ ├── Request
│ │ │ └── Demo
│ │ │ │ ├── MemPua.cs
│ │ │ │ ├── Enummm.cs
│ │ │ │ ├── CustomerValidator.cs
│ │ │ │ ├── Buyer.cs
│ │ │ │ └── Customer.cs
│ │ └── Response
│ │ │ └── ResponseMapperConfig.cs
│ ├── Job
│ │ ├── JobA.cs
│ │ └── JobB.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Program.cs
│ ├── ApiDemo.csproj
│ ├── Controllers
│ │ └── UnAuthController.cs
│ ├── appsettings.json
│ ├── ApiDemo.xml
│ └── Startup.cs
├── ServiceDemo
│ ├── ServiceDemo.csproj
│ ├── IStudentService.cs
│ └── StudentService.cs
├── RepositoryDemo
│ ├── RepositoryDemo.csproj
│ ├── DbModel
│ │ ├── Model
│ │ │ ├── IdTest.cs
│ │ │ ├── Category.cs
│ │ │ ├── PassageCategory.cs
│ │ │ ├── Passage.cs
│ │ │ ├── StudentAddress.cs
│ │ │ └── Student.cs
│ │ ├── RelationMap
│ │ │ ├── StudentAddressMap.cs
│ │ │ └── PassageCategoryMap.cs
│ │ └── MoelConfig
│ │ │ ├── StudentConfig.cs
│ │ │ ├── CategoryConfig.cs
│ │ │ ├── PassageConfig.cs
│ │ │ └── StudentAddressConfig.cs
│ ├── DbSet.cs
│ ├── MyDbContext.cs
│ ├── DesignTimeDbContextFactory.cs
│ └── Migrations
│ │ ├── 20181102012925_v1.cs
│ │ ├── MyDbContextModelSnapshot.cs
│ │ └── 20181102012925_v1.Designer.cs
└── TemplateT4Demo
│ ├── TemplateT4Demo.csproj
│ └── Program.cs
├── Framework
├── Sunny.Repository
│ ├── DbModel
│ │ ├── IDbModel.cs
│ │ ├── IRelationMap.cs
│ │ └── BaseModel.cs
│ ├── Sunny.Repository.xml
│ ├── FluentApiTools.cs
│ └── Sunny.Repository.csproj
├── Sunny.Common
│ ├── ConfigOption
│ │ ├── NetLoggerOption.cs
│ │ ├── RedisOption.cs
│ │ ├── SnowflakeOption.cs
│ │ ├── IpInfoQueryOption.cs
│ │ ├── SmsOption.cs
│ │ ├── TokenValidateOption.cs
│ │ ├── MailOption.cs
│ │ └── JobOption.cs
│ ├── Log
│ │ ├── LogData.cs
│ │ ├── NetLoggerProvider.cs
│ │ ├── NetLoggerProcessor.cs
│ │ └── NetLogger.cs
│ ├── Extend
│ │ ├── IDispose
│ │ │ └── IDisposeExtend.cs
│ │ ├── DateTime
│ │ │ └── DateTimeExtend.cs
│ │ ├── CollectionQuery
│ │ │ ├── PageQuery.cs
│ │ │ ├── PageInfo.cs
│ │ │ ├── PageData.cs
│ │ │ └── CollectionQuery.cs
│ │ ├── Exception
│ │ │ └── BizException.cs
│ │ ├── GenericType
│ │ │ ├── ListExtend.cs
│ │ │ └── ClassTypeExtend.cs
│ │ ├── Object
│ │ │ └── ObjectExtend.cs
│ │ ├── Enum
│ │ │ └── EnumExtend.cs
│ │ ├── Log
│ │ │ └── LoggerFactoryExtend.cs
│ │ └── Cache
│ │ │ └── IDistributedCacheExtend.cs
│ ├── Properties
│ │ └── PublishProfiles
│ │ │ └── FolderProfile.pubxml
│ ├── DependencyInjection
│ │ └── DenendencyInjectionInterface.cs
│ ├── Enum
│ │ └── Enums.cs
│ ├── Helper
│ │ ├── Id
│ │ │ └── IdHelper.cs
│ │ ├── String
│ │ │ ├── JsonHelper.cs
│ │ │ └── StringHelper.cs
│ │ ├── Serialize
│ │ │ └── SerializeHelper.cs
│ │ ├── Calc
│ │ │ └── CalcHelper.cs
│ │ ├── File
│ │ │ └── FileHelper.cs
│ │ ├── Security
│ │ │ └── SecurityHelper.cs
│ │ ├── Xml
│ │ │ └── XMLHelper.cs
│ │ └── Di
│ │ │ └── DIHelper.cs
│ ├── JsonTypeConverter
│ │ ├── LongConverter.cs
│ │ └── DecimalConverter.cs
│ ├── Sunny.Common.csproj
│ └── Id
│ │ └── SnowFlake.cs
├── Sunny.Api
│ ├── FluentValidation
│ │ └── Validator.cs
│ ├── DTO
│ │ ├── Request
│ │ │ └── PageInfoValidator.cs
│ │ └── Response
│ │ │ └── Result.cs
│ ├── Quartz
│ │ ├── IJobEntity.cs
│ │ └── JobWrapper.cs
│ ├── Controllers
│ │ └── SunnyController.cs
│ ├── Properties
│ │ └── PublishProfiles
│ │ │ └── FolderProfile.pubxml
│ ├── Extend
│ │ ├── SunnyControllerExtend.cs
│ │ └── IApplicationBuilderExtend.cs
│ ├── Midware
│ │ ├── TokenValidateMiddleware.cs
│ │ ├── BizExceptionHandlerMiddleware.cs
│ │ └── ExceptionHandlingMiddleware.cs
│ ├── Sunny.Api.csproj
│ └── Sunny.Api.xml
└── TemplateT4
│ ├── FluentApiConfig
│ ├── DbModelConfig.tt
│ ├── RelationMapConfig.tt
│ ├── FluentApiConfigProcesser.cs
│ ├── RelationMapConfigPartial.cs
│ └── DbModelConfigPartial.cs
│ └── Sunny.TemplateT4.csproj
├── .gitignore
└── Sunny.sln
/Doc/diInst.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/diInst.png
--------------------------------------------------------------------------------
/Doc/netLog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/netLog.png
--------------------------------------------------------------------------------
/Doc/packT4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/packT4.png
--------------------------------------------------------------------------------
/Doc/sunny.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/sunny.png
--------------------------------------------------------------------------------
/Doc/packApi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/packApi.png
--------------------------------------------------------------------------------
/Doc/commentXml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/commentXml.png
--------------------------------------------------------------------------------
/Doc/myDbContext.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/myDbContext.png
--------------------------------------------------------------------------------
/Doc/offlineLog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/offlineLog.png
--------------------------------------------------------------------------------
/Doc/pageRequest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/pageRequest.png
--------------------------------------------------------------------------------
/Doc/swaggerXml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/swaggerXml.png
--------------------------------------------------------------------------------
/Doc/T4Repository.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/T4Repository.png
--------------------------------------------------------------------------------
/Doc/pageResponse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/pageResponse.png
--------------------------------------------------------------------------------
/Doc/packRepository.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MackYang/Sunny/HEAD/Doc/packRepository.png
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/UseDemo/ServiceDemo/ServiceDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Framework/Sunny.Repository/DbModel/IDbModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Repository.DbModel
6 | {
7 | ///
8 | /// 用于标识某类是DbModel,继承此接口后可通过T4模板生成ModelConfig类
9 | ///
10 | public interface IDbModel
11 | {
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/NetLoggerOption.cs:
--------------------------------------------------------------------------------
1 | namespace Sunny.Common.ConfigOption
2 | {
3 | public class NetLoggerOption
4 | {
5 | public string Url { get; set; }
6 | public string SystemId { get; set; }
7 | public string OfflineLogPath { get; set; } = @"C:\SunnyFramework\OfflineLog";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/RepositoryDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Framework/Sunny.Repository/DbModel/IRelationMap.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Repository.DbModel
6 | {
7 | ///
8 | /// 用于标识用于映射多对多的类,继承此接口后可通过T4模板生成RelationMap类
9 | ///
10 | public interface IRelationMap:IDbModel
11 | {
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/Model/IdTest.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Common.Enum;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace RepositoryDemo.DbModel
7 | {
8 | public class IdTest
9 | {
10 | public long Id { get; set; }
11 |
12 | public Enums.RequestType requestType { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Log/LogData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.Log
6 | {
7 | public class LogData
8 | {
9 | public string Message { get; set; }
10 |
11 | public string LevelString { get; set; }
12 |
13 | public Exception Exception { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/DTO/Request/Demo/MemPua.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace ApiDemo.FluentValidation2
8 | {
9 | public class MemPua
10 | {
11 | [Required]
12 | public string Name { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/RedisOption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.ConfigOption
6 | {
7 | public class RedisOption
8 | {
9 | public string ConnectionString { get; set; }
10 | public string InstanceName { get; set; }
11 |
12 | public int DefaultSlidingExpiration { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/Model/Category.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Repository.DbModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace RepositoryDemo.DbModel
7 | {
8 | public class Category:BaseModel
9 | {
10 |
11 | public string CategoryName { get; set; }
12 |
13 | public IList PassageCategories { get; set; }
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/DTO/Request/Demo/Enummm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace ApiDemo.DTO.Request
8 | {
9 | public class Enummm
10 | {
11 |
12 | public enum IntEnum {
13 |
14 | Wifi,
15 | [Description("这是一个很NB的GPS哦")]
16 | GPS
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/SnowflakeOption.cs:
--------------------------------------------------------------------------------
1 | namespace Sunny.Common.ConfigOption
2 | {
3 | public class SnowflakeOption
4 | {
5 |
6 | ///
7 | /// 数据中心ID,1到31之间
8 | ///
9 | public long DatacenterId { get; set; }
10 |
11 | ///
12 | /// 机器ID,1到31之间
13 | ///
14 | public long MachineId { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/IDispose/IDisposeExtend.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace System
6 | {
7 | public static class IDisposeExtend
8 | {
9 | public static void DisposeIfNotNull(this IDisposable disObj)
10 | {
11 | if (disObj != null)
12 | {
13 | disObj.Dispose();
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/UseDemo/TemplateT4Demo/TemplateT4Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/IpInfoQueryOption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.ConfigOption
6 | {
7 | ///
8 | /// 查询IP信息的配置
9 | ///
10 | public class IpInfoQueryOption
11 | {
12 | ///
13 | /// IP查询的API
14 | ///
15 | public string ApiUrl { get; set; }
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/FluentValidation/Validator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using Sunny.Common.DependencyInjection;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace Sunny.Api.FluentValidation
9 | {
10 | ///
11 | /// 需要验证的实体请继承自这个类
12 | ///
13 | ///
14 | public abstract class Validator: AbstractValidator, ISingleton
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/Model/PassageCategory.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Repository.DbModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace RepositoryDemo.DbModel
7 | {
8 | public class PassageCategory : IRelationMap
9 | {
10 | public long CategoryId { get; set; }
11 |
12 | public Category Category { get; set; }
13 |
14 | public long PassageId { get; set; }
15 |
16 | public Passage Passage { get; set; }
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/SmsOption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.ConfigOption
6 | {
7 | public class SmsOption
8 | {
9 | ///
10 | /// 短信网关的API地址
11 | ///
12 | public string ApiUrl { get; set; }
13 | ///
14 | /// IP白名单列表,在列表中的IP发短信前不执行检查事件
15 | ///
16 | public string[] IPWhiteList { get; set; }
17 |
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/UseDemo/TemplateT4Demo/Program.cs:
--------------------------------------------------------------------------------
1 | using Sunny.TemplateT4.FluentApiConfig;
2 | using System;
3 |
4 | namespace TemplateT4Demo
5 | {
6 | class Program
7 | {
8 | static void Main(string[] args)
9 | {
10 | string assemblyName = "RepositoryDemo";
11 | string configNameSpace = "RepositoryDemo.DbModel.ModelConfig";
12 |
13 | FluentApiConfigProcesser.GenerateFiles(assemblyName,configNameSpace);
14 |
15 | Console.Read();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/TokenValidateOption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.ConfigOption
6 | {
7 | public class TokenValidateOption
8 | {
9 | ///
10 | /// 根据TokenKey的值从HttpHeader中获取对应的字符串,默认是"token"
11 | ///
12 | public string TokenKey { get; set; } = "token";
13 |
14 | ///
15 | /// 以此开头的API都需要验证Token
16 | ///
17 | public string AuthApiStartWith { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/DateTime/DateTimeExtend.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace System
6 | {
7 | public static class DateTimeExtend
8 | {
9 | ///
10 | /// 返回yyyy-MM-dd HH:mm:ss 格式的时间
11 | ///
12 | ///
13 | ///
14 | public static string ToNormalString(this DateTime dateTime)
15 | {
16 | return dateTime.ToString("yyyy-MM-dd HH:mm:ss");
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | Release
9 | Any CPU
10 | netcoreapp2.1
11 | bin\Debug\netcoreapp2.1\publish\
12 |
13 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbSet.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using RepositoryDemo.DbModel;
3 |
4 | namespace RepositoryDemo
5 | {
6 |
7 | public partial class MyDbContext
8 | {
9 | public DbSet Category { get; set; }
10 | public DbSet Passage { get; set; }
11 | public DbSet PassageCategory { get; set; }
12 |
13 |
14 | public DbSet Student { get; set; }
15 |
16 | public DbSet StudentAddress { get; set; }
17 |
18 | public DbSet IdTest { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/Model/Passage.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Repository.DbModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace RepositoryDemo.DbModel
7 | {
8 | public class Passage : BaseModel
9 | {
10 | //文章编号
11 |
12 |
13 |
14 | //标题
15 | public string Title { get; set; }
16 |
17 |
18 | //最后编辑时间
19 | public DateTime LastEditTime { get; set; }
20 |
21 | public bool IsDelete { get; set; }
22 | //文章分类(使用技术等)
23 | public IList PassageCategories { get; set; }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/CollectionQuery/PageQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.Extend.CollectionQuery
6 | {
7 | ///
8 | /// 带分页的查询条件
9 | ///
10 | ///
11 | public class PageQuery
12 | {
13 | ///
14 | /// 查询条件
15 | ///
16 | public T Condition { get; set; }
17 |
18 | ///
19 | /// 分页信息
20 | ///
21 | public PageInfo PageInfo { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Framework/Sunny.Repository/DbModel/BaseModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | using System.Text;
6 |
7 | namespace Sunny.Repository.DbModel
8 | {
9 | public class BaseModel: IDbModel
10 | {
11 |
12 | public long Id { get; set; }
13 |
14 | public DateTime CreateTime { get; set; }
15 |
16 | public long CreaterId { get; set; }
17 |
18 | public DateTime UpdateTime { get; set; }
19 |
20 | public long UpdaterId { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/UseDemo/ServiceDemo/IStudentService.cs:
--------------------------------------------------------------------------------
1 | using RepositoryDemo.DbModel;
2 | using Sunny.Common.DependencyInjection;
3 | using System;
4 | using System.Threading.Tasks;
5 |
6 | namespace ServiceDemo
7 | {
8 | public interface IStudentServic:IScoped
9 | {
10 |
11 | Task GetStudent();
12 |
13 | Task GetStudent2();
14 |
15 | Task BizExceptionTest();
16 | }
17 |
18 |
19 | public class SomeoneClass : IScoped
20 | {
21 |
22 | public string SomeoneMethod()
23 | {
24 | return "hello this is di class";
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/RelationMap/StudentAddressMap.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace RepositoryDemo.DbModel.RelationMap
8 | {
9 | public class StudentAddressMap : IEntityTypeConfiguration
10 | {
11 | public void Configure(EntityTypeBuilder builder)
12 | {
13 | builder.HasOne(x => x.Student).WithOne(s => s.Address).HasForeignKey(x => x.StudentId);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/DTO/Request/PageInfoValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using Sunny.Api.FluentValidation;
3 | using Sunny.Common.Extend.CollectionQuery;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Text;
7 |
8 | namespace Sunny.Api.DTO.Request
9 | {
10 | public class PageInfoValidator : Validator
11 | {
12 | public PageInfoValidator()
13 | {
14 | RuleFor(x => x.PageIndex).GreaterThanOrEqualTo(1).WithMessage("PageIndex必须大于0");
15 | RuleFor(x => x.PageSize).GreaterThanOrEqualTo(1).WithMessage("PageSize必须大于0");
16 | }
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/Job/JobA.cs:
--------------------------------------------------------------------------------
1 | using Quartz;
2 | using Sunny.Api.Quartz;
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace ApiDemo.Job
8 | {
9 | public class JobA : IJobEntity
10 | {
11 |
12 | public string JobName => "名字随便取,你能看懂就行,最终会在日志中出现";
13 |
14 | public string Describe => "这个Job是干嘛用的..最终会在日志中出现";
15 |
16 | public async Task ExecuteAsync(IJobExecutionContext jobContext)
17 | {
18 | Console.WriteLine("hello job 这有中文 execing...");
19 | await Task.Run(() => Thread.Sleep(3000));
20 | Console.WriteLine("job end");
21 |
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 | *.log
10 | *.ide
11 | *.cache
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 |
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the
31 |
32 | /UseDemo/RepositoryDemo/Migrations
33 | /UseDemo/ApiDemo/Properties/PublishProfiles/FolderProfile.pubxml
34 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/Exception/BizException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.Serialization;
4 | using System.Text;
5 |
6 | namespace System
7 | {
8 | ///
9 | /// 用于表示业务异常的类
10 | ///
11 | public class BizException : Exception
12 | {
13 | public BizException(string message) : base(message)
14 | {
15 | }
16 |
17 | public BizException(string message, Exception innerException) : base(message, innerException)
18 | {
19 | }
20 |
21 | protected BizException(SerializationInfo info, StreamingContext context) : base(info, context)
22 | {
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/DependencyInjection/DenendencyInjectionInterface.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.DependencyInjection
6 | {
7 | ///
8 | /// 用于标记需要批量注入的接口
9 | ///
10 | public interface IDependency
11 | {
12 | }
13 | ///
14 | /// 每次使用时都实例化一次
15 | ///
16 | public interface ITransient : IDependency { }
17 |
18 | ///
19 | /// 每个请求中只实例化一次
20 | ///
21 | public interface IScoped : IDependency { }
22 |
23 | ///
24 | /// 整个系统中只实例化一次
25 | ///
26 | public interface ISingleton : IDependency { }
27 | }
28 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/Model/StudentAddress.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Repository.DbModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel.DataAnnotations.Schema;
5 | using System.Text;
6 |
7 | namespace RepositoryDemo.DbModel
8 | {
9 | public class StudentAddress : BaseModel
10 | {
11 |
12 | // public int StudentAddressId { get; set; }
13 |
14 | public string Address1 { get; set; }
15 |
16 | public int Zipcode { get; set; }
17 | public string State { get; set; }
18 | public string Country { get; set; }
19 |
20 | public Student Student { get; set; }
21 |
22 | public long StudentId { get; set; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/MyDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Sunny.Repository;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace RepositoryDemo
8 | {
9 | public partial class MyDbContext : DbContext
10 | {
11 | public MyDbContext(DbContextOptions options)
12 | : base(options)
13 | {
14 |
15 | }
16 |
17 | protected override void OnModelCreating(ModelBuilder modelBuilder)
18 | {
19 | base.OnModelCreating(modelBuilder);
20 | FluentApiTools.ApplyDbModelFluentApiConfig(modelBuilder, "RepositoryDemo");
21 | }
22 | }
23 |
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/Model/Student.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Repository.DbModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace RepositoryDemo.DbModel
7 | {
8 | public class Student : BaseModel
9 | {
10 | public Student() { }
11 |
12 |
13 | public string StudentName { get; set; }
14 |
15 | public string Test { get; set; }
16 |
17 | private string AA2 { get; set; }
18 |
19 | private new DateTime UpdateTime { get; set; }
20 |
21 | private new long UpdaterId { get; set; }
22 |
23 | public StudentAddress Address { get; set; }
24 |
25 | public decimal Score { get; set; }
26 |
27 |
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Quartz/IJobEntity.cs:
--------------------------------------------------------------------------------
1 | using Quartz;
2 | using Sunny.Common.DependencyInjection;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace Sunny.Api.Quartz
9 | {
10 | ///
11 | /// 任务信息
12 | ///
13 | public interface IJobEntity:ITransient
14 | {
15 | ///
16 | /// 任务的名称
17 | ///
18 | string JobName { get; }
19 | ///
20 | /// 任务的描述
21 | ///
22 | string Describe { get; }
23 |
24 | ///
25 | /// 任务内容
26 | ///
27 | Task ExecuteAsync(IJobExecutionContext jobContext);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/MailOption.cs:
--------------------------------------------------------------------------------
1 | namespace Sunny.Common.ConfigOption
2 | {
3 | ///
4 | /// 邮件配置信息
5 | ///
6 | public class MailOption
7 | {
8 | ///
9 | /// 邮件服务器地址
10 | ///
11 | public string EmailHost { get; set; }
12 | ///
13 | /// 用户名
14 | ///
15 | public string EmailUserName { get; set; }
16 | ///
17 | /// 密码
18 | ///
19 | public string EmailPassword { get; set; }
20 | ///
21 | /// IP白名单列表,在列表中的IP发邮件前不执行检查事件
22 | ///
23 | public string[] IPWhiteList { get; set; }
24 |
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/GenericType/ListExtend.cs:
--------------------------------------------------------------------------------
1 | namespace System.Collections.Generic
2 | {
3 | static public class ListExtend
4 | {
5 | ///
6 | /// 对集合中的项进行扩展,返回一个dynamic集合
7 | ///
8 | ///
9 | ///
10 | /// 如ToDynamic(x=>{x.Extend(new{EnumCn=x.EnumX.GetDescribe()})})
11 | ///
12 | static public List ToDynamic(this List source, Func func)
13 | {
14 | List list = new List();
15 | source.ForEach(x => list.Add(func.Invoke(x)));
16 | return list;
17 |
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/Object/ObjectExtend.cs:
--------------------------------------------------------------------------------
1 | namespace System
2 | {
3 | public static class ObjectExtend
4 | {
5 | ///
6 | /// 将objValue的值转换成defValue的类型,如果转换失败,返回defValue
7 | ///
8 | /// 目标类型
9 | /// 要转换的原数据
10 | /// 转换失败时返回的默认值
11 | ///
12 | static public T ConvertTo(this object objValue, T defValue)
13 | {
14 | try
15 | {
16 | return (T)Convert.ChangeType(objValue, typeof(T));
17 | }
18 | catch
19 | {
20 | return defValue;
21 | }
22 |
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Controllers/SunnyController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.Extensions.Options;
3 | using Sunny.Api.DTO.Response;
4 | using Sunny.Common.ConfigOption;
5 |
6 | namespace Sunny.Api.Controllers
7 | {
8 |
9 | [ApiController]
10 | public class SunnyController : Controller
11 | {
12 |
13 | ///
14 | /// 返回一个动态类型的值,通常用于想返回扩展类型的场景,比如在源对象上增加一个枚举值描述的动态属性
15 | ///
16 | ///
17 | ///
18 | protected Result SuccessDynamic(dynamic responseData)
19 | {
20 | Result r = new Result();
21 | r.Data = responseData;
22 |
23 | return r;
24 | }
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Enum/Enums.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Text;
5 |
6 | namespace Sunny.Common.Enum
7 | {
8 | public class Enums
9 | {
10 | #region 操作状态,表示本次操作成功失败或异常
11 |
12 | public enum OperationStatus
13 | {
14 | [Description("操作异常")]
15 | Exception = -1,
16 |
17 |
18 | [Description("操作成功")]
19 | Success = 0,
20 |
21 |
22 | [Description("操作失败")]
23 | Fail = 1
24 |
25 | }
26 | #endregion
27 |
28 | public enum RequestType
29 | {
30 | [Description("Get请求")]
31 | Get,
32 | [Description("Post请求")]
33 | Post
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/ConfigOption/JobOption.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Sunny.Common.ConfigOption
4 | {
5 | ///
6 | /// 任务的配置项
7 | ///
8 | public class JobOption
9 | {
10 | ///
11 | /// 任务类的名称
12 | ///
13 | public string JobClassName { get; set; }
14 |
15 | ///
16 | ///任务所属的组名称,同一组类不能有2个相同的任务
17 | ///
18 | public string JobGroup { get; set; }
19 |
20 | ///
21 | /// 在什么时候运行,用Cron表达式
22 | ///
23 | public string RunAtCron { get; set; }
24 |
25 | ///
26 | ///任务的参数
27 | ///
28 | public IDictionary Args { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/Enum/EnumExtend.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.ComponentModel;
3 | using System.Reflection;
4 |
5 | namespace System
6 | {
7 | static public class EnumExtend
8 | {
9 |
10 | public static string GetDescribe(this T t) where T : Enum
11 | {
12 | string describe = t.ToString();
13 |
14 | MemberInfo[] memInfo = typeof(T).GetMember(t.ToString());
15 |
16 |
17 | if (memInfo != null && memInfo.Length > 0)
18 | {
19 | object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
20 | if (attrs != null && attrs.Length > 0)
21 | describe = ((DescriptionAttribute)attrs[0]).Description;
22 | }
23 |
24 | return describe;
25 | }
26 |
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/TemplateT4/FluentApiConfig/DbModelConfig.tt:
--------------------------------------------------------------------------------
1 | <#@ template language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Text;
11 |
12 | namespace <#=ConfigNamespace#>
13 | {
14 | public class <#=DbModelType.Name#>Config : IEntityTypeConfiguration<<#=DbModelType.Name#>>
15 | {
16 |
17 | public void Configure(EntityTypeBuilder<<#=DbModelType.Name#>> builder)
18 | {
19 | <#=GetTableConfig() #>
20 | <#=GetFieldsConfig() #>
21 |
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DesignTimeDbContextFactory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Design;
3 | using Microsoft.Extensions.Logging;
4 | using RepositoryDemo;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Text;
8 |
9 | namespace Sunny.Repository
10 | {
11 | public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory
12 |
13 | {
14 |
15 | public MyDbContext CreateDbContext(string[] args)
16 |
17 | {
18 |
19 | var builder = new DbContextOptionsBuilder();
20 |
21 |
22 | builder.UseMySql("server=localhost;database=test;user=root;password=youPassword;");
23 |
24 | return new MyDbContext(builder.Options);
25 |
26 |
27 | }
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Framework/TemplateT4/FluentApiConfig/RelationMapConfig.tt:
--------------------------------------------------------------------------------
1 | <#@ template language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Text;
11 |
12 | namespace <#=ConfigNamespace#>
13 | {
14 | public class <#=DbModelType.Name#>Map : IEntityTypeConfiguration<<#=DbModelType.Name#>>
15 | {
16 |
17 | public void Configure(EntityTypeBuilder<<#=DbModelType.Name#>> builder)
18 | {
19 | builder.ToTable("<#=DbModelType.Name.UpperCharToUnderLine()#>");
20 | <#=GetRelationConfig()#>
21 |
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/DTO/Response/ResponseMapperConfig.cs:
--------------------------------------------------------------------------------
1 | using ApiDemo.DTO.Request.Demo;
2 | using ApiDemo.FluentValidation2;
3 | using AutoMapper;
4 | using RepositoryDemo.DbModel;
5 |
6 | namespace ApiDemo.DTO.Response
7 | {
8 | public class ResponseMapperConfig : Profile
9 | {
10 | public ResponseMapperConfig()
11 | {
12 |
13 | CreateMap()
14 | .ForMember(cus => cus.LocalType, opt => opt.MapFrom(id => id.requestType)).ReverseMap();
15 | //手动指定字段映射关系
16 | //.ForMember(cus => cus.LocalType, opt => opt.MapFrom(id => id.requestType))
17 | //.ReverseMap()映射反向转换
18 |
19 | CreateMap().ReverseMap();
20 | CreateMap().ReverseMap();
21 | CreateMap().ReverseMap();
22 | }
23 |
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/UseDemo/ServiceDemo/StudentService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using RepositoryDemo;
5 | using RepositoryDemo.DbModel;
6 |
7 | namespace ServiceDemo
8 | {
9 | public class StudentService : IStudentServic
10 | {
11 | MyDbContext db;
12 | public StudentService(MyDbContext db)
13 | {
14 | this.db = db;
15 | }
16 |
17 | public async Task GetStudent()
18 | {
19 | return await Task.Run(()=>db.Student.FirstOrDefault());
20 | }
21 |
22 | public async Task GetStudent2()
23 | {
24 | return await Task.Run(() => db.Student.FirstOrDefault());
25 | }
26 |
27 | public async Task BizExceptionTest()
28 | {
29 | throw new BizException("订单不存在,这是一个测试抛出的业务异常");
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/Job/JobB.cs:
--------------------------------------------------------------------------------
1 | using Quartz;
2 | using ServiceDemo;
3 | using Sunny.Api.Quartz;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 |
9 | namespace ApiDemo.Job
10 | {
11 | public class JobB : IJobEntity
12 | {
13 |
14 | public string JobName => "this job B Name";
15 |
16 | public string Describe => "this job B Describe";
17 |
18 | IStudentServic studentServic;
19 |
20 | public JobB(IStudentServic studentServic)
21 | {
22 | this.studentServic = studentServic;
23 |
24 | }
25 |
26 |
27 | public async Task ExecuteAsync(IJobExecutionContext jobContext)
28 | {
29 | Console.WriteLine(jobContext.JobDetail.JobDataMap["pxxx"]);//使用了配置中传来的参数,参数的名称要和配置里的一样
30 | Console.WriteLine( (await studentServic.GetStudent()).StudentName);
31 |
32 | }
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/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:60960",
8 | "sslPort": 44381
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "unauthapi/unauth",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "ApiDemo": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "unauthapi/unauth",
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/Id/IdHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using Sunny.Common.ConfigOption;
3 | using Sunny.Common.Id;
4 |
5 | namespace Sunny.Common.Helper
6 | {
7 | public class IdHelper
8 | {
9 | private static Snowflake snowflake = null;
10 | private IdHelper()
11 | {
12 |
13 | }
14 |
15 | static IdHelper()
16 | {
17 |
18 | }
19 |
20 | ///
21 | /// 程序启动时调用一次就OK
22 | ///
23 | public static void InitSnowflake(SnowflakeOption options)
24 | {
25 | snowflake = new Snowflake(options.MachineId, options.DatacenterId);
26 | }
27 |
28 | ///
29 | /// 获取ID
30 | ///
31 | ///
32 | public static long GenId()
33 | {
34 | return snowflake.NextId();
35 |
36 | }
37 | }
38 |
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | FileSystem
9 | FileSystem
10 | Release
11 | Any CPU
12 |
13 | True
14 | False
15 | 913ae651-2048-4be1-9199-63c8ab3b8299
16 | bin\Release\netcoreapp2.1\publish\
17 | False
18 |
19 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/JsonTypeConverter/LongConverter.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 |
4 | namespace Sunny.Common.JsonTypeConverter
5 | {
6 | public class LongConverter : JsonConverter
7 | {
8 | public override bool CanConvert(Type objectType)
9 | {
10 | return objectType == typeof(long) || objectType == typeof(long?);
11 | }
12 |
13 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
14 | {
15 | long result = 0;
16 | if (reader.Value != null)
17 | {
18 | long.TryParse(reader.Value.ToString(), out result);
19 | }
20 | return result;
21 | }
22 |
23 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
24 | {
25 | writer.WriteValue(value.ToString());
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/JsonTypeConverter/DecimalConverter.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 |
4 | namespace Sunny.Common.JsonTypeConverter
5 | {
6 | public class DecimalConverter : JsonConverter
7 | {
8 | public override bool CanConvert(Type objectType)
9 | {
10 | return objectType == typeof(decimal) || objectType == typeof(decimal?);
11 | }
12 |
13 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
14 | {
15 | decimal result = 0;
16 | if (reader.Value != null)
17 | {
18 | decimal.TryParse(reader.Value.ToString(), out result);
19 | }
20 | return result;
21 | }
22 |
23 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
24 | {
25 | writer.WriteValue(value.ToString());
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace ApiDemo
12 | {
13 | public class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | BuildWebHost(args).Run();
18 | }
19 |
20 | public static IWebHost BuildWebHost(string[] args) =>
21 | WebHost.CreateDefaultBuilder(args)
22 | .ConfigureLogging(logging =>
23 | {
24 | //logging.SetMinimumLevel(LogLevel.Warning);
25 | //微软默认添加了console和debue的级别大于Warning的日志输出,不习惯可以清除了自己添加
26 | logging.ClearProviders();
27 | })
28 | .UseStartup()
29 | .Build();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Framework/Sunny.Repository/Sunny.Repository.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sunny.Repository
5 |
6 |
7 |
8 |
9 | 用于标识某类是DbModel,继承此接口后可通过T4模板生成ModelConfig类
10 |
11 |
12 |
13 |
14 | 用于标识用于映射多对多的类,继承此接口后可通过T4模板生成RelationMap类
15 |
16 |
17 |
18 |
19 | 对DbModel应用FlentApi字段配置
20 |
21 |
22 | DbModel所在的程序集名称
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/MoelConfig/StudentConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 | using System;
4 |
5 | namespace RepositoryDemo.DbModel.ModelConfig
6 | {
7 | public class StudentConfig : IEntityTypeConfiguration
8 | {
9 |
10 | public void Configure(EntityTypeBuilder builder)
11 | {
12 | builder.ToTable("student");
13 | builder.Property(x => x.Id).HasColumnName("id");
14 | builder.Property(x => x.StudentName).HasColumnName("student_name").HasMaxLength(30);
15 | builder.Property(x => x.Test).HasColumnName("test").HasMaxLength(30);
16 | builder.Property(x => x.Score).HasColumnName("score").HasColumnType("decimal(18, 2)");
17 | builder.Property(x => x.CreateTime).HasColumnName("create_time");
18 | builder.Property(x => x.CreaterId).HasColumnName("creater_id");
19 |
20 |
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/CollectionQuery/PageInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Sunny.Common.Extend.CollectionQuery
5 | {
6 | ///
7 | /// 分页信息
8 | ///
9 | public class PageInfo
10 | {
11 | ///
12 | /// 当前页索引
13 | ///
14 | public int PageIndex { get; set; }
15 |
16 | ///
17 | /// 页大小
18 | ///
19 | public int PageSize { get; set; }
20 |
21 | ///
22 | /// 总记录数
23 | ///
24 | public int RecordTotal { get; set; }
25 |
26 | ///
27 | /// 总页数
28 | ///
29 | public int PageTotal
30 | {
31 | get
32 | {
33 | // (recordTotal-1)/pageSize+1当recordTotal=0时,pageTotal=1
34 | return (int)Math.Ceiling(RecordTotal / (double)PageSize);
35 | }
36 | }
37 |
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/MoelConfig/CategoryConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace RepositoryDemo.DbModel.ModelConfig
8 | {
9 | public class CategoryConfig : IEntityTypeConfiguration
10 | {
11 |
12 | public void Configure(EntityTypeBuilder builder)
13 | {
14 | builder.ToTable("category");
15 | builder.Property(x => x.Id).HasColumnName("id");
16 | builder.Property(x => x.CategoryName).HasColumnName("category_name").HasMaxLength(30);
17 | builder.Property(x => x.CreateTime).HasColumnName("create_time").HasDefaultValue(DateTime.Now);
18 | builder.Property(x => x.CreaterId).HasColumnName("creater_id");
19 | builder.Property(x => x.UpdateTime).HasColumnName("update_time").IsRowVersion();
20 | builder.Property(x => x.UpdaterId).HasColumnName("updater_id");
21 |
22 |
23 |
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/DTO/Request/Demo/CustomerValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using Sunny.Api.FluentValidation;
3 | using Sunny.Common.DependencyInjection;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 |
9 | namespace ApiDemo.FluentValidation2
10 | {
11 | public class CustomerValidator : Validator
12 | {
13 | public CustomerValidator()
14 | {
15 | RuleFor(x => x.Surname).NotEmpty();
16 | RuleFor(x => x.Forename).NotEmpty().WithMessage("PleasFFe specify a first name");
17 | RuleFor(x => x.Discount).NotEqual(0).When(x => x.HasDiscount);
18 | RuleFor(x => x.Address).Length(20, 250);
19 | RuleFor(x => x.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
20 | }
21 |
22 | private bool BeAValidPostcode(string postcode)
23 | {
24 | return postcode == "123";
25 | // custom postcode validating logic goes here
26 | }
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Extend/SunnyControllerExtend.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Api.DTO.Response;
2 | using Sunny.Common.Enum;
3 |
4 | namespace Sunny.Api.Controllers
5 | {
6 | static public class SunnyControllerExtend
7 | {
8 |
9 | #region 封装操作结果
10 | public static Result GetResult(this SunnyController controller, T responseData, int code, string msg)
11 | {
12 | return new Result { Code = code, Msg = msg , Data = responseData};
13 | }
14 |
15 | public static Result Success(this SunnyController controller, T responseData)
16 | {
17 | Result r = new Result();
18 | r.Data = responseData;
19 | return r;
20 | }
21 |
22 |
23 |
24 | public static Result Success(this SunnyController controller)
25 | {
26 | return new Result();
27 | }
28 |
29 | public static Result Fail(this SunnyController controller, string msg)
30 | {
31 | return new Result { Code = Enums.OperationStatus.Fail.GetHashCode(), Msg = msg };
32 | }
33 |
34 | #endregion
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/DTO/Response/Result.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Common.Enum;
2 | using System;
3 |
4 | namespace Sunny.Api.DTO.Response
5 | {
6 | ///
7 | /// 带返回数据的结果
8 | ///
9 | /// 返回的类型
10 | public class Result
11 | {
12 | public int Code { get; set; } = Enums.OperationStatus.Success.GetHashCode();
13 |
14 | public string Msg { get; set; } = Enums.OperationStatus.Success.GetDescribe();
15 | public new T Data { get; set; } = default(T);
16 |
17 | public static implicit operator Result(Result result)
18 | {
19 | return new Result
20 | {
21 | Code = result.Code,
22 | Msg = result.Msg
23 | };
24 | }
25 | }
26 |
27 | ///
28 | /// 不带返回数据的Result
29 | /// 如果要给前端返回数据时,请用泛型版本,这样看API时就知道返回的数据长什么样子
30 | ///
31 | public class Result
32 | {
33 | public int Code { get; set; } = Enums.OperationStatus.Success.GetHashCode();
34 |
35 | public string Msg { get; set; } = Enums.OperationStatus.Success.GetDescribe();
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/ApiDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 | 1701;1702;1591
9 | D:\Work\Code\Sunny\UseDemo\ApiDemo\ApiDemo.xml
10 |
11 |
12 |
13 | D:\Work\Code\Sunny\UseDemo\ApiDemo\ApiDemo.xml
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/MoelConfig/PassageConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace RepositoryDemo.DbModel.ModelConfig
8 | {
9 | public class PassageConfig : IEntityTypeConfiguration
10 | {
11 |
12 | public void Configure(EntityTypeBuilder builder)
13 | {
14 | builder.ToTable("passage").HasQueryFilter(x => !x.IsDelete);
15 | builder.Property(x => x.Id).HasColumnName("id");
16 | builder.Property(x => x.Title).HasColumnName("title").HasMaxLength(30);
17 | builder.Property(x => x.LastEditTime).HasColumnName("last_edit_time");
18 | builder.Property(x => x.IsDelete).HasColumnName("is_delete");
19 | builder.Property(x => x.CreateTime).HasColumnName("create_time").HasDefaultValueSql("now()");
20 | builder.Property(x => x.CreaterId).HasColumnName("creater_id");
21 | builder.Property(x => x.UpdateTime).HasColumnName("update_time").IsRowVersion();
22 | builder.Property(x => x.UpdaterId).HasColumnName("updater_id");
23 |
24 |
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/CollectionQuery/PageData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Sunny.Common.Extend.CollectionQuery
5 | {
6 | ///
7 | /// 包含分页信息的数据
8 | ///
9 | ///
10 | public class PageData
11 | {
12 | ///
13 | /// 数据
14 | ///
15 | public List List { get; set; }
16 |
17 | ///
18 | /// 分页信息
19 | ///
20 | public PageInfo PageInfo { get; set; }
21 |
22 | ///
23 | /// 将数据中的每一项转为dynamic,并返回一个新实例,
24 | /// 通常用于要对数据项进行扩展时,比如为数据项添加枚举的中文意思并返回给前端
25 | /// 如ToDynamic(x=>{x.Extend(new{EnumCn=x.EnumX.GetDescribe()})})
26 | ///
27 | ///
28 | ///
29 | public PageData ToDynamic(Func func)
30 | {
31 | PageData result = new PageData();
32 | result.PageInfo = PageInfo;
33 | if (List != null && List.Count > 0)
34 | {
35 | result.List = List.ToDynamic(func);
36 | }
37 | return result;
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/MoelConfig/StudentAddressConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace RepositoryDemo.DbModel.ModelConfig
8 | {
9 | public class StudentAddressConfig : IEntityTypeConfiguration
10 | {
11 |
12 | public void Configure(EntityTypeBuilder builder)
13 | {
14 | builder.ToTable("student_address");
15 | builder.Property(x => x.Id).HasColumnName("id");
16 | builder.Property(x => x.Address1).HasColumnName("address1").HasMaxLength(30);
17 | builder.Property(x => x.Zipcode).HasColumnName("zipcode");
18 | builder.Property(x => x.State).HasColumnName("state").HasMaxLength(30);
19 | builder.Property(x => x.Country).HasColumnName("country").HasMaxLength(30);
20 | builder.Property(x => x.StudentId).HasColumnName("student_id");
21 | builder.Property(x => x.CreateTime).HasColumnName("create_time");
22 | builder.Property(x => x.CreaterId).HasColumnName("creater_id");
23 | builder.Property(x => x.UpdateTime).HasColumnName("update_time").IsRowVersion();
24 | builder.Property(x => x.UpdaterId).HasColumnName("updater_id");
25 |
26 |
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/String/JsonHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Newtonsoft.Json;
5 | namespace Sunny.Common.Helper
6 | {
7 | public class JsonHelper
8 | {
9 | ///
10 | /// 将对象转换成json字符串
11 | ///
12 | /// 要转换的对象
13 | /// json字符串
14 | static public string ToJsonString(object obj)
15 | {
16 |
17 | return JsonConvert.SerializeObject(obj);
18 |
19 | }
20 |
21 | ///
22 | /// 将json字符串转换成指定类型的对象
23 | ///
24 | /// 指定类型
25 | /// json字符串
26 | /// 指定类型的对象
27 | static public T FromJsonString(string jsonString)
28 | {
29 | return JsonConvert.DeserializeObject(jsonString);
30 | }
31 |
32 | ///
33 | /// 将json字符串转换成dynamic类型的对象
34 | ///
35 | /// json字符串
36 | /// 动态解析类型的对象
37 | static public dynamic FromJsonString(string jsonString)
38 | {
39 | return JsonConvert.DeserializeObject(jsonString);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/DTO/Request/Demo/Buyer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ApiDemo.DTO.Request.Demo
4 | {
5 | public class Buyer
6 | {
7 | public string Name { get; set; }
8 |
9 | public string Phone { get; set; }
10 |
11 | public OrderInfo Order { get; set; }
12 |
13 | public BuyerComment Comment { get; set; }
14 | }
15 |
16 | public class BuyerSub : Buyer
17 | {
18 |
19 | public string Address { get; set; }
20 | }
21 |
22 | public class OrderInfo
23 | {
24 |
25 | public Decimal Price { get; set; }
26 |
27 | public int ProductCount { get; set; }
28 |
29 | }
30 |
31 | public class Seller
32 | {
33 |
34 | public string NaMe { get; set; }
35 |
36 | public DateTime SellTime { get; set; }
37 |
38 | public string Phone { get; set; }
39 |
40 | public OrderInfo Order { get; set; }
41 |
42 | public SellerComment Comment { get; set; }
43 |
44 | }
45 |
46 | public class SellerSub : Seller
47 | {
48 | public string Address { get; set; }
49 | }
50 |
51 | public class BuyerComment
52 | {
53 |
54 | public string Content { get; set; }
55 |
56 | public string BuyerName { get; set; }
57 |
58 | }
59 |
60 | public class SellerComment
61 | {
62 |
63 | public int Content { get; set; }
64 | public string SellerName { get; set; }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/DbModel/RelationMap/PassageCategoryMap.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace RepositoryDemo.DbModel.RelationMap
8 | {
9 | public class PassageCategoryMap : IEntityTypeConfiguration
10 | {
11 | ///
12 | /// PassageCategories FluentAPI配置
13 | ///
14 | /// 添加复合主键、配置多对多关系
15 | ///
16 | ///
17 | public void Configure(EntityTypeBuilder builder)
18 | {
19 | //添加复合主键
20 | builder.HasKey(t => new { t.PassageId, t.CategoryId });
21 |
22 | ///
23 | ///
24 | /// 配置Passage与PassageCategories的一对多关系
25 | ///
26 | /// EFCore中,新增默认级联模式为ClientSetNull
27 | ///
28 | /// 依赖实体的外键会被设置为空,同时删除操作不会作用到依赖的实体上,依赖实体保持不变,同下
29 | ///
30 | ///
31 |
32 | //配置Passage与PassageCategories的一对多关系
33 | builder.HasOne(t => t.Passage).WithMany(p => p.PassageCategories).HasForeignKey(t => t.PassageId);
34 |
35 | //配置Category与PassageCategories的一对多关系
36 | builder.HasOne(t => t.Category).WithMany(p => p.PassageCategories).HasForeignKey(t => t.CategoryId);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Quartz/JobWrapper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Logging;
3 | using Quartz;
4 | using Sunny.Common.Helper;
5 | using System;
6 | using System.Diagnostics;
7 | using System.Threading.Tasks;
8 |
9 | namespace Sunny.Api.Quartz
10 | {
11 | [PersistJobDataAfterExecution]
12 | public class JobWrapper : IJob where T : IJobEntity
13 | {
14 | ILogger logger;
15 | T job;
16 |
17 |
18 | public JobWrapper()
19 | {
20 | logger = DiHelper.GetService().CreateLogger(typeof(T));
21 | job = DiHelper.CreateInstance();
22 |
23 | }
24 |
25 | public async Task Execute(IJobExecutionContext context)
26 | {
27 | Stopwatch sw = new Stopwatch();
28 |
29 | try
30 | {
31 | sw.Start();
32 | await job.ExecuteAsync(context);
33 | sw.Stop();
34 |
35 | logger.LogInformation($"Job( Name: {job.JobName} , Describe:{job.Describe})已执行,耗时:{sw.ElapsedMilliseconds} 毫秒.");
36 |
37 | }
38 | catch (Exception ex)
39 | {
40 | sw.Stop();
41 | logger.LogError($"Job( Name: {job.JobName} , Describe:{job.Describe})执行时发生异常.", ex);
42 | }
43 | finally
44 | {
45 | IDisposable disposable = job as IDisposable;
46 | disposable?.Dispose();
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/CollectionQuery/CollectionQuery.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Common.Extend.CollectionQuery;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Text;
7 |
8 | namespace System.Linq
9 | {
10 | static public class CollectionQuery
11 | {
12 | ///
13 | /// 分页
14 | ///
15 | /// 数据源
16 | /// 第几页
17 | /// 每页记录数
18 | /// 记录总数
19 | ///
20 | public static List Pagination(this IQueryable list, int pageIndex, int pageSize, out int recordTotal)
21 | {
22 | recordTotal = list.Count();
23 | return list.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
24 | }
25 |
26 | ///
27 | /// 分页并返回包含分页信息的数据
28 | ///
29 | ///
30 | ///
31 | /// 分页要求
32 | ///
33 | public static PageData Pagination(this IQueryable list,PageInfo pageInfo)
34 | {
35 | pageInfo.RecordTotal = list.Count();
36 | var data=list.Skip((pageInfo.PageIndex - 1) * pageInfo.PageSize).Take(pageInfo.PageSize);
37 | return new PageData() { List = data.ToList(), PageInfo = pageInfo };
38 | }
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Framework/Sunny.Repository/FluentApiTools.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using System;
3 | using System.Linq;
4 | using System.Reflection;
5 |
6 | namespace Sunny.Repository
7 | {
8 |
9 | public class FluentApiTools {
10 |
11 |
12 | ///
13 | /// 对DbModel应用FlentApi字段配置
14 | ///
15 | ///
16 | /// DbModel所在的程序集名称
17 | public static void ApplyDbModelFluentApiConfig(ModelBuilder modelBuilder,string dbModelAssemblyName)
18 | {
19 |
20 | /*下边这段代码,如果项目中使用了2个以上的DbContext,也就是使用2个数据库,那么在CodeFirst场景中,
21 | 通过控制台命令进行数据库迁移时,会将2个数据库的表都生成到目标库里,
22 | 有一个解决的办法是在DbModel中继续一个自定义的接口,用以筛选要迁移的表,
23 | 然后在下边的语句中加筛选条件,如.Where(q => q.GetInterface(typeof(你定义的接口).FullName) != null&&q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null);
24 |
25 | 还有一个更好的办法就是为新的库新建立一个项目(程序集),不要将2个DbContext放于一个项目(程序集)中
26 | *
27 | */
28 |
29 | //查找所有FluentAPI配置
30 | var typesToRegister = Assembly.Load(dbModelAssemblyName).GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null);
31 |
32 | //应用FluentAPI
33 | foreach (var type in typesToRegister)
34 | {
35 | //dynamic使C#具有弱语言的特性,在编译时不对类型进行检查
36 |
37 | dynamic configurationInstance = Activator.CreateInstance(type);
38 | modelBuilder.ApplyConfiguration(configurationInstance);
39 |
40 | }
41 |
42 | }
43 |
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/DTO/Request/Demo/Customer.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Common.Helper;
2 | using System;
3 | using static ApiDemo.DTO.Request.Enummm;
4 |
5 | namespace ApiDemo.FluentValidation2
6 | {
7 | public class Customer
8 | {
9 |
10 | public long Id{ get; set; }
11 | public String Surname { get; set; }
12 | public String Forename { get; set; }
13 | public int Discount { get; set; }
14 | ///
15 | /// 地址
16 | ///
17 | public String Address { get; set; }
18 | public String Postcode { get; set; }
19 | public bool HasDiscount { get; set; }
20 |
21 | public IntEnum LocalType { get; set; } = IntEnum.GPS;
22 |
23 | public decimal Price { get; set; } = decimal.MaxValue;
24 | public decimal? PriceNull { get; set; }
25 | public decimal PriceDefault { get; set; }
26 |
27 | public string PriceCN { get; set; } = decimal.MaxValue.ToString();
28 |
29 | public double DoubleNum { get; set; }= double.MaxValue;
30 | public string DoubleCN { get; set; } = double.MaxValue.ToString();
31 |
32 | public float FloatNum { get; set; }= float.MaxValue;
33 |
34 | public string FloatCN { get; set; } = float.MaxValue.ToString();
35 |
36 | public long LongNum { get; set; } = long.MaxValue;
37 | public string LongCn { get; set; } = long.MaxValue.ToString();
38 |
39 | public DateTime Now { get; set; } = DateTime.Now;
40 | public Int64 IntNum { get; set; } = Int64.MaxValue;
41 |
42 | public string IntCn { get; set; } = Int64.MaxValue.ToString();
43 |
44 |
45 | public long PuaID { get; set; } = IdHelper.GenId();
46 |
47 | public string STRINGTEST { get; set; }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Midware/TokenValidateMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.Caching.Distributed;
3 | using Microsoft.Extensions.Options;
4 | using Microsoft.Extensions.Primitives;
5 | using Sunny.Common.ConfigOption;
6 | using System.Threading.Tasks;
7 |
8 | namespace Sunny.Api.Midware
9 | {
10 | public class TokenValidateMiddleware
11 | {
12 | private readonly RequestDelegate next;
13 | private readonly IDistributedCache cache;
14 | private readonly TokenValidateOption option;
15 |
16 |
17 | public TokenValidateMiddleware(RequestDelegate next, IDistributedCache cache, IOptions options)
18 | {
19 | this.next = next;
20 | this.cache = cache;
21 | this.option = options.Value;
22 | }
23 |
24 | public async Task Invoke(HttpContext context)
25 | {
26 | bool validOk = false;
27 |
28 | if (!string.IsNullOrWhiteSpace(option.AuthApiStartWith) && context.Request.Path.ToString().StartsWith(option.AuthApiStartWith))
29 | {
30 | StringValues token = StringValues.Empty;
31 |
32 | if (context.Request.Headers.TryGetValue(option.TokenKey, out token))
33 | {
34 | if (await cache.ExistsAsync(token))
35 | {
36 | validOk = true;
37 | }
38 | }
39 | }
40 | else
41 | {
42 | validOk = true;
43 | }
44 |
45 |
46 | if (!validOk)
47 | {
48 | context.Response.StatusCode = StatusCodes.Status401Unauthorized;
49 | }
50 | else
51 | {
52 | await next(context);
53 | }
54 | }
55 |
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/Framework/Sunny.Repository/Sunny.Repository.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 | https://github.com/MackYang/Sunny.git
6 | https://github.com/MackYang/Sunny/raw/master/Doc/sunny.png
7 | https://github.com/MackYang/Sunny
8 | Sunny框架的Repository部分,用于项目中的数据仓储
9 | MackYang
10 | 1.0.4
11 | 增加了IDbModel和IRelationMap,以优化生成的FluentApi配置
12 | 1.0.4.0
13 | 1.0.4.0
14 |
15 |
16 |
17 | 1701;1702;1591
18 | D:\Work\Code\Sunny\Framework\Sunny.Repository\Sunny.Repository.xml
19 |
20 |
21 |
22 | 1701;1702;1591
23 | D:\Work\Code\Sunny\Framework\Sunny.Repository\Sunny.Repository.xml
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Sunny.Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 | 7.3
6 | Sunny框架需要依赖的通用部分
7 | https://github.com/MackYang/Sunny
8 | https://github.com/MackYang/Sunny/raw/master/Doc/sunny.png
9 | https://github.com/MackYang/Sunny.git
10 | MackYang
11 | 1.0.7
12 | 添加了分页查询的Model
13 |
14 |
15 |
16 | 1701;1702;1591
17 | D:\Work\Code\Sunny\Framework\Sunny.Common\Sunny.Common.xml
18 | Off
19 |
20 |
21 |
22 | 1701;1702;1591
23 | D:\Work\Code\Sunny\Framework\Sunny.Common\Sunny.Common.xml
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/GenericType/ClassTypeExtend.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Dynamic;
5 | using System.Text;
6 |
7 | namespace System
8 | {
9 | public static class ClassTypeExtend
10 | {
11 |
12 | ///
13 | /// 使用另一个对象对源对象进行扩展,这类似于 jQuery 中的 extend 方法。
14 | ///
15 | /// 源对象。
16 | /// 用于扩展的另一个对象。
17 | /// 是否要覆盖源对象中同名属性的值,默认为否
18 | ///
19 | public static dynamic Extend(this T source, object other, bool isOverride = false) where T : class
20 | {
21 | if (source == null)
22 | {
23 | return other;
24 | }
25 |
26 | if (other == null)
27 | {
28 | return source;
29 | }
30 |
31 | var sourceProperties = TypeDescriptor.GetProperties(source);
32 | var otherProperties = TypeDescriptor.GetProperties(other);
33 | var expando = new ExpandoObject();
34 | var dictionary = (IDictionary)expando;
35 |
36 |
37 | foreach (PropertyDescriptor p in sourceProperties)
38 | {
39 | dictionary.Add(p.Name, p.GetValue(source));
40 | }
41 |
42 | foreach (PropertyDescriptor p in otherProperties)
43 | {
44 | if (!dictionary.ContainsKey(p.Name) && !isOverride)
45 | {
46 | dictionary.Add(p.Name, p.GetValue(other));
47 | }
48 | else
49 | {
50 | throw new Exception($"源对象已经包含了名为{p.Name}的属性,如果要覆盖请给isOverride参数传true值");
51 | }
52 | }
53 |
54 | return expando;
55 |
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/Controllers/UnAuthController.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Extensions.Caching.Distributed;
4 | using Microsoft.Extensions.Logging;
5 | using Quartz;
6 | using RepositoryDemo;
7 | using RepositoryDemo.DbModel;
8 | using ServiceDemo;
9 | using Sunny.Api.Controllers;
10 | using Sunny.Api.DTO.Response;
11 | using Sunny.Common.DependencyInjection;
12 | using System;
13 | using System.Threading.Tasks;
14 |
15 | namespace ApiDemo.Api.Controllers
16 | {
17 | ///
18 | /// 不需要验证登录即可访问的API
19 | ///
20 | [Route("unAuthApi/[controller]")]
21 | public class UnAuthController : SunnyController
22 | {
23 |
24 | IDistributedCache cache;
25 | MyDbContext db;
26 | ILogger logger;
27 | IMapper mapper;
28 |
29 |
30 | //TDbContext tDbContex;
31 | public UnAuthController(MyDbContext efDbContext, ILogger logger, IMapper mapper, IDistributedCache cache)
32 | {
33 | this.db = efDbContext;
34 | this.logger = logger;
35 | this.mapper = mapper;
36 | this.cache = cache;
37 |
38 | //this.tDbContex = tDbContext;
39 | }
40 |
41 |
42 | ///
43 | /// 设置Token,使以/api开头的方法可以通过验证
44 | ///
45 | ///
46 | [HttpGet]
47 | public async Task> Get()
48 | {
49 | return await Task.Run(() => this.Success(true));
50 | }
51 |
52 | ///
53 | /// 设置Token,使以/api开头的方法可以通过验证
54 | ///
55 | ///
56 | [HttpGet("SetToken")]
57 | public Result SetToken()
58 | {
59 |
60 | cache.Set("123", "aweiskOK23");
61 | return this.Success(true);
62 | }
63 |
64 | ///
65 | /// 测试异常日志记录
66 | ///
67 | ///
68 | [HttpGet("GetException")]
69 | public Result GetException()
70 | {
71 |
72 | throw new Exception("Test Excexxxx");
73 | }
74 |
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/Serialize/SerializeHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.Serialization;
4 | using System.Runtime.Serialization.Formatters.Binary;
5 |
6 | namespace Sunny.Common.Helper
7 | {
8 | public class SerializeHelper
9 | {
10 | ///
11 | /// 将对象序列化为Base64字符串
12 | ///
13 | /// 需要序列化的对象
14 | /// Base64字符串
15 | public static string SerializeObject(object obj)
16 | {
17 | try
18 | {
19 | IFormatter formatter = new BinaryFormatter();
20 | MemoryStream ms = new MemoryStream();
21 | formatter.Serialize(ms, obj);
22 | ms.Position = 0;
23 | byte[] buffer = new byte[ms.Length];
24 | ms.Read(buffer, 0, buffer.Length);
25 | ms.Flush();
26 | ms.Close();
27 | return Convert.ToBase64String(buffer);
28 | }
29 | catch (Exception ex)
30 | {
31 | throw new Exception("将对象序列化成Base64字符串时发生异常:" + ex);
32 | }
33 |
34 | }
35 |
36 | ///
37 | /// 将Base64字符串反序列化为指定类型的对象
38 | ///
39 | /// 对象类型
40 | /// Base64字符串
41 | /// 出现异常时是否抛出
42 | /// T类型的对象
43 | public static T Desrialize(string base64Str)
44 | {
45 |
46 | try
47 | {
48 | T obj = default(T);
49 | IFormatter formatter = new BinaryFormatter();
50 | byte[] buffer = Convert.FromBase64String(base64Str);
51 | MemoryStream memoryStream = new MemoryStream(buffer);
52 | obj = (T)formatter.Deserialize(memoryStream);
53 | memoryStream.Flush();
54 | memoryStream.Close();
55 | return obj;
56 | }
57 | catch (Exception ex)
58 | {
59 | throw new Exception("将Base64字符串反序列化为对象时出现异常:" + ex);
60 | }
61 |
62 |
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/Calc/CalcHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Sunny.Common.Helper
4 | {
5 | public class CalcHelper
6 | {
7 |
8 | ///
9 | /// 计算增减率返回%比形式字符串,保留2位小数
10 | ///
11 | /// 本期
12 | /// 同期
13 | ///
14 | public static string CalcDifferenceRate(decimal a, decimal b)
15 | {
16 | if (b == 0)
17 | {
18 | if (a == b)
19 | {
20 | return "0.00%";
21 | }
22 | if (a > b)
23 | {
24 | return "100.00%";
25 | }
26 | else
27 | {
28 | return "-100.00%";
29 | }
30 |
31 | }
32 |
33 | return ((a - b) / b).ToString("0.##%");
34 |
35 |
36 | }
37 |
38 |
39 | ///
40 | /// 计算增减率返回转成百分比后的decimal
41 | ///
42 | /// 本期
43 | /// 同期
44 | ///
45 | public static decimal CalcDifferenceRateReturnDecimal(decimal a, decimal b)
46 | {
47 | if (b == 0)
48 | {
49 | if (a == b)
50 | {
51 | return 0;
52 | }
53 | if (a > b)
54 | {
55 | return 100;
56 | }
57 | else
58 | {
59 | return -100;
60 | }
61 |
62 | }
63 |
64 | return Math.Round(((a - b) / b) * 100, 2);
65 |
66 |
67 | }
68 |
69 |
70 | ///
71 | /// 除法计算保留几位小数
72 | ///
73 | /// 被除数
74 | /// 除数
75 | ///
76 | public static decimal CalcDivision(decimal a, decimal b, int decimals = 2)
77 | {
78 | if (b == 0)
79 | {
80 | return 0.00M;
81 | }
82 | return Math.Round(a / b, decimals);
83 |
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Sunny.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 | Library
7 |
8 | Sunny框架的WebApi部分,提供一些Api的常用功能
9 | https://github.com/MackYang/Sunny.git
10 | https://github.com/MackYang/Sunny
11 | https://github.com/MackYang/Sunny/raw/master/Doc/sunny.png
12 | 1.1.0
13 | MackYang
14 | 解决Swagger文档生成的问题
15 |
16 |
17 |
18 | D:\Work\Code\Sunny\Framework\Sunny.Api\Sunny.Api.xml
19 | 1701;1702;1591
20 |
21 |
22 |
23 | 1701;1702;1591
24 | D:\Work\Code\Sunny\Framework\Sunny.Api\Sunny.Api.xml
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Midware/BizExceptionHandlerMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Sunny.Common.Enum;
3 | using Sunny.Common.Helper;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Sunny.Api.Midware
10 | {
11 |
12 | ///
13 | /// 业务异常中间件
14 | ///
15 | public class BizExceptionHandlerMiddleware
16 | {
17 | private readonly RequestDelegate next;
18 |
19 | public BizExceptionHandlerMiddleware(RequestDelegate next)
20 | {
21 | this.next = next;
22 | }
23 |
24 | public async Task Invoke(HttpContext context)
25 | {
26 | try
27 | {
28 | await next(context);
29 | }
30 | catch (BizException ex)
31 | {
32 | await HandleBizExceptionAsync(context, ex);
33 | }
34 |
35 | }
36 |
37 | ///
38 | ///处理业务异常
39 | ///
40 | ///
41 | ///
42 | ///
43 | public async Task HandleBizExceptionAsync(HttpContext context, BizException ex)
44 | {
45 | var data = new SpeciResult { code = Enums.OperationStatus.Fail.GetHashCode(), msg = ex.Message };
46 | await ResponseInfo(context, data);
47 | }
48 |
49 | ///
50 | /// 向前端输出信息
51 | ///
52 | ///
53 | ///
54 | ///
55 | private static async Task ResponseInfo(HttpContext context, SpeciResult data)
56 | {
57 | var result = JsonHelper.ToJsonString(data);
58 | context.Response.ContentType = "application/json;charset=utf-8";
59 | await context.Response.WriteAsync(result);
60 |
61 | }
62 | }
63 |
64 | ///
65 | /// 特殊的返回结果类,和IResult所定义的不同之处在于字段全是小写的,因为发生异常时,后边的中间件不会被调用,所以没法将大驼峰转小驼峰再返回给前端
66 | ///
67 | public class SpeciResult
68 | {
69 | public int code { get; set; }
70 | public string msg { get; set; }
71 | public object data { get; set; }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Framework/TemplateT4/FluentApiConfig/FluentApiConfigProcesser.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Common.Helper;
2 | using Sunny.Repository.DbModel;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 |
9 | namespace Sunny.TemplateT4.FluentApiConfig
10 | {
11 | public class FluentApiConfigProcesser
12 | {
13 | ///
14 | /// 生成DbModel的FluentApi配置文件
15 | ///
16 | /// DbModel所在的程序集名称,如Sunny.Repository
17 | /// 所生成的配置文件的命名空间,如Sunny.Repository.DbModel.MoelConfig
18 | /// 生成文件的输出目录,默认是D:\SunnyFramework\Output\DbConfig\
19 | static public void GenerateFiles(string assemblyName,string outputFileNamespace,string outputDir = @"D:\SunnyFramework\Output\DbConfig\")
20 | {
21 | Assembly assembly = Assembly.Load(assemblyName);
22 | List ts = assembly.GetTypes().ToList();
23 |
24 | var typeList = ts.Where(s => !s.IsInterface && (typeof(IDbModel).IsAssignableFrom(s) || typeof(IRelationMap).IsAssignableFrom(s)) && !s.GetTypeInfo().IsAbstract && s.FullName != "Sunny.Repository.DbModel.BaseModel");
25 | foreach (var item in typeList)
26 | {
27 | var fullNameArr = item.FullName.Split(".");
28 | var filePath = $@"{fullNameArr[fullNameArr.Length - 2]}\{fullNameArr[fullNameArr.Length - 1]}";
29 |
30 | if (typeof(IRelationMap).IsAssignableFrom(item))
31 | {
32 | RelationMapConfig config = new RelationMapConfig(item, outputFileNamespace);
33 | string outputFile = outputDir + @"\RelationConfig\" + filePath + "Map.cs";
34 | FileHelper.WriteFile(outputFile, config.TransformText(), false);
35 | }
36 | else
37 | {
38 | DbModelConfig config = new DbModelConfig(item, outputFileNamespace);
39 | string outputFile = outputDir+@"\FieldConfig\" + filePath+"Config.cs";
40 | FileHelper.WriteFile(outputFile, config.TransformText(), false);
41 | }
42 | }
43 |
44 | Console.WriteLine("DbModalFluentApiConfig Generated To:");
45 | Console.WriteLine(outputDir);
46 |
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/Log/LoggerFactoryExtend.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging.Console;
2 | using Sunny.Common.ConfigOption;
3 | using Sunny.Common.Log;
4 | using System;
5 |
6 |
7 | namespace Microsoft.Extensions.Logging
8 | {
9 | public static class LoggerFactoryExtend
10 | {
11 |
12 | public static ILoggerFactory AddNetLogger(this ILoggerFactory factory, NetLoggerOption option)
13 | {
14 | factory.AddProvider(new NetLoggerProvider(option));
15 | return factory;
16 | }
17 |
18 | public static ILoggerFactory AddNetLogger(this ILoggerFactory factory, NetLoggerOption option, Func filter)
19 | {
20 | factory.AddProvider(new NetLoggerProvider(option, filter, false));
21 | return factory;
22 | }
23 |
24 | ///
25 | /// 使用默认的过虑规则启用网络日志记录,默认规则是只记录微软Warning及以上的日志,或非微软Info及以上的日志
26 | ///
27 | ///
28 | /// 网络日志配置
29 | ///
30 | public static ILoggerFactory AddNetLoggerUseDefaultFilter(this ILoggerFactory factory, NetLoggerOption option)
31 | {
32 | Func filter = (category, level) =>
33 | {
34 |
35 | return (level >= LogLevel.Information && !category.StartsWith("Microsoft")) || (level >= LogLevel.Warning);
36 |
37 | };
38 |
39 | factory.AddProvider(new NetLoggerProvider(option, filter, false));
40 | return factory;
41 | }
42 |
43 | ///
44 | /// 使用默认的过虑规则启用控制台日志记录,默认规则是只记录微软Warning及以上的日志,或非微软Info及以上的日志,或SQL语句
45 | ///
46 | ///
47 | ///
48 | ///
49 | public static ILoggerFactory AddConsoleLoggerUseDefaultFilter(this ILoggerFactory factory)
50 | {
51 | Func filter = (category, level) =>
52 | {
53 |
54 | return (level >= LogLevel.Information && !category.StartsWith("Microsoft")) || (level >= LogLevel.Warning || category.StartsWith("Microsoft.EntityFrameworkCore.Database.Command"));
55 |
56 | };
57 |
58 | factory.AddProvider(new ConsoleLoggerProvider(filter, false));
59 | return factory;
60 | }
61 |
62 |
63 | public static ILoggerFactory AddNetLogger(this ILoggerFactory factory, NetLoggerOption option, Func filter, bool includeScopes)
64 | {
65 | factory.AddProvider(new NetLoggerProvider(option, filter, includeScopes));
66 | return factory;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Extend/IApplicationBuilderExtend.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Quartz;
3 | using Sunny.Api.Quartz;
4 | using Sunny.Common.ConfigOption;
5 | using Sunny.Common.Helper;
6 | using System.Linq;
7 | using System.Reflection;
8 |
9 | namespace Microsoft.AspNetCore.Builder
10 | {
11 | static public class IApplicationBuilderExtend
12 | {
13 | ///
14 | /// 初始化DiHelper中的ServiceProvider
15 | ///
16 | ///
17 | static public void InitServiceProvider(this IApplicationBuilder builder)
18 | {
19 | DiHelper.ServiceProvider = builder.ApplicationServices;
20 | }
21 |
22 | ///
23 | /// 启用Job
24 | ///
25 | ///
26 | ///
27 | ///
28 | static public async void EnableJob(this IApplicationBuilder builder, IConfiguration configuration, ISchedulerFactory schedulerFactory)
29 | {
30 | var option = configuration.GetSection("SunnyOptions:JobOption").Get();
31 | if (option != null && option.Length > 0)
32 | {
33 | //1、通过调度工厂获得调度器
34 | var scheduler = await schedulerFactory.GetScheduler();
35 | //2、开启调度器
36 | await scheduler.Start();
37 |
38 | var types = DiHelper.GetCustomizeTypes(t => typeof(IJobEntity).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract);
39 |
40 |
41 | foreach (var item in option)
42 | {
43 | var tArr = types.Where(x => x.Name == item.JobClassName);
44 | foreach (var t in tArr)
45 | {
46 | //3、创建一个触发器
47 | var trigger = TriggerBuilder.Create()
48 | .WithCronSchedule(item.RunAtCron)
49 | .Build();
50 |
51 | var wrapperType = typeof(JobWrapper<>).MakeGenericType(t.GetTypeInfo());
52 |
53 | //4、创建任务
54 | var jobDetail = JobBuilder.Create(wrapperType)
55 | .WithIdentity(item.JobClassName, item.JobGroup)
56 | .SetJobData(item.Args == null ? new JobDataMap() : new JobDataMap(item.Args))
57 | .Build();
58 |
59 |
60 | //5、将触发器和任务器绑定到调度器中
61 | await scheduler.ScheduleJob(jobDetail, trigger);
62 |
63 | }
64 |
65 |
66 | }
67 | }
68 |
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/File/FileHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 |
4 |
5 | namespace Sunny.Common.Helper
6 | {
7 |
8 | public class FileHelper
9 | {
10 | ///
11 | /// 写入文件
12 | ///
13 | /// 文件名
14 | /// 文件内容
15 | /// 默认是追加,如果要替换原内容传flase
16 | public static void WriteFile(string filePath, string content, bool isAppend = true)
17 | {
18 | var dirInfo = Directory.GetParent(filePath).ToString();
19 | CreatePathIfNotExists(dirInfo);
20 | if (isAppend)
21 | {
22 | System.IO.File.AppendAllText(filePath, content);
23 | }
24 | else
25 | {
26 | System.IO.File.WriteAllText(filePath, content);
27 | }
28 |
29 |
30 | }
31 |
32 | ///
33 | /// 读取文件
34 | ///
35 | /// 文件路径
36 | ///
37 | public static string ReadFile(string filePath)
38 | {
39 | return System.IO.File.ReadAllText(filePath);
40 |
41 | }
42 |
43 |
44 |
45 | /////
46 | ///// 自动清除24小时以前创建的缓存文件,每一小时运行一次
47 | /////
48 | //public static void AutoClearCacheFile()
49 | //{
50 | // string cachePath = HttpContext.Current.Server.MapPath(Vars.CacheFilePath);
51 | // TimeSpan timeSpan = new TimeSpan(1, 0, 0);//1小时
52 | // while (true)
53 | // {
54 | // try
55 | // {
56 | // List fileList = GetFileList(cachePath);
57 | // if (fileList != null && fileList.Count > 0)
58 | // {
59 | // foreach (string item in fileList)
60 | // {
61 | // DateTime cacheTime = File.GetCreationTime(item);
62 | // if (DateTime.Now.Subtract(new TimeSpan(24, 0, 0)) > cacheTime)
63 | // {
64 | // File.Delete(item);
65 | // }
66 | // }
67 | // }
68 | // }
69 | // catch (Exception ex)
70 | // {
71 | // Utility.Logger.Error("删除缓存文件时发生异常:" + ex);
72 | // }
73 |
74 | // Thread.Sleep(timeSpan);
75 | // }
76 | //}
77 |
78 | ///
79 | /// 创建目录(如果目录不存在)
80 | ///
81 | /// 要创建的目录
82 | public static void CreatePathIfNotExists(string path)
83 | {
84 |
85 | if (!Directory.Exists(path))
86 | {
87 | Directory.CreateDirectory(path);
88 | }
89 |
90 |
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "Debug": {
5 | "LogLevel": {
6 | "Default": "Information"
7 | }
8 | },
9 | "Console": {
10 | "LogLevel": {
11 | "Default": "Information"
12 | }
13 | }
14 | },
15 |
16 | "ConnectionStrings": {
17 | "MySql": "server=127.0.0.1;Port=3306;database=test;uid=root;pwd=yourPassword;Charset=utf8;Pooling=true;Max Pool Size=1000;",
18 | "MySq2": "server=127.0.0.1;Port=3306;database=test2;uid=root;pwd=yourPassword.;Charset=utf8;"
19 | },
20 |
21 | "SunnyOptions": {
22 | "NetLoggerOption": {
23 | "Url": "http://test.log.loc-mall.com/Api/AddLog",
24 | //每个业务系统配置自己的ID,请到http://test.log.loc-mall.com/ui/pages/apply.aspx申请,并妥善保存,
25 | //写入的日志在这里查看http://test.log.loc-mall.com/ui/pages/log.aspx?systemid=xxxxxxxx
26 | //生产环境去掉test.即可,或者到https://github.com/MackYang/LogService.git下来部署载自己的日志系统
27 | "SystemId": "b06b7e4d-bbce-11e8-98ca-00163e063309",
28 | //存放离线日志的目录,当网络日志记录失败时,会将日志以yyyy-MM-dd.txt写到该目录下
29 | "OfflineLogPath": "D:\\SunnyFramework\\OfflineLog"
30 | },
31 | "SnowflakeOption": {
32 | "DatacenterId": 1,
33 | "MachineId": 1
34 | },
35 | "RedisOptions": {
36 | //连接字符串
37 | "ConnectionString": "127.0.0.1:6379",
38 | //调用方实例名称,redis中的key会自动以设置的字符串开头,用以标识是哪台机器存入的key
39 | "InstanceName": "api_",
40 | //默认的滑动过期时间多少秒,为了防止缓存击穿,实际存入时会在该时间上加10秒类的随机数
41 | "DefaultSlidingExpiration": 600
42 | },
43 | "TokenValidateOption": {
44 | //将根据该值来从HttpHeader中存取对应的token值,默认就是"token"
45 | "TokenKey": "token",
46 | // 以此开头的API都需要验证Token,如果不需要可不配置
47 | "AuthApiStartWith": "/api"
48 |
49 | },
50 | //邮件配置
51 | "MailOption": {
52 | // 邮件服务器地址
53 | "EmailHost": "smtp.ym.163.com",
54 | // 用户名
55 | "EmailUserName": "you user name",
56 | // 密码
57 | "EmailPassword": "you password",
58 | // IP白名单列表,在列表中的IP发邮件前不执行检查事件
59 | "IPWhiteList": [ "127.0.0.1" ]
60 | },
61 | //短信配置
62 | "SmsOption": {
63 | // 这个短信平台不能用了,以后接入其他平台
64 | "ApiUrl": "http://sms.pica.com/zqhdServer/sendSMS.jsp?regcode=yourCode&pwd=yourPassword&phone=$TOPHONE&content=$CONTENT&extnum=&level=1&schtime=&reportflag=1&url=url&smstype=0",
65 | // IP白名单列表,在列表中的IP发短信前不执行检查事件
66 | "IPWhiteList": [ "127.0.0.1" ]
67 | },
68 | //查询IP信息的配置
69 | "IpInfoQueryOption": {
70 | //IP查询的API
71 | "ApiUrl": "http://www.ip.cn/index.php?ip="
72 | },
73 | "JobOption": [
74 | {
75 | //Job所的的类名称
76 | "JobClassName": "JobB",
77 | //Job所属的组,同一组中不能有2个相同的任务
78 | "JobGroup": "group1",
79 | //Job在什么时候运行,用Cron表达式
80 | "RunAtCron": "*/55 * * * * ?",
81 | //Job的参数,没有可以不写
82 | "Args": {
83 | //参数名字和你在任务中写的要相同
84 | "pxxx": "kkk",
85 | "nnn": 123
86 | }
87 | },
88 |
89 | {
90 | //Job所的的类名称
91 | "JobClassName": "JobA",
92 | //Job在什么时候运行,用Cron表达式
93 | "RunAtCron": "*/59 * * * * ?"
94 |
95 | }
96 | ]
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Framework/TemplateT4/Sunny.TemplateT4.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | netcoreapp2.1
6 |
7 |
8 | https://github.com/MackYang/Sunny.git
9 | https://github.com/MackYang/Sunny/raw/master/Doc/sunny.png
10 | https://github.com/MackYang/Sunny
11 | Sunny框架的T4模板部分,主要用于生成DbModel的FluentApi配置
12 | MackYang
13 | 1.0.9
14 | 增加varchar字段默认不能为空的配置
15 | 1.0.9.0
16 | 1.0.9.0
17 |
18 |
19 |
20 | D:\Work\Code\Sunny\Framework\TemplateT4\Sunny.TemplateT4.xml
21 | 1701;1702;1591
22 |
23 |
24 |
25 | 1701;1702;1591
26 | D:\Work\Code\Sunny\Framework\TemplateT4\Sunny.TemplateT4.xml
27 |
28 |
29 |
30 |
31 | True
32 | True
33 | RelationMapConfig.tt
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | mscorlib
48 |
49 |
50 | System
51 |
52 |
53 | System.Core
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | True
64 | True
65 | DbModelConfig.tt
66 |
67 |
68 | True
69 | True
70 | RelationMapConfig.tt
71 |
72 |
73 |
74 |
75 |
76 | TextTemplatingFilePreprocessor
77 | DbModelConfig.cs
78 |
79 |
80 | TextTemplatingFilePreprocessor
81 | RelationMapConfig.cs
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Log/NetLoggerProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Sunny.Common.ConfigOption;
3 | using System;
4 | using System.Collections.Concurrent;
5 | using System.Collections.Generic;
6 | using System.Text;
7 |
8 | namespace Sunny.Common.Log
9 | {
10 | class NetLoggerProvider : ILoggerProvider, ISupportExternalScope
11 | {
12 | private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary();
13 |
14 | private readonly Func _filter;
15 |
16 | private readonly NetLoggerProcessor _messageQueue;
17 |
18 | private static readonly Func trueFilter = (cat, level) => true;
19 | private static readonly Func falseFilter = (cat, level) => false;
20 | private IDisposable _optionsReloadToken;
21 | private bool _includeScopes;
22 | private bool _disableColors;
23 | private IExternalScopeProvider _scopeProvider;
24 |
25 | public NetLoggerProvider(NetLoggerOption option) : this(option,null, false) { }
26 |
27 | public NetLoggerProvider(NetLoggerOption option,Func filter, bool includeScopes)
28 | {
29 | _filter = filter;
30 | _includeScopes = includeScopes;
31 | _messageQueue = new NetLoggerProcessor(option);
32 |
33 | }
34 |
35 |
36 |
37 |
38 | public ILogger CreateLogger(string name)
39 | {
40 | return _loggers.GetOrAdd(name, CreateLoggerImplementation);
41 | }
42 |
43 | private NetLogger CreateLoggerImplementation(string name)
44 | {
45 | return new NetLogger(name, GetFilter( ), null, _messageQueue)
46 | ;
47 | }
48 |
49 | private Func GetFilter( )
50 | {
51 | if (_filter != null)
52 | {
53 | return _filter;
54 | }
55 |
56 |
57 | return trueFilter;
58 | }
59 |
60 | private IEnumerable GetKeyPrefixes(string name)
61 | {
62 | while (!string.IsNullOrEmpty(name))
63 | {
64 | yield return name;
65 | var lastIndexOfDot = name.LastIndexOf('.');
66 | if (lastIndexOfDot == -1)
67 | {
68 | yield return "Default";
69 | break;
70 | }
71 | name = name.Substring(0, lastIndexOfDot);
72 | }
73 | }
74 |
75 | private IExternalScopeProvider GetScopeProvider()
76 | {
77 | if (_includeScopes && _scopeProvider == null)
78 | {
79 | _scopeProvider = new LoggerExternalScopeProvider();
80 | }
81 | return _includeScopes ? _scopeProvider : null;
82 | }
83 |
84 | public void Dispose()
85 | {
86 | _optionsReloadToken?.Dispose();
87 | _messageQueue.Dispose();
88 | }
89 |
90 | public void SetScopeProvider(IExternalScopeProvider scopeProvider)
91 | {
92 | _scopeProvider = scopeProvider;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Framework/TemplateT4/FluentApiConfig/RelationMapConfigPartial.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Repository.DbModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 |
8 | namespace Sunny.TemplateT4.FluentApiConfig
9 | {
10 | public partial class RelationMapConfig
11 | {
12 | public Type DbModelType { get; set; }
13 |
14 | public string ConfigNamespace { get; set; }
15 |
16 |
17 | public string GetRelationConfig()
18 | {
19 | //当前类型的所有公共属性
20 | var allFields = DbModelType.GetProperties();
21 |
22 | //获取简单类型字段
23 | var simpleTypeFields = allFields.Where(x => !x.PropertyType.IsGenericType && !typeof(IDbModel).IsAssignableFrom(x.PropertyType));
24 |
25 | //导航属性字段,表示关系
26 | var navigationFields = allFields.Where(x => x.PropertyType.IsGenericType || typeof(IDbModel).IsAssignableFrom(x.PropertyType)).ToList();
27 |
28 | if (simpleTypeFields.Count() != 2|| navigationFields.Count()!=2)
29 | {
30 | throw new Exception($"实体{DbModelType}的导航属性配置不正确,在多对多的关系映射中,必须包含2个Model的Id以及对应的导航属性,如果不是多对多的映射,不需要继承自IRelationMap接口,直接遵守微软约定大约配置的法则EFCore即可自动配置.");
31 | }
32 |
33 | StringBuilder sb = new StringBuilder();
34 |
35 | //复合主键设置
36 | var mutliKey = $"builder.HasKey(t => new { "{" + string.Join(',', simpleTypeFields.Select(x => "t." + x.Name)) + "}" });";
37 | sb.AppendLine(mutliKey);
38 |
39 | var leftModelName = navigationFields[0].Name;
40 |
41 | var leftModelNavigations = navigationFields[0].PropertyType.GetProperties().Where(x => x.PropertyType.GenericTypeArguments.Any(n => n == DbModelType));
42 | if (leftModelNavigations.Count() != 1)
43 | {
44 | throw new Exception($"类型{navigationFields[0].PropertyType}中有且只能有一个泛型类型为{DbModelType}的导航属性");
45 | }
46 |
47 | var leftModelNavigationFieldName = leftModelNavigations.First().Name;
48 |
49 | var rightModelName = navigationFields[1].Name;
50 |
51 | var rightModelNavigations = navigationFields[1].PropertyType.GetProperties().Where(x => x.PropertyType.GenericTypeArguments.Any(n => n == DbModelType));
52 | if (rightModelNavigations.Count() != 1)
53 | {
54 | throw new Exception($"类型{navigationFields[0].PropertyType}中有且只能有一个泛型类型为{DbModelType}的导航属性");
55 | }
56 |
57 | var rightModelNavigationFieldName = rightModelNavigations.First().Name;
58 |
59 | //配置左边的一对多关系
60 | var leftConfig = $"builder.HasOne(t => t.{leftModelName}).WithMany(x => x.{leftModelNavigationFieldName}).HasForeignKey(t => t.{leftModelName+"Id"});";
61 |
62 | //配置右边的一对多关系
63 | var rightConfig = $"builder.HasOne(t => t.{rightModelName}).WithMany(x => x.{rightModelNavigationFieldName}).HasForeignKey(t => t.{rightModelName + "Id"});";
64 |
65 | sb.AppendLine(leftConfig);
66 | sb.AppendLine(rightConfig);
67 |
68 | return sb.ToString();
69 | }
70 |
71 |
72 |
73 | public RelationMapConfig(Type dbModelType, string configNamespace)
74 | {
75 | this.DbModelType = dbModelType;
76 | this.ConfigNamespace = configNamespace;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Midware/ExceptionHandlingMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.Logging;
3 | using Sunny.Common.Enum;
4 | using Sunny.Common.Helper;
5 | using System;
6 | using System.Threading.Tasks;
7 |
8 |
9 | namespace Sunny.Api.Midware
10 | {
11 | public class ErrorHandlingMiddleware
12 | {
13 | private readonly RequestDelegate next;
14 | private readonly ILoggerFactory loggerFactory;
15 | public ErrorHandlingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
16 | {
17 |
18 | this.next = next;
19 | this.loggerFactory = loggerFactory;
20 |
21 | }
22 |
23 | public async Task Invoke(HttpContext context)
24 | {
25 | var statusCode = context.Response.StatusCode;
26 |
27 | try
28 | {
29 | await next(context);
30 | }
31 | catch (BizException ex)
32 | {
33 | await HandleBizExceptionAsync(context, ex);
34 | }
35 | catch (Exception ex)
36 | {
37 | await HandleExceptionAsync(context, ex);
38 | }
39 | finally
40 | {
41 | if (statusCode != StatusCodes.Status200OK)
42 | {
43 | await HandleErrorAsync(context, statusCode);
44 | }
45 | }
46 | }
47 |
48 | private async Task HandleExceptionAsync(HttpContext context, Exception ex)
49 | {
50 | context.Response.StatusCode = StatusCodes.Status500InternalServerError;
51 | var logger = loggerFactory.CreateLogger(ex.TargetSite.DeclaringType);
52 | logger.LogError(ex, ex.Message);
53 |
54 | var data = new SpeciResult { code = Enums.OperationStatus.Exception.GetHashCode(), msg = "我们已经收到此次异常信息,将尽快解决!" };
55 |
56 | await ResponseInfo(context, data);
57 | }
58 |
59 | private async Task HandleErrorAsync(HttpContext context, int statusCode)
60 | {
61 |
62 | var msg = "未知错误";
63 |
64 | switch (statusCode)
65 | {
66 | case StatusCodes.Status401Unauthorized:
67 | msg = "未授权";
68 | break;
69 | case StatusCodes.Status404NotFound:
70 | msg = "未找到服务";
71 | break;
72 | case StatusCodes.Status502BadGateway:
73 | msg = "请求错误";
74 | break;
75 | }
76 | var data = new SpeciResult { code = Enums.OperationStatus.Fail.GetHashCode(), msg = msg };
77 | await ResponseInfo(context, data);
78 | }
79 |
80 | ///
81 | ///处理业务异常
82 | ///
83 | ///
84 | ///
85 | ///
86 | private async Task HandleBizExceptionAsync(HttpContext context, BizException ex)
87 | {
88 | var data = new SpeciResult { code = Enums.OperationStatus.Fail.GetHashCode(), msg = ex.Message };
89 | await ResponseInfo(context, data);
90 | }
91 |
92 | ///
93 | /// 向前端输出信息
94 | ///
95 | ///
96 | ///
97 | ///
98 | private async Task ResponseInfo(HttpContext context, SpeciResult data)
99 | {
100 | var result = JsonHelper.ToJsonString(data);
101 | context.Response.ContentType = "application/json;charset=utf-8";
102 | await context.Response.WriteAsync(result);
103 |
104 | }
105 |
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Extend/Cache/IDistributedCacheExtend.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Threading.Tasks;
4 |
5 | namespace Microsoft.Extensions.Caching.Distributed
6 | {
7 | static public class IDistributedCacheExtend
8 | {
9 | static public int DefaultSlidingExpiration { get; set; } = 600;
10 |
11 | static Random random = new Random();
12 |
13 | ///
14 | /// 将对象转成json字符串后存到缓存中
15 | ///
16 | ///
17 | ///
18 | ///
19 | /// 过期选项,如果不指定,则在配置的默认时间后滑动过期
20 | public static void Set(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions options = null)
21 | {
22 | if (options == null)
23 | {
24 | options = new DistributedCacheEntryOptions();
25 | }
26 |
27 | options.SlidingExpiration = TimeSpan.FromSeconds(DefaultSlidingExpiration + random.Next(10));
28 |
29 | cache.SetString(key, JsonConvert.SerializeObject(value), options);
30 | }
31 |
32 | ///
33 | /// 将对象转成json字符串后存到缓存中
34 | ///
35 | ///
36 | ///
37 | ///
38 | /// 过期选项,如果不指定,则在配置的默认时间后滑动过期
39 | public static async Task SetAsync(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions options = null)
40 | {
41 | if (options == null)
42 | {
43 | options = new DistributedCacheEntryOptions();
44 | }
45 |
46 | options.SlidingExpiration = TimeSpan.FromSeconds(DefaultSlidingExpiration + random.Next(10));
47 |
48 | var jsonStr = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(value));
49 |
50 | await cache.SetStringAsync(key, jsonStr, options);
51 | }
52 |
53 | ///
54 | /// 从缓存中获取json字符串并转成指定类型对象返回
55 | ///
56 | ///
57 | ///
58 | ///
59 | /// 如果没有取到返回的默认值
60 | ///
61 | public static T Get(this IDistributedCache cache, string key,T defaultValue=default)
62 | {
63 | var jsonStr = cache.GetString(key);
64 | return string.IsNullOrWhiteSpace(jsonStr) ? defaultValue : JsonConvert.DeserializeObject(jsonStr);
65 | }
66 |
67 | ///
68 | /// 从缓存中获取json字符串并转成指定类型对象返回
69 | ///
70 | ///
71 | ///
72 | ///
73 | /// 如果没有取到返回的默认值
74 | ///
75 | public static async Task GetAsync(this IDistributedCache cache, string key, T defaultValue = default)
76 | {
77 | var jsonStr =await cache.GetStringAsync(key);
78 |
79 | if (string.IsNullOrWhiteSpace(jsonStr))
80 | {
81 | return defaultValue;
82 | }
83 |
84 | return await Task.Factory.StartNew(() => JsonConvert.DeserializeObject(jsonStr));
85 | }
86 |
87 | ///
88 | /// 指定的key是否存在
89 | ///
90 | ///
91 | ///
92 | ///
93 | public static async Task ExistsAsync(this IDistributedCache cache, string key)
94 | {
95 | var bytes = await cache.GetAsync(key);
96 |
97 | return bytes != null && bytes.Length > 0;
98 | }
99 |
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/Framework/TemplateT4/FluentApiConfig/DbModelConfigPartial.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Repository.DbModel;
2 | using System;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Security.Cryptography.X509Certificates;
6 | using System.Text;
7 |
8 | namespace Sunny.TemplateT4.FluentApiConfig
9 | {
10 | public partial class DbModelConfig
11 | {
12 |
13 | public Type DbModelType { get; set; }
14 |
15 | public string ConfigNamespace { get; set; }
16 |
17 | private string InheritBaseModel()
18 | {
19 | var baseFields = typeof(BaseModel).GetProperties();
20 |
21 | //当前类型的所有公共属性
22 | var allFields = DbModelType.GetProperties();
23 |
24 | //排除BaseModel的属性
25 | var selfFields = allFields.Where(x => !baseFields.Any(dx => { return dx.Name == x.Name; }));
26 |
27 | //排除导航属性字段,这些字段是以关系的形式处理
28 | var simpleTypeFields = selfFields.Where(x => !x.PropertyType.IsGenericType && !typeof(IDbModel).IsAssignableFrom(x.PropertyType));
29 |
30 |
31 | StringBuilder sb = new StringBuilder();
32 | sb.Clear();
33 | sb.AppendLine(GetFieldConfig(baseFields.Where(x => x.Name == "Id").First()));
34 | simpleTypeFields.ToList().ForEach(x => sb.AppendLine(GetFieldConfig(x)));
35 | //排除在子类中被private的字段
36 | baseFields.Where(x => x.Name != "Id" && allFields.Any(n => { return n.Name == x.Name; })).ToList().ForEach(x => sb.AppendLine(GetFieldConfig(x)));
37 |
38 | return sb.ToString();
39 | }
40 |
41 | private string OtherModel()
42 | {
43 | //当前类型的所有公共属性
44 | var allFields = DbModelType.GetProperties();
45 |
46 | //排除导航属性字段,这些字段是以关系的形式处理
47 | var simpleTypeFields = allFields.Where(x => !x.PropertyType.IsGenericType && !typeof(IDbModel).IsAssignableFrom(x.PropertyType));
48 |
49 | StringBuilder sb = new StringBuilder();
50 | simpleTypeFields.ToList().ForEach(x => sb.AppendLine(GetFieldConfig(x)));
51 |
52 | return sb.ToString();
53 | }
54 |
55 | public string GetTableConfig()
56 | {
57 | var queryFilterConfig = "";
58 |
59 | //是否有软删除字段
60 | var hasIsDelete = DbModelType.GetProperties().Count(x => x.Name == "IsDelete") > 0;
61 |
62 | if (hasIsDelete)
63 | {
64 | queryFilterConfig = ".HasQueryFilter(x=> !x.IsDelete)";
65 | }
66 | var tabConfig = $"builder.ToTable(\"{DbModelType.Name.UpperCharToUnderLine()}\"){queryFilterConfig};";
67 | return tabConfig;
68 | }
69 |
70 | public string GetFieldsConfig()
71 | {
72 | if (typeof(BaseModel).IsAssignableFrom(DbModelType))
73 | {
74 | return InheritBaseModel();
75 | }
76 | else
77 | {
78 | return OtherModel();
79 | }
80 | }
81 |
82 |
83 | private string GetFieldConfig(PropertyInfo pi)
84 | {
85 |
86 | string originName = pi.Name;
87 |
88 | string destName = originName.UpperCharToUnderLine();
89 |
90 | string fieldConfig = $"builder.Property(x => x.{originName}).HasColumnName(\"{ destName}\")";
91 |
92 | if (originName == "CreateTime")
93 | {
94 | fieldConfig += ".HasDefaultValueSql(\"now()\")";
95 | }
96 |
97 | if (originName == "UpdateTime")
98 | {
99 | fieldConfig += ".IsRowVersion()";
100 | }
101 |
102 | if (pi.PropertyType == typeof(String))
103 | {
104 | fieldConfig += ".HasMaxLength(30).IsRequired()";
105 | }
106 | if (pi.PropertyType == typeof(Decimal))
107 | {
108 | fieldConfig += ".HasColumnType(\"decimal(18, 2)\")";
109 | }
110 |
111 | return fieldConfig + ";";
112 |
113 | }
114 |
115 |
116 | public DbModelConfig(Type dbModelType, string configNamespace)
117 | {
118 | this.DbModelType = dbModelType;
119 | this.ConfigNamespace = configNamespace;
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/Framework/Sunny.Api/Sunny.Api.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sunny.Api
5 |
6 |
7 |
8 |
9 | 返回一个动态类型的值,通常用于想返回扩展类型的场景,比如在源对象上增加一个枚举值描述的动态属性
10 |
11 |
12 |
13 |
14 |
15 |
16 | 带返回数据的结果
17 |
18 | 返回的类型
19 |
20 |
21 |
22 | 不带返回数据的Result
23 | 如果要给前端返回数据时,请用泛型版本,这样看API时就知道返回的数据长什么样子
24 |
25 |
26 |
27 |
28 | 需要验证的实体请继承自这个类
29 |
30 |
31 |
32 |
33 |
34 | 业务异常中间件
35 |
36 |
37 |
38 |
39 | 处理业务异常
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 向前端输出信息
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 特殊的返回结果类,和IResult所定义的不同之处在于字段全是小写的,因为发生异常时,后边的中间件不会被调用,所以没法将大驼峰转小驼峰再返回给前端
56 |
57 |
58 |
59 |
60 | 处理业务异常
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 向前端输出信息
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | 任务信息
77 |
78 |
79 |
80 |
81 | 任务的名称
82 |
83 |
84 |
85 |
86 | 任务的描述
87 |
88 |
89 |
90 |
91 | 任务内容
92 |
93 |
94 |
95 |
96 | 初始化DiHelper中的ServiceProvider
97 |
98 |
99 |
100 |
101 |
102 | 启用Job
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Id/SnowFlake.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Sunny.Common.Id
6 | {
7 | public class Snowflake
8 | {
9 |
10 | //基准时间
11 | private static long StartStmp = 1539837182162L;
12 |
13 | /*每一部分占用的位数*/
14 | //机器标识位数
15 | const int MachineIdBits = 5;
16 | //数据标志位数
17 | const int DatacenterIdBits = 5;
18 | //序列号识位数
19 | const int SequenceBits = 12;
20 |
21 | /* 每一部分的最大值*/
22 | //机器ID最大值
23 | const long MaxMachineNum = -1L ^ (-1L << MachineIdBits);
24 | //数据标志ID最大值
25 | const long MaxDatacenterNum = -1L ^ (-1L << DatacenterIdBits);
26 | //序列号ID最大值
27 | private const long MaxSequenceNum = -1L ^ (-1L << SequenceBits);
28 |
29 | /*每一部分向左的位移*/
30 | //机器ID偏左移12位
31 | private const int MachineShift = SequenceBits;
32 | //数据ID偏左移17位
33 | private const int DatacenterIdShift = SequenceBits + MachineIdBits;
34 | //时间毫秒左移22位
35 | public const int TimestampLeftShift = SequenceBits + MachineIdBits + DatacenterIdBits;
36 |
37 |
38 | private long _sequence = 0L;//序列号
39 | private long _lastTimestamp = -1L;//上一次时间戳
40 | public long MachineId { get; protected set; }//机器标识
41 | public long DatacenterId { get; protected set; }//数据中心
42 | //public long Sequence = 0L;//序列号
43 | //{
44 | // get { return _sequence; }
45 | // internal set { _sequence = value; }
46 | //}
47 |
48 | private readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
49 | private readonly object _lock = new Object();
50 | public Snowflake(long machineId, long datacenterId)
51 | {
52 | // 如果超出范围就抛出异常
53 | if (machineId > MaxMachineNum || machineId < 0)
54 | {
55 | throw new ArgumentException(string.Format("machineId 必须大于0,MaxMachineNum: {0}", MaxMachineNum));
56 | }
57 |
58 | if (datacenterId > MaxDatacenterNum || datacenterId < 0)
59 | {
60 | throw new ArgumentException(string.Format("datacenterId必须大于0,且不能大于MaxDatacenterNum: {0}", MaxDatacenterNum));
61 | }
62 |
63 | //先检验再赋值
64 | MachineId = machineId;
65 | DatacenterId = datacenterId;
66 | //_sequence = sequence;
67 | }
68 |
69 | //public static Init(long machineId, long datacenterId)
70 | //{
71 |
72 | //}
73 | public long NextId()
74 | {
75 | lock (_lock)
76 | {
77 | var timestamp = TimeGen();
78 | if (timestamp < _lastTimestamp)
79 | {
80 | throw new Exception(string.Format("时间戳必须大于上一次生成ID的时间戳. 拒绝为{0}毫秒生成id", _lastTimestamp - timestamp));
81 | }
82 |
83 | //如果上次生成时间和当前时间相同,在同一毫秒内
84 | if (_lastTimestamp == timestamp)
85 | {
86 | //sequence自增,和sequenceMask相与一下,去掉高位
87 | _sequence = (_sequence + 1) & MaxSequenceNum;
88 | //判断是否溢出,也就是每毫秒内超过1024,当为1024时,与sequenceMask相与,sequence就等于0
89 | if (_sequence == 0L)
90 | {
91 | //等待到下一毫秒
92 | timestamp = TilNextMillis(_lastTimestamp);
93 | }
94 | }
95 | else
96 | {
97 | //如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加,
98 | //为了保证尾数随机性更大一些,最后一位可以设置一个随机数
99 | _sequence = 0L;//new Random().Next(10);
100 | }
101 |
102 | _lastTimestamp = timestamp;
103 | return ((timestamp - StartStmp) << TimestampLeftShift) | (DatacenterId << DatacenterIdShift) | (MachineId << MachineShift) | _sequence;
104 | }
105 | }
106 |
107 | // 防止产生的时间比之前的时间还要小(由于NTP回拨等问题),保持增量的趋势.
108 | protected virtual long TilNextMillis(long lastTimestamp)
109 | {
110 | var timestamp = TimeGen();
111 | while (timestamp <= lastTimestamp)
112 | {
113 | timestamp = TimeGen();
114 | }
115 | return timestamp;
116 | }
117 |
118 | // 获取当前的时间戳
119 | protected virtual long TimeGen()
120 | {
121 | //return TimeExtensions.CurrentTimeMillis();
122 | return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/ApiDemo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ApiDemo
5 |
6 |
7 |
8 |
9 | 不需要验证登录即可访问的API
10 |
11 |
12 |
13 |
14 | 设置Token,使以/api开头的方法可以通过验证
15 |
16 |
17 |
18 |
19 |
20 | 设置Token,使以/api开头的方法可以通过验证
21 |
22 |
23 |
24 |
25 |
26 | 测试异常日志记录
27 |
28 |
29 |
30 |
31 |
32 | Session测试
33 |
34 |
35 |
36 |
37 |
38 | Redis测试
39 |
40 |
41 |
42 |
43 |
44 | 不带返回值的成功场景测试
45 |
46 |
47 |
48 |
49 |
50 | 带返回值的成功场景测试,测试模型验证
51 |
52 |
53 |
54 |
55 |
56 | 带返回值,且值为动态扩展对象的场景测试,分页测试
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 带查询条件分页测试
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 失败返回值测试
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | 路由测试
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | 模型绑定测试
89 |
90 |
91 |
92 |
93 |
94 |
95 | AutoMapper测试
96 |
97 |
98 |
99 |
100 |
101 | 随机返回成功和失败测试
102 |
103 |
104 |
105 |
106 |
107 | 模型验证测试
108 |
109 |
110 |
111 |
112 |
113 |
114 | 数据存取和日志记录测试
115 |
116 |
117 |
118 |
119 |
120 | 地址
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/Security/SecurityHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 |
6 | namespace Sunny.Common.Helper
7 | {
8 | ///安全策略
9 | public static class SecurityHelper
10 | {
11 | private static DESCryptoServiceProvider descryptoServiceProvider = null;
12 | static SecurityHelper()
13 | {
14 | descryptoServiceProvider = new DESCryptoServiceProvider();
15 | }
16 |
17 | ///
18 | /// DES加密字符串
19 | ///
20 | /// 源字符串
21 | /// 用于加解密的key
22 | /// 加密后的字符串
23 | public static string DesEncrypt(string input, string key)
24 | {
25 | try
26 | {
27 | byte[] inputByteArray;
28 | inputByteArray = Encoding.Default.GetBytes(input);
29 | descryptoServiceProvider.Key = ASCIIEncoding.ASCII.GetBytes(key.Substring(0, 8));
30 | descryptoServiceProvider.IV = ASCIIEncoding.ASCII.GetBytes(key.Substring(0, 8));
31 | MemoryStream memoryStream = new MemoryStream();
32 | CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Write);
33 | cryptoStream.Write(inputByteArray, 0, inputByteArray.Length);
34 | cryptoStream.FlushFinalBlock();
35 | StringBuilder ret = new StringBuilder();
36 | foreach (byte b in memoryStream.ToArray())
37 | {
38 | ret.AppendFormat("{0:X2}", b);
39 | }
40 | return ret.ToString();
41 | }
42 | catch (Exception ex)
43 | {
44 | throw new Exception("给字符串" + input + "加密时出现异常:" + ex);
45 | }
46 |
47 | }
48 |
49 | ///
50 | /// 解密DES字符串
51 | ///
52 | /// DES字符串
53 | /// 用于加解密的key
54 | /// 解密后的字符串
55 | public static string DesDecrypt(string DESString, string key)
56 | {
57 |
58 |
59 | try
60 | {
61 | int len = DESString.Length / 2;
62 | byte[] inputByteArray = new byte[len];
63 | int x, i;
64 | for (x = 0; x < len; x++)
65 | {
66 | i = Convert.ToInt32(DESString.Substring(x * 2, 2), 16);
67 | inputByteArray[x] = (byte)i;
68 | }
69 | descryptoServiceProvider.Key = ASCIIEncoding.ASCII.GetBytes(key.Substring(0, 8));
70 | descryptoServiceProvider.IV = ASCIIEncoding.ASCII.GetBytes(key.Substring(0, 8));
71 | MemoryStream ms = new MemoryStream();
72 | CryptoStream cs = new CryptoStream(ms, descryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Write);
73 | cs.Write(inputByteArray, 0, inputByteArray.Length);
74 | cs.FlushFinalBlock();
75 | return Encoding.Default.GetString(ms.ToArray());
76 | }
77 | catch (Exception ex)
78 | {
79 | throw new Exception("解密DES字符串" + DESString + "时出现异常:" + ex);
80 | }
81 |
82 | }
83 |
84 | ///
85 | /// 以MD5的方式加密字符串
86 | ///
87 | /// 源字符串
88 | /// 加密后的MD5格式字符串
89 | public static string MD5Encrypt(string input)
90 | {
91 | try
92 | {
93 | using (var md5 = MD5.Create())
94 | {
95 | var result = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
96 | var strResult = BitConverter.ToString(result);
97 | return strResult.Replace("-", "");
98 | }
99 | }
100 | catch (Exception ex)
101 | {
102 | throw new Exception("将字符串加密成MD5格式时,出现异常:" + ex);
103 | }
104 |
105 | }
106 |
107 |
108 | ///
109 | /// 对字符串进行SHA1加密
110 | ///
111 | /// 需要加密的字符串
112 | /// 密文
113 | public static string SHA1Encrypt(string input)
114 | {
115 |
116 | try
117 | {
118 | byte[] StrRes = Encoding.Default.GetBytes(input);
119 | HashAlgorithm iSHA = new SHA1CryptoServiceProvider();
120 | StrRes = iSHA.ComputeHash(StrRes);
121 | StringBuilder EnText = new StringBuilder();
122 | foreach (byte iByte in StrRes)
123 | {
124 | EnText.AppendFormat("{0:x2}", iByte);
125 | }
126 | return EnText.ToString();
127 | }
128 | catch (Exception ex)
129 | {
130 | throw new Exception("将字符串加密成SHA1格式时,出现异常:" + ex);
131 | }
132 |
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Log/NetLoggerProcessor.cs:
--------------------------------------------------------------------------------
1 | using Sunny.Common.ConfigOption;
2 | using Sunny.Common.Helper;
3 | using System;
4 | using System.Collections.Concurrent;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace Sunny.Common.Log
9 | {
10 | class NetLoggerProcessor : IDisposable
11 | {
12 | private const int _maxQueuedMessages = 1024;
13 |
14 | private readonly BlockingCollection _messageQueue = new BlockingCollection(_maxQueuedMessages);
15 | private readonly Thread _outputThread;
16 | private readonly NetLoggerOption option;
17 |
18 |
19 | public NetLoggerProcessor(NetLoggerOption option)
20 | {
21 | this.option = option;
22 | // Start Console message queue processor
23 | _outputThread = new Thread(ProcessLogQueue)
24 | {
25 | IsBackground = true,
26 | Name = "Net logger queue processing thread"
27 | };
28 | _outputThread.Start();
29 | }
30 |
31 | public virtual void EnqueueMessage(LogData message)
32 | {
33 | if (!_messageQueue.IsAddingCompleted)
34 | {
35 | try
36 | {
37 | _messageQueue.Add(message);
38 | return;
39 | }
40 | catch (InvalidOperationException ex) { WriteOffLineLog("AddLogQueue Err", ex); }
41 | }
42 |
43 | // Adding is completed so just log the message
44 | WriteMessage(message);
45 | }
46 |
47 | // for testing
48 | internal virtual async void WriteMessage(LogData log)
49 | {
50 | //Console.WriteLine($"NetXXX:Message{message.Message},Level:{message.LevelString}")
51 |
52 | await AddLog(log.LevelString, log.Message, null, log.Exception?.StackTrace);
53 | }
54 |
55 |
56 |
57 |
58 | private async Task AddLog(string logLevel, string logMessage, string attData, string stackInfo)
59 | {
60 | bool flag = false;
61 |
62 | var jsonObj = new
63 | {
64 | systemId = option.SystemId,
65 | logLevel = logLevel,
66 | logMessage = logMessage,
67 | attData = attData,
68 | // stackInfo = stackInfo 如果是异常,消息里已经包含了堆栈了,不用再添加
69 | };
70 | string json = JsonHelper.ToJsonString(jsonObj);
71 | try
72 | {
73 | var res = await NetHelper.PostWithJson(option.Url, json);
74 | OPResult opRes = JsonHelper.FromJsonString(res);
75 | if (opRes.State == 1)
76 | {
77 | flag = true;
78 | }
79 | else
80 | {
81 | WriteOffLineLog(json);
82 | }
83 | }
84 | catch (Exception ex)
85 | {
86 | WriteOffLineLog(json, ex);
87 | }
88 |
89 | return flag;
90 |
91 | }
92 |
93 |
94 |
95 | private void WriteOffLineLog(string originalLogInfo, Exception offlineExInfo = null)
96 | {
97 |
98 | try
99 | {
100 | string offlineFilePath = $@"{option.OfflineLogPath}\{DateTime.Now.ToString("yyyy-MM-dd")}.txt";
101 |
102 | string data = $"Time:{DateTime.Now.ToNormalString()}\r\n\r\nOriginalLogInfo:{originalLogInfo}";
103 | if (offlineExInfo != null)
104 | {
105 | data += $"\r\n\r\nOfflineExceptionInfo:{$"Message:{offlineExInfo.Message},Stack:{offlineExInfo.StackTrace}"}";
106 | }
107 | data += "\r\n\r\n\r\n\r\n\r\n";
108 |
109 | FileHelper.WriteFile(offlineFilePath, data);
110 |
111 | }
112 | catch (Exception ex)
113 | {
114 | Console.WriteLine(JsonHelper.ToJsonString(ex));
115 |
116 | }
117 |
118 | }
119 |
120 | private class OPResult
121 | {
122 | public int State { get; set; }
123 |
124 | public object Data { get; set; }
125 |
126 | }
127 |
128 |
129 |
130 |
131 | private void ProcessLogQueue()
132 | {
133 | try
134 | {
135 | foreach (var message in _messageQueue.GetConsumingEnumerable())
136 | {
137 | WriteMessage(message);
138 | }
139 | }
140 | catch
141 | {
142 | try
143 | {
144 | _messageQueue.CompleteAdding();
145 | }
146 | catch (Exception ex) { WriteOffLineLog("AddLogQueue Err", ex); }
147 | }
148 | }
149 |
150 | public void Dispose()
151 | {
152 | _messageQueue.CompleteAdding();
153 |
154 | try
155 | {
156 | _outputThread.Join(1500); // with timeout in-case Console is locked by user input
157 | }
158 | catch (ThreadStateException) { }
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/Sunny.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2036
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sunny.Common", "Framework\Sunny.Common\Sunny.Common.csproj", "{430C45D0-09D8-42FA-88B4-526385BFAE8F}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sunny.Repository", "Framework\Sunny.Repository\Sunny.Repository.csproj", "{1F8A0634-DAFC-49FC-9E4E-BF93A4BE1683}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{25C48BCE-DBD6-4261-A047-AA8AB47F5EA6}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sunny.Api", "Framework\Sunny.Api\Sunny.Api.csproj", "{913AE651-2048-4BE1-9199-63C8AB3B8299}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sunny.TemplateT4", "Framework\TemplateT4\Sunny.TemplateT4.csproj", "{1B813AAE-92C9-4AE5-869C-87AC1B3519CD}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UseDemo", "UseDemo", "{4267E4AE-CBE4-4D18-A4B5-078FD2ED6719}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiDemo", "UseDemo\ApiDemo\ApiDemo.csproj", "{BF2B2FBD-12B0-41BC-ABDD-A39400062361}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RepositoryDemo", "UseDemo\RepositoryDemo\RepositoryDemo.csproj", "{A3E24D59-31A3-4E6C-B0DC-EE0642E815A7}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceDemo", "UseDemo\ServiceDemo\ServiceDemo.csproj", "{910654BD-EAD7-4887-9F0C-8764FD9EF097}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemplateT4Demo", "UseDemo\TemplateT4Demo\TemplateT4Demo.csproj", "{927A980D-14DD-4EB1-BB7B-1BBE529C7ECD}"
25 | EndProject
26 | Global
27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
28 | Debug|Any CPU = Debug|Any CPU
29 | Release|Any CPU = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {430C45D0-09D8-42FA-88B4-526385BFAE8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {430C45D0-09D8-42FA-88B4-526385BFAE8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {430C45D0-09D8-42FA-88B4-526385BFAE8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {430C45D0-09D8-42FA-88B4-526385BFAE8F}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {1F8A0634-DAFC-49FC-9E4E-BF93A4BE1683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {1F8A0634-DAFC-49FC-9E4E-BF93A4BE1683}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {1F8A0634-DAFC-49FC-9E4E-BF93A4BE1683}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {1F8A0634-DAFC-49FC-9E4E-BF93A4BE1683}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {913AE651-2048-4BE1-9199-63C8AB3B8299}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {913AE651-2048-4BE1-9199-63C8AB3B8299}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {913AE651-2048-4BE1-9199-63C8AB3B8299}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {913AE651-2048-4BE1-9199-63C8AB3B8299}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {1B813AAE-92C9-4AE5-869C-87AC1B3519CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {1B813AAE-92C9-4AE5-869C-87AC1B3519CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {1B813AAE-92C9-4AE5-869C-87AC1B3519CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {1B813AAE-92C9-4AE5-869C-87AC1B3519CD}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {BF2B2FBD-12B0-41BC-ABDD-A39400062361}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {BF2B2FBD-12B0-41BC-ABDD-A39400062361}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {BF2B2FBD-12B0-41BC-ABDD-A39400062361}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {BF2B2FBD-12B0-41BC-ABDD-A39400062361}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {A3E24D59-31A3-4E6C-B0DC-EE0642E815A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {A3E24D59-31A3-4E6C-B0DC-EE0642E815A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {A3E24D59-31A3-4E6C-B0DC-EE0642E815A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {A3E24D59-31A3-4E6C-B0DC-EE0642E815A7}.Release|Any CPU.Build.0 = Release|Any CPU
56 | {910654BD-EAD7-4887-9F0C-8764FD9EF097}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 | {910654BD-EAD7-4887-9F0C-8764FD9EF097}.Debug|Any CPU.Build.0 = Debug|Any CPU
58 | {910654BD-EAD7-4887-9F0C-8764FD9EF097}.Release|Any CPU.ActiveCfg = Release|Any CPU
59 | {910654BD-EAD7-4887-9F0C-8764FD9EF097}.Release|Any CPU.Build.0 = Release|Any CPU
60 | {927A980D-14DD-4EB1-BB7B-1BBE529C7ECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61 | {927A980D-14DD-4EB1-BB7B-1BBE529C7ECD}.Debug|Any CPU.Build.0 = Debug|Any CPU
62 | {927A980D-14DD-4EB1-BB7B-1BBE529C7ECD}.Release|Any CPU.ActiveCfg = Release|Any CPU
63 | {927A980D-14DD-4EB1-BB7B-1BBE529C7ECD}.Release|Any CPU.Build.0 = Release|Any CPU
64 | EndGlobalSection
65 | GlobalSection(SolutionProperties) = preSolution
66 | HideSolutionNode = FALSE
67 | EndGlobalSection
68 | GlobalSection(NestedProjects) = preSolution
69 | {430C45D0-09D8-42FA-88B4-526385BFAE8F} = {25C48BCE-DBD6-4261-A047-AA8AB47F5EA6}
70 | {1F8A0634-DAFC-49FC-9E4E-BF93A4BE1683} = {25C48BCE-DBD6-4261-A047-AA8AB47F5EA6}
71 | {913AE651-2048-4BE1-9199-63C8AB3B8299} = {25C48BCE-DBD6-4261-A047-AA8AB47F5EA6}
72 | {1B813AAE-92C9-4AE5-869C-87AC1B3519CD} = {25C48BCE-DBD6-4261-A047-AA8AB47F5EA6}
73 | {BF2B2FBD-12B0-41BC-ABDD-A39400062361} = {4267E4AE-CBE4-4D18-A4B5-078FD2ED6719}
74 | {A3E24D59-31A3-4E6C-B0DC-EE0642E815A7} = {4267E4AE-CBE4-4D18-A4B5-078FD2ED6719}
75 | {910654BD-EAD7-4887-9F0C-8764FD9EF097} = {4267E4AE-CBE4-4D18-A4B5-078FD2ED6719}
76 | {927A980D-14DD-4EB1-BB7B-1BBE529C7ECD} = {4267E4AE-CBE4-4D18-A4B5-078FD2ED6719}
77 | EndGlobalSection
78 | GlobalSection(ExtensibilityGlobals) = postSolution
79 | SolutionGuid = {CFBAF3FC-372F-4113-B957-F789D18BD6BE}
80 | EndGlobalSection
81 | EndGlobal
82 |
--------------------------------------------------------------------------------
/UseDemo/ApiDemo/Startup.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using FluentValidation.AspNetCore;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.Extensions.Caching.Distributed;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.DependencyInjection;
9 | using Microsoft.Extensions.Logging;
10 | using Newtonsoft.Json;
11 | using Newtonsoft.Json.Serialization;
12 | using Quartz;
13 | using Quartz.Impl;
14 | using RepositoryDemo;
15 | using Sunny.Api.Midware;
16 | using Sunny.Common.ConfigOption;
17 | using Sunny.Common.Helper;
18 | using Sunny.Common.JsonTypeConverter;
19 | using Swashbuckle.AspNetCore.Swagger;
20 | using System.IO;
21 |
22 | namespace ApiDemo
23 | {
24 | public class Startup
25 | {
26 | public Startup(IConfiguration configuration)
27 | {
28 | var builder = new ConfigurationBuilder()
29 | .SetBasePath(Directory.GetCurrentDirectory())
30 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
31 |
32 | Configuration = builder.Build();
33 |
34 | }
35 |
36 | public IConfiguration Configuration { get; }
37 |
38 |
39 | // This method gets called by the runtime. Use this method to add services to the container.
40 | public void ConfigureServices(IServiceCollection services)
41 | {
42 | DiHelper.AutoRegister(services);
43 |
44 | var connection = Configuration.GetConnectionString("MySql");
45 | services.AddDbContext(options =>
46 | options.UseMySql(connection));
47 |
48 | ////同时使用多个数据库的DEMO
49 | //var connection2 = Configuration.GetConnectionString("MySq2");
50 | //services.AddDbContext(options =>
51 | // options.UseMySql(connection2));
52 |
53 | //services.Configure(Configuration.GetSection("ConfigOptions:NetLoggerOption"));
54 |
55 | //根据需要在这里配置要使用的Option,然后在要使用的地方通过构造器注入IOptions得到TOption
56 | services.Configure(Configuration.GetSection("SunnyOptions:TokenValidateOption"));
57 | services.Configure(Configuration.GetSection("SunnyOptions:MailOption"));
58 | services.Configure(Configuration.GetSection("SunnyOptions:IpInfoQueryOption"));
59 | //services.Configure(Configuration.GetSection("SunnyOptions:SmsOption"));
60 |
61 | services.AddMvcCore()
62 | .AddFluentValidation()
63 | .AddJsonFormatters(x =>
64 | {
65 | x.Converters.Add(new LongConverter());
66 | x.Converters.Add(new DecimalConverter());
67 | x.DateFormatString = "yyyy-MM-dd HH:mm:ss";
68 | x.ContractResolver = new CamelCasePropertyNamesContractResolver();
69 | x.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
70 | })
71 | .AddCors()
72 | .AddFormatterMappings()
73 | .AddDataAnnotations()
74 | .AddApiExplorer();
75 |
76 | services.AddAutoMapper();
77 | services.AddDistributedRedisCache(options =>
78 | {
79 | var configOption = Configuration.GetSection("SunnyOptions:RedisOptions").Get();
80 | options.Configuration = configOption.ConnectionString;
81 | options.InstanceName = configOption.InstanceName;
82 | IDistributedCacheExtend.DefaultSlidingExpiration = configOption.DefaultSlidingExpiration;
83 | });
84 | services.AddSession();
85 | services.AddSingleton();//注册ISchedulerFactory的实例。
86 | services.AddSwaggerGen(c =>
87 | {
88 | c.SwaggerDoc("v1", new Info { Title = "ApiDemo Project Swagger API", Version = "v1" });
89 | // 为 Swagger JSON and UI设置xml文档注释路径
90 | var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径)
91 | var xmlPath = Path.Combine(basePath, "ApiDemo.xml");
92 | c.IncludeXmlComments(xmlPath);
93 | });
94 |
95 | }
96 |
97 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
98 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ISchedulerFactory schedulerFactory)
99 | {
100 | app.InitServiceProvider();
101 | app.EnableJob(Configuration, schedulerFactory);
102 | loggerFactory.AddConsoleLoggerUseDefaultFilter();
103 | loggerFactory.AddNetLoggerUseDefaultFilter(Configuration.GetSection("SunnyOptions:NetLoggerOption").Get());
104 | IdHelper.InitSnowflake(Configuration.GetSection("SunnyOptions:SnowflakeOption").Get());
105 |
106 |
107 | if (env.IsDevelopment())
108 | {
109 | app.UseDeveloperExceptionPage();
110 | app.UseMiddleware();
111 | }
112 | else
113 | {
114 | app.UseMiddleware();
115 | }
116 |
117 | app.UseStaticFiles();
118 | app.UseSession();
119 | app.UseMiddleware();
120 |
121 | app.UseMvc();
122 |
123 | // Enable middleware to serve generated Swagger as a JSON endpoint.
124 | app.UseSwagger();
125 |
126 | // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
127 | // specifying the Swagger JSON endpoint.
128 | app.UseSwaggerUI(c =>
129 | {
130 | c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
131 | });
132 |
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Log/NetLogger.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Microsoft.Extensions.Logging.Abstractions.Internal;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Sunny.Common.Log
8 | {
9 | public class NetLogger : ILogger
10 | {
11 |
12 |
13 | private static readonly string _messagePadding;
14 | private static readonly string _newLineWithMessagePadding;
15 | private readonly NetLoggerProcessor _queueProcessor;
16 | private Func _filter;
17 |
18 | [ThreadStatic]
19 | private static StringBuilder _logBuilder;
20 |
21 | public string Name { get; }
22 | internal IExternalScopeProvider ScopeProvider { get; set; }
23 |
24 | public IDisposable BeginScope(TState state) => ScopeProvider?.Push(state) ?? NullScope.Instance;
25 |
26 |
27 |
28 | internal NetLogger(string name, Func filter, IExternalScopeProvider scopeProvider, NetLoggerProcessor loggerProcessor)
29 | {
30 | if (name == null)
31 | {
32 | throw new ArgumentNullException(nameof(name));
33 | }
34 |
35 | Name = name;
36 | Filter = filter ?? ((category, logLevel) => true);
37 | ScopeProvider = scopeProvider;
38 | _queueProcessor = loggerProcessor;
39 |
40 | }
41 |
42 | public Func Filter
43 | {
44 | get { return _filter; }
45 | set
46 | {
47 | if (value == null)
48 | {
49 | throw new ArgumentNullException(nameof(value));
50 | }
51 |
52 | _filter = value;
53 | }
54 | }
55 |
56 | private static string GetLogLevelString(LogLevel logLevel)
57 | {
58 | switch (logLevel)
59 | {
60 | case LogLevel.Trace:
61 | return "Trace";
62 | case LogLevel.Debug:
63 | return "Debug";
64 | case LogLevel.Information:
65 | return "Info";
66 | case LogLevel.Warning:
67 | return "Warn";
68 | case LogLevel.Error:
69 | return "Error";
70 | case LogLevel.Critical:
71 | return "Fotal";
72 | default:
73 | throw new ArgumentOutOfRangeException(nameof(logLevel));
74 | }
75 |
76 |
77 | }
78 |
79 | private void GetScopeInformation(StringBuilder stringBuilder)
80 | {
81 | var scopeProvider = ScopeProvider;
82 | if (scopeProvider != null)
83 | {
84 | var initialLength = stringBuilder.Length;
85 |
86 | scopeProvider.ForEachScope((scope, state) =>
87 | {
88 | var (builder, length) = state;
89 | var first = length == builder.Length;
90 | builder.Append(first ? "=> " : " => ").Append(scope);
91 | }, (stringBuilder, initialLength));
92 |
93 | if (stringBuilder.Length > initialLength)
94 | {
95 | stringBuilder.Insert(initialLength, _messagePadding);
96 | stringBuilder.AppendLine();
97 | }
98 | }
99 | }
100 |
101 |
102 | public virtual void WriteMessage(LogLevel logLevel, string logName, int eventId, string message, Exception exception)
103 | {
104 | var logBuilder = _logBuilder;
105 | _logBuilder = null;
106 |
107 | if (logBuilder == null)
108 | {
109 | logBuilder = new StringBuilder();
110 | }
111 |
112 | var logLevelString = GetLogLevelString(logLevel);
113 | // category and event id
114 | logBuilder.Append(logName);
115 | logBuilder.Append($"[eventid={eventId}]");
116 |
117 |
118 | // scope information
119 | GetScopeInformation(logBuilder);
120 |
121 | if (!string.IsNullOrEmpty(message))
122 | {
123 | // message
124 | logBuilder.Append(_messagePadding);
125 |
126 | var len = logBuilder.Length;
127 | logBuilder.AppendLine(message);
128 | logBuilder.Replace(Environment.NewLine, _newLineWithMessagePadding, len, message.Length);
129 | }
130 |
131 | //// Example:
132 | //// System.InvalidOperationException
133 | //// at Namespace.Class.Function() in File:line X
134 | if (exception != null)
135 | {
136 | // exception message
137 | logBuilder.AppendLine(exception.ToString());
138 | }
139 |
140 | if (logBuilder.Length > 0)
141 | {
142 | var hasLevel = !string.IsNullOrEmpty(logLevelString);
143 | // Queue log message
144 | _queueProcessor.EnqueueMessage(new LogData()
145 | {
146 | Message = logBuilder.ToString(),
147 | LevelString = hasLevel ? logLevelString : null,
148 | Exception = exception
149 |
150 | });
151 | }
152 |
153 | logBuilder.Clear();
154 | if (logBuilder.Capacity > 1024)
155 | {
156 | logBuilder.Capacity = 1024;
157 | }
158 | _logBuilder = logBuilder;
159 | }
160 |
161 | public bool IsEnabled(LogLevel logLevel)
162 | {
163 | if (logLevel == LogLevel.None)
164 | {
165 | return false;
166 | }
167 |
168 | return Filter(Name, logLevel);
169 | }
170 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
171 | {
172 | if (!IsEnabled(logLevel))
173 | {
174 | return;
175 | }
176 |
177 | if (formatter == null)
178 | {
179 | throw new ArgumentNullException(nameof(formatter));
180 | }
181 |
182 | var message = formatter(state, exception);
183 |
184 | if (!string.IsNullOrEmpty(message) || exception != null)
185 | {
186 | WriteMessage(logLevel, Name, eventId.Id, message, exception);
187 | }
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/Xml/XMLHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Xml;
5 |
6 | namespace Sunny.Common.Helper
7 | {
8 | public class XMLHelper
9 | {
10 | private static XmlDocument xmlDoc = new XmlDocument();
11 | private static object locker = new object();
12 |
13 | ///
14 | /// 获取XML节点的属性值
15 | ///
16 | /// xml文件路径
17 | /// Xpath路径
18 | /// 属性名称
19 |
20 | /// 属性值
21 | public static string GetXmlNodeAttribute(string filePath, string xpath, string key)
22 | {
23 | try
24 | {
25 | lock (locker)
26 | {
27 | xmlDoc.Load(filePath);
28 | XmlNode node = xmlDoc.SelectSingleNode(xpath);
29 | string value = node.Attributes[key].Value;
30 | return value;
31 | }
32 | }
33 | catch (Exception ex)
34 | {
35 | throw new Exception("读取XML文件" + filePath + "的节点" + xpath + "属性" + key + "时出现异常:" + ex);
36 | }
37 |
38 | }
39 | ///
40 | /// 设置XML节点的属性值
41 | ///
42 | /// xml文件路径
43 | /// Xpath路径
44 | /// 属性名称
45 | /// 要赋的值
46 |
47 | /// true表示设置成功
48 | public static bool SetXmlNodeAttribute(string filePath, string xpath, string key, string value)
49 | {
50 |
51 | bool flag = false;
52 | try
53 | {
54 | lock (locker)
55 | {
56 | xmlDoc.Load(filePath);
57 | XmlNode node = xmlDoc.SelectSingleNode(xpath);
58 | node.Attributes[key].Value = value;
59 | xmlDoc.Save(filePath);
60 | flag = true;
61 | }
62 | }
63 | catch (Exception ex)
64 | {
65 | throw new Exception("设置XML文件" + filePath + "的节点" + xpath + "属性" + key + "时出现异常:" + ex);
66 | }
67 | return flag;
68 | }
69 |
70 |
71 | ///
72 | /// 获取xml节点值
73 | ///
74 | /// xml文件
75 | /// xpath访问路径
76 |
77 | /// 节点值
78 | public static String GetXmlNodeValue(string xmlFile, string xpath)
79 | {
80 |
81 | try
82 | {
83 | lock (locker)
84 | {
85 | xmlDoc.Load(xmlFile);
86 | XmlNode node = xmlDoc.SelectSingleNode(xpath);
87 | return node.InnerText;
88 | }
89 | }
90 | catch (Exception ex)
91 | {
92 | throw new Exception("读取XML文件 " + xmlFile + " 的" + xpath + "节点内容时出现异常:" + ex);
93 | }
94 | }
95 |
96 | ///
97 | /// 设置xml节点值
98 | ///
99 | /// xml文件
100 | /// xpath访问路径
101 | /// 要设置的值
102 |
103 | /// true表示设置成功
104 | public static bool SetXmlNodeValue(string xmlFile, string xpath, string value)
105 | {
106 |
107 | bool flag = false;
108 | try
109 | {
110 | lock (locker)
111 | {
112 | xmlDoc.Load(xmlFile);
113 | XmlNode node = xmlDoc.SelectSingleNode(xpath);
114 | node.InnerText = value;
115 | xmlDoc.Save(xmlFile);
116 | flag = true;
117 | }
118 | }
119 | catch (Exception ex)
120 | {
121 | throw new Exception("设置XML文件 " + xmlFile + " 的 " + xpath + " 节点值时出现异常:" + ex);
122 |
123 | }
124 | return flag;
125 | }
126 |
127 |
128 | ///
129 | /// 获取xml节点集合的值
130 | ///
131 | /// xml文件
132 | /// xpath访问路径
133 |
134 | /// 节点值
135 | public static List GetXmlNodeCollectValue(string xmlFile, string xpath)
136 | {
137 |
138 |
139 | try
140 | {
141 | lock (locker)
142 | {
143 | xmlDoc.Load(xmlFile);
144 | XmlNodeList nodes = xmlDoc.SelectNodes(xpath);
145 | List list = new List();
146 | foreach (XmlNode item in nodes)
147 | {
148 | list.Add(item.InnerText);
149 | }
150 | return list;
151 | }
152 | }
153 | catch (Exception ex)
154 | {
155 | throw new Exception("读取XML文件 " + xmlFile + " 的" + xpath + "节点集合时出现异常:" + ex);
156 | }
157 | }
158 |
159 | ///
160 | /// 获取xml节点集合的2个属性值,封装成Hashtable返回
161 | ///
162 | /// xml文件
163 | /// xpath访问路径
164 | /// 属性值作为Hashtable键的属性名称
165 | /// 属性值作为Hashtable值的属性名称
166 |
167 | /// 节点值
168 | public static Hashtable GetHashtableFromXmlNodeCollectAttr(string xmlFile, string xpath, string keyAttrName, string valueAttrName)
169 | {
170 |
171 | try
172 | {
173 | lock (locker)
174 | {
175 | xmlDoc.Load(xmlFile);
176 | XmlNodeList nodes = xmlDoc.SelectNodes(xpath);
177 | Hashtable ht = new Hashtable();
178 | foreach (XmlNode item in nodes)
179 | {
180 | ht.Add(item.Attributes[keyAttrName].Value, item.Attributes[valueAttrName].Value);
181 | }
182 | return ht;
183 | }
184 | }
185 | catch (Exception ex)
186 | {
187 | throw new Exception("读取XML文件 " + xmlFile + " 的" + xpath + "节点集合的" + keyAttrName + "," + valueAttrName + "属性时出现异常:" + ex);
188 | }
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/Framework/Sunny.Common/Helper/String/StringHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Sunny.Common.Helper
7 | {
8 | public static class StringHelper
9 | {
10 | ///
11 | /// 获取文件扩展名
12 | ///
13 | ///
14 | ///
15 | public static string GetExName(string fileName)
16 | {
17 | return fileName.Substring(fileName.LastIndexOf('.'));
18 | }
19 |
20 | ///
21 | /// 转为Base64字符串
22 | ///
23 | ///
24 | ///
25 | public static string ToBase64(this string input)
26 | {
27 | if (!string.IsNullOrWhiteSpace(input))
28 | {
29 | byte[] bytes = Encoding.Default.GetBytes(input);
30 | return Convert.ToBase64String(bytes);
31 |
32 | }
33 | return input;
34 | }
35 |
36 | ///
37 | /// 从base64字符串转成普通字符串
38 | ///
39 | ///
40 | ///
41 | public static string FromBase64(this string input)
42 | {
43 | if (!string.IsNullOrWhiteSpace(input))
44 | {
45 | byte[] outputb = Convert.FromBase64String(input);
46 | return Encoding.Default.GetString(outputb);
47 | }
48 | return input;
49 | }
50 |
51 |
52 | ///
53 | /// 获取AB两个字符串中最大的相同部分
54 | ///
55 | ///
56 | ///
57 | /// 是否区分大小写
58 | ///
59 | public static List GetMaxSameString(string a, string b, bool diffCaps = true)
60 | {
61 | List res = new List();
62 | if (!string.IsNullOrWhiteSpace(a) && !string.IsNullOrWhiteSpace(b))
63 | {
64 | if (a.Length > b.Length)
65 | {
66 | string temp = a; a = b; b = a;
67 | }
68 | List list = new List();
69 | for (int posA = 0; posA < a.Length; posA++)//将a字符串中的每一个字符作为首字符串进行对比
70 | {
71 | int tempPosA = posA;
72 | StringBuilder sb = new StringBuilder();
73 |
74 | for (int posB = 0; posB < b.Length; posB++)
75 | {
76 | string tempA = a.Substring(tempPosA, 1);
77 | if (tempPosA == posA)//如果是对比首字符串,那么就用indexof进行快速定位,定位到要开始对比字符串的位置,而不用从头开始逐个对比
78 | {
79 | if (diffCaps)
80 | {
81 | posB = b.IndexOf(tempA, posB);
82 | }
83 | else
84 | {
85 | posB = b.IndexOf(tempA, posB, StringComparison.CurrentCultureIgnoreCase);
86 | }
87 | if (posB == -1) { break; }//如果剩下未对比字符中没有找到以首字符开头的,就退出
88 | }
89 | string tempB = b.Substring(posB, 1);
90 | bool eqRes = false;
91 | if (diffCaps)
92 | {
93 | eqRes = tempA.Equals(tempB);
94 | }
95 | else
96 | {
97 | eqRes = tempA.ToLower().Equals(tempB.ToLower());
98 | }
99 | if (eqRes)//如果匹配,就接着对比下一字符
100 | {
101 | sb.Append(tempA);
102 | if (tempPosA + 1 < a.Length)
103 | {
104 | ++tempPosA;
105 | }
106 |
107 | }
108 | else//如果不匹配,则-1对比当前字符是否可作为下次对比的首字符
109 | {
110 | tempPosA = posA;
111 |
112 | if (sb.Length > 0)
113 | {
114 | list.Add(sb.ToString());
115 | sb.Clear();
116 | --posB;
117 | }
118 |
119 | }
120 | }
121 | }
122 | string maxStr = "";
123 | foreach (string item in list)//找最大长度的字符
124 | {
125 | if (item.Length > maxStr.Length)
126 | {
127 | maxStr = item;
128 |
129 | }
130 | }
131 | foreach (string item in list)//找与最大长度相等内容不等的字符
132 | {
133 | if (item.Length == maxStr.Length)
134 | {
135 | if (diffCaps)
136 | {
137 | if (!item.Equals(maxStr))
138 | {
139 | res.Add(item);
140 | }
141 | }
142 | else
143 | {
144 | if (!item.ToLower().Equals(maxStr.ToLower()))
145 | {
146 | res.Add(item);
147 | }
148 | }
149 |
150 | }
151 | }
152 | res.Add(maxStr);
153 | }
154 | return res;
155 | }
156 |
157 | ///
158 | /// 根据长度参数,生成随机字符串
159 | ///
160 | /// 长度
161 | /// 返回随机字符串
162 | public static string CreateValidCode(int codeLen)
163 | {
164 | //下边这个是用于验证码的字符串。
165 | string codeSerial = "2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,S,T,U,V,W,X,Y,Z";
166 |
167 | string[] arr = codeSerial.Split(',');
168 |
169 | string code = "";
170 |
171 | int randValue = -1;
172 |
173 | Random rand = new Random(unchecked((int)DateTime.Now.Ticks));
174 |
175 | for (int i = 0; i < codeLen; i++)
176 | {
177 | randValue = rand.Next(0, arr.Length - 1);
178 |
179 | code += arr[randValue];
180 | }
181 |
182 | return code;
183 | }
184 |
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/Migrations/20181102012925_v1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Metadata;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 |
5 | namespace RepositoryDemo.Migrations
6 | {
7 | public partial class v1 : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.CreateTable(
12 | name: "category",
13 | columns: table => new
14 | {
15 | id = table.Column(nullable: false)
16 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
17 | create_time = table.Column(nullable: false),
18 | creater_id = table.Column(nullable: false),
19 | update_time = table.Column(nullable: false)
20 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn),
21 | updater_id = table.Column(nullable: false),
22 | category_name = table.Column(maxLength: 30, nullable: true)
23 | },
24 | constraints: table =>
25 | {
26 | table.PrimaryKey("PK_category", x => x.id);
27 | });
28 |
29 | migrationBuilder.CreateTable(
30 | name: "IdTest",
31 | columns: table => new
32 | {
33 | Id = table.Column(nullable: false)
34 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
35 | requestType = table.Column(nullable: false)
36 | },
37 | constraints: table =>
38 | {
39 | table.PrimaryKey("PK_IdTest", x => x.Id);
40 | });
41 |
42 | migrationBuilder.CreateTable(
43 | name: "passage",
44 | columns: table => new
45 | {
46 | id = table.Column(nullable: false)
47 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
48 | create_time = table.Column(nullable: false),
49 | creater_id = table.Column(nullable: false),
50 | update_time = table.Column(nullable: false)
51 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn),
52 | updater_id = table.Column(nullable: false),
53 | title = table.Column(maxLength: 30, nullable: true),
54 | last_edit_time = table.Column(nullable: false)
55 | },
56 | constraints: table =>
57 | {
58 | table.PrimaryKey("PK_passage", x => x.id);
59 | });
60 |
61 | migrationBuilder.CreateTable(
62 | name: "student",
63 | columns: table => new
64 | {
65 | id = table.Column(nullable: false)
66 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
67 | create_time = table.Column(nullable: false),
68 | creater_id = table.Column(nullable: false),
69 | update_time = table.Column(nullable: false)
70 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn),
71 | updater_id = table.Column(nullable: false),
72 | student_name = table.Column(maxLength: 30, nullable: true),
73 | test = table.Column(maxLength: 30, nullable: true),
74 | a_a2 = table.Column(maxLength: 30, nullable: true),
75 | score = table.Column(type: "decimal(18, 2)", nullable: false)
76 | },
77 | constraints: table =>
78 | {
79 | table.PrimaryKey("PK_student", x => x.id);
80 | });
81 |
82 | migrationBuilder.CreateTable(
83 | name: "passage_category",
84 | columns: table => new
85 | {
86 | id = table.Column(nullable: false),
87 | create_time = table.Column(nullable: false),
88 | creater_id = table.Column(nullable: false),
89 | update_time = table.Column(nullable: false)
90 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn),
91 | updater_id = table.Column(nullable: false),
92 | category_id = table.Column(nullable: false),
93 | passage_id = table.Column(nullable: false)
94 | },
95 | constraints: table =>
96 | {
97 | table.PrimaryKey("PK_passage_category", x => new { x.passage_id, x.category_id });
98 | table.ForeignKey(
99 | name: "FK_passage_category_category_category_id",
100 | column: x => x.category_id,
101 | principalTable: "category",
102 | principalColumn: "id",
103 | onDelete: ReferentialAction.Cascade);
104 | table.ForeignKey(
105 | name: "FK_passage_category_passage_passage_id",
106 | column: x => x.passage_id,
107 | principalTable: "passage",
108 | principalColumn: "id",
109 | onDelete: ReferentialAction.Cascade);
110 | });
111 |
112 | migrationBuilder.CreateTable(
113 | name: "student_address",
114 | columns: table => new
115 | {
116 | id = table.Column(nullable: false)
117 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
118 | create_time = table.Column(nullable: false),
119 | creater_id = table.Column(nullable: false),
120 | update_time = table.Column(nullable: false)
121 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn),
122 | updater_id = table.Column(nullable: false),
123 | address1 = table.Column(maxLength: 30, nullable: true),
124 | zipcode = table.Column(nullable: false),
125 | state = table.Column(maxLength: 30, nullable: true),
126 | country = table.Column(maxLength: 30, nullable: true),
127 | student_id = table.Column(nullable: false)
128 | },
129 | constraints: table =>
130 | {
131 | table.PrimaryKey("PK_student_address", x => x.id);
132 | table.ForeignKey(
133 | name: "FK_student_address_student_student_id",
134 | column: x => x.student_id,
135 | principalTable: "student",
136 | principalColumn: "id",
137 | onDelete: ReferentialAction.Cascade);
138 | });
139 |
140 | migrationBuilder.CreateIndex(
141 | name: "IX_passage_category_category_id",
142 | table: "passage_category",
143 | column: "category_id");
144 |
145 | migrationBuilder.CreateIndex(
146 | name: "IX_student_address_student_id",
147 | table: "student_address",
148 | column: "student_id",
149 | unique: true);
150 | }
151 |
152 | protected override void Down(MigrationBuilder migrationBuilder)
153 | {
154 | migrationBuilder.DropTable(
155 | name: "IdTest");
156 |
157 | migrationBuilder.DropTable(
158 | name: "passage_category");
159 |
160 | migrationBuilder.DropTable(
161 | name: "student_address");
162 |
163 | migrationBuilder.DropTable(
164 | name: "category");
165 |
166 | migrationBuilder.DropTable(
167 | name: "passage");
168 |
169 | migrationBuilder.DropTable(
170 | name: "student");
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/Migrations/MyDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
6 | using RepositoryDemo;
7 |
8 | namespace RepositoryDemo.Migrations
9 | {
10 | [DbContext(typeof(MyDbContext))]
11 | partial class MyDbContextModelSnapshot : ModelSnapshot
12 | {
13 | protected override void BuildModel(ModelBuilder modelBuilder)
14 | {
15 | #pragma warning disable 612, 618
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "2.1.1-rtm-30846")
18 | .HasAnnotation("Relational:MaxIdentifierLength", 64);
19 |
20 | modelBuilder.Entity("RepositoryDemo.DbModel.Category", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd()
24 | .HasColumnName("id");
25 |
26 | b.Property("CategoryName")
27 | .HasColumnName("category_name")
28 | .HasMaxLength(30);
29 |
30 | b.Property("CreateTime")
31 | .HasColumnName("create_time");
32 |
33 | b.Property("CreaterId")
34 | .HasColumnName("creater_id");
35 |
36 | b.Property("UpdateTime")
37 | .IsConcurrencyToken()
38 | .ValueGeneratedOnAddOrUpdate()
39 | .HasColumnName("update_time");
40 |
41 | b.Property("UpdaterId")
42 | .HasColumnName("updater_id");
43 |
44 | b.HasKey("Id");
45 |
46 | b.ToTable("category");
47 | });
48 |
49 | modelBuilder.Entity("RepositoryDemo.DbModel.IdTest", b =>
50 | {
51 | b.Property("Id")
52 | .ValueGeneratedOnAdd();
53 |
54 | b.Property("requestType");
55 |
56 | b.HasKey("Id");
57 |
58 | b.ToTable("IdTest");
59 | });
60 |
61 | modelBuilder.Entity("RepositoryDemo.DbModel.Passage", b =>
62 | {
63 | b.Property("Id")
64 | .ValueGeneratedOnAdd()
65 | .HasColumnName("id");
66 |
67 | b.Property("CreateTime")
68 | .HasColumnName("create_time");
69 |
70 | b.Property("CreaterId")
71 | .HasColumnName("creater_id");
72 |
73 | b.Property("LastEditTime")
74 | .HasColumnName("last_edit_time");
75 |
76 | b.Property("Title")
77 | .HasColumnName("title")
78 | .HasMaxLength(30);
79 |
80 | b.Property("UpdateTime")
81 | .IsConcurrencyToken()
82 | .ValueGeneratedOnAddOrUpdate()
83 | .HasColumnName("update_time");
84 |
85 | b.Property("UpdaterId")
86 | .HasColumnName("updater_id");
87 |
88 | b.HasKey("Id");
89 |
90 | b.ToTable("passage");
91 | });
92 |
93 | modelBuilder.Entity("RepositoryDemo.DbModel.PassageCategory", b =>
94 | {
95 | b.Property("PassageId")
96 | .HasColumnName("passage_id");
97 |
98 | b.Property("CategoryId")
99 | .HasColumnName("category_id");
100 |
101 | b.Property("CreateTime")
102 | .HasColumnName("create_time");
103 |
104 | b.Property("CreaterId")
105 | .HasColumnName("creater_id");
106 |
107 | b.Property("Id")
108 | .HasColumnName("id");
109 |
110 | b.Property("UpdateTime")
111 | .IsConcurrencyToken()
112 | .ValueGeneratedOnAddOrUpdate()
113 | .HasColumnName("update_time");
114 |
115 | b.Property("UpdaterId")
116 | .HasColumnName("updater_id");
117 |
118 | b.HasKey("PassageId", "CategoryId");
119 |
120 | b.HasIndex("CategoryId");
121 |
122 | b.ToTable("passage_category");
123 | });
124 |
125 | modelBuilder.Entity("RepositoryDemo.DbModel.Student", b =>
126 | {
127 | b.Property("Id")
128 | .ValueGeneratedOnAdd()
129 | .HasColumnName("id");
130 |
131 | b.Property("AA2")
132 | .HasColumnName("a_a2")
133 | .HasMaxLength(30);
134 |
135 | b.Property("CreateTime")
136 | .HasColumnName("create_time");
137 |
138 | b.Property("CreaterId")
139 | .HasColumnName("creater_id");
140 |
141 | b.Property("Score")
142 | .HasColumnName("score")
143 | .HasColumnType("decimal(18, 2)");
144 |
145 | b.Property("StudentName")
146 | .HasColumnName("student_name")
147 | .HasMaxLength(30);
148 |
149 | b.Property("Test")
150 | .HasColumnName("test")
151 | .HasMaxLength(30);
152 |
153 | b.Property("UpdateTime")
154 | .IsConcurrencyToken()
155 | .ValueGeneratedOnAddOrUpdate()
156 | .HasColumnName("update_time");
157 |
158 | b.Property("UpdaterId")
159 | .HasColumnName("updater_id");
160 |
161 | b.HasKey("Id");
162 |
163 | b.ToTable("student");
164 | });
165 |
166 | modelBuilder.Entity("RepositoryDemo.DbModel.StudentAddress", b =>
167 | {
168 | b.Property("Id")
169 | .ValueGeneratedOnAdd()
170 | .HasColumnName("id");
171 |
172 | b.Property("Address1")
173 | .HasColumnName("address1")
174 | .HasMaxLength(30);
175 |
176 | b.Property("Country")
177 | .HasColumnName("country")
178 | .HasMaxLength(30);
179 |
180 | b.Property("CreateTime")
181 | .HasColumnName("create_time");
182 |
183 | b.Property("CreaterId")
184 | .HasColumnName("creater_id");
185 |
186 | b.Property("State")
187 | .HasColumnName("state")
188 | .HasMaxLength(30);
189 |
190 | b.Property("StudentId")
191 | .HasColumnName("student_id");
192 |
193 | b.Property("UpdateTime")
194 | .IsConcurrencyToken()
195 | .ValueGeneratedOnAddOrUpdate()
196 | .HasColumnName("update_time");
197 |
198 | b.Property("UpdaterId")
199 | .HasColumnName("updater_id");
200 |
201 | b.Property("Zipcode")
202 | .HasColumnName("zipcode");
203 |
204 | b.HasKey("Id");
205 |
206 | b.HasIndex("StudentId")
207 | .IsUnique();
208 |
209 | b.ToTable("student_address");
210 | });
211 |
212 | modelBuilder.Entity("RepositoryDemo.DbModel.PassageCategory", b =>
213 | {
214 | b.HasOne("RepositoryDemo.DbModel.Category", "Category")
215 | .WithMany("PassageCategories")
216 | .HasForeignKey("CategoryId")
217 | .OnDelete(DeleteBehavior.Cascade);
218 |
219 | b.HasOne("RepositoryDemo.DbModel.Passage", "Passage")
220 | .WithMany("PassageCategories")
221 | .HasForeignKey("PassageId")
222 | .OnDelete(DeleteBehavior.Cascade);
223 | });
224 |
225 | modelBuilder.Entity("RepositoryDemo.DbModel.StudentAddress", b =>
226 | {
227 | b.HasOne("RepositoryDemo.DbModel.Student", "Student")
228 | .WithOne("Address")
229 | .HasForeignKey("RepositoryDemo.DbModel.StudentAddress", "StudentId")
230 | .OnDelete(DeleteBehavior.Cascade);
231 | });
232 | #pragma warning restore 612, 618
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/UseDemo/RepositoryDemo/Migrations/20181102012925_v1.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 | using RepositoryDemo;
8 |
9 | namespace RepositoryDemo.Migrations
10 | {
11 | [DbContext(typeof(MyDbContext))]
12 | [Migration("20181102012925_v1")]
13 | partial class v1
14 | {
15 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
16 | {
17 | #pragma warning disable 612, 618
18 | modelBuilder
19 | .HasAnnotation("ProductVersion", "2.1.1-rtm-30846")
20 | .HasAnnotation("Relational:MaxIdentifierLength", 64);
21 |
22 | modelBuilder.Entity("RepositoryDemo.DbModel.Category", b =>
23 | {
24 | b.Property("Id")
25 | .ValueGeneratedOnAdd()
26 | .HasColumnName("id");
27 |
28 | b.Property("CategoryName")
29 | .HasColumnName("category_name")
30 | .HasMaxLength(30);
31 |
32 | b.Property("CreateTime")
33 | .HasColumnName("create_time");
34 |
35 | b.Property("CreaterId")
36 | .HasColumnName("creater_id");
37 |
38 | b.Property("UpdateTime")
39 | .IsConcurrencyToken()
40 | .ValueGeneratedOnAddOrUpdate()
41 | .HasColumnName("update_time");
42 |
43 | b.Property("UpdaterId")
44 | .HasColumnName("updater_id");
45 |
46 | b.HasKey("Id");
47 |
48 | b.ToTable("category");
49 | });
50 |
51 | modelBuilder.Entity("RepositoryDemo.DbModel.IdTest", b =>
52 | {
53 | b.Property("Id")
54 | .ValueGeneratedOnAdd();
55 |
56 | b.Property("requestType");
57 |
58 | b.HasKey("Id");
59 |
60 | b.ToTable("IdTest");
61 | });
62 |
63 | modelBuilder.Entity("RepositoryDemo.DbModel.Passage", b =>
64 | {
65 | b.Property("Id")
66 | .ValueGeneratedOnAdd()
67 | .HasColumnName("id");
68 |
69 | b.Property("CreateTime")
70 | .HasColumnName("create_time");
71 |
72 | b.Property("CreaterId")
73 | .HasColumnName("creater_id");
74 |
75 | b.Property("LastEditTime")
76 | .HasColumnName("last_edit_time");
77 |
78 | b.Property("Title")
79 | .HasColumnName("title")
80 | .HasMaxLength(30);
81 |
82 | b.Property("UpdateTime")
83 | .IsConcurrencyToken()
84 | .ValueGeneratedOnAddOrUpdate()
85 | .HasColumnName("update_time");
86 |
87 | b.Property("UpdaterId")
88 | .HasColumnName("updater_id");
89 |
90 | b.HasKey("Id");
91 |
92 | b.ToTable("passage");
93 | });
94 |
95 | modelBuilder.Entity("RepositoryDemo.DbModel.PassageCategory", b =>
96 | {
97 | b.Property("PassageId")
98 | .HasColumnName("passage_id");
99 |
100 | b.Property("CategoryId")
101 | .HasColumnName("category_id");
102 |
103 | b.Property("CreateTime")
104 | .HasColumnName("create_time");
105 |
106 | b.Property("CreaterId")
107 | .HasColumnName("creater_id");
108 |
109 | b.Property("Id")
110 | .HasColumnName("id");
111 |
112 | b.Property("UpdateTime")
113 | .IsConcurrencyToken()
114 | .ValueGeneratedOnAddOrUpdate()
115 | .HasColumnName("update_time");
116 |
117 | b.Property("UpdaterId")
118 | .HasColumnName("updater_id");
119 |
120 | b.HasKey("PassageId", "CategoryId");
121 |
122 | b.HasIndex("CategoryId");
123 |
124 | b.ToTable("passage_category");
125 | });
126 |
127 | modelBuilder.Entity("RepositoryDemo.DbModel.Student", b =>
128 | {
129 | b.Property("Id")
130 | .ValueGeneratedOnAdd()
131 | .HasColumnName("id");
132 |
133 | b.Property("AA2")
134 | .HasColumnName("a_a2")
135 | .HasMaxLength(30);
136 |
137 | b.Property("CreateTime")
138 | .HasColumnName("create_time");
139 |
140 | b.Property("CreaterId")
141 | .HasColumnName("creater_id");
142 |
143 | b.Property("Score")
144 | .HasColumnName("score")
145 | .HasColumnType("decimal(18, 2)");
146 |
147 | b.Property("StudentName")
148 | .HasColumnName("student_name")
149 | .HasMaxLength(30);
150 |
151 | b.Property("Test")
152 | .HasColumnName("test")
153 | .HasMaxLength(30);
154 |
155 | b.Property("UpdateTime")
156 | .IsConcurrencyToken()
157 | .ValueGeneratedOnAddOrUpdate()
158 | .HasColumnName("update_time");
159 |
160 | b.Property