diff --git a/Npoi.Mapper/src/Npoi.Mapper/Attributes/ColumnAttribute.cs b/Npoi.Mapper/src/Npoi.Mapper/Attributes/ColumnAttribute.cs index 92a52a8..39115d2 100644 --- a/Npoi.Mapper/src/Npoi.Mapper/Attributes/ColumnAttribute.cs +++ b/Npoi.Mapper/src/Npoi.Mapper/Attributes/ColumnAttribute.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using LogicExtensions; namespace Npoi.Mapper.Attributes; @@ -138,7 +139,7 @@ public ColumnAttribute(string name) return null; } - Getter ??= MapHelper.CreateConditionalGetter(host, PropertyFullPath); + Getter ??= PropertyFullPath.CreateConditionalGetter(host, pathStartsWithHostType: true); return (Func)Getter; } @@ -149,7 +150,7 @@ public ColumnAttribute(string name) return null; } - Setter ??= MapHelper.CreateConditionalSetter(host, PropertyFullPath); + Setter ??= PropertyFullPath.CreateConditionalSetter(host, pathStartsWithHostType: true); return (Action)Setter; } diff --git a/Npoi.Mapper/src/Npoi.Mapper/MapHelper.cs b/Npoi.Mapper/src/Npoi.Mapper/MapHelper.cs index 645859d..5e4cc3f 100644 --- a/Npoi.Mapper/src/Npoi.Mapper/MapHelper.cs +++ b/Npoi.Mapper/src/Npoi.Mapper/MapHelper.cs @@ -485,123 +485,6 @@ public static (PropertyInfo propertyInfo, string fullPath) GetPropertyInfo(st return (pi, fullPath); } - public static Expression> CreateSelectorExpression(T host, string propertyFullPath) - { - if (host is null) - { - throw new ArgumentNullException(nameof(host)); - } - - var parts = propertyFullPath?.Split('.'); - if (parts is null || parts.Length == 0) - { - throw new ArgumentException($"Property path is invalid: {propertyFullPath}", nameof(propertyFullPath)); - } - - if (parts.Length <= 1) - { - throw new ArgumentException($"Property full path should include the host type: {propertyFullPath}", nameof(propertyFullPath)); - } - - var hostType = host.GetType(); - var param = Expression.Parameter(typeof(T), "x"); // T will be 'object' for dynamic - Expression body = Expression.Convert(param, hostType); // force convert to underlying type for dynamic - - // Skip the first part, which is the host object type. - for (var i = 1; i < parts.Length; i++) - { - var member = parts[i]; - body = Expression.PropertyOrField(body, member); - } - - var selector = Expression.Lambda>(Expression.Convert(body, ObjectType), param); - - return selector; - } - - public static Func CreateConditionalGetter(T host, string propertyFullPath) - { - var selector = CreateSelectorExpression(host, propertyFullPath); - return CreateConditionalGetter(selector); - } - - public static Func CreateConditionalGetter(Expression> propertySelector) - { - if (propertySelector is not LambdaExpression lambdaExpression) - { - throw new ArgumentException($"Unsupported property selector: {propertySelector}", nameof(propertySelector)); - } - - var body = lambdaExpression.Body; - var newBody = body; - while (body is MemberExpression or UnaryExpression{ Operand: MemberExpression }) - { - var memberAccess = body as MemberExpression ?? (MemberExpression)((UnaryExpression)body).Operand; - - if (!memberAccess.Expression.Type.IsValueType) - { - newBody = Expression.Condition( - Expression.Equal(memberAccess.Expression, Expression.Constant(null)), - Expression.Convert(Expression.Constant(null), ObjectType), - newBody); - } - - body = memberAccess.Expression; - } - - newBody = Expression.Convert(newBody, ObjectType); - return Expression.Lambda>(newBody, propertySelector.Parameters.First()).Compile(); - } - - public static Action CreateConditionalSetter(T host, string propertyFullPath) - { - var selector = CreateSelectorExpression(host, propertyFullPath); - return CreateConditionalSetter(selector); - } - - public static Action CreateConditionalSetter(Expression> propertySelector) - { - if (propertySelector is not LambdaExpression lambdaExpression) - { - throw new ArgumentException($"Unsupported property selector: {propertySelector}", nameof(propertySelector)); - } - - var writeableBody = lambdaExpression.Body is UnaryExpression { Operand: MemberExpression writeableMember } - ? writeableMember - : (MemberExpression)lambdaExpression.Body; - var newValueParam = Expression.Parameter(ObjectType, "v"); - - var rightExpression = Expression.Condition(// Write default value if the new value is null. - Expression.Equal(newValueParam, Expression.Constant(null)), - Expression.Default(writeableBody.Type), - Expression.Convert(newValueParam, writeableBody.Type)); - var assignExpression = Expression.Assign(writeableBody, rightExpression); - var statements = new List { assignExpression }; - - var innerBody = writeableBody.Expression; - - while (innerBody is MemberExpression or UnaryExpression{ Operand: MemberExpression }) - { - var memberAccess = innerBody as MemberExpression ?? (MemberExpression)((UnaryExpression)innerBody).Operand; - - if (!memberAccess.Type.IsValueType) - { - var ifNullAssignNew = Expression.IfThen( - Expression.Equal(memberAccess, Expression.Constant(null)), - Expression.Assign(memberAccess, Expression.New(memberAccess.Type))); - - statements.Add(ifNullAssignNew); - } - - innerBody = memberAccess.Expression; - } - - statements.Reverse(); - var newBody = Expression.Block(statements); - - return Expression.Lambda>(newBody, propertySelector.Parameters[0], newValueParam).Compile(); - } - /// /// Get refined name by removing specified chars and truncating by specified chars. /// diff --git a/Npoi.Mapper/src/Npoi.Mapper/Npoi.Mapper.csproj b/Npoi.Mapper/src/Npoi.Mapper/Npoi.Mapper.csproj index aa5ffa5..7cc3c1e 100644 --- a/Npoi.Mapper/src/Npoi.Mapper/Npoi.Mapper.csproj +++ b/Npoi.Mapper/src/Npoi.Mapper/Npoi.Mapper.csproj @@ -30,10 +30,12 @@ + +