diff --git a/IRaCIS.Core.Domain/Common/NotDefault.cs b/IRaCIS.Core.Domain/Common/NotDefault.cs index b38738133..d6585ac01 100644 --- a/IRaCIS.Core.Domain/Common/NotDefault.cs +++ b/IRaCIS.Core.Domain/Common/NotDefault.cs @@ -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( - AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, - AllowMultiple = false)] - public class GuidNotEmptyAttribute : ValidationAttribute + public const string DefaultErrorMessage = "The {0} field must not be empty"; + public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { } + + public override bool IsValid(object value) { - public const string DefaultErrorMessage = "The {0} field must not be empty"; - public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { } - - public override bool IsValid(object value) + //NotEmpty doesn't necessarily mean required + if (value is null) { - //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; } - } - - 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) + switch (value) { - if (value is null) - { - return false; - } - - if (DateTime.TryParse(value.ToString(), out _) == true) - { + case Guid guid: + return guid != Guid.Empty; + default: 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; + } } diff --git a/IRaCIS.Core.Infra.EFCore/Context/Convention/DecimalPrecisionConvention.cs b/IRaCIS.Core.Infra.EFCore/Context/Convention/DecimalPrecisionConvention.cs index 9b473d1cf..caf8ebd9f 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/Convention/DecimalPrecisionConvention.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/Convention/DecimalPrecisionConvention.cs @@ -9,7 +9,9 @@ using System.Threading.Tasks; namespace IRaCIS.Core.Infra.EFCore; - +/// +/// 设置decimal 默认精度问题 (但是用ef迁移工具的时候,还是会提示No store type was specified for the decimal property,但是迁移文件里面体现了该配置,在dbcontext里写,那么就会没该警告) +/// public class DecimalPrecisionConvention : IModelFinalizingConvention { private readonly int _precision; diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs index 5eeb22984..3a666c46a 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs @@ -24,6 +24,7 @@ using System.Reflection.Metadata; using System.Collections.Generic; using System.ComponentModel; using Microsoft.VisualBasic; +using System.ComponentModel.DataAnnotations; namespace IRaCIS.Core.Infra.EFCore; @@ -67,7 +68,7 @@ public class IRaCISDBContext : DbContext protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { //decimal 不配置,默认精度是18,2 - configurationBuilder.Conventions.Add(_ => new DecimalPrecisionConvention(18,2)); + //configurationBuilder.Conventions.Add(_ => new DecimalPrecisionConvention(18,2)); //针对字符串使用默认的长度配置为200,如果标注了StringLength 其他长度,就是标注的长度,如果标注了MaxLength 那么就是nvarcharMax configurationBuilder.Conventions.Add(_ => new DefaultStringLengthConvention(200)); } @@ -94,6 +95,35 @@ public class IRaCISDBContext : DbContext #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); foreach (var type in typesToRegister) diff --git a/IRaCIS.Core.Test/CodeTemplates/EFCore/EntityType.t4 b/IRaCIS.Core.Test/CodeTemplates/EFCore/EntityType.t4 index 6246b3f15..25249cb1d 100644 --- a/IRaCIS.Core.Test/CodeTemplates/EFCore/EntityType.t4 +++ b/IRaCIS.Core.Test/CodeTemplates/EFCore/EntityType.t4 @@ -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) .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) { + // [Column(TypeName = "decimal(18,1)")] TypeName ֱӹ&& a.Type != typeof(ColumnAttribute) ᱨ + if(dataAnnotation.Arguments?.Count==0) + { + continue; + } + #> <#= code.Fragment(dataAnnotation) #> <# diff --git a/IRaCIS.Core.Test/模板使用说明 b/IRaCIS.Core.Test/模板使用说明 index f74d40bb9..81f681f8b 100644 --- a/IRaCIS.Core.Test/模板使用说明 +++ b/IRaCIS.Core.Test/模板使用说明 @@ -9,6 +9,8 @@ 4、微软官方针对这些不同的库,生成实体的T4模板是一样的,而且也提供了自定义模板的功能,我们可以在官方模板的基础上进行自定义,以适配属于我们项目的实体生成 1)继承我们自己的审计基类 2)生成的时候过滤Id 以及我们自己的审计字段 + 3)为适配多种数据库,自定义指定字符串长度(默认长度不用标注,特殊长度才标注) + 4)为适配多种数据库,自定义精度特性,统一处理 程序包管理控制台命令行工具使用: