增加codefirst迁移配置
continuous-integration/drone/push Build is passing Details

IRC_NewDev
hang 2024-09-17 14:26:12 +08:00
parent 5990477fdc
commit ab68a63bdd
20 changed files with 928 additions and 1006 deletions

View File

@ -1,107 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<SignAssembly>false</SignAssembly>
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
<LangVersion>default</LangVersion>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
<NoWarn>1701;1702;1591;</NoWarn>
<OutputPath>..\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
<OutputPath>bin\Release\</OutputPath>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Controllers\ReviewerApi\**" />
<Compile Remove="UploadFile\**" />
<Content Remove="Controllers\ReviewerApi\**" />
<Content Remove="UploadFile\**" />
<EmbeddedResource Remove="Controllers\ReviewerApi\**" />
<EmbeddedResource Remove="UploadFile\**" />
<None Remove="Controllers\ReviewerApi\**" />
<None Remove="UploadFile\**" />
</ItemGroup>
<ItemGroup>
<Content Remove="web.config" />
<Content Remove="wwwroot\swagger\ui\abp.js" />
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
<Content Remove="wwwroot\swagger\ui\Index.html" />
</ItemGroup>
<ItemGroup>
<None Remove=".preview.jpg" />
<None Remove="GrpcToken.proto" />
<None Remove="IRaCIS.Core.API.xml" />
<None Remove="Protos\GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js" />
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js" />
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\GrpcToken.proto">
<GrpcServices>Client</GrpcServices>
</Protobuf>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="4.0.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1" />
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.4.1" />
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.41.0" />
<PackageReference Include="Grpc.Tools" Version="2.42.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Hangfire.Tags.SqlServer" Version="1.8.0" />
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
<PackageReference Include="LogDashboard" Version="1.4.8" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="1.1.4" />
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
<ItemGroup>
<Content Update="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" /></VisualStudio></ProjectExtensions>
</Project>

View File

@ -96,6 +96,12 @@
<Folder Include="Properties\PublishProfiles\" /> <Folder Include="Properties\PublishProfiles\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ProjectExtensions> <ProjectExtensions>
<VisualStudio> <VisualStudio>
<UserProperties properties_4launchsettings_1json__JsonSchema="" /> <UserProperties properties_4launchsettings_1json__JsonSchema="" />

View File

@ -375,11 +375,6 @@
<param name="memberSerialization">序列化成员</param> <param name="memberSerialization">序列化成员</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="T:TimeZoneAdjustmentMiddleware">
<summary>
废弃,没用
</summary>
</member>
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService"> <member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
<summary> <summary>
对称可逆加密 对称可逆加密

View File

@ -1,118 +0,0 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 废弃,没用
/// </summary>
public class TimeZoneAdjustmentMiddleware
{
private readonly RequestDelegate _next;
public TimeZoneAdjustmentMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (string.IsNullOrEmpty(context.Request.ContentType))
{
// 请求没有内容体,可能是一个没有请求体的请求,比如 GET 请求
await _next(context);
return;
}
var timeZoneId = "Asia/Shanghai"; // 客户端默认时区
var timeZoneIdHeaderValue = context.Request.Headers["TimeZoneId"];
if (!string.IsNullOrEmpty(timeZoneIdHeaderValue))
{
timeZoneId = timeZoneIdHeaderValue;
}
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
// 处理 JSON 请求体中的时间字段
if (context.Request.ContentType.StartsWith("application/json"))
{
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
// 使用 JSON.NET 或 System.Text.Json 解析 JSON 请求体
// 假设请求体中有一个名为 "dateTime" 的时间字段
dynamic jsonData = JsonConvert.DeserializeObject(requestBody);
if (jsonData.dateTime != null)
{
if (DateTime.TryParse((string)jsonData.dateTime, out DateTime dateTime))
{
// 将 JSON 请求体中的时间字段转换为服务器时区的时间
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
jsonData.dateTime = serverTime;
}
}
// 将修改后的 JSON 请求体重新写入请求流中
var jsonBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jsonData));
context.Request.Body = new MemoryStream(jsonBytes);
context.Request.ContentLength = jsonBytes.Length;
}
// 处理 URL 表单参数
var modifiedQuery = new Dictionary<string, StringValues>();
foreach (var key in context.Request.Query.Keys)
{
if (DateTime.TryParse(context.Request.Query[key], out DateTime dateTime))
{
// 将 URL 表单参数中的时间转换为服务器时区的时间
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
modifiedQuery[key] = new StringValues(serverTime.ToString());
}
else
{
modifiedQuery[key] = context.Request.Query[key];
}
}
context.Request.Query = new QueryCollection(modifiedQuery);
// 处理Form请求体中的参数
if (context.Request.HasFormContentType)
{
var modifiedForm = new Dictionary<string, StringValues>();
foreach (var key in context.Request.Form.Keys)
{
if (DateTime.TryParse(context.Request.Form[key], out DateTime dateTime))
{
// 将请求体中的时间转换为服务器时区的时间
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
modifiedForm[key] = new StringValues(serverTime.ToString());
}
else
{
modifiedForm[key] = context.Request.Form[key];
}
}
var newFormCollection = new FormCollection(modifiedForm);
// 将新的表单集合设置回请求对象
context.Request.Form = newFormCollection;
}
await _next(context);
}
}

View File

@ -8,9 +8,11 @@ using Medallion.Threading;
using Medallion.Threading.SqlServer; using Medallion.Threading.SqlServer;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis; using StackExchange.Redis;
namespace IRaCIS.Core.API namespace IRaCIS.Core.API
@ -46,10 +48,11 @@ namespace IRaCIS.Core.API
else else
{ {
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
} }
//迁移的时候,不生成外键
options.ReplaceService<IMigrationsSqlGenerator, NoForeignKeyMigrationsSqlGenerator>();
options.UseLoggerFactory(logFactory); options.UseLoggerFactory(logFactory);

View File

@ -43,40 +43,6 @@
} }
] ]
}, },
"easycaching": {
"inmemory": {
"MaxRdSecond": 120,
"EnableLogging": false,
"LockMs": 5000,
"SleepMs": 300,
"DBConfig": {
"SizeLimit": 10000,
"ExpirationScanFrequency": 60,
"EnableReadDeepClone": true,
"EnableWriteDeepClone": false
}
},
"redis": {
"MaxRdSecond": 120,
"EnableLogging": false,
"LockMs": 5000,
"SleepMs": 300,
"dbconfig": {
"Password": "xc@123456",
"IsSsl": false,
"SslHost": null,
"ConnectionTimeout": 5000,
"AllowAdmin": true,
"Endpoints": [
{
"Host": "47.117.164.182",
"Port": 6379
}
],
"Database": 0
}
}
},
"IRaCISImageStore": { "IRaCISImageStore": {
"SwitchingMode": "RemainingDiskCapacity", "SwitchingMode": "RemainingDiskCapacity",
"SwitchingRatio": 80, "SwitchingRatio": 80,
@ -93,7 +59,6 @@
"DefaultPassword": "123456", "DefaultPassword": "123456",
"ImageShareExpireDays": 10 "ImageShareExpireDays": 10
}, },
"EncrypteResponseConfig": { "EncrypteResponseConfig": {
"IsEnable": true, "IsEnable": true,
"ApiPathList": [ "ApiPathList": [

View File

@ -0,0 +1,19 @@
# 程序包管理控制台使用方式
1、生成迁移文件
add-migration init -Context IRaCISDBContext
# 使用dotnet 命令迁移
# dotnet ef migrations add 本地迁移名字 -p 项目名 -c 数据库上下文名 -o 迁移文件生成目录
1、生成迁移文件
dotnet ef migrations add Initial -s IRaCIS.Core.API -p IRaCIS.Core.Infra.EFCore -c IRaCISDBContext -o CodeFirst_MSSQL/Migrations

View File

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Infra.EFCore;
public class DecimalPrecisionConvention : IModelFinalizingConvention
{
private readonly int _precision;
private readonly int _scale;
public DecimalPrecisionConvention(int precision, int scale)
{
_precision = precision;
_scale = scale;
}
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(decimal) || property.ClrType == typeof(decimal?))))
{
// 设置精度和小数位数
property.SetPrecision(_precision);
property.SetScale(_scale);
}
}
}

View File

@ -0,0 +1,56 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace IRaCIS.Core.Infra.EFCore;
/// <summary>
/// Efcore 最新支持批量配置字符串类型长度作为保底的 官网参考https://learn.microsoft.com/zh-cn/ef/core/modeling/bulk-configuration#conventions
/// 设置不标注默认长度然后标注了与默认长度不一致那么就是以标注的为准同时如果标注了MaxLength 对于mssql 那就是nvarcharMax,对于pgsql 就是text
/// </summary>
public class DefaultStringLengthConvention : IModelFinalizingConvention
{
private readonly int _defaultLenth;
public DefaultStringLengthConvention(int defaultLenth)
{
_defaultLenth = defaultLenth;
}
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(string))))
{
// 获取 MaxLength 特性
var maxLengthAttribute = property.PropertyInfo?.GetCustomAttributes(typeof(MaxLengthAttribute), false)
.FirstOrDefault() as MaxLengthAttribute;
// 获取 StringLength 特性
var stringLengthAttribute = property.PropertyInfo?.GetCustomAttributes(typeof(StringLengthAttribute), false)
.FirstOrDefault() as StringLengthAttribute;
// 输出调试信息,看看是哪种特性生效
if (stringLengthAttribute != null)
{
//Console.WriteLine($"{property.Name}: StringLength({stringLengthAttribute.MaximumLength})");
property.Builder.HasMaxLength(stringLengthAttribute.MaximumLength);
}
else if (maxLengthAttribute != null)
{
//Console.WriteLine($"{property.Name}: MaxLength (no specific length, allowing max)");
}
else
{
//Console.WriteLine($"{property.Name}: Default length 200");
property.Builder.HasMaxLength(_defaultLenth);
}
}
}
}

View File

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
namespace IRaCIS.Core.Infra.EFCore;
/// <summary>
/// 迁移的时候忽略显示外键的建立增加灵活性同时为了兼容之前dbfirst 一个字段关联多个表
/// </summary>
public class NoForeignKeyMigrationsSqlGenerator : MigrationsSqlGenerator
{
public NoForeignKeyMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies) : base(dependencies)
{
}
protected override void Generate(Microsoft.EntityFrameworkCore.Migrations.Operations.CreateTableOperation operation, IModel? model, MigrationCommandListBuilder builder, bool terminate = true)
{
operation.ForeignKeys.Clear();
base.Generate(operation, model, builder, terminate);
}
}

View File

@ -5,246 +5,245 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace IRaCIS.Core.Infra.EFCore namespace IRaCIS.Core.Infra.EFCore;
public static class DbContextExt
{ {
public static class DbContextExt
/// <summary>
/// 获取变化的实体信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetChanges<T>(this DbContext db)
{ {
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified && e.Entity is T).Select(e =>
/// <summary>
/// 获取变化的实体信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetChanges<T>(this DbContext db)
{ {
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified && e.Entity is T).Select(e => var originalObject = e.OriginalValues.ToObject();
var currentObject = e.CurrentValues.ToObject();
return new ChangeEntry
{ {
var originalObject = e.OriginalValues.ToObject(); EntityState = e.State,
var currentObject = e.CurrentValues.ToObject(); Entity = e.Entity,
return new ChangeEntry EntityType = e.OriginalValues.EntityType.ClrType,
ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo()
{ {
EntityState = e.State, PropertyInfo = t1.Property.PropertyInfo,
Entity = e.Entity, OriginalValue = t1.Value,
EntityType = e.OriginalValues.EntityType.ClrType, CurrentValue = t2.Value,
ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo() IsPrimaryKey = t1.Property.IsPrimaryKey(),
{ IsForeignKey = t1.Property.IsForeignKey()
PropertyInfo = t1.Property.PropertyInfo, }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
OriginalValue = t1.Value, };
CurrentValue = t2.Value, });
IsPrimaryKey = t1.Property.IsPrimaryKey(),
IsForeignKey = t1.Property.IsForeignKey()
}).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
};
});
}
/// <summary>
/// 获取变化的实体信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetChanges(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e =>
{
var originalObject = e.OriginalValues.ToObject();
var currentObject = e.CurrentValues.ToObject();
return new ChangeEntry()
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.OriginalValues.EntityType.ClrType,
ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo()
{
PropertyInfo = t1.Property.PropertyInfo,
OriginalValue = t1.Value,
CurrentValue = t2.Value,
IsPrimaryKey = t1.Property.IsPrimaryKey(),
IsForeignKey = t1.Property.IsForeignKey(),
}).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
};
});
}
/// <summary>
/// 获取添加的实体信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAdded<T>(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity is T).Select(e =>
{
var currentObject = e.CurrentValues.ToObject();
return new ChangeEntry
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.CurrentValues.EntityType.ClrType,
ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
{
PropertyInfo = p.PropertyInfo,
CurrentValue = p.PropertyInfo.GetValue(currentObject),
IsPrimaryKey = p.IsPrimaryKey(),
IsForeignKey = p.IsForeignKey(),
}).ToList()
};
});
}
/// <summary>
/// 获取添加的实体信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAdded(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e =>
{
var currentObject = e.CurrentValues.ToObject();
return new ChangeEntry
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.CurrentValues.EntityType.ClrType,
ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
{
PropertyInfo = p.PropertyInfo,
CurrentValue = p.PropertyInfo.GetValue(currentObject),
IsPrimaryKey = p.IsPrimaryKey(),
IsForeignKey = p.IsForeignKey(),
}).ToList()
};
});
}
/// <summary>
/// 获取移除的实体信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetRemoved<T>(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted && e.Entity is T).Select(e =>
{
var originalObject = e.OriginalValues.ToObject();
return new ChangeEntry
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.OriginalValues.EntityType.ClrType,
ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
{
PropertyInfo = p.PropertyInfo,
OriginalValue = p.PropertyInfo.GetValue(originalObject),
IsPrimaryKey = p.IsPrimaryKey(),
IsForeignKey = p.IsForeignKey(),
}).ToList()
};
});
}
/// <summary>
/// 获取移除的实体信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetRemoved(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted).Select(e =>
{
var originalObject = e.OriginalValues.ToObject();
return new ChangeEntry
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.OriginalValues.EntityType.ClrType,
ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
{
PropertyInfo = p.PropertyInfo,
OriginalValue = p.PropertyInfo.GetValue(originalObject),
IsPrimaryKey = p.IsPrimaryKey(),
IsForeignKey = p.IsForeignKey(),
}).ToList()
};
});
}
/// <summary>
/// 获取所有的变更信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAllChanges<T>(this DbContext db)
{
return GetChanges<T>(db).Union(GetAdded<T>(db)).Union(GetRemoved<T>(db));
}
/// <summary>
/// 获取所有的变更信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAllChanges(this DbContext db)
{
return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db));
}
} }
public class ChangePropertyInfo
/// <summary>
/// 获取变化的实体信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetChanges(this DbContext db)
{ {
/// <summary> return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e =>
/// 属性 {
/// </summary> var originalObject = e.OriginalValues.ToObject();
public PropertyInfo PropertyInfo { get; set; } var currentObject = e.CurrentValues.ToObject();
return new ChangeEntry()
/// <summary> {
/// 原始值 EntityState = e.State,
/// </summary> Entity = e.Entity,
public object OriginalValue { get; set; } EntityType = e.OriginalValues.EntityType.ClrType,
ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo()
/// <summary> {
/// 新值 PropertyInfo = t1.Property.PropertyInfo,
/// </summary> OriginalValue = t1.Value,
public object CurrentValue { get; set; } CurrentValue = t2.Value,
IsPrimaryKey = t1.Property.IsPrimaryKey(),
/// <summary> IsForeignKey = t1.Property.IsForeignKey(),
/// 是否是主键 }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
/// </summary> };
public bool IsPrimaryKey { get; set; } });
/// <summary>
/// 是否是外键
/// </summary>
public bool IsForeignKey { get; set; }
} }
public class ChangeEntry /// <summary>
/// 获取添加的实体信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAdded<T>(this DbContext db)
{ {
/// <summary> return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity is T).Select(e =>
/// 所属实体 {
/// </summary> var currentObject = e.CurrentValues.ToObject();
public object Entity { get; set; } return new ChangeEntry
{
/// <summary> EntityState = e.State,
/// 实体类型 Entity = e.Entity,
/// </summary> EntityType = e.CurrentValues.EntityType.ClrType,
public Type EntityType { get; set; } ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
{
/// <summary> PropertyInfo = p.PropertyInfo,
/// 变更类型 CurrentValue = p.PropertyInfo.GetValue(currentObject),
/// </summary> IsPrimaryKey = p.IsPrimaryKey(),
public EntityState EntityState { get; set; } IsForeignKey = p.IsForeignKey(),
}).ToList()
/// <summary> };
/// 字段变更信息 });
/// </summary>
public List<ChangePropertyInfo> ChangeProperties { get; set; }
} }
/// <summary>
/// 获取添加的实体信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAdded(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e =>
{
var currentObject = e.CurrentValues.ToObject();
return new ChangeEntry
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.CurrentValues.EntityType.ClrType,
ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
{
PropertyInfo = p.PropertyInfo,
CurrentValue = p.PropertyInfo.GetValue(currentObject),
IsPrimaryKey = p.IsPrimaryKey(),
IsForeignKey = p.IsForeignKey(),
}).ToList()
};
});
}
/// <summary>
/// 获取移除的实体信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetRemoved<T>(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted && e.Entity is T).Select(e =>
{
var originalObject = e.OriginalValues.ToObject();
return new ChangeEntry
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.OriginalValues.EntityType.ClrType,
ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
{
PropertyInfo = p.PropertyInfo,
OriginalValue = p.PropertyInfo.GetValue(originalObject),
IsPrimaryKey = p.IsPrimaryKey(),
IsForeignKey = p.IsForeignKey(),
}).ToList()
};
});
}
/// <summary>
/// 获取移除的实体信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetRemoved(this DbContext db)
{
return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted).Select(e =>
{
var originalObject = e.OriginalValues.ToObject();
return new ChangeEntry
{
EntityState = e.State,
Entity = e.Entity,
EntityType = e.OriginalValues.EntityType.ClrType,
ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
{
PropertyInfo = p.PropertyInfo,
OriginalValue = p.PropertyInfo.GetValue(originalObject),
IsPrimaryKey = p.IsPrimaryKey(),
IsForeignKey = p.IsForeignKey(),
}).ToList()
};
});
}
/// <summary>
/// 获取所有的变更信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAllChanges<T>(this DbContext db)
{
return GetChanges<T>(db).Union(GetAdded<T>(db)).Union(GetRemoved<T>(db));
}
/// <summary>
/// 获取所有的变更信息
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public static IEnumerable<ChangeEntry> GetAllChanges(this DbContext db)
{
return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db));
}
}
public class ChangePropertyInfo
{
/// <summary>
/// 属性
/// </summary>
public PropertyInfo PropertyInfo { get; set; }
/// <summary>
/// 原始值
/// </summary>
public object OriginalValue { get; set; }
/// <summary>
/// 新值
/// </summary>
public object CurrentValue { get; set; }
/// <summary>
/// 是否是主键
/// </summary>
public bool IsPrimaryKey { get; set; }
/// <summary>
/// 是否是外键
/// </summary>
public bool IsForeignKey { get; set; }
}
public class ChangeEntry
{
/// <summary>
/// 所属实体
/// </summary>
public object Entity { get; set; }
/// <summary>
/// 实体类型
/// </summary>
public Type EntityType { get; set; }
/// <summary>
/// 变更类型
/// </summary>
public EntityState EntityState { get; set; }
/// <summary>
/// 字段变更信息
/// </summary>
public List<ChangePropertyInfo> ChangeProperties { get; set; }
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Infra.EFCore.Context;
/// <summary>
/// Design-time DbContext Creation 用于迁移时指定使用哪个数据库
/// </summary>
public class IRaCISDBContextFactory : IDesignTimeDbContextFactory<IRaCISDBContext>
{
public IRaCISDBContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<IRaCISDBContext>();
optionsBuilder.UseSqlServer("Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
return new IRaCISDBContext(optionsBuilder.Options);
}
}

View File

@ -4,15 +4,13 @@ using MassTransit;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.ValueGeneration; using Microsoft.EntityFrameworkCore.ValueGeneration;
namespace IRaCIS.Core.Infra.EFCore namespace IRaCIS.Core.Infra.EFCore;
{
public class MySequentialGuidValueGenerator : ValueGenerator<Guid>
{
public override Guid Next(EntityEntry entry)
{
return NewId.NextSequentialGuid();
}
public override bool GeneratesTemporaryValues => false;
}
public class MySequentialGuidValueGenerator : ValueGenerator<Guid>
{
public override Guid Next(EntityEntry entry)
{
return NewId.NextSequentialGuid();
}
public override bool GeneratesTemporaryValues => false;
} }

View File

@ -25,6 +25,15 @@
<PackageReference Include="EntityFrameworkCore.Exceptions.SqlServer" Version="8.1.3" /> <PackageReference Include="EntityFrameworkCore.Exceptions.SqlServer" Version="8.1.3" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.8" /> <PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -20,7 +20,7 @@ https://www.cnblogs.com/cqpanda/p/16815263.html
5、撤销某次更新到数据库的迁移自动执行down 方法) 5、撤销某次更新到数据库的迁移自动执行down 方法)
dotnet ef database update 某次迁移的前一次迁移名称 -p IRaCIS.Core.Test -c IRCContext dotnet ef database update 某次迁移的前一次迁移名称 -p IRaCIS.Core.Test -c IRCContext
dotnet ef migrations add RemoveForeignKey -p IRaCIS.Core.Test -c IRCContext -o CodeFirstTest/MSSQL/Migrations dotnet ef migrations add RemoveForeignKey -p IRaCIS.Core.Test -c IRCContext -o CodeFirstTest/MSSQL/Migrations

