From 1ca1638a61c6447990b9274363176dc5caa2d4b4 Mon Sep 17 00:00:00 2001 From: Michael Pendon Date: Fri, 17 Sep 2021 22:13:10 +0200 Subject: [PATCH] #886 Added the PropertyHandlerAttributes() method on the FluentMapper class. --- .../Attributes/Parameter/NameAttributeTest.cs | 2 +- .../Mappers/FluentMapperTest.cs | 1 + .../Attributes/Parameter/DbTypeAttribute.cs | 4 +- .../Parameter/DirectionAttribute.cs | 4 +- .../Parameter/IsNullableAttribute.cs | 4 +- .../Attributes/Parameter/NameAttribute.cs | 13 ++- .../Parameter/PrecisionAttribute.cs | 4 +- .../Parameter/PropertyValueAttribute.cs | 9 +- .../Attributes/Parameter/ScaleAttribute.cs | 4 +- .../Attributes/Parameter/SizeAttribute.cs | 4 +- .../Cachers/PropertyValueAttributeCache.cs | 1 + RepoDb.Core/RepoDb/ClassProperty.cs | 21 ++++ .../Extensions/ClassPropertyExtension.cs | 4 +- .../RepoDb/Extensions/DbCommandExtension.cs | 20 +--- .../Extensions/PropertyInfoExtension.cs | 17 ++- .../Mappers/EntityMapFluentDefinition.cs | 105 +++++++++++++++++- .../Compiler/PropertyValueAttributes.cs | 16 +-- 17 files changed, 176 insertions(+), 57 deletions(-) diff --git a/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Attributes/Parameter/NameAttributeTest.cs b/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Attributes/Parameter/NameAttributeTest.cs index dc12163c6..16d9b0ef3 100644 --- a/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Attributes/Parameter/NameAttributeTest.cs +++ b/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Attributes/Parameter/NameAttributeTest.cs @@ -48,7 +48,7 @@ public void TestNameAttributeViaEntityViaCreateParameters() Assert.AreEqual(1, command.Parameters.Count); // Assert - var parameter = command.Parameters["TableColumnName"]; + var parameter = command.Parameters["@TableColumnName"]; Assert.IsNotNull(parameter); } } diff --git a/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Mappers/FluentMapperTest.cs b/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Mappers/FluentMapperTest.cs index c65714948..44f062bd1 100644 --- a/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Mappers/FluentMapperTest.cs +++ b/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Mappers/FluentMapperTest.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using RepoDb.Attributes; +using RepoDb.Attributes.Parameter; using RepoDb.Exceptions; using System.Data; diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/DbTypeAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/DbTypeAttribute.cs index 48b46bde5..e7abe164e 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/DbTypeAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/DbTypeAttribute.cs @@ -4,8 +4,8 @@ namespace RepoDb.Attributes.Parameter { /// - /// An attribute used to define a value to the - /// property via an entity property before the actual execution. + /// An attribute that is being used to define a value to the + /// property via a class property mapping.. /// public class DbTypeAttribute : PropertyValueAttribute { diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/DirectionAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/DirectionAttribute.cs index 20ff894d3..42f6fd277 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/DirectionAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/DirectionAttribute.cs @@ -4,8 +4,8 @@ namespace RepoDb.Attributes.Parameter { /// - /// An attribute used to define a value to the - /// property via an entity property before the actual execution. + /// An attribute that is being used to define a value to the + /// property via a class property mapping.. /// public class DirectionAttribute : PropertyValueAttribute { diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/IsNullableAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/IsNullableAttribute.cs index f29766985..f13e1a6be 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/IsNullableAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/IsNullableAttribute.cs @@ -4,8 +4,8 @@ namespace RepoDb.Attributes.Parameter { /// - /// An attribute used to define a value to the - /// property via an entity property before the actual execution. + /// An attribute that is being used to define a value to the + /// property via a class property mapping.. /// public class IsNullableAttribute : PropertyValueAttribute { diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/NameAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/NameAttribute.cs index b3feee3e7..ccb2ccbe4 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/NameAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/NameAttribute.cs @@ -1,10 +1,11 @@ -using System.Data.Common; +using RepoDb.Extensions; +using System.Data.Common; namespace RepoDb.Attributes.Parameter { /// - /// An attribute used to define a value to the - /// property via an entity property before the actual execution. + /// An attribute that is being used to define a value to the + /// property via a class property mapping.. /// public class NameAttribute : PropertyValueAttribute { @@ -20,5 +21,11 @@ public NameAttribute(string name) /// Gets the mapped name of the equivalent database object/field. /// public string Name => (string)Value; + + /// + /// + /// + /// + internal override object GetValue() => Name.AsParameter(); } } \ No newline at end of file diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/PrecisionAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/PrecisionAttribute.cs index d64f1b4a9..28ab2cbba 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/PrecisionAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/PrecisionAttribute.cs @@ -3,8 +3,8 @@ namespace RepoDb.Attributes.Parameter { /// - /// An attribute used to define a value to the - /// property via an entity property before the actual execution. + /// An attribute that is being used to define a value to the + /// property via a class property mapping.. /// public class PrecisionAttribute : PropertyValueAttribute { diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/PropertyValueAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/PropertyValueAttribute.cs index e66748f9b..cb3e395f3 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/PropertyValueAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/PropertyValueAttribute.cs @@ -98,10 +98,17 @@ internal void SetValue(IDbDataParameter parameter) if (ParameterType.IsAssignableFrom(parameter.GetType())) { - PropertyInfo.SetValue(parameter, Value); + // The reason why we use the 'GetValue()' method over the 'Value' property is because + // of the fact that derived class should sometime customize the value. + PropertyInfo.SetValue(parameter, GetValue()); } } + /// + /// + /// + internal virtual object GetValue() => Value; + /* * Helpers */ diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/ScaleAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/ScaleAttribute.cs index d090a0a8c..dcb070524 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/ScaleAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/ScaleAttribute.cs @@ -3,8 +3,8 @@ namespace RepoDb.Attributes.Parameter { /// - /// An attribute used to define a value to the - /// property via an entity property before the actual execution. + /// An attribute that is being used to define a value to the + /// property via a class property mapping.. /// public class ScaleAttribute : PropertyValueAttribute { diff --git a/RepoDb.Core/RepoDb/Attributes/Parameter/SizeAttribute.cs b/RepoDb.Core/RepoDb/Attributes/Parameter/SizeAttribute.cs index 4143030fd..c1e318bcf 100644 --- a/RepoDb.Core/RepoDb/Attributes/Parameter/SizeAttribute.cs +++ b/RepoDb.Core/RepoDb/Attributes/Parameter/SizeAttribute.cs @@ -3,8 +3,8 @@ namespace RepoDb.Attributes.Parameter { /// - /// An attribute used to define a value to the - /// property via an entity property before the actual execution. + /// An attribute that is being used to define a value to the + /// property via a class property mapping.. /// public class SizeAttribute : PropertyValueAttribute { diff --git a/RepoDb.Core/RepoDb/Cachers/PropertyValueAttributeCache.cs b/RepoDb.Core/RepoDb/Cachers/PropertyValueAttributeCache.cs index 5b857d2cb..1d8b0159c 100644 --- a/RepoDb.Core/RepoDb/Cachers/PropertyValueAttributeCache.cs +++ b/RepoDb.Core/RepoDb/Cachers/PropertyValueAttributeCache.cs @@ -141,6 +141,7 @@ internal static IEnumerable Get(Type entityType, PropertyInfo propertyInfo) { // Validate + ObjectExtension.ThrowIfNull(entityType, "EntityType"); ObjectExtension.ThrowIfNull(propertyInfo, "PropertyInfo"); // Variables diff --git a/RepoDb.Core/RepoDb/ClassProperty.cs b/RepoDb.Core/RepoDb/ClassProperty.cs index f4fee1b90..2cf944797 100644 --- a/RepoDb.Core/RepoDb/ClassProperty.cs +++ b/RepoDb.Core/RepoDb/ClassProperty.cs @@ -2,6 +2,7 @@ using RepoDb.Attributes.Parameter; using RepoDb.Extensions; using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data; using System.Reflection; @@ -293,6 +294,26 @@ public TPropertyHandler GetPropertyHandler() public string GetMappedName() => mappedName ??= PropertyMappedNameCache.Get(GetDeclaringType(), PropertyInfo); + /* + * PropertyHandlerAttributes + */ + private bool isPropertyValueAttributesWasSet; + private IEnumerable propertyValueAttributes; + + /// + /// Gets the list of mapped object for the current property. + /// + /// The list of mapped object. + public IEnumerable GetPropertyValueAttributes() + { + if (isPropertyValueAttributesWasSet == true) + { + return propertyValueAttributes; + } + isPropertyValueAttributesWasSet = true; + return propertyValueAttributes = PropertyInfo.GetPropertyValueAttributes(); + } + #endregion #region Comparers diff --git a/RepoDb.Core/RepoDb/Extensions/ClassPropertyExtension.cs b/RepoDb.Core/RepoDb/Extensions/ClassPropertyExtension.cs index 98b06ebe6..55f4d1da8 100644 --- a/RepoDb.Core/RepoDb/Extensions/ClassPropertyExtension.cs +++ b/RepoDb.Core/RepoDb/Extensions/ClassPropertyExtension.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using RepoDb.Attributes.Parameter; +using System.Collections.Generic; using System.Linq; +using System.Reflection; namespace RepoDb.Extensions { diff --git a/RepoDb.Core/RepoDb/Extensions/DbCommandExtension.cs b/RepoDb.Core/RepoDb/Extensions/DbCommandExtension.cs index 1b4b801ad..d8da3d55d 100644 --- a/RepoDb.Core/RepoDb/Extensions/DbCommandExtension.cs +++ b/RepoDb.Core/RepoDb/Extensions/DbCommandExtension.cs @@ -703,15 +703,15 @@ private static int GetSize(int? size, private static void InvokePropertyValueAttributes(IDbDataParameter parameter, ClassProperty classProperty) { - var attributes = GetPropertyValueAttributes(classProperty); + var attributes = classProperty?.GetPropertyValueAttributes(); if (attributes?.Any() != true) { return; } - // In RepoDb, the only way the parameter has '_' is when the time you call the QueryField.IsForUpdate() + // In RepoDb, the only way the parameter has '@_' is when the time you call the QueryField.IsForUpdate() // method and it is only happening on update operations. - var isForUpdate = parameter.ParameterName.StartsWith("_"); + var isForUpdate = parameter.ParameterName.StartsWith("_") || parameter.ParameterName.StartsWith("@_"); foreach (var attribute in attributes) { @@ -723,20 +723,6 @@ private static void InvokePropertyValueAttributes(IDbDataParameter parameter, } } - /// - /// - /// - /// - /// - private static IEnumerable GetPropertyValueAttributes(ClassProperty classProperty) => - classProperty? - .PropertyInfo? - .GetCustomAttributes()? - .Where(e => - StaticType.PropertyValueAttribute.IsAssignableFrom(e.GetType())) - .Select(e => - (PropertyValueAttribute)e); - /// /// /// diff --git a/RepoDb.Core/RepoDb/Extensions/PropertyInfoExtension.cs b/RepoDb.Core/RepoDb/Extensions/PropertyInfoExtension.cs index a9a7867ac..e862bb7d5 100644 --- a/RepoDb.Core/RepoDb/Extensions/PropertyInfoExtension.cs +++ b/RepoDb.Core/RepoDb/Extensions/PropertyInfoExtension.cs @@ -83,7 +83,7 @@ public static QueryField AsQueryField(this PropertyInfo property, internal static QueryField AsQueryField(this PropertyInfo property, object entity, bool appendUnderscore) => - new (property.AsField(), Operation.Equal, property.GetHandledValue(entity), appendUnderscore); + new(property.AsField(), Operation.Equal, property.GetHandledValue(entity), appendUnderscore); /// /// Converts a into a mapped name. @@ -176,6 +176,21 @@ public static IEnumerable AsFields(this IEnumerable propert public static IEnumerable AsFields(this PropertyInfo[] properties) => AsFields(properties.AsEnumerable()); + /// + /// Returns the list of object that is currently mapped + /// on the target object. + /// + /// The target property. + /// The list of mapped objects. + public static IEnumerable GetPropertyValueAttributes(this PropertyInfo propertyInfo) => + propertyInfo? + .GetCustomAttributes()? + .Where(e => + StaticType.PropertyValueAttribute.IsAssignableFrom(e.GetType())) + .Select(e => + (PropertyValueAttribute)e) ?? + PropertyValueAttributeCache.Get(propertyInfo?.DeclaringType, propertyInfo); + /// /// Returns the value of the data entity property. If the property handler is defined in the property, then the /// handled value will be returned. diff --git a/RepoDb.Core/RepoDb/Mappers/EntityMapFluentDefinition.cs b/RepoDb.Core/RepoDb/Mappers/EntityMapFluentDefinition.cs index 07f817e71..0f6ca5295 100644 --- a/RepoDb.Core/RepoDb/Mappers/EntityMapFluentDefinition.cs +++ b/RepoDb.Core/RepoDb/Mappers/EntityMapFluentDefinition.cs @@ -1,5 +1,7 @@ -using RepoDb.Interfaces; +using RepoDb.Attributes.Parameter; +using RepoDb.Interfaces; using System; +using System.Collections.Generic; using System.Data; using System.Linq.Expressions; @@ -396,7 +398,7 @@ public EntityMapFluentDefinition DbType(Field field, /// /// The type of the . /// The current instance. - public EntityMapFluentDefinition ClassHandler() + public EntityMapFluentDefinition ClassHandler() where TClassHandler : new() => ClassHandler(new TClassHandler()); @@ -407,7 +409,7 @@ public EntityMapFluentDefinition ClassHandler() /// The type of the . /// A value that indicates whether to force the mapping. If one is already exists, then it will be overwritten. /// The current instance. - public EntityMapFluentDefinition ClassHandler(bool force) + public EntityMapFluentDefinition ClassHandler(bool force) where TClassHandler : new() => ClassHandler(new TClassHandler(), force); @@ -449,7 +451,7 @@ public EntityMapFluentDefinition ClassHandler(TClassHand /// The type of the . /// The expression to be parsed. /// The current instance. - public EntityMapFluentDefinition PropertyHandler(Expression> expression) + public EntityMapFluentDefinition PropertyHandler(Expression> expression) where TPropertyHandler : new() => PropertyHandler(expression, new TPropertyHandler()); @@ -462,7 +464,7 @@ public EntityMapFluentDefinition PropertyHandler(Expr /// A value that indicates whether to force the mapping. If one is already exists, then it will be overwritten. /// The current instance. public EntityMapFluentDefinition PropertyHandler(Expression> expression, - bool force) + bool force) where TPropertyHandler : new() => PropertyHandler(expression, new TPropertyHandler(), force); @@ -504,7 +506,7 @@ public EntityMapFluentDefinition PropertyHandler(Expr /// The type of the . /// The name of the class property to be mapped. /// The current instance. - public EntityMapFluentDefinition PropertyHandler(string propertyName) + public EntityMapFluentDefinition PropertyHandler(string propertyName) where TPropertyHandler : new() => PropertyHandler(propertyName, new TPropertyHandler(), false); @@ -578,5 +580,96 @@ public EntityMapFluentDefinition PropertyHandler(Fiel } #endregion + + #region PropertyValueAttributes + + /* + * Expression + */ + + /// + /// Defines the class property parameter value attributes. + /// + /// The expression to be parsed. + /// The list of property value attributes to be mapped. + /// The current instance. + public EntityMapFluentDefinition PropertyValueAttributes(Expression> expression, + IEnumerable attributes) => + PropertyValueAttributes(expression, attributes, false); + + /// + /// Defines the class property parameter value attributes. + /// + /// The expression to be parsed. + /// The list of property value attributes to be mapped. + /// A value that indicates whether to force the mapping. If one is already exists, then it will be overwritten. + /// The current instance. + public EntityMapFluentDefinition PropertyValueAttributes(Expression> expression, + IEnumerable attributes, + bool force) + { + PropertyValueAttributeMapper.Add(expression, attributes, force); + return this; + } + + /* + * PropertyName + */ + + /// + /// Defines the class property parameter value attributes (via property name). + /// + /// The name of the class property to be mapped. + /// The list of property value attributes to be mapped. + /// The current instance. + public EntityMapFluentDefinition PropertyValueAttributes(string propertyName, + IEnumerable attributes) => + PropertyValueAttributes(propertyName, attributes, false); + + /// + /// Defines the class property parameter value attributes (via property name). + /// + /// The name of the class property to be mapped. + /// The list of property value attributes to be mapped. + /// A value that indicates whether to force the mapping. If one is already exists, then it will be overwritten. + /// The current instance. + public EntityMapFluentDefinition PropertyValueAttributes(string propertyName, + IEnumerable attributes, + bool force) + { + PropertyValueAttributeMapper.Add(propertyName, attributes, force); + return this; + } + + /* + * Field + */ + + /// + /// Defines the class property parameter value attributes (via object). + /// + /// The instance of object to be mapped. + /// The list of property value attributes to be mapped. + /// The current instance. + public EntityMapFluentDefinition PropertyValueAttributes(Field field, + IEnumerable attributes) => + PropertyValueAttributes(field, attributes, false); + + /// + /// Defines the class property parameter value attributes (via object). + /// + /// The instance of object to be mapped. + /// The list of property value attributes to be mapped. + /// A value that indicates whether to force the mapping. If one is already exists, then it will be overwritten. + /// The current instance. + public EntityMapFluentDefinition PropertyValueAttributes(Field field, + IEnumerable attributes, + bool force) + { + PropertyValueAttributeMapper.Add(field, attributes, force); + return this; + } + + #endregion } } diff --git a/RepoDb.Core/RepoDb/Reflection/Compiler/PropertyValueAttributes.cs b/RepoDb.Core/RepoDb/Reflection/Compiler/PropertyValueAttributes.cs index 9608753ff..547326327 100644 --- a/RepoDb.Core/RepoDb/Reflection/Compiler/PropertyValueAttributes.cs +++ b/RepoDb.Core/RepoDb/Reflection/Compiler/PropertyValueAttributes.cs @@ -32,7 +32,7 @@ internal static IEnumerable GetParameterPropertyValueSetterAttribute Expression parameterVariable, ClassProperty classProperty) { - var attributes = GetPropertyValueAttributes(classProperty); + var attributes = classProperty?.GetPropertyValueAttributes(); if (attributes?.Any() != true) { return default; @@ -100,20 +100,6 @@ internal static MethodInfo GetPropertyValueAttributeSetValueMethod() => StaticType.PropertyValueAttribute.GetMethod("SetValue", BindingFlags.Instance | BindingFlags.NonPublic); - /// - /// - /// - /// - /// - private static IEnumerable GetPropertyValueAttributes(ClassProperty classProperty) => - classProperty? - .PropertyInfo? - .GetCustomAttributes()? - .Where(e => - StaticType.PropertyValueAttribute.IsAssignableFrom(e.GetType())) - .Select(e => - (PropertyValueAttribute)e); - #endregion } }