diff --git a/src/FluentNHibernate/Automapping/DefaultAutomappingConfiguration.cs b/src/FluentNHibernate/Automapping/DefaultAutomappingConfiguration.cs index c116aecbc..1e6777227 100644 --- a/src/FluentNHibernate/Automapping/DefaultAutomappingConfiguration.cs +++ b/src/FluentNHibernate/Automapping/DefaultAutomappingConfiguration.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Reflection; using System.Runtime.CompilerServices; using FluentNHibernate.Automapping.Alterations; using FluentNHibernate.Automapping.Steps; @@ -27,6 +29,10 @@ public virtual bool ShouldMap(Type type) public virtual bool IsId(Member member) { + if (member.MemberInfo.GetCustomAttribute() != null) + { + return true; + } return member.Name.Equals("id", StringComparison.InvariantCultureIgnoreCase); } diff --git a/src/FluentNHibernate/Automapping/Steps/IdentityStep.cs b/src/FluentNHibernate/Automapping/Steps/IdentityStep.cs index e132b791c..173ebbd4c 100644 --- a/src/FluentNHibernate/Automapping/Steps/IdentityStep.cs +++ b/src/FluentNHibernate/Automapping/Steps/IdentityStep.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Reflection; using FluentNHibernate.Mapping; using FluentNHibernate.MappingModel; using FluentNHibernate.MappingModel.ClassBased; @@ -35,6 +37,8 @@ public void Map(ClassMappingBase classMap, Member member) idMapping.Member = member; idMapping.Set(x => x.Generator, Layer.Defaults, GetDefaultGenerator(member)); + columnMapping.TryApplyAttributesFrom(member.MemberInfo,isIdColumn:true); + SetDefaultAccess(member, idMapping); ((ClassMapping)classMap).Set(x => x.Id, Layer.Defaults, idMapping); @@ -67,6 +71,8 @@ GeneratorMapping GetDefaultGenerator(Member property) else defaultGenerator.Assigned(); + generatorMapping.TryApply(property.MemberInfo.GetCustomAttribute()); + return generatorMapping; } } diff --git a/src/FluentNHibernate/Automapping/Steps/PropertyStep.cs b/src/FluentNHibernate/Automapping/Steps/PropertyStep.cs index 661811897..b96ce798b 100644 --- a/src/FluentNHibernate/Automapping/Steps/PropertyStep.cs +++ b/src/FluentNHibernate/Automapping/Steps/PropertyStep.cs @@ -89,6 +89,8 @@ private PropertyMapping GetPropertyMapping(Type type, Member property) mapping.Set(x => x.Name, Layer.Defaults, mapping.Member.Name); mapping.Set(x => x.Type, Layer.Defaults, GetDefaultType(property)); + columnMapping.TryApplyAttributesFrom(property.MemberInfo,isIdColumn:false); + SetDefaultAccess(property, mapping); return mapping; diff --git a/src/FluentNHibernate/FluentNHibernate.csproj b/src/FluentNHibernate/FluentNHibernate.csproj index 019a415c2..857882004 100644 --- a/src/FluentNHibernate/FluentNHibernate.csproj +++ b/src/FluentNHibernate/FluentNHibernate.csproj @@ -1,7 +1,6 @@  - - netstandard2.0;net461;netcoreapp2.0 + netstandard2.0;net461;netcoreapp2.0;net6.0 AnyCpu Library true @@ -9,15 +8,13 @@ true ../FluentKey.snk - + - FluentKey.snk - \ No newline at end of file diff --git a/src/FluentNHibernate/FluentNHibernateAnnotationsExtensions.cs b/src/FluentNHibernate/FluentNHibernateAnnotationsExtensions.cs new file mode 100644 index 000000000..3b7f0825b --- /dev/null +++ b/src/FluentNHibernate/FluentNHibernateAnnotationsExtensions.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Reflection; +using System.Text; +using FluentNHibernate.MappingModel; +using FluentNHibernate.MappingModel.Identity; +using FluentNHibernate.Utils; +using NHibernate; +using NHibernate.Hql.Ast.ANTLR.Tree; +using NHibernate.Id; + +namespace FluentNHibernate; + +public static class FluentNHibernateAnnotationsExtensions +{ + private static INHibernateLogger Log { get; set; } + + private static void UsingLog(Action log) + { + var currentLog = Log; + if (currentLog == null) + { + currentLog = NHibernateLogger.For(typeof(FluentNHibernateAnnotationsExtensions)); + if (currentLog != null) + { + Log = currentLog; + log?.Invoke(currentLog); + } + } + else + { + log?.Invoke(currentLog); + } + } + + private static void Debug(string format, params object[] args) + { + UsingLog(log => log.Debug(format, args)); + } + + /// + /// Try apply an attribute to target. + /// + /// Target type + /// Attribute type + /// Target object + /// Attribute object + /// Change the target object based on an attribute. + /// [Apply] function should return true, only when an attribute be applied or the target object be changed. + /// + /// + /// Returns false where any arguments is null, otherwise return [Apply] function's result. + /// + public static bool TryApply( + TTarget target, + TAttribute attribute, + Func apply + ) + where TAttribute : Attribute + { + if (target == null || attribute == null || apply == null) + { + return false; + } + return apply.Invoke(target, attribute); + } + + /// + /// Set ColumnMapping.NotNull when [Required] present. + /// + /// + /// + /// ColumnMapping + /// RequiredAttribute + /// + public static bool TryApply(this ColumnMapping mapping, RequiredAttribute attribute) + { + return TryApply( + mapping, + attribute, + (mapping, attribute) => + { + Debug( + "[{0}] TryApply(mapping={1}, attribute={2}) Set(NotNull=true)", + typeof(FluentNHibernateAnnotationsExtensions), + mapping, + attribute + ); + + mapping.Set(x => x.NotNull, Layer.Defaults, true); + return true; + } + ); + } + + /// + /// Set ColumnMapping.Length when [MaxLength] present. + /// + /// + /// ColumnMapping + /// MaxLengthAttribute + /// + public static bool TryApply(this ColumnMapping mapping, MaxLengthAttribute attribute) + { + return TryApply( + mapping, + attribute, + (mapping, attribute) => + { + Debug( + "[{0}] TryApply(mapping={1}, attribute={2}) Set(Length={3})", + typeof(FluentNHibernateAnnotationsExtensions), + mapping, + attribute, + attribute.Length + ); + + mapping.Set(x => x.Length, Layer.Defaults, attribute.Length); + return true; + } + ); + } + + /// + /// Configure GeneratorMapping.Class to [assinged] when [DatabaseGenerated(DatabaseGeneratedOption.None)] present. + /// Configure GeneratorMapping.Class to [identity] when [DatabaseGenerated(DatabaseGeneratedOption.Identity)] present. + /// + /// + /// + /// + /// GeneratorMapping + /// DatabaseGeneratedAttribute + /// + public static bool TryApply(this GeneratorMapping mapping, DatabaseGeneratedAttribute attribute) + { + return TryApply( + mapping, + attribute, + (mapping, attribute) => + { + var option = attribute.DatabaseGeneratedOption; + if (option == DatabaseGeneratedOption.None) + { + Debug( + "[{0}] TryApply(mapping={1}, attribute={2}) Set(Class={3})", + typeof(FluentNHibernateAnnotationsExtensions), + mapping, + attribute, + "assigned" + ); + + mapping.Set(x => x.Class, Layer.Defaults, "assigned"); + return true; + } + if (option == DatabaseGeneratedOption.Identity) + { + Debug( + "[{0}] TryApply(mapping={1}, attribute={2}) Set(Class={3})", + typeof(FluentNHibernateAnnotationsExtensions), + mapping, + attribute, + "identity" + ); + + mapping.Set(x => x.Class, Layer.Defaults, "identity"); + return true; + } + return false; + } + ); + } + + public static void TryApplyAttributesFrom( + this ColumnMapping mapping, + MemberInfo memberInfo, + bool isIdColumn + ) + { + //Prefer RequiredAttribute. + bool requiredApplied = mapping.TryApply(memberInfo.GetCustomAttribute()); + //GetType().IsNullable. + if ((!requiredApplied) && (!memberInfo.GetType().IsNullable())) + { + RequiredAttribute requiredAttribute = new (); + Debug( + "[{0}] TryApply(mapping={1}, attribute={2}) Sender=TryApplyAttributesFrom Cause=!GetType().IsNullable()", + typeof(FluentNHibernateAnnotationsExtensions), + mapping, + requiredAttribute + ); + mapping.TryApply(requiredAttribute); + } + mapping.TryApply(memberInfo.GetCustomAttribute()); + } +}