decimal 精度解决
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
ab68a63bdd
commit
65e47d9918
|
@ -1,78 +1,89 @@
|
||||||
namespace System.ComponentModel.DataAnnotations
|
namespace System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
[AttributeUsage(
|
||||||
|
AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
|
||||||
|
AllowMultiple = false)]
|
||||||
|
public class GuidNotEmptyAttribute : ValidationAttribute
|
||||||
{
|
{
|
||||||
[AttributeUsage(
|
public const string DefaultErrorMessage = "The {0} field must not be empty";
|
||||||
AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
|
public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { }
|
||||||
AllowMultiple = false)]
|
|
||||||
public class GuidNotEmptyAttribute : ValidationAttribute
|
public override bool IsValid(object value)
|
||||||
{
|
{
|
||||||
public const string DefaultErrorMessage = "The {0} field must not be empty";
|
//NotEmpty doesn't necessarily mean required
|
||||||
public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { }
|
if (value is null)
|
||||||
|
|
||||||
public override bool IsValid(object value)
|
|
||||||
{
|
{
|
||||||
//NotEmpty doesn't necessarily mean required
|
|
||||||
if (value is null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case Guid guid:
|
|
||||||
return guid != Guid.Empty;
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NotDefaultAttribute : ValidationAttribute
|
|
||||||
{
|
|
||||||
public const string DefaultErrorMessage = "The {0} field is is not passed or not set a valid value";
|
|
||||||
public NotDefaultAttribute() : base(DefaultErrorMessage) { }
|
|
||||||
|
|
||||||
public override bool IsValid(object value)
|
|
||||||
{
|
|
||||||
//NotDefault doesn't necessarily mean required
|
|
||||||
if (value is null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = value.GetType();
|
|
||||||
if (type.IsValueType)
|
|
||||||
{
|
|
||||||
var defaultValue = Activator.CreateInstance(type);
|
|
||||||
return !value.Equals(defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-null ref type
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
switch (value)
|
||||||
public class CanConvertToTimeAttribute : ValidationAttribute
|
|
||||||
{
|
|
||||||
public const string DefaultErrorMessage = "The {0} field is is not a valid DateTime value";
|
|
||||||
public CanConvertToTimeAttribute() : base(DefaultErrorMessage) { }
|
|
||||||
|
|
||||||
public override bool IsValid(object value)
|
|
||||||
{
|
{
|
||||||
if (value is null)
|
case Guid guid:
|
||||||
{
|
return guid != Guid.Empty;
|
||||||
return false;
|
default:
|
||||||
}
|
|
||||||
|
|
||||||
if (DateTime.TryParse(value.ToString(), out _) == true)
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NotDefaultAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
public const string DefaultErrorMessage = "The {0} field is is not passed or not set a valid value";
|
||||||
|
public NotDefaultAttribute() : base(DefaultErrorMessage) { }
|
||||||
|
|
||||||
|
public override bool IsValid(object value)
|
||||||
|
{
|
||||||
|
//NotDefault doesn't necessarily mean required
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = value.GetType();
|
||||||
|
if (type.IsValueType)
|
||||||
|
{
|
||||||
|
var defaultValue = Activator.CreateInstance(type);
|
||||||
|
return !value.Equals(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-null ref type
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class CanConvertToTimeAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
public const string DefaultErrorMessage = "The {0} field is is not a valid DateTime value";
|
||||||
|
public CanConvertToTimeAttribute() : base(DefaultErrorMessage) { }
|
||||||
|
|
||||||
|
public override bool IsValid(object value)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.TryParse(value.ToString(), out _) == true)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||||
|
public class DecimalPrecisionAttribute : Attribute
|
||||||
|
{
|
||||||
|
public int Precision { get; }
|
||||||
|
public int Scale { get; }
|
||||||
|
|
||||||
|
public DecimalPrecisionAttribute(int precision, int scale)
|
||||||
|
{
|
||||||
|
Precision = precision;
|
||||||
|
Scale = scale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Infra.EFCore;
|
namespace IRaCIS.Core.Infra.EFCore;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置decimal 默认精度问题 (但是用ef迁移工具的时候,还是会提示No store type was specified for the decimal property,但是迁移文件里面体现了该配置,在dbcontext里写,那么就会没该警告)
|
||||||
|
/// </summary>
|
||||||
public class DecimalPrecisionConvention : IModelFinalizingConvention
|
public class DecimalPrecisionConvention : IModelFinalizingConvention
|
||||||
{
|
{
|
||||||
private readonly int _precision;
|
private readonly int _precision;
|
||||||
|
|
|
@ -24,6 +24,7 @@ using System.Reflection.Metadata;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Microsoft.VisualBasic;
|
using Microsoft.VisualBasic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Infra.EFCore;
|
namespace IRaCIS.Core.Infra.EFCore;
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ public class IRaCISDBContext : DbContext
|
||||||
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
|
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
|
||||||
{
|
{
|
||||||
//decimal 不配置,默认精度是18,2
|
//decimal 不配置,默认精度是18,2
|
||||||
configurationBuilder.Conventions.Add(_ => new DecimalPrecisionConvention(18,2));
|
//configurationBuilder.Conventions.Add(_ => new DecimalPrecisionConvention(18,2));
|
||||||
//针对字符串使用默认的长度配置为200,如果标注了StringLength 其他长度,就是标注的长度,如果标注了MaxLength 那么就是nvarcharMax
|
//针对字符串使用默认的长度配置为200,如果标注了StringLength 其他长度,就是标注的长度,如果标注了MaxLength 那么就是nvarcharMax
|
||||||
configurationBuilder.Conventions.Add(_ => new DefaultStringLengthConvention(200));
|
configurationBuilder.Conventions.Add(_ => new DefaultStringLengthConvention(200));
|
||||||
}
|
}
|
||||||
|
@ -94,6 +95,35 @@ public class IRaCISDBContext : DbContext
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region decimal 自定义精度,适配多种数据库
|
||||||
|
//foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||||
|
//{
|
||||||
|
// foreach (var property in entityType.GetProperties())
|
||||||
|
// {
|
||||||
|
// // 如果属性类型是 decimal 或 nullable decimal
|
||||||
|
// if (property.ClrType == typeof(decimal) || property.ClrType == typeof(decimal?))
|
||||||
|
// {
|
||||||
|
// // 获取自定义的 DecimalPrecisionAttribute
|
||||||
|
// var precisionAttr = property.PropertyInfo?.GetCustomAttributes(typeof(DecimalPrecisionAttribute), false)
|
||||||
|
// .FirstOrDefault() as DecimalPrecisionAttribute;
|
||||||
|
|
||||||
|
// if (precisionAttr != null)
|
||||||
|
// {
|
||||||
|
// property.SetPrecision(precisionAttr.Precision);
|
||||||
|
// property.SetScale(precisionAttr.Scale);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // 默认的精度设置
|
||||||
|
// property.SetPrecision(18);
|
||||||
|
// property.SetScale(2);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
//遍历实体模型手动配置
|
//遍历实体模型手动配置
|
||||||
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null);
|
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null);
|
||||||
foreach (var type in typesToRegister)
|
foreach (var type in typesToRegister)
|
||||||
|
|
|
@ -109,11 +109,41 @@ public partial class <#= EntityType.Name #>: BaseFullAuditEntity
|
||||||
<#
|
<#
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (property.ClrType == typeof(decimal) || property.ClrType == typeof(decimal?))
|
||||||
|
{
|
||||||
|
var typeName = property.GetColumnType()??""; // 获取数据库类型名称
|
||||||
|
// 匹配 decimal, numeric, 或者 number 格式
|
||||||
|
var match = System.Text.RegularExpressions.Regex.Match(typeName, @"(decimal|numeric|number)\((\d+),(\d+)\)", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var precision = match.Groups[2].Value;
|
||||||
|
var scale = match.Groups[3].Value;
|
||||||
|
#>
|
||||||
|
[DecimalPrecision(<#= precision #>, <#= scale #>)]
|
||||||
|
<#
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#>
|
||||||
|
[DecimalPrecision(18, 2)] // 默认值
|
||||||
|
<#
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var dataAnnotations = property.GetDataAnnotations(annotationCodeGenerator)
|
var dataAnnotations = property.GetDataAnnotations(annotationCodeGenerator)
|
||||||
.Where(a => !(a.Type == typeof(RequiredAttribute) && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType)
|
.Where(a => !(a.Type == typeof(RequiredAttribute) && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType)
|
||||||
&& a.Type != typeof(StringLengthAttribute)); // Åųý StringLengthAttribute
|
&& a.Type != typeof(StringLengthAttribute) // 排除 StringLengthAttribute
|
||||||
|
);
|
||||||
|
|
||||||
foreach (var dataAnnotation in dataAnnotations)
|
foreach (var dataAnnotation in dataAnnotations)
|
||||||
{
|
{
|
||||||
|
//过滤 [Column(TypeName = "decimal(18,1)")] 这种设置TypeName的 直接过滤&& a.Type != typeof(ColumnAttribute) 会报错,很奇怪
|
||||||
|
if(dataAnnotation.Arguments?.Count==0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
#>
|
#>
|
||||||
<#= code.Fragment(dataAnnotation) #>
|
<#= code.Fragment(dataAnnotation) #>
|
||||||
<#
|
<#
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
4、微软官方针对这些不同的库,生成实体的T4模板是一样的,而且也提供了自定义模板的功能,我们可以在官方模板的基础上进行自定义,以适配属于我们项目的实体生成
|
4、微软官方针对这些不同的库,生成实体的T4模板是一样的,而且也提供了自定义模板的功能,我们可以在官方模板的基础上进行自定义,以适配属于我们项目的实体生成
|
||||||
1)继承我们自己的审计基类
|
1)继承我们自己的审计基类
|
||||||
2)生成的时候过滤Id 以及我们自己的审计字段
|
2)生成的时候过滤Id 以及我们自己的审计字段
|
||||||
|
3)为适配多种数据库,自定义指定字符串长度(默认长度不用标注,特殊长度才标注)
|
||||||
|
4)为适配多种数据库,自定义精度特性,统一处理
|
||||||
|
|
||||||
程序包管理控制台命令行工具使用:
|
程序包管理控制台命令行工具使用:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue