This commit is contained in:
owenchen committed 2026-05-12 10:09:31 +08:00
1 parent 31fbe199c1
commit 1c0c83af5f
469 files changed
+62544 -5

No files matched your search

@@ -0,0 +1,58 @@
using CloudBuilder.Core.Application.Builders;
using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor
{
public class ApplicationService : IApplicationService
{
public ApplicationService(
CloudBuilderBuilder cloudBuilder,
IConfiguration configuration,
IServiceProvider serviceProvider,
IUnitOfWork unitOfWork,
IHttpContextAccessor httpContext
)
{
CloudBuilder = cloudBuilder;
this.Configuration = configuration;
this.ServiceProvider = serviceProvider;
this.UnitOfWork = unitOfWork;
this.HttpContext = httpContext;
if (httpContext.HttpContext != null)
{
UserId = ClaimPolicy.GetClaimValue(HttpContext, ClaimPolicy.CLAINM_USERID);
string key = ClaimPolicy.GetHeaderKeyValue(HttpContext, ClaimPolicy.HEADER_LOCALE);
if (!string.IsNullOrEmpty(key))
{
Locale = key;
}
}
}
public IServiceProvider ServiceProvider { get; private set; }
public IHttpContextAccessor HttpContext { get; private set; }
public string UserId { get; set; }
public string Locale { get; set; } = ClaimPolicy.CLAINM_LOCALE;
public IUnitOfWork UnitOfWork { get; set; }
public CloudBuilderBuilder CloudBuilder { get; set; }
public IConfiguration Configuration { get; private set; }
public KRepository GetRepository<KRepository>()
{
return ServiceProvider.GetRequiredService<KRepository>();
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 表示数据库类型,如SqlServer,Sqlite等
/// </summary>
public enum DatabaseType
{
/// <summary>
/// SqlServer数据库类型
/// </summary>
SqlServer,
/// <summary>
/// Sqlite数据库类型
/// </summary>
Sqlite,
/// <summary>
/// MySql数据库类型
/// </summary>
MySql,
/// <summary>
/// PostgreSql数据库类型
/// </summary>
PostgreSql,
/// <summary>
/// Oracle数据库类型
/// </summary>
Oracle
}
}
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 实体类基类
/// </summary>
public abstract class EntityBase<TKey> : IEntity<TKey>
{
/// <summary>
/// 获取或设置 编号
/// </summary>
[DisplayName("编号")]
[NotMapped]
public TKey? Id { get; set; }
public virtual string GetSelectFrom()
{
return null;
}
public virtual string GetSelectCountFrom(string sqlWhere)
{
return null;
}
public virtual string GetSelectTopFrom(int limit, int start, string orderby, string orderbytag, string sqlWhere)
{
return null;
}
/// <summary>
/// 判断两个实体是否是同一数据记录的实体
/// </summary>
/// <param name="obj">要比较的实体信息</param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (!(obj is EntityBase<TKey> entity))
{
return false;
}
return IsKeyEqual(entity.Id, Id);
}
/// <summary>
/// 实体ID是否相等
/// </summary>
public static bool IsKeyEqual(TKey id1, TKey id2)
{
if (id1 == null && id2 == null)
{
return true;
}
if (id1 == null || id2 == null)
{
return false;
}
return Equals(id1, id2);
}
/// <summary>
/// 用作特定类型的哈希函数。
/// </summary>
/// <returns>
/// 当前 <see cref="T:System.Object"/> 的哈希代码。<br/>
/// 如果<c>Id</c>为<c>null</c>则返回0,
/// 如果不为<c>null</c>则返回<c>Id</c>对应的哈希值
/// </returns>
public override int GetHashCode()
{
if (Id == null)
{
return 0;
}
return Id.ToString().GetHashCode();
}
}
}
@@ -0,0 +1,37 @@
using CloudBuilder.Core.Application.Builders;
using CloudBuilder.Core.Policy;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
public interface IApplicationService
{
CloudBuilderBuilder CloudBuilder { get; set; }
IConfiguration Configuration { get; }
/// <summary>
/// 服务提供器
/// </summary>
IServiceProvider ServiceProvider { get; }
/// <summary>
/// 登录ID
/// </summary>
string UserId { get; set; }
string Locale { get; set; }
/// <summary>
/// 事务
/// </summary>
IUnitOfWork UnitOfWork { get; set; }
/// <summary>
/// 数据仓库
/// </summary>
/// <typeparam name="K"></typeparam>
/// <returns></returns>
K GetRepository<K>();
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 数据模型接口
/// </summary>
public interface IEntity<TKey> : IGet
{
/// <summary>
/// 获取或设置 实体唯一标识,主键
/// </summary>
TKey Id { get; set; }
}
}
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
public interface IEntityAudited
{
public const string CREATED_DATETIME = "CreatedDatetime";
public const string CREATED_BY = "CreatedBy";
public const string UPDATED_DATETIME = "UpdatedDatetime";
public const string UPDATED_BY = "UpdatedBy";
public const string ROW_VERSION = "RowVersion";
}
}
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
public interface IGet
{
string GetSelectFrom();
}
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 注册主库实体类
/// </summary>
public interface IMaster
{
}
}
@@ -0,0 +1,167 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 定义实体仓储模型的数据标准操作
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public interface IRepository<TEntity>
where TEntity : class
{
/// <summary>
/// 实体集合
/// </summary>
DbSet<TEntity> Entities { get; }
/// <summary>
/// 不跟踪的(脱轨)实体
/// </summary>
IQueryable<TEntity> DetachedEntities { get; }
/// <summary>
/// 服务提供器
/// </summary>
IServiceProvider ServiceProvider { get; }
/// <summary>
/// 事务
/// </summary>
IUnitOfWork UnitOfWork { get; set; }
DbContext GetDbContext();
K GetRepository<K>();
/// <summary>
/// 动态改变主数据库
/// </summary>
void ChangeMasterDatabase();
/// <summary>
/// 动态改变主数据库
/// </summary>
void ChangeSecondaryDatabase();
#region FromSqlRaw
IQueryable<TEntity> FromSql(string sql, params object[] parameters);
IQueryable<TEntity> FromSqlWhere(TEntity t, string where, params object[] parameters);
TEntity[] FindBySql(string sql, params object[] parameters);
TEntity[] FindBySqlWhere(TEntity t, string where, params object[] parameters);
void PackSqlParameter(List<object> parameters, string parameterName, object value);
void ExecuteNonQuery(string sql, params object[] parameters);
object ExecuteScalar(string sql, params object[] parameters);
DataTable SelectDataTable(string sql, params object[] parameters);
#endregion
#region Insert
EntityEntry<TEntity> Insert(TEntity entity);
void Insert(params TEntity[] entities);
void Insert(IEnumerable<TEntity> entities);
Task<EntityEntry<TEntity>> InsertAsync(TEntity entity, CancellationToken cancellationToken = default);
Task InsertAsync(params TEntity[] entities);
Task InsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default);
EntityEntry<TEntity> InsertNow(TEntity entity);
void InsertNow(params TEntity[] entities);
void InsertNow(IEnumerable<TEntity> entities);
Task<EntityEntry<TEntity>> InsertNowAsync(TEntity entity, CancellationToken cancellationToken = default);
Task InsertNowAsync(params TEntity[] entities);
Task InsertNowAsync(TEntity[] entities, CancellationToken cancellationToken = default);
Task InsertNowAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default);
int SaveNow();
Task<int> SaveNowAsync(CancellationToken cancellationToken = default);
#endregion
#region Update
EntityEntry<TEntity> Update(TEntity entity);
void Update(params TEntity[] entities);
void Update(IEnumerable<TEntity> entities);
Task<EntityEntry<TEntity>> UpdateAsync(TEntity entity);
Task UpdateAsync(params TEntity[] entities);
Task UpdateAsync(IEnumerable<TEntity> entities);
EntityEntry<TEntity> UpdateNow(TEntity entity);
void UpdateNow(params TEntity[] entities);
void UpdateNow(IEnumerable<TEntity> entities);
Task<EntityEntry<TEntity>> UpdateNowAsync(TEntity entity, CancellationToken cancellationToken = default);
Task UpdateNowAsync(params TEntity[] entities);
Task UpdateNowAsync(TEntity[] entities, CancellationToken cancellationToken = default);
Task UpdateNowAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default);
EntityEntry<TEntity> UpdateInclude(TEntity entity, IEnumerable<string> propertyNames);
EntityEntry<TEntity> UpdateInclude(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates);
Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, string[] propertyNames);
Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates);
Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, IEnumerable<string> propertyNames);
Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates);
EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, string[] propertyNames);
EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates);
EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, IEnumerable<string> propertyNames);
EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates);
Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default);
Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates, CancellationToken cancellationToken = default);
Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, IEnumerable<string> propertyNames, CancellationToken cancellationToken = default);
Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates, CancellationToken cancellationToken = default);
EntityEntry<TEntity> UpdateExclude(TEntity entity, IEnumerable<string> propertyNames);
EntityEntry<TEntity> UpdateExclude(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates);
Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, string[] propertyNames);
Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates);
Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, IEnumerable<string> propertyNames);
Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates);
EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, string[] propertyNames);
EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates);
EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, IEnumerable<string> propertyNames);
EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates);
Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default);
Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates, CancellationToken cancellationToken = default);
Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, IEnumerable<string> propertyNames, CancellationToken cancellationToken = default);
Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates, CancellationToken cancellationToken = default);
#endregion
#region Delete
EntityEntry<TEntity> Delete(TEntity entity);
void Delete(params TEntity[] entities);
void Delete(IEnumerable<TEntity> entities);
Task<EntityEntry<TEntity>> DeleteAsync(TEntity entity);
Task DeleteAsync(params TEntity[] entities);
Task DeleteAsync(IEnumerable<TEntity> entities);
EntityEntry<TEntity> DeleteNow(TEntity entity);
void DeleteNow(params TEntity[] entities);
void DeleteNow(IEnumerable<TEntity> entities);
Task<EntityEntry<TEntity>> DeleteNowAsync(TEntity entity, CancellationToken cancellationToken = default);
Task DeleteNowAsync(params TEntity[] entities);
Task DeleteNowAsync(TEntity[] entities, CancellationToken cancellationToken = default);
Task DeleteNowAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default);
#endregion
}
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 注入从库实体类
/// </summary>
public interface ISecondary
{
}
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 实现接口将生成TypeScript文件的条件
/// </summary>
public interface IData
{
}
}
@@ -0,0 +1,53 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
/// <summary>
/// 业务单元操作接口
/// </summary>
public interface IUnitOfWork : IDisposable
{
/// <summary>
/// 获取 工作单元的事务是否已提交
/// </summary>
bool HasCommitted { get; }
/// <summary>
/// 添加DbContext共享事务
/// </summary>
void AddDbContext(DbContext dbContext);
/// <summary>
/// 获取指定数据实体的上下文类型
/// </summary>
/// <param name="entityType">实体类型</param>
/// <returns>实体所属上下文实例</returns>
DbContext GetDbContext();
/// <summary>
/// 对数据库连接开启事务
/// </summary>
void BeginOrUseTransaction();
/// <summary>
/// 对数据库连接开启事务
/// </summary>
/// <param name="cancellationToken">异步取消标记</param>
/// <returns></returns>
Task BeginOrUseTransactionAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 提交当前上下文的事务更改
/// </summary>
void Commit();
/// <summary>
/// 回滚所有事务
/// </summary>
void Rollback();
}
}
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
public abstract class ViewEntityBase : IEntity<string>
{
/// <summary>
/// View没有主键
/// </summary>
[NotMapped]
public string? Id { get; set; }
public virtual string GetSelectFrom()
{
return null!;
}
public virtual string GetSelectCountFrom(string sqlWhere)
{
return null!;
}
public virtual string GetSelectTopFrom(int limit, int start, string orderby, string orderbytag, string sqlWhere)
{
return null!;
}
}
}
@@ -0,0 +1,248 @@
using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Core.DependencyInjection.EFCore;
using CloudBuilder.Core.Policy;
using CloudBuilder.Core.Service;
using DocumentFormat.OpenXml.Math;
using DocumentFormat.OpenXml.Vml.Office;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.JsonWebTokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor
{
public class MasterDbContext : DbContext
{
public const string MASTER_DB_CONTEXT = "MasterDbContext";
public IEnumerable<IMasterRegistEntityType<ModelBuilder>> _entityManagers;
private readonly IServiceProvider _serviceProvider;
private readonly IConfiguration configuration;
private readonly IHttpContextAccessor httpContextAccessor;
public MasterDbContext(DbContextOptions<MasterDbContext> options,
IEnumerable<IMasterRegistEntityType<ModelBuilder>> entityManagers,
IServiceProvider serviceProvider,
IConfiguration configuration,
IHttpContextAccessor httpContextAccessor
) : base(options)
{
_entityManagers = entityManagers;
_serviceProvider = serviceProvider;
this.configuration = configuration;
this.httpContextAccessor = httpContextAccessor;
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
try
{
SavingChangesEvent();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
catch (Exception ex)
{
//自动检验数据是否过时
//ALTER TABLE table ADD row_version rowversion;
//[Column("row_version")]
//[Timestamp]
//public byte[] RowVersion { get; set; }
if (ex.Message.Contains("data may have been modified or deleted since entities were loaded."))
{
throw new ValidatedException(CoreMessagePolicy.GetInstance().ComposeMessage(CoreMessagePolicy.THE_DATA_HAS_BEEN_MODIFIED));
}
throw new ValidatedException(ex.InnerException?.Message ?? ex.Message);
}
}
public void SavingChangesEvent()
{
var entities = this.ChangeTracker.Entries()
.Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)).ToList();
if (entities == null || entities.Count < 1) return;
string uid = this.httpContextAccessor.HttpContext?.User?.Identity?.Name;
DateTime now = DateTime.Now;
if (string.IsNullOrEmpty(uid))
{
uid = GetUserIdFromToken(httpContextAccessor.HttpContext!);
}
foreach (var entity in entities)
{
if (!(entity.State == EntityState.Added || entity.State == EntityState.Modified)) continue;
// 获取所有实体有效属性,排除 [NotMapper] 属性
var props = entity.OriginalValues.Properties;
// 获取实体当前(现在)的值
var currentValues = entity.CurrentValues;
// 遍历所有属性
foreach (var prop in props)
{
// 获取属性名
var propName = prop.Name;
if (propName == IEntityAudited.CREATED_BY && entity.State == EntityState.Added)
{
currentValues[propName] = uid;
continue;
}
if (propName == IEntityAudited.CREATED_DATETIME && entity.State == EntityState.Added)
{
currentValues[propName] = DateTime.Now;
continue;
}
if (propName == IEntityAudited.UPDATED_BY)
{
currentValues[propName] = uid;
continue;
}
if (propName == IEntityAudited.UPDATED_DATETIME)
{
currentValues[propName] = DateTime.Now;
continue;
}
}
}
foreach (var entity in entities)
{
var entityName = entity.Metadata.GetTableName() ?? entity.Entity.GetType().Name;
var state = entity.State;
// ======================================
// 1. 获取所有主键(支持复合主键)
// ======================================
var pkProps = entity.Properties
.Where(p => p.Metadata.IsPrimaryKey())
.ToList();
// 拼接 WHERE 条件:Id=1 AND TenantId=2
var whereClauses = pkProps
.Select(p => $"{p.Metadata.GetColumnName()?? p.Metadata.Name} = '{p.CurrentValue?.ToString()?.Replace("'", "''")}'");
string whereSql = string.Join(" AND ", whereClauses);
string sqlLog = "";
// ======================================
// 修改 UPDATE
// ======================================
if (state == EntityState.Modified)
{
var setClauses = entity.Properties
.Where(p => p.IsModified) // 只记录真正修改的字段
.Select(p => $"{p.Metadata.GetColumnName() ?? p.Metadata.Name} = '{p.CurrentValue?.ToString()?.Replace("'", "''")}'");
string setSql = string.Join(", ", setClauses);
sqlLog = $"[{uid}][{now.ToString("yyyy-MM-dd HH:mm:ss")}]UPDATE {entityName} SET {setSql} WHERE {whereSql}";
}
// ======================================
// 新增 INSERT
// ======================================
else if (state == EntityState.Added)
{
var data = entity.Properties.ToDictionary(p => p.Metadata.GetColumnName() ?? p.Metadata.Name, p => p.CurrentValue);
var columns = string.Join(", ", data.Keys);
var values = string.Join(", ", data.Values.Select(v => $"'{v?.ToString()?.Replace("'", "''")}'"));
sqlLog = $"[{uid}][{now.ToString("yyyy-MM-dd HH:mm:ss")}]INSERT INTO {entityName} ({columns}) VALUES ({values})";
}
// ======================================
// 删除 DELETE
// ======================================
else if (state == EntityState.Deleted)
{
sqlLog = $"[{uid}][{now.ToString("yyyy-MM-dd HH:mm:ss")}]DELETE FROM {entityName} WHERE {whereSql}";
}
WriteToFile(sqlLog);
}
}
private void WriteToFile(string log)
{
try
{
long MaxFileSizeBytes = (long)(configuration.GetValue<int>("Logging:MaxFileSizeMB") * 1024 * 1024);
string today = DateTime.Now.ToString("yyyyMMdd");
string basePath = configuration["Logging:LogFileDirectory"];
Directory.CreateDirectory(basePath);
string baseFileName = $"log-{today}";
string ext = ".log";
string finalPath = Path.Combine(basePath, $"{baseFileName}{ext}");
int index = 1;
// 循环找没超过大小的文件
while (File.Exists(finalPath) && new FileInfo(finalPath).Length >= MaxFileSizeBytes)
{
finalPath = Path.Combine(basePath, $"{baseFileName}-{index}{ext}");
index++;
}
// 写入(线程安全)
lock (this)
{
File.AppendAllText(finalPath, log + Environment.NewLine, Encoding.UTF8);
}
}
catch { }
}
protected override void OnModelCreating(ModelBuilder builder)
{
foreach (IMasterRegistEntityType<ModelBuilder> entityManager in _entityManagers)
{
entityManager.RegistEntity(builder);
}
base.OnModelCreating(builder);
}
public string GetJwtBearerToken(HttpContext httpContext, string headerKey = "Authorization", string tokenPrefix = "Bearer ")
{
if (httpContext == null) return null;
// 判断请求报文头中是否有 "Authorization" 报文头
var bearerToken = httpContext.Request.Headers[headerKey].ToString();
if (string.IsNullOrWhiteSpace(bearerToken)) return default;
var prefixLenght = tokenPrefix.Length;
return bearerToken.StartsWith(tokenPrefix, true, null) && bearerToken.Length > prefixLenght ? bearerToken[prefixLenght..] : default;
}
public string GetUserIdFromToken(HttpContext httpContext)
{
string token = GetJwtBearerToken(httpContextAccessor.HttpContext!);
if (!string.IsNullOrEmpty(token))
{
try
{
JsonWebToken json = new JsonWebTokenHandler().ReadJsonWebToken(token);
return json.GetClaim(ClaimPolicy.CLAINM_USERID).Value;
}
catch (Exception)
{
return string.Empty;
}
}
return string.Empty;
}
}
}
@@ -0,0 +1,893 @@
using CloudBuilder.Core.Application.Builders;
using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Extensions.DependencyInjection;
using System.Data;
using System.Data.Common;
using System.Linq.Expressions;
namespace CloudBuilder.Core.DatabaseAccessor.Entity
{
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly CloudBuilderBuilder builder;
private DbContext dbContext;
private readonly IHttpContextAccessor httpContext;
public Repository(IServiceProvider serviceProvider,
[FromKeyedServices(MasterDbContext.MASTER_DB_CONTEXT)] DbContext dbContext,
IUnitOfWork unitOfWork,
IHttpContextAccessor httpContext)
{
this.httpContext = httpContext;
this.dbContext = dbContext;
this.UnitOfWork = unitOfWork;
ServiceProvider = serviceProvider;
Entities = this.dbContext.Set<TEntity>()!;
DetachedEntities = Entities.AsNoTracking()!;
}
public IUnitOfWork UnitOfWork { get; set; }
public DbContext GetDbContext() { return dbContext; }
public DbSet<TEntity> Entities { get; set; } = null!;
public IQueryable<TEntity> DetachedEntities { get; set; } = null!;
public DbConnection DbConnection
{
get
{
return dbContext.Database.GetDbConnection();
}
}
public IServiceProvider ServiceProvider { get; set; } = null!;
public virtual TRepository GetRepository<TRepository>()
{
return ServiceProvider.GetRequiredService<TRepository>();
}
public void ChangeMasterDatabase()
{
this.dbContext = ServiceProvider.GetRequiredKeyedService<DbContext>(MasterDbContext.MASTER_DB_CONTEXT);
Entities = this.dbContext.Set<TEntity>()!;
DetachedEntities = Entities.AsNoTracking()!;
}
public void ChangeSecondaryDatabase()
{
this.dbContext = ServiceProvider.GetRequiredKeyedService<DbContext>(SecondaryDbContext.SECONDARY_DB_CONTEXT);
Entities = this.dbContext.Set<TEntity>()!;
DetachedEntities = Entities.AsNoTracking()!;
}
#region FromSqlRaw
public virtual IQueryable<TEntity> FromSql(string sql, params object[] parameters)
{
if (parameters == null)
return Entities.FromSqlRaw(sql);
else
return Entities.FromSqlRaw(sql, parameters);
}
public virtual IQueryable<TEntity> FromSqlWhere(TEntity t, string where, params object[] parameters)
{
string sql = $"{((IEntity<object>)t).GetSelectFrom()} {where} ";
if (parameters == null)
return Entities.FromSqlRaw(sql);
else
return Entities.FromSqlRaw(sql, parameters);
}
public virtual TEntity[] FindBySql(string sql, params object[] parameters)
{
if (parameters == null)
return Entities.FromSqlRaw(sql).ToArray();
else
return Entities.FromSqlRaw(sql, parameters).ToArray();
}
public virtual TEntity[] FindBySqlWhere(TEntity t, string where, params object[] parameters)
{
string sql = $"{((IGet)t).GetSelectFrom()} {where} ";
if (parameters == null)
return Entities.FromSqlRaw(sql).ToArray();
else
return Entities.FromSqlRaw(sql, parameters).ToArray();
}
public void PackSqlParameter(List<object> parameters, string parameterName, object value)
{
if (parameters == null) parameters = new List<object>();
else
{
string formatedValue;
if (value == null) formatedValue = $"null";
else
{
formatedValue = $"'{value.ToString()}'";
if (value is DateTime && !string.IsNullOrEmpty(formatedValue))
formatedValue = $"'{Convert.ToDateTime(formatedValue).ToString("yyyy-MM-dd HH:mm:ss")}'";
}
parameters.Add(new SqlParameter(parameterName, value == null ? DBNull.Value : value));
}
}
public virtual void ExecuteNonQuery(string sql, params object[] parameters)
{
var conn = dbContext.Database.GetDbConnection(); ;
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = sql;
if (parameters != null && parameters.Length > 0)
cmd.Parameters.AddRange(parameters);
cmd.ExecuteNonQuery();
}
public virtual object ExecuteScalar(string sql, params object[] parameters)
{
var conn = dbContext.Database.GetDbConnection(); ;
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = sql;
if (parameters != null && parameters.Length > 0)
cmd.Parameters.AddRange(parameters);
return cmd.ExecuteScalar();
}
public virtual DataTable SelectDataTable(string sql, params object[] parameters)
{
var conn = dbContext.Database.GetDbConnection(); ;
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = sql;
if (parameters != null && parameters.Length > 0)
cmd.Parameters.AddRange(parameters);
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd as SqlCommand;
DataTable dt = new DataTable();
da.Fill(dt);
dt.TableName = "TableName";
return dt;
}
#endregion
#region Insert
public virtual EntityEntry<TEntity> Insert(TEntity entity)
{
var entryEntity = Entities.Add(entity);
return entryEntity;
}
public virtual void Insert(params TEntity[] entities)
{
Entities.AddRange(entities);
}
public virtual void Insert(IEnumerable<TEntity> entities)
{
Entities.AddRange(entities);
}
public virtual async Task<EntityEntry<TEntity>> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
{
var entityEntry = await Entities.AddAsync(entity, cancellationToken);
return entityEntry;
}
public virtual Task InsertAsync(params TEntity[] entities)
{
return Entities.AddRangeAsync(entities);
}
public virtual Task InsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
{
return Entities.AddRangeAsync(entities, cancellationToken);
}
public virtual EntityEntry<TEntity> InsertNow(TEntity entity)
{
var entityEntry = Insert(entity);
SaveNow();
return entityEntry;
}
/// <summary>
/// 新增多条记录
/// </summary>
/// <param name="entities">多个实体</param>
public virtual void InsertNow(params TEntity[] entities)
{
Insert(entities);
SaveNow();
}
/// <summary>
/// 新增多条记录
/// </summary>
/// <param name="entities">多个实体</param>
public virtual void InsertNow(IEnumerable<TEntity> entities)
{
Insert(entities);
SaveNow();
}
/// <summary>
/// 新增一条记录并立即提交
/// </summary>
/// <param name="entity">实体</param>
/// <param name="ignoreNullValues"></param>
/// <param name="cancellationToken">取消异步令牌</param>
/// <returns>数据库中返回的实体</returns>
public virtual async Task<EntityEntry<TEntity>> InsertNowAsync(TEntity entity, CancellationToken cancellationToken = default)
{
var entityEntry = await InsertAsync(entity, cancellationToken);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
/// <summary>
/// 新增多条记录并立即提交
/// </summary>
/// <param name="entities">多个实体</param>
/// <returns>Task</returns>
public virtual async Task InsertNowAsync(params TEntity[] entities)
{
await InsertAsync(entities);
await SaveNowAsync();
}
/// <summary>
/// 新增多条记录并立即提交
/// </summary>
/// <param name="entities">多个实体</param>
/// <param name="cancellationToken">取消异步令牌</param>
/// <returns>Task</returns>
public virtual async Task InsertNowAsync(TEntity[] entities, CancellationToken cancellationToken = default)
{
await InsertAsync(entities);
await SaveNowAsync(cancellationToken);
}
/// <summary>
/// 新增多条记录并立即提交
/// </summary>
/// <param name="entities">多个实体</param>
/// <param name="cancellationToken">取消异步令牌</param>
/// <returns>Task</returns>
public virtual async Task InsertNowAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
{
await InsertAsync(entities, cancellationToken);
await SaveNowAsync(cancellationToken);
}
/// <summary>
/// 提交更改操作
/// </summary>
/// <returns></returns>
public virtual int SaveNow()
{
return dbContext.SaveChanges();
}
/// <summary>
/// 提交更改操作(异步)
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<int> SaveNowAsync(CancellationToken cancellationToken = default)
{
return dbContext.SaveChangesAsync(cancellationToken);
}
#endregion
#region Update
public virtual EntityEntry<TEntity> Update(TEntity entity)
{
var entityEntry = Entities.Update(entity);
return entityEntry;
}
public virtual void Update(params TEntity[] entities)
{
Entities.UpdateRange(entities);
}
public virtual void Update(IEnumerable<TEntity> entities)
{
Entities.UpdateRange(entities);
}
public virtual Task<EntityEntry<TEntity>> UpdateAsync(TEntity entity)
{
return Task.FromResult(Update(entity));
}
public virtual Task UpdateAsync(params TEntity[] entities)
{
Update(entities);
return Task.CompletedTask;
}
public virtual Task UpdateAsync(IEnumerable<TEntity> entities)
{
Update(entities);
return Task.CompletedTask;
}
public virtual EntityEntry<TEntity> UpdateNow(TEntity entity)
{
var entityEntry = Update(entity);
SaveNow();
return entityEntry;
}
public virtual void UpdateNow(params TEntity[] entities)
{
Update(entities);
SaveNow();
}
public virtual void UpdateNow(IEnumerable<TEntity> entities)
{
Update(entities);
SaveNow();
}
public virtual async Task<EntityEntry<TEntity>> UpdateNowAsync(TEntity entity, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateAsync(entity);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task UpdateNowAsync(params TEntity[] entities)
{
await UpdateAsync(entities);
await SaveNowAsync();
}
public virtual async Task UpdateNowAsync(TEntity[] entities, CancellationToken cancellationToken = default)
{
await UpdateAsync(entities);
await SaveNowAsync(cancellationToken);
}
public virtual async Task UpdateNowAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
{
await UpdateAsync(entities);
await SaveNowAsync(cancellationToken);
}
public virtual EntityEntry<TEntity> UpdateInclude(TEntity entity, IEnumerable<string> propertyNames)
{
var entityEntry = ChangeEntityState(entity, EntityState.Detached);
foreach (var propertyName in propertyNames)
{
EntityPropertyEntry(entity, propertyName).IsModified = true;
}
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateInclude(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates)
{
var entityEntry = ChangeEntityState(entity, EntityState.Detached);
foreach (var propertyPredicate in propertyPredicates)
{
EntityPropertyEntry(entity, propertyPredicate).IsModified = true;
}
return entityEntry;
}
public virtual Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, string[] propertyNames)
{
return Task.FromResult(UpdateInclude(entity, propertyNames));
}
public virtual Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates)
{
return Task.FromResult(UpdateInclude(entity, propertyPredicates));
}
public virtual Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, IEnumerable<string> propertyNames)
{
return Task.FromResult(UpdateInclude(entity, propertyNames));
}
public virtual Task<EntityEntry<TEntity>> UpdateIncludeAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates)
{
return Task.FromResult(UpdateInclude(entity, propertyPredicates));
}
public virtual EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, string[] propertyNames)
{
var entityEntry = UpdateInclude(entity, propertyNames);
SaveNow();
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates)
{
var entityEntry = UpdateInclude(entity, propertyPredicates);
SaveNow();
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, IEnumerable<string> propertyNames)
{
var entityEntry = UpdateInclude(entity, propertyNames);
SaveNow();
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateIncludeNow(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates)
{
var entityEntry = UpdateInclude(entity, propertyPredicates);
SaveNow();
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateIncludeAsync(entity, propertyNames);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateIncludeAsync(entity, propertyPredicates);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, IEnumerable<string> propertyNames, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateIncludeAsync(entity, propertyNames);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateIncludeNowAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateIncludeAsync(entity, propertyPredicates);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateExclude(TEntity entity, IEnumerable<string> propertyNames)
{
var entityEntry = ChangeEntityState(entity, EntityState.Modified);
foreach (var propertyName in propertyNames)
{
EntityPropertyEntry(entity, propertyName).IsModified = false;
}
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateExclude(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates)
{
var entityEntry = ChangeEntityState(entity, EntityState.Modified);
foreach (var propertyPredicate in propertyPredicates)
{
EntityPropertyEntry(entity, propertyPredicate).IsModified = false;
}
return entityEntry;
}
public virtual Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, string[] propertyNames)
{
return Task.FromResult(UpdateExclude(entity, propertyNames));
}
public virtual Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates)
{
return Task.FromResult(UpdateExclude(entity, propertyPredicates));
}
public virtual Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, IEnumerable<string> propertyNames)
{
return Task.FromResult(UpdateExclude(entity, propertyNames));
}
public virtual Task<EntityEntry<TEntity>> UpdateExcludeAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates)
{
return Task.FromResult(UpdateExclude(entity, propertyPredicates));
}
public virtual EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, string[] propertyNames)
{
var entityEntry = UpdateExclude(entity, propertyNames);
SaveNow();
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates)
{
var entityEntry = UpdateExclude(entity, propertyPredicates);
SaveNow();
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, IEnumerable<string> propertyNames)
{
var entityEntry = UpdateExclude(entity, propertyNames);
SaveNow();
return entityEntry;
}
public virtual EntityEntry<TEntity> UpdateExcludeNow(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates)
{
var entityEntry = UpdateExclude(entity, propertyPredicates);
SaveNow();
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, string[] propertyNames, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateExcludeAsync(entity, propertyNames);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, Expression<Func<TEntity, object>>[] propertyPredicates, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateExcludeAsync(entity, propertyPredicates);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, IEnumerable<string> propertyNames, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateExcludeAsync(entity, propertyNames);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task<EntityEntry<TEntity>> UpdateExcludeNowAsync(TEntity entity, IEnumerable<Expression<Func<TEntity, object>>> propertyPredicates, CancellationToken cancellationToken = default)
{
var entityEntry = await UpdateExcludeAsync(entity, propertyPredicates);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
#endregion
#region Delete
public virtual EntityEntry<TEntity> Delete(TEntity entity)
{
return Entities.Remove(entity);
}
public virtual void Delete(params TEntity[] entities)
{
Entities.RemoveRange(entities);
}
public virtual void Delete(IEnumerable<TEntity> entities)
{
Entities.RemoveRange(entities);
}
public virtual Task<EntityEntry<TEntity>> DeleteAsync(TEntity entity)
{
return Task.FromResult(Delete(entity));
}
public virtual Task DeleteAsync(params TEntity[] entities)
{
Delete(entities);
return Task.CompletedTask;
}
public virtual Task DeleteAsync(IEnumerable<TEntity> entities)
{
Delete(entities);
return Task.CompletedTask;
}
public virtual EntityEntry<TEntity> DeleteNow(TEntity entity)
{
var entityEntry = Delete(entity);
SaveNow();
return entityEntry;
}
public virtual void DeleteNow(params TEntity[] entities)
{
Delete(entities);
SaveNow();
}
public virtual void DeleteNow(IEnumerable<TEntity> entities)
{
Delete(entities);
SaveNow();
}
public virtual async Task<EntityEntry<TEntity>> DeleteNowAsync(TEntity entity, CancellationToken cancellationToken = default)
{
var entityEntry = await DeleteAsync(entity);
await SaveNowAsync(cancellationToken);
return entityEntry;
}
public virtual async Task DeleteNowAsync(params TEntity[] entities)
{
await DeleteAsync(entities);
await SaveNowAsync();
}
public virtual async Task DeleteNowAsync(TEntity[] entities, CancellationToken cancellationToken = default)
{
await DeleteAsync(entities);
await SaveNowAsync(cancellationToken);
}
public virtual async Task DeleteNowAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
{
await DeleteAsync(entities);
await SaveNowAsync(cancellationToken);
}
#endregion
#region EntityEntry
/// <summary>
/// 将实体加入数据上下文托管
/// </summary>
/// <param name="entity">实体</param>
/// <returns>EntityEntry</returns>
public virtual EntityEntry Entry(object entity)
{
return dbContext.Entry(entity);
}
/// <summary>
/// 将实体加入数据上下文托管
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
public virtual EntityEntry<TEntity> Entry(TEntity entity)
{
return dbContext.Entry(entity);
}
/// <summary>
/// 获取实体状态
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
public virtual EntityState EntityEntryState(object entity)
{
return Entry(entity).State;
}
/// <summary>
/// 获取实体状态
/// </summary>
/// <param name="entity">实体</param>
/// <returns>EntityState</returns>
public virtual EntityState EntityEntryState(TEntity entity)
{
return Entry(entity).State;
}
/// <summary>
/// 将实体属性加入托管
/// </summary>
/// <param name="entity">实体</param>
/// <param name="propertyName">属性名</param>
/// <returns>PropertyEntry</returns>
public virtual PropertyEntry EntityPropertyEntry(object entity, string propertyName)
{
return Entry(entity).Property(propertyName);
}
/// <summary>
/// 将实体属性加入托管
/// </summary>
/// <param name="entity">实体</param>
/// <param name="propertyName">属性名</param>
/// <returns>PropertyEntry</returns>
public virtual PropertyEntry EntityPropertyEntry(TEntity entity, string propertyName)
{
return Entry(entity).Property(propertyName);
}
/// <summary>
/// 将实体属性加入托管
/// </summary>
/// <typeparam name="TProperty">属性类型</typeparam>
/// <param name="entity">实体</param>
/// <param name="propertyPredicate">属性表达式</param>
/// <returns>PropertyEntry</returns>
public virtual PropertyEntry<TEntity, TProperty> EntityPropertyEntry<TProperty>(TEntity entity, Expression<Func<TEntity, TProperty>> propertyPredicate)
{
return Entry(entity).Property(propertyPredicate);
}
/// <summary>
/// 改变实体状态
/// </summary>
/// <param name="entity">实体</param>
/// <param name="entityState">实体状态</param>
/// <returns>EntityEntry</returns>
public virtual EntityEntry ChangeEntityState(object entity, EntityState entityState)
{
var entityEntry = Entry(entity);
entityEntry.State = entityState;
return entityEntry;
}
/// <summary>
/// 改变实体状态
/// </summary>
/// <param name="entity">实体</param>
/// <param name="entityState">实体状态</param>
/// <returns>EntityEntry{TEntity}</returns>
public virtual EntityEntry<TEntity> ChangeEntityState(TEntity entity, EntityState entityState)
{
var entityEntry = Entry(entity);
entityEntry.State = entityState;
return entityEntry;
}
/// <summary>
/// 改变实体状态
/// </summary>
/// <param name="entityEntry">实体条目</param>
/// <param name="entityState">实体状态</param>
/// <returns>EntityEntry</returns>
public virtual EntityEntry ChangeEntityState(EntityEntry entityEntry, EntityState entityState)
{
entityEntry.State = entityState;
return entityEntry;
}
/// <summary>
/// 改变实体状态
/// </summary>
/// <param name="entityEntry">实体条目</param>
/// <param name="entityState">实体状态</param>
/// <returns>EntityEntry{TEntity}</returns>
public virtual EntityEntry<TEntity> ChangeEntityState(EntityEntry<TEntity> entityEntry, EntityState entityState)
{
entityEntry.State = entityState;
return entityEntry;
}
/// <summary>
/// 判断是否被附加
/// </summary>
/// <param name="entity">实体</param>
/// <returns>bool</returns>
public virtual bool IsAttached(object entity)
{
return EntityEntryState(entity) != EntityState.Detached;
}
/// <summary>
/// 判断是否被附加
/// </summary>
/// <param name="entity">实体</param>
/// <returns>bool</returns>
public virtual bool IsAttached(TEntity entity)
{
return EntityEntryState(entity) != EntityState.Detached;
}
/// <summary>
/// 附加实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns>EntityEntry</returns>
public virtual EntityEntry Attach(object entity)
{
return dbContext.Attach(entity);
}
/// <summary>
/// 附加实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns>EntityEntry</returns>
public virtual EntityEntry<TEntity> Attach(TEntity entity)
{
return dbContext.Attach(entity);
}
/// <summary>
/// 附加多个实体
/// </summary>
/// <param name="entities">多个实体</param>
public virtual void AttachRange(params object[] entities)
{
dbContext.AttachRange(entities);
}
/// <summary>
/// 附加多个实体
/// </summary>
/// <param name="entities">多个实体</param>
public virtual void AttachRange(IEnumerable<TEntity> entities)
{
dbContext.AttachRange(entities);
}
/// <summary>
/// 取消附加实体
/// </summary>
/// <param name="entity">实体</param>
public virtual void Detach(object entity)
{
ChangeEntityState(entity, EntityState.Detached);
}
/// <summary>
/// 取消附加实体
/// </summary>
/// <param name="entity">实体</param>
public virtual void Detach(TEntity entity)
{
ChangeEntityState(entity, EntityState.Detached);
}
/// <summary>
/// 取消附加实体
/// </summary>
/// <param name="entityEntry">实体条目</param>
public virtual void Detach(EntityEntry entityEntry)
{
ChangeEntityState(entityEntry, EntityState.Detached);
}
/// <summary>
/// 取消附加实体
/// </summary>
/// <param name="entityEntry">实体条目</param>
public virtual void Detach(EntityEntry<TEntity> entityEntry)
{
ChangeEntityState(entityEntry, EntityState.Detached);
}
#endregion
}
}
@@ -0,0 +1,142 @@
using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Core.DependencyInjection.EFCore;
using CloudBuilder.Core.Policy;
using CloudBuilder.Core.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.JsonWebTokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor
{
public class SecondaryDbContext : DbContext
{
public const string SECONDARY_DB_CONTEXT = "SecondaryDbContext";
public IEnumerable<IMasterRegistEntityType<ModelBuilder>> _entityManagers;
private readonly IServiceProvider _serviceProvider;
private readonly IHttpContextAccessor httpContextAccessor;
public SecondaryDbContext(DbContextOptions<SecondaryDbContext> options,
IEnumerable<IMasterRegistEntityType<ModelBuilder>> entityManagers,
IServiceProvider serviceProvider,
IHttpContextAccessor httpContextAccessor) : base(options)
{
_entityManagers = entityManagers;
_serviceProvider = serviceProvider;
this.httpContextAccessor = httpContextAccessor;
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
try
{
SavingChangesEvent();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
catch (Exception ex)
{
if (ex.Message.Contains("data may have been modified or deleted since entities were loaded."))
{
throw new ValidatedException(CoreMessagePolicy.GetInstance().ComposeMessage(CoreMessagePolicy.THE_DATA_HAS_BEEN_MODIFIED));
}
throw new ValidatedException(ex.InnerException?.Message ?? ex.Message);
}
}
public void SavingChangesEvent()
{
var entities = this.ChangeTracker.Entries()
.Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)).ToList();
if (entities == null || entities.Count < 1) return;
string uid = this.httpContextAccessor.HttpContext?.User?.Identity?.Name;
if (string.IsNullOrEmpty(uid))
{
uid = string.Empty;
string token = GetJwtBearerToken(httpContextAccessor.HttpContext!);
if (!string.IsNullOrEmpty(token))
{
try
{
JsonWebToken json = new JsonWebTokenHandler().ReadJsonWebToken(token);
uid = json.GetClaim(ClaimPolicy.CLAINM_USERID).Value;
}
catch (Exception)
{
}
}
}
foreach (var entity in entities)
{
if (!(entity.State == EntityState.Added || entity.State == EntityState.Modified)) continue;
// 获取所有实体有效属性,排除 [NotMapper] 属性
var props = entity.OriginalValues.Properties;
// 获取实体当前(现在)的值
var currentValues = entity.CurrentValues;
// 遍历所有属性
foreach (var prop in props)
{
// 获取属性名
var propName = prop.Name;
if (propName == IEntityAudited.CREATED_BY && entity.State == EntityState.Added)
{
currentValues[propName] = uid;
continue;
}
if (propName == IEntityAudited.CREATED_DATETIME && entity.State == EntityState.Added)
{
currentValues[propName] = DateTime.Now;
continue;
}
if (propName == IEntityAudited.UPDATED_BY)
{
currentValues[propName] = uid;
continue;
}
if (propName == IEntityAudited.UPDATED_DATETIME)
{
currentValues[propName] = DateTime.Now;
continue;
}
}
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
foreach (IMasterRegistEntityType<ModelBuilder> entityManager in _entityManagers)
{
entityManager.RegistEntity(builder);
}
base.OnModelCreating(builder);
}
public string GetJwtBearerToken(HttpContext httpContext, string headerKey = "Authorization", string tokenPrefix = "Bearer ")
{
if (httpContext == null) return null;
// 判断请求报文头中是否有 "Authorization" 报文头
var bearerToken = httpContext.Request.Headers[headerKey].ToString();
if (string.IsNullOrWhiteSpace(bearerToken)) return default;
var prefixLenght = tokenPrefix.Length;
return bearerToken.StartsWith(tokenPrefix, true, null) && bearerToken.Length > prefixLenght ? bearerToken[prefixLenght..] : default;
}
}
}
@@ -0,0 +1,200 @@
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CloudBuilder.Core.DatabaseAccessor.Entity;
using Microsoft.Extensions.DependencyInjection;
namespace CloudBuilder.Core.DatabaseAccessor
{
public class UnitOfWork : IUnitOfWork
{
private readonly List<DbContext> _dbContexts = new List<DbContext>();
private DbTransaction _transaction;
private DbConnection _connection;
private DbContext _dbContext;
/// <summary>
/// 初始化一个<see cref="UnitOfWork"/>类型的新实例
/// </summary>
public UnitOfWork([FromKeyedServices(MasterDbContext.MASTER_DB_CONTEXT)] DbContext context)
{
_dbContext = context;
_dbContexts.Add(_dbContext);
_connection = _dbContext.Database.GetDbConnection();
}
/// <summary>
/// 获取 事务是否已提交
/// </summary>
public bool HasCommitted { get; private set; }
/// <summary>
/// 添加DbContext共享事务
/// </summary>
public void AddDbContext(DbContext dbContext)
{
_dbContexts.Add(dbContext);
}
public DbContext GetDbContext()
{
return _dbContext;
}
/// <summary>
/// 对数据库连接开启事务
/// </summary>
public virtual void BeginOrUseTransaction()
{
if (_dbContexts.Count == 0)
{
return;
}
if (_transaction?.Connection == null)
{
if (_connection.State != ConnectionState.Open)
{
_connection.Open();
}
_transaction = _connection.BeginTransaction();
}
foreach (DbContext context in _dbContexts)
{
if (context.Database.CurrentTransaction != null && context.Database.CurrentTransaction.GetDbTransaction() == _transaction)
{
continue;
}
//if (context.IsRelationalTransaction())
//{
//共享事务仅限关系数据库
context.Database.UseTransaction(_transaction);
//}
//else
//{
// context.Database.BeginTransaction();
//}
}
HasCommitted = false;
}
/// <summary>
/// 对数据库连接开启事务
/// </summary>
/// <param name="cancellationToken">异步取消标记</param>
/// <returns></returns>
public virtual async Task BeginOrUseTransactionAsync(CancellationToken cancellationToken = default)
{
if (_dbContexts.Count == 0)
{
return;
}
if (_transaction?.Connection == null)
{
if (_connection.State != ConnectionState.Open)
{
await _connection.OpenAsync(cancellationToken);
}
_transaction = _connection.BeginTransaction();
}
foreach (DbContext context in _dbContexts)
{
if (context.Database.CurrentTransaction != null && context.Database.CurrentTransaction.GetDbTransaction() == _transaction)
{
continue;
}
//if (context.IsRelationalTransaction())
//{
context.Database.UseTransaction(_transaction);
//}
//else
//{
// await context.Database.BeginTransactionAsync(cancellationToken);
//}
}
HasCommitted = false;
}
/// <summary>
/// 提交当前上下文的事务更改
/// </summary>
public virtual void Commit()
{
if (HasCommitted || _dbContexts.Count == 0 || _transaction == null)
{
return;
}
_transaction.Commit();
foreach (DbContext context in _dbContexts)
{
//if (context.IsRelationalTransaction())
//{
context.Database.CurrentTransaction.Dispose();
//关系型数据库共享事务
continue;
//}
//context.Database.CommitTransaction();
}
HasCommitted = true;
}
/// <summary>
/// 回滚所有事务
/// </summary>
public virtual void Rollback()
{
if (_transaction?.Connection != null)
{
_transaction.Rollback();
}
foreach (var context in _dbContexts)
{
//if (context.IsRelationalTransaction())
//{
CleanChanges(context);
if (context.Database.CurrentTransaction != null)
{
//context.Database.CurrentTransaction.Rollback();
context.Database.CurrentTransaction.Dispose();
}
continue;
//}
//context.Database.RollbackTransaction();
}
HasCommitted = true;
}
private static void CleanChanges(DbContext context)
{
var entries = context.ChangeTracker.Entries().ToArray();
foreach (var entry in entries)
{
entry.State = EntityState.Detached;
}
}
public void Dispose()
{
_transaction?.Dispose();
foreach (DbContext context in _dbContexts)
{
context.Dispose();
}
}
}
}
@@ -0,0 +1,56 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace CloudBuilder.Core.DatabaseAccessor
{
/*
事务范围不匹配:TransactionScope 管理的是分布式事务,但对于单个数据库连接,通常不需要分布式事务。
OnActionExecutionAsync 是 ASP.NET Core 中异步的 Action 过滤器执行方法。
// OnActionExecuting: Action 执行前
[开始事务]
↓
[Action 方法执行]
↓
// OnActionExecuted: Action 执行后(无论是否异常)
[检查异常]
[提交或回滚事务]
*/
[AttributeUsage(AttributeTargets.Method)]
public class UnitOfWorkAttribute : ActionFilterAttribute
{
private TransactionScope _transactionScope;
public override void OnActionExecuting(ActionExecutingContext context)
{
_transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TransactionManager.MaximumTimeout
});
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
if (_transactionScope != null)
{
if (context.Exception == null)
{
//必须要有 SaveChanges() 调用,否则数据只会停留在内存中,不会真正保存到数据库。
_transactionScope.Complete();
}
_transactionScope.Dispose();
}
base.OnActionExecuted(context);
}
}
}
@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Core.DatabaseAccessor
{
public class WebService
{
public WebService(IServiceCollection services)
{
Services = services;
}
public IServiceCollection Services { get; }
}
}