View File

@ -11,6 +11,12 @@ namespace IRaCIS.Core.Test.CodeFirstTest.PGSQL.Migrations
/// </summary> /// </summary>
public class MaxStringLengthConvention : IModelFinalizingConvention public class MaxStringLengthConvention : IModelFinalizingConvention
{ {
private readonly int _defaultLenth;
public MaxStringLengthConvention(int defaultLenth)
{
_defaultLenth = defaultLenth;
}
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context) public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{ {
foreach (var property in modelBuilder.Metadata.GetEntityTypes() foreach (var property in modelBuilder.Metadata.GetEntityTypes()
@ -31,17 +37,17 @@ namespace IRaCIS.Core.Test.CodeFirstTest.PGSQL.Migrations
// 输出调试信息,看看是哪种特性生效 // 输出调试信息,看看是哪种特性生效
if (stringLengthAttribute != null) if (stringLengthAttribute != null)
{ {
Console.WriteLine($"{property.Name}: StringLength({stringLengthAttribute.MaximumLength})"); //Console.WriteLine($"{property.Name}: StringLength({stringLengthAttribute.MaximumLength})");
property.Builder.HasMaxLength(stringLengthAttribute.MaximumLength); property.Builder.HasMaxLength(stringLengthAttribute.MaximumLength);
} }
else if (maxLengthAttribute != null) else if (maxLengthAttribute != null)
{ {
Console.WriteLine($"{property.Name}: MaxLength (no specific length, allowing max)"); //Console.WriteLine($"{property.Name}: MaxLength (no specific length, allowing max)");
} }
else else
{ {
Console.WriteLine($"{property.Name}: Default length 200"); //Console.WriteLine($"{property.Name}: Default length {_defaultLenth}");
property.Builder.HasMaxLength(200); property.Builder.HasMaxLength(_defaultLenth);
} }
} }
} }

View File

@ -70,7 +70,7 @@ public partial class PGContext : DbContext
//configurationBuilder.Conventions.Add(_ => new NoForeignKeyConvention()); //configurationBuilder.Conventions.Add(_ => new NoForeignKeyConvention());
//针对字符串使用默认的长度配置 //针对字符串使用默认的长度配置
configurationBuilder.Conventions.Add(_ => new MaxStringLengthConvention()); configurationBuilder.Conventions.Add(_ => new MaxStringLengthConvention(200));
} }
partial void OnModelCreatingPartial(ModelBuilder modelBuilder); partial void OnModelCreatingPartial(ModelBuilder modelBuilder);

View File

@ -52,16 +52,11 @@
<PackageReference Include="Fluid.Core" Version="2.11.1" /> <PackageReference Include="Fluid.Core" Version="2.11.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -17,7 +17,7 @@
-c 指定数据库上下文名字 -c 指定数据库上下文名字
-d 使用数据注解 不指定默认是fluentAPI -d 使用数据注解 不指定默认是fluentAPI
-t 指定要生成的表名 -t 指定要生成的表名
-p 指定项目名字 -p 指定项目名字 (包含上下文,并且产生迁移文件的项目)
备注: 因为是从数据库反向生成实体所以会默认生成dbcontext ,每次生成想要的实体后删除指定的名称context即可 备注: 因为是从数据库反向生成实体所以会默认生成dbcontext ,每次生成想要的实体后删除指定的名称context即可
针对字符串类型我们避免string字段 数据库存储null string? 数据库才存储null除非有合理的理由这样可以避免代码里面用string 变量总是要判断是否为null 针对字符串类型我们避免string字段 数据库存储null string? 数据库才存储null除非有合理的理由这样可以避免代码里面用string 变量总是要判断是否为null