diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 2d35082c..ae3a2e0b 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"mapster.tool": {
- "version": "7.0.3",
+ "version": "7.1.3",
"commands": [
"dotnet-mapster"
]
diff --git a/src/Mapster.Core/Mapster.Core.csproj b/src/Mapster.Core/Mapster.Core.csproj
index b814b85b..089286d7 100644
--- a/src/Mapster.Core/Mapster.Core.csproj
+++ b/src/Mapster.Core/Mapster.Core.csproj
@@ -15,7 +15,7 @@
Mapster.Core.snk
icon.png
https://cloud.githubusercontent.com/assets/5763993/26522718/d16f3e42-4330-11e7-9b78-f8c7402624e7.png
- 1.0.0
+ 1.1.2
8.0
enable
Mapster
diff --git a/src/Mapster.Core/Mapster.Core.csproj.DotSettings b/src/Mapster.Core/Mapster.Core.csproj.DotSettings
index 027fde29..41ce25f7 100644
--- a/src/Mapster.Core/Mapster.Core.csproj.DotSettings
+++ b/src/Mapster.Core/Mapster.Core.csproj.DotSettings
@@ -1,4 +1,5 @@
True
True
- True
\ No newline at end of file
+ True
+ True
\ No newline at end of file
diff --git a/src/Mapster.Core/Register/AdaptAttributeBuilder.cs b/src/Mapster.Core/Register/AdaptAttributeBuilder.cs
new file mode 100644
index 00000000..4e80e247
--- /dev/null
+++ b/src/Mapster.Core/Register/AdaptAttributeBuilder.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Mapster
+{
+ public class AdaptAttributeBuilder
+ {
+ public BaseAdaptAttribute Attribute { get; }
+ public Dictionary> TypeSettings { get; } = new Dictionary>();
+ public List> AlterTypes { get; } = new List>();
+
+ public AdaptAttributeBuilder(BaseAdaptAttribute attribute)
+ {
+ this.Attribute = attribute;
+ }
+
+ public AdaptAttributeBuilder ForTypes(params Type[] types)
+ {
+ foreach (var type in types)
+ {
+ if (!this.TypeSettings.ContainsKey(type))
+ this.TypeSettings.Add(type, new Dictionary());
+ }
+
+ return this;
+ }
+
+ public AdaptAttributeBuilder ForAllTypesInNamespace(Assembly assembly, string @namespace)
+ {
+ foreach (var type in assembly.GetTypes())
+ {
+ if (type.Namespace == @namespace && !this.TypeSettings.ContainsKey(type))
+ this.TypeSettings.Add(type, new Dictionary());
+ }
+
+ return this;
+ }
+
+ public AdaptAttributeBuilder ForType(Action>? propertyConfig = null)
+ {
+ if (!this.TypeSettings.TryGetValue(typeof(T), out var settings))
+ {
+ settings = new Dictionary();
+ this.TypeSettings.Add(typeof(T), settings);
+ }
+
+ propertyConfig?.Invoke(new PropertySettingBuilder(settings));
+ return this;
+ }
+
+ public AdaptAttributeBuilder ExcludeTypes(params Type[] types)
+ {
+ foreach (var type in types)
+ {
+ this.TypeSettings.Remove(type);
+ }
+
+ return this;
+ }
+
+ public AdaptAttributeBuilder ExcludeTypes(Func predicate)
+ {
+ foreach (var type in this.TypeSettings.Keys.ToList())
+ {
+ if (predicate(type))
+ this.TypeSettings.Remove(type);
+ }
+
+ return this;
+ }
+
+ public AdaptAttributeBuilder IgnoreAttributes(params Type[] attributes)
+ {
+ this.Attribute.IgnoreAttributes = attributes;
+ return this;
+ }
+
+ public AdaptAttributeBuilder IgnoreNoAttributes(params Type[] attributes)
+ {
+ this.Attribute.IgnoreNoAttributes = attributes;
+ return this;
+ }
+
+ public AdaptAttributeBuilder IgnoreNamespaces(params string[] namespaces)
+ {
+ this.Attribute.IgnoreNamespaces = namespaces;
+ return this;
+ }
+
+ public AdaptAttributeBuilder IgnoreNullValues(bool value)
+ {
+ this.Attribute.IgnoreNullValues = value;
+ return this;
+ }
+
+ public AdaptAttributeBuilder MapToConstructor(bool value)
+ {
+ this.Attribute.MapToConstructor = value;
+ return this;
+ }
+
+ public AdaptAttributeBuilder MaxDepth(int depth)
+ {
+ this.Attribute.MaxDepth = depth;
+ return this;
+ }
+
+ public AdaptAttributeBuilder PreserveReference(bool value)
+ {
+ this.Attribute.PreserveReference = value;
+ return this;
+ }
+
+ public AdaptAttributeBuilder ShallowCopyForSameType(bool value)
+ {
+ this.Attribute.ShallowCopyForSameType = value;
+ return this;
+ }
+
+ public AdaptAttributeBuilder AlterType()
+ {
+ this.AlterTypes.Add(type => type == typeof(TFrom) ? typeof(TTo) : null);
+ return this;
+ }
+
+ public AdaptAttributeBuilder AlterType(Func predicate, Type toType)
+ {
+ this.AlterTypes.Add(type => predicate(type) ? toType : null);
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Mapster.Core/Register/CodeGenerationConfig.cs b/src/Mapster.Core/Register/CodeGenerationConfig.cs
new file mode 100644
index 00000000..def1f462
--- /dev/null
+++ b/src/Mapster.Core/Register/CodeGenerationConfig.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+
+namespace Mapster
+{
+ public class CodeGenerationConfig
+ {
+ public List AdaptAttributeBuilders { get; } = new List();
+ public List GenerateMapperAttributeBuilders { get; } = new List();
+ public AdaptAttributeBuilder Default { get; } = new AdaptAttributeBuilder(new AdaptFromAttribute("void"));
+
+ public AdaptAttributeBuilder AdaptTo(string name, MapType? mapType = null)
+ {
+ var builder = new AdaptAttributeBuilder(new AdaptToAttribute(name) {MapType = mapType ?? 0});
+ AdaptAttributeBuilders.Add(builder);
+ return builder;
+ }
+
+ public AdaptAttributeBuilder AdaptFrom(string name, MapType? mapType = null)
+ {
+ var builder = new AdaptAttributeBuilder(new AdaptFromAttribute(name) {MapType = mapType ?? 0});
+ AdaptAttributeBuilders.Add(builder);
+ return builder;
+ }
+
+ public AdaptAttributeBuilder AdaptTwoWays(string name, MapType? mapType = null)
+ {
+ var builder = new AdaptAttributeBuilder(new AdaptTwoWaysAttribute(name) {MapType = mapType ?? 0});
+ AdaptAttributeBuilders.Add(builder);
+ return builder;
+ }
+
+ public GenerateMapperAttributeBuilder GenerateMapper(string name)
+ {
+ var builder = new GenerateMapperAttributeBuilder(new GenerateMapperAttribute());
+ GenerateMapperAttributeBuilders.Add(builder);
+ return builder;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Mapster.Core/Register/GenerateMapperAttributeBuilder.cs b/src/Mapster.Core/Register/GenerateMapperAttributeBuilder.cs
new file mode 100644
index 00000000..1576da9f
--- /dev/null
+++ b/src/Mapster.Core/Register/GenerateMapperAttributeBuilder.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Mapster
+{
+ public class GenerateMapperAttributeBuilder
+ {
+ public GenerateMapperAttribute Attribute { get; }
+ public HashSet Types { get; } = new HashSet();
+
+ public GenerateMapperAttributeBuilder(GenerateMapperAttribute attribute)
+ {
+ this.Attribute = attribute;
+ }
+
+ public GenerateMapperAttributeBuilder ForTypes(params Type[] types)
+ {
+ this.Types.UnionWith(types);
+ return this;
+ }
+
+ public GenerateMapperAttributeBuilder ForAllTypesInNamespace(Assembly assembly, string @namespace)
+ {
+ this.Types.UnionWith(assembly.GetTypes().Where(it => it.Namespace == @namespace));
+ return this;
+ }
+
+ public GenerateMapperAttributeBuilder ForType()
+ {
+ this.Types.Add(typeof(T));
+ return this;
+ }
+
+ public GenerateMapperAttributeBuilder ExcludeTypes(params Type[] types)
+ {
+ this.Types.ExceptWith(types);
+ return this;
+ }
+
+ public GenerateMapperAttributeBuilder ExcludeTypes(Predicate predicate)
+ {
+ this.Types.RemoveWhere(predicate);
+ return this;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Mapster.Core/Register/ICodeGenerationRegister.cs b/src/Mapster.Core/Register/ICodeGenerationRegister.cs
new file mode 100644
index 00000000..f372c19d
--- /dev/null
+++ b/src/Mapster.Core/Register/ICodeGenerationRegister.cs
@@ -0,0 +1,7 @@
+namespace Mapster
+{
+ public interface ICodeGenerationRegister
+ {
+ void Register(CodeGenerationConfig config);
+ }
+}
diff --git a/src/Mapster.Core/Register/PropertySetting.cs b/src/Mapster.Core/Register/PropertySetting.cs
new file mode 100644
index 00000000..b4c66bf0
--- /dev/null
+++ b/src/Mapster.Core/Register/PropertySetting.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Linq.Expressions;
+
+namespace Mapster
+{
+ public class PropertySetting
+ {
+ public bool Ignore { get; set; }
+ public string? TargetPropertyName { get; set; }
+ public Type? TargetPropertyType { get; set; }
+ public LambdaExpression? MapFunc { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Mapster.Core/Register/PropertySettingBuilder.cs b/src/Mapster.Core/Register/PropertySettingBuilder.cs
new file mode 100644
index 00000000..03d10634
--- /dev/null
+++ b/src/Mapster.Core/Register/PropertySettingBuilder.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using Mapster.Utils;
+
+namespace Mapster
+{
+ public class PropertySettingBuilder
+ {
+ public Dictionary Settings { get; }
+ public PropertySettingBuilder(Dictionary settings)
+ {
+ this.Settings = settings;
+ }
+
+ private PropertySetting ForProperty(string name)
+ {
+ if (!this.Settings.TryGetValue(name, out var setting))
+ {
+ setting = new PropertySetting();
+ this.Settings.Add(name, setting);
+ }
+ return setting;
+ }
+
+ public PropertySettingBuilder Ignore(Expression> member)
+ {
+ var setting = ForProperty(member.GetMemberName());
+ setting.Ignore = true;
+ return this;
+ }
+
+ public PropertySettingBuilder Map(Expression> member, string targetPropertyName)
+ {
+ var setting = ForProperty(member.GetMemberName());
+ setting.TargetPropertyName = targetPropertyName;
+ return this;
+ }
+
+ public PropertySettingBuilder Map(Expression> member, Type targetPropertyType, string? targetPropertyName = null)
+ {
+ var setting = ForProperty(member.GetMemberName());
+ setting.TargetPropertyType = targetPropertyType;
+ setting.TargetPropertyName = targetPropertyName;
+ return this;
+ }
+
+ public PropertySettingBuilder Map(Expression> member, Expression> mapFunc, string? targetPropertyName = null)
+ {
+ var setting = ForProperty(member.GetMemberName());
+ setting.MapFunc = mapFunc;
+ setting.TargetPropertyName = targetPropertyName;
+ return this;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Mapster.Core/Utils/Extensions.cs b/src/Mapster.Core/Utils/Extensions.cs
index d33f1987..b8c8dc5a 100644
--- a/src/Mapster.Core/Utils/Extensions.cs
+++ b/src/Mapster.Core/Utils/Extensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq.Expressions;
namespace Mapster.Utils
{
@@ -9,7 +10,22 @@ public static Type GetTypeInfo(this Type type)
{
return type;
}
-
#endif
+
+ public static string GetMemberName(this LambdaExpression lambda)
+ {
+ string? prop = null;
+ var expr = lambda.Body;
+ if (expr.NodeType == ExpressionType.MemberAccess)
+ {
+ var memEx = (MemberExpression)expr;
+ prop = memEx.Member.Name;
+ expr = (Expression?)memEx.Expression;
+ }
+ if (prop == null || expr?.NodeType != ExpressionType.Parameter)
+ throw new ArgumentException("Allow only first level member access (eg. obj => obj.Name)", nameof(lambda));
+ return prop;
+ }
+
}
}
diff --git a/src/Mapster.Tool/Extensions.cs b/src/Mapster.Tool/Extensions.cs
index 6b898306..0deba21a 100644
--- a/src/Mapster.Tool/Extensions.cs
+++ b/src/Mapster.Tool/Extensions.cs
@@ -127,6 +127,36 @@ public static Attribute[] SafeGetCustomAttributes(this MemberInfo element)
}
}
+ public static IEnumerable GetAdaptAttributeBuilders(this Type type, CodeGenerationConfig config)
+ {
+ foreach (var attribute in type.SafeGetCustomAttributes())
+ {
+ if (attribute is BaseAdaptAttribute adaptAttr)
+ yield return new AdaptAttributeBuilder(adaptAttr);
+ }
+
+ foreach (var builder in config.AdaptAttributeBuilders)
+ {
+ if (builder.TypeSettings.ContainsKey(type))
+ yield return builder;
+ }
+ }
+
+ public static IEnumerable GetGenerateMapperAttributes(this Type type, CodeGenerationConfig config)
+ {
+ foreach (var attribute in type.SafeGetCustomAttributes())
+ {
+ if (attribute is GenerateMapperAttribute genMapperAttr)
+ yield return genMapperAttr;
+ }
+
+ foreach (var builder in config.GenerateMapperAttributeBuilders)
+ {
+ if (builder.Types.Contains(type))
+ yield return builder.Attribute;
+ }
+ }
+
public static bool IsNullable(this Type type)
{
return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
@@ -141,5 +171,18 @@ public static Type MakeNullable(this Type type)
{
return type.CanBeNull() ? type : typeof(Nullable<>).MakeGenericType(type);
}
+
+ public static void Scan(this CodeGenerationConfig config, Assembly assembly)
+ {
+ var registers = assembly.GetTypes()
+ .Where(x => typeof(ICodeGenerationRegister).GetTypeInfo().IsAssignableFrom(x.GetTypeInfo()) &&
+ x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract)
+ .Select(type => (ICodeGenerationRegister) Activator.CreateInstance(type)!);
+
+ foreach (var register in registers)
+ {
+ register.Register(config);
+ }
+ }
}
}
diff --git a/src/Mapster.Tool/Mapster.Tool.csproj b/src/Mapster.Tool/Mapster.Tool.csproj
index 48cbc119..1030cdac 100644
--- a/src/Mapster.Tool/Mapster.Tool.csproj
+++ b/src/Mapster.Tool/Mapster.Tool.csproj
@@ -15,7 +15,7 @@
true
Mapster.Tool.snk
true
- 7.0.4
+ 7.1.3
MIT
Copyright (c) 2020 Chaowlert Chaisrichalermpol
icon.png
diff --git a/src/Mapster.Tool/Program.cs b/src/Mapster.Tool/Program.cs
index 94c4f875..7fe159d6 100644
--- a/src/Mapster.Tool/Program.cs
+++ b/src/Mapster.Tool/Program.cs
@@ -117,26 +117,28 @@ private static void GenerateModels(ModelOptions opt)
{
using var dynamicContext = new AssemblyResolver(Path.GetFullPath(opt.Assembly));
var assembly = dynamicContext.Assembly;
+ var codeGenConfig = new CodeGenerationConfig();
+ codeGenConfig.Scan(assembly);
foreach (var type in assembly.GetTypes())
{
- var attrs = type.SafeGetCustomAttributes()
- .OfType()
- .Where(it => !string.IsNullOrEmpty(it.Name) && it.Name != "[name]")
+ var builders = type.GetAdaptAttributeBuilders(codeGenConfig)
+ .Where(it => !string.IsNullOrEmpty(it.Attribute.Name) && it.Attribute.Name != "[name]")
.ToList();
- if (attrs.Count == 0)
+ if (builders.Count == 0)
continue;
Console.WriteLine($"Processing: {type.FullName}");
- foreach (var attr in attrs)
+ foreach (var builder in builders)
{
- CreateModel(opt, type, attr);
+ CreateModel(opt, type, builder);
}
}
}
- private static void CreateModel(ModelOptions opt, Type type, BaseAdaptAttribute attr)
+ private static void CreateModel(ModelOptions opt, Type type, AdaptAttributeBuilder builder)
{
+ var attr = builder.Attribute;
var definitions = new TypeDefinitions
{
Namespace = opt.Namespace ?? type.Namespace,
@@ -176,17 +178,25 @@ private static void CreateModel(ModelOptions opt, Type type, BaseAdaptAttribute
properties = properties.Where(it => getPropType(it).Namespace?.StartsWith(ns) != true);
}
}
+
+ var propSettings = builder.TypeSettings.GetValueOrDefault(type);
var isReadOnly = isAdaptTo && attr.MapToConstructor;
var isNullable = !isAdaptTo && attr.IgnoreNullValues;
foreach (var member in properties)
{
+ var setting = propSettings?.GetValueOrDefault(member.Name);
+ if (setting?.Ignore == true)
+ continue;
+
var adaptMember = member.GetCustomAttribute();
if (!isTwoWays && adaptMember?.Side != null && adaptMember.Side != side)
adaptMember = null;
- var propType = GetPropertyType(member, getPropType(member), attr.GetType(), opt.Namespace);
+ var propType = setting?.MapFunc?.ReturnType ??
+ setting?.TargetPropertyType ??
+ GetPropertyType(member, getPropType(member), attr.GetType(), opt.Namespace, builder);
translator.Properties.Add(new PropertyDefinitions
{
- Name = adaptMember?.Name ?? member.Name,
+ Name = setting?.TargetPropertyName ?? adaptMember?.Name ?? member.Name,
Type = isNullable ? propType.MakeNullable() : propType,
IsReadOnly = isReadOnly
});
@@ -203,7 +213,7 @@ static Type getPropType(MemberInfo mem)
}
private static readonly Dictionary _mockTypes = new Dictionary();
- private static Type GetPropertyType(MemberInfo member, Type propType, Type attrType, string? ns)
+ private static Type GetPropertyType(MemberInfo member, Type propType, Type attrType, string? ns, AdaptAttributeBuilder builder)
{
var navAttr = member.SafeGetCustomAttributes()
.OfType()
@@ -214,20 +224,29 @@ private static Type GetPropertyType(MemberInfo member, Type propType, Type attrT
if (propType.IsCollection() && propType.IsCollectionCompatible() && propType.IsGenericType && propType.GetGenericArguments().Length == 1)
{
var elementType = propType.GetGenericArguments()[0];
- var newType = GetPropertyType(member, elementType, attrType, ns);
+ var newType = GetPropertyType(member, elementType, attrType, ns, builder);
if (elementType == newType)
return propType;
var generic = propType.GetGenericTypeDefinition();
return generic.MakeGenericType(newType);
}
+ var alterType = builder.AlterTypes
+ .Select(fn => fn(propType))
+ .FirstOrDefault(it => it != null);
+ if (alterType != null)
+ return alterType;
+
var propTypeAttrs = propType.SafeGetCustomAttributes();
navAttr = propTypeAttrs.OfType()
.FirstOrDefault(it => it.ForAttributes?.Contains(attrType) != false);
if (navAttr != null)
return navAttr.Type;
- var adaptAttr = propTypeAttrs.OfType()
- .FirstOrDefault(it => it.GetType() == attrType);
+
+ var adaptAttr = builder.TypeSettings.ContainsKey(propType)
+ ? (BaseAdaptAttribute?) builder.Attribute
+ : propTypeAttrs.OfType()
+ .FirstOrDefault(it => it.GetType() == attrType);
if (adaptAttr == null)
return propType;
if (adaptAttr.Type != null)
@@ -242,6 +261,57 @@ private static Type GetPropertyType(MemberInfo member, Type propType, Type attrT
return mockType;
}
+ private static Type? GetFromType(Type type, BaseAdaptAttribute attr, Type[] types)
+ {
+ if (!(attr is AdaptFromAttribute) && !(attr is AdaptTwoWaysAttribute))
+ return null;
+
+ var fromType = attr.Type;
+ if (fromType == null && attr.Name != null)
+ {
+ var name = attr.Name.Replace("[name]", type.Name);
+ fromType = Array.Find(types, it => it.Name == name);
+ }
+
+ return fromType;
+ }
+
+ private static Type? GetToType(Type type, BaseAdaptAttribute attr, Type[] types)
+ {
+ if (!(attr is AdaptToAttribute))
+ return null;
+
+ var toType = attr.Type;
+ if (toType == null && attr.Name != null)
+ {
+ var name = attr.Name.Replace("[name]", type.Name);
+ toType = Array.Find(types, it => it.Name == name);
+ }
+
+ return toType;
+ }
+
+ private static void ApplySettings(TypeAdapterSetter setter, BaseAdaptAttribute attr, Dictionary settings)
+ {
+ setter.ApplyAdaptAttribute(attr);
+ foreach (var (name, setting) in settings)
+ {
+ if (setting.MapFunc != null)
+ {
+ setter.Settings.Resolvers.Add(new InvokerModel
+ {
+ DestinationMemberName = setting.TargetPropertyName ?? name,
+ SourceMemberName = name,
+ Invoker = setting.MapFunc,
+ });
+ }
+ else if (setting.TargetPropertyName != null)
+ {
+ setter.Map(setting.TargetPropertyName, name);
+ }
+ }
+ }
+
private static void GenerateExtensions(ExtensionOptions opt)
{
using var dynamicContext = new AssemblyResolver(Path.GetFullPath(opt.Assembly));
@@ -249,13 +319,32 @@ private static void GenerateExtensions(ExtensionOptions opt)
var config = TypeAdapterConfig.GlobalSettings;
config.SelfContainedCodeGeneration = true;
config.Scan(assembly);
+ var codeGenConfig = new CodeGenerationConfig();
+ codeGenConfig.Scan(assembly);
var types = assembly.GetTypes();
+ var configDict = new Dictionary();
+ foreach (var builder in codeGenConfig.AdaptAttributeBuilders)
+ {
+ var attr = builder.Attribute;
+ var cloned = config.Clone();
+ foreach (var (type, settings) in builder.TypeSettings)
+ {
+ var fromType = GetFromType(type, attr, types);
+ if (fromType != null)
+ ApplySettings(cloned.ForType(fromType, type), attr, settings);
+
+ var toType = GetToType(type, attr, types);
+ if (toType != null)
+ ApplySettings(cloned.ForType(type, toType), attr, settings);
+ }
+
+ configDict[attr] = cloned;
+ }
+
foreach (var type in types)
{
- var attrs = type.SafeGetCustomAttributes();
- var mapperAttr = attrs.OfType()
- .FirstOrDefault();
+ var mapperAttr = type.GetGenerateMapperAttributes(codeGenConfig).FirstOrDefault();
var ruleMaps = config.RuleMap
.Where(it => it.Key.Source == type &&
it.Value.Settings.GenerateMapper is MapType)
@@ -265,11 +354,10 @@ private static void GenerateExtensions(ExtensionOptions opt)
mapperAttr ??= new GenerateMapperAttribute();
var set = mapperAttr.ForAttributes?.ToHashSet();
- var adaptAttrs = attrs
- .OfType()
+ var builders = type.GetAdaptAttributeBuilders(codeGenConfig)
.Where(it => set?.Contains(it.GetType()) != false)
.ToList();
- if (adaptAttrs.Count == 0 && ruleMaps.Count == 0)
+ if (builders.Count == 0 && ruleMaps.Count == 0)
continue;
Console.WriteLine($"Processing: {type.FullName}");
@@ -284,43 +372,26 @@ private static void GenerateExtensions(ExtensionOptions opt)
};
var translator = new ExpressionTranslator(definitions);
- foreach (var attr in adaptAttrs)
+ foreach (var builder in builders)
{
- if (attr is AdaptFromAttribute || attr is AdaptTwoWaysAttribute)
+ var attr = builder.Attribute;
+ var cloned = configDict.GetValueOrDefault(attr) ?? config;
+ var fromType = GetFromType(type, attr, types);
+ if (fromType != null)
{
- var fromType = attr.Type;
- if (fromType == null && attr.Name != null)
- {
- var name = attr.Name.Replace("[name]", type.Name);
- fromType = Array.Find(types, it => it.Name == name);
- }
-
- if (fromType == null)
- continue;
-
var tuple = new TypeTuple(fromType, type);
var mapType = attr.MapType == 0 ? MapType.Map | MapType.MapToTarget : attr.MapType;
- GenerateExtensionMethods(mapType, config, tuple, translator, type, mapperAttr.IsHelperClass);
+ GenerateExtensionMethods(mapType, cloned, tuple, translator, type, mapperAttr.IsHelperClass);
}
- if (attr is AdaptToAttribute)
+ var toType = GetToType(type, attr, types);
+ if (toType != null && (!(attr is AdaptTwoWaysAttribute) || type != toType))
{
- var toType = attr.Type;
- if (toType == null && attr.Name != null)
- {
- var name = attr.Name.Replace("[name]", type.Name);
- toType = Array.Find(types, it => it.Name == name);
- }
-
- if (toType == null)
- continue;
-
- if (attr is AdaptTwoWaysAttribute && type == toType)
- continue;
-
var tuple = new TypeTuple(type, toType);
- var mapType = attr.MapType == 0 ? MapType.Map | MapType.MapToTarget | MapType.Projection : attr.MapType;
- GenerateExtensionMethods(mapType, config, tuple, translator, type, mapperAttr.IsHelperClass);
+ var mapType = attr.MapType == 0
+ ? MapType.Map | MapType.MapToTarget | MapType.Projection
+ : attr.MapType;
+ GenerateExtensionMethods(mapType, cloned, tuple, translator, type, mapperAttr.IsHelperClass);
}
}
diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs
index 8d0dce69..b4684c7e 100644
--- a/src/Mapster/Adapters/ClassAdapter.cs
+++ b/src/Mapster/Adapters/ClassAdapter.cs
@@ -112,12 +112,23 @@ protected override Expression CreateBlockExpression(Expression source, Expressio
var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member, destMember);
if (!member.UseDestinationValue)
{
- adapt = member.DestinationMember.SetExpression(destination, adapt);
if (arg.Settings.IgnoreNullValues == true && member.Getter.CanBeNull())
{
+ if (adapt is ConditionalExpression condEx)
+ {
+ if (condEx.Test is BinaryExpression {NodeType: ExpressionType.Equal} binEx &&
+ binEx.Left == member.Getter &&
+ binEx.Right is ConstantExpression {Value: null})
+ adapt = condEx.IfFalse;
+ }
+ adapt = member.DestinationMember.SetExpression(destination, adapt);
var condition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type));
adapt = Expression.IfThen(condition, adapt);
}
+ else
+ {
+ adapt = member.DestinationMember.SetExpression(destination, adapt);
+ }
}
else if (!adapt.IsComplex())
continue;
diff --git a/src/Mapster/Mapster.csproj b/src/Mapster/Mapster.csproj
index 0f0c8991..0f4fcf2f 100644
--- a/src/Mapster/Mapster.csproj
+++ b/src/Mapster/Mapster.csproj
@@ -20,7 +20,7 @@
1.6.1
True
Mapster
- 7.0.1
+ 7.1.3
8.0
enable
1701;1702;8618
diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs
index 29bdb784..e9c06547 100644
--- a/src/Mapster/TypeAdapterConfig.cs
+++ b/src/Mapster/TypeAdapterConfig.cs
@@ -516,32 +516,7 @@ private TypeAdapterSettings CreateSettings(BaseAdaptAttribute attr)
{
var settings = new TypeAdapterSettings();
var setter = new TypeAdapterSetter(settings, this);
- if (attr.IgnoreAttributes != null)
- setter.IgnoreAttribute(attr.IgnoreAttributes);
- if (attr.IgnoreNoAttributes != null)
- {
- setter.IgnoreMember((member, _) => !member.GetCustomAttributesData()
- .Select(it => it.GetAttributeType())
- .Intersect(attr.IgnoreNoAttributes)
- .Any());
- }
- if (attr.IgnoreNamespaces != null)
- {
- foreach (var ns in attr.IgnoreNamespaces)
- {
- setter.IgnoreMember((member, _) => member.Type.Namespace?.StartsWith(ns) == true);
- }
- }
- if (attr.IgnoreNullValues)
- setter.IgnoreNullValues(attr.IgnoreNullValues);
- if (attr.MapToConstructor)
- setter.MapToConstructor(attr.MapToConstructor);
- if (attr.MaxDepth > 0)
- setter.MaxDepth(attr.MaxDepth);
- if (attr.PreserveReference)
- setter.PreserveReference(attr.PreserveReference);
- if (attr.ShallowCopyForSameType)
- setter.ShallowCopyForSameType(attr.ShallowCopyForSameType);
+ setter.ApplyAdaptAttribute(attr);
return settings;
}
diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs
index bbb696ba..8eb0b4cd 100644
--- a/src/Mapster/TypeAdapterSetter.cs
+++ b/src/Mapster/TypeAdapterSetter.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Mapster.Adapters;
@@ -272,6 +273,36 @@ internal static TSetter Include(this TSetter setter, Type sourceType, T
return setter;
}
+ public static TSetter ApplyAdaptAttribute(this TSetter setter, BaseAdaptAttribute attr) where TSetter : TypeAdapterSetter
+ {
+ if (attr.IgnoreAttributes != null)
+ setter.IgnoreAttribute(attr.IgnoreAttributes);
+ if (attr.IgnoreNoAttributes != null)
+ {
+ setter.IgnoreMember((member, _) => !member.GetCustomAttributesData()
+ .Select(it => it.GetAttributeType())
+ .Intersect(attr.IgnoreNoAttributes)
+ .Any());
+ }
+ if (attr.IgnoreNamespaces != null)
+ {
+ foreach (var ns in attr.IgnoreNamespaces)
+ {
+ setter.IgnoreMember((member, _) => member.Type.Namespace?.StartsWith(ns) == true);
+ }
+ }
+ if (attr.IgnoreNullValues)
+ setter.IgnoreNullValues(attr.IgnoreNullValues);
+ if (attr.MapToConstructor)
+ setter.MapToConstructor(attr.MapToConstructor);
+ if (attr.MaxDepth > 0)
+ setter.MaxDepth(attr.MaxDepth);
+ if (attr.PreserveReference)
+ setter.PreserveReference(attr.PreserveReference);
+ if (attr.ShallowCopyForSameType)
+ setter.ShallowCopyForSameType(attr.ShallowCopyForSameType);
+ return setter;
+ }
}
public class TypeAdapterSetter : TypeAdapterSetter
diff --git a/src/Sample.CodeGen/Attributes/DtoPropertyTypeAttribute.cs b/src/Sample.CodeGen/Attributes/DtoPropertyTypeAttribute.cs
deleted file mode 100644
index 1c976cf7..00000000
--- a/src/Sample.CodeGen/Attributes/DtoPropertyTypeAttribute.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using Mapster;
-
-namespace Sample.CodeGen.Attributes
-{
- public sealed class DtoPropertyTypeAttribute : PropertyTypeAttribute
- {
- public DtoPropertyTypeAttribute(Type type) : base(type)
- {
- ForAttributes = new[] {typeof(GenerateDtoAttribute)};
- }
- }
-}
diff --git a/src/Sample.CodeGen/Attributes/GenerateAddAttribute.cs b/src/Sample.CodeGen/Attributes/GenerateAddAttribute.cs
deleted file mode 100644
index 69ea28a3..00000000
--- a/src/Sample.CodeGen/Attributes/GenerateAddAttribute.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using Mapster;
-using Newtonsoft.Json;
-
-namespace Sample.CodeGen.Attributes
-{
- public sealed class GenerateAddAttribute : AdaptFromAttribute
- {
- public GenerateAddAttribute() : base("[name]Add")
- {
- Initialize();
- }
-
- public GenerateAddAttribute(Type type) : base(type)
- {
- Initialize();
- }
-
- private void Initialize()
- {
- IgnoreAttributes = new[]
- {
- typeof(NoModifyAttribute),
- typeof(JsonIgnoreAttribute),
- };
- MapType = MapType.Map;
- ShallowCopyForSameType = true;
- }
- }
-}
diff --git a/src/Sample.CodeGen/Attributes/GenerateDtoAttribute.cs b/src/Sample.CodeGen/Attributes/GenerateDtoAttribute.cs
deleted file mode 100644
index e4cc1828..00000000
--- a/src/Sample.CodeGen/Attributes/GenerateDtoAttribute.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using Mapster;
-using Newtonsoft.Json;
-
-namespace Sample.CodeGen.Attributes
-{
- public sealed class GenerateDtoAttribute : AdaptToAttribute
- {
- public GenerateDtoAttribute() : base("[name]Dto")
- {
- Initialize();
- }
-
- public GenerateDtoAttribute(Type type) : base(type)
- {
- Initialize();
- }
-
- private void Initialize()
- {
- IgnoreAttributes = new[] {typeof(JsonIgnoreAttribute)};
- ShallowCopyForSameType = true;
- }
- }
-}
diff --git a/src/Sample.CodeGen/Attributes/GenerateMergeAttribute.cs b/src/Sample.CodeGen/Attributes/GenerateMergeAttribute.cs
deleted file mode 100644
index 3d614926..00000000
--- a/src/Sample.CodeGen/Attributes/GenerateMergeAttribute.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using Mapster;
-using Newtonsoft.Json;
-
-namespace Sample.CodeGen.Attributes
-{
- public sealed class GenerateMergeAttribute : AdaptFromAttribute
- {
- public GenerateMergeAttribute() : base("[name]Merge")
- {
- Initialize();
- }
-
- public GenerateMergeAttribute(Type type) : base(type)
- {
- Initialize();
- }
-
- private void Initialize()
- {
- IgnoreAttributes = new[]
- {
- typeof(KeyAttribute),
- typeof(NoModifyAttribute),
- typeof(JsonIgnoreAttribute),
- };
- MapType = MapType.MapToTarget;
- ShallowCopyForSameType = true;
- IgnoreNullValues = true;
- }
- }
-}
diff --git a/src/Sample.CodeGen/Attributes/GenerateUpdateAttribute.cs b/src/Sample.CodeGen/Attributes/GenerateUpdateAttribute.cs
deleted file mode 100644
index e7bd77f9..00000000
--- a/src/Sample.CodeGen/Attributes/GenerateUpdateAttribute.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using Mapster;
-using Newtonsoft.Json;
-
-namespace Sample.CodeGen.Attributes
-{
- public sealed class GenerateUpdateAttribute : AdaptFromAttribute
- {
- public GenerateUpdateAttribute() : base("[name]Update")
- {
- Initialize();
- }
-
- public GenerateUpdateAttribute(Type type) : base(type)
- {
- Initialize();
- }
-
- private void Initialize()
- {
- IgnoreAttributes = new[]
- {
- typeof(KeyAttribute),
- typeof(NoModifyAttribute),
- typeof(JsonIgnoreAttribute),
- };
- MapType = MapType.MapToTarget;
- ShallowCopyForSameType = true;
- }
- }
-}
diff --git a/src/Sample.CodeGen/Attributes/NoModifyAttribute.cs b/src/Sample.CodeGen/Attributes/NoModifyAttribute.cs
deleted file mode 100644
index 083513de..00000000
--- a/src/Sample.CodeGen/Attributes/NoModifyAttribute.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace Sample.CodeGen.Attributes
-{
- [AttributeUsage(AttributeTargets.Property)]
- public sealed class NoModifyAttribute : Attribute
- {
- }
-}
diff --git a/src/Sample.CodeGen/Domains/Course.cs b/src/Sample.CodeGen/Domains/Course.cs
index 22c6f83c..997e5894 100644
--- a/src/Sample.CodeGen/Domains/Course.cs
+++ b/src/Sample.CodeGen/Domains/Course.cs
@@ -1,12 +1,9 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
-using Mapster;
-using Sample.CodeGen.Attributes;
namespace Sample.CodeGen.Domains
{
- [GenerateDto, GenerateAdd, GenerateUpdate, GenerateMerge, GenerateMapper]
public class Course
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
diff --git a/src/Sample.CodeGen/Domains/Enrollment.cs b/src/Sample.CodeGen/Domains/Enrollment.cs
index a0f4eb51..8a7998a6 100644
--- a/src/Sample.CodeGen/Domains/Enrollment.cs
+++ b/src/Sample.CodeGen/Domains/Enrollment.cs
@@ -1,28 +1,17 @@
-using Mapster;
-using Newtonsoft.Json;
-using Sample.CodeGen.Attributes;
-
-namespace Sample.CodeGen.Domains
+namespace Sample.CodeGen.Domains
{
public enum Grade
{
A, B, C, D, F
}
- [GenerateDto, GenerateAdd, GenerateUpdate, GenerateMerge]
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
-
- [PropertyType(typeof(string))]
public Grade? Grade { get; set; }
-
- [JsonIgnore]
public Course Course { get; set; }
-
- [NoModify]
public Student Student { get; set; }
}
}
diff --git a/src/Sample.CodeGen/Domains/Student.cs b/src/Sample.CodeGen/Domains/Student.cs
index 331ce0c2..a40b65cf 100644
--- a/src/Sample.CodeGen/Domains/Student.cs
+++ b/src/Sample.CodeGen/Domains/Student.cs
@@ -1,14 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using Mapster;
-using Newtonsoft.Json;
-using Sample.CodeGen.Attributes;
-using Sample.CodeGen.Models;
namespace Sample.CodeGen.Domains
{
- [GenerateDto, GenerateAdd, GenerateUpdate, GenerateMerge, GenerateMapper, DtoPropertyType(typeof(Person))]
public class Student
{
[Key]
@@ -16,8 +11,6 @@ public class Student
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
-
- [JsonIgnore]
public ICollection Enrollments { get; set; }
}
}
diff --git a/src/Sample.CodeGen/MappingRegister.cs b/src/Sample.CodeGen/MappingRegister.cs
index 0841c4a5..e2c4e825 100644
--- a/src/Sample.CodeGen/MappingRegister.cs
+++ b/src/Sample.CodeGen/MappingRegister.cs
@@ -1,14 +1,60 @@
-using Mapster;
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Reflection;
+using Mapster;
+using Sample.CodeGen.Domains;
using Sample.CodeGen.Models;
namespace Sample.CodeGen
{
- public class MappingRegister : IRegister
+ public class MappingRegister : ICodeGenerationRegister
{
- public void Register(TypeAdapterConfig config)
+ public void Register(CodeGenerationConfig config)
{
- config.ForType()
- .GenerateMapper(MapType.Map | MapType.MapToTarget);
+ config.AdaptTo("[name]Dto", MapType.Map | MapType.MapToTarget | MapType.Projection)
+ .ApplyDefaultRule()
+ .AlterType();
+
+ config.AdaptFrom("[name]Add", MapType.Map)
+ .ApplyDefaultRule()
+ .IgnoreNoModifyProperties();
+
+ config.AdaptFrom("[name]Update", MapType.MapToTarget)
+ .ApplyDefaultRule()
+ .IgnoreAttributes(typeof(KeyAttribute))
+ .IgnoreNoModifyProperties();
+
+ config.AdaptFrom("[name]Merge", MapType.MapToTarget)
+ .ApplyDefaultRule()
+ .IgnoreAttributes(typeof(KeyAttribute))
+ .IgnoreNoModifyProperties()
+ .IgnoreNullValues(true);
+
+ config.GenerateMapper("[name]Mapper")
+ .ForType()
+ .ForType();
+ }
+
+ }
+
+ static class RegisterExtensions
+ {
+ public static AdaptAttributeBuilder ApplyDefaultRule(this AdaptAttributeBuilder builder)
+ {
+ return builder
+ .ForAllTypesInNamespace(Assembly.GetExecutingAssembly(), "Sample.CodeGen.Domains")
+ .ExcludeTypes(typeof(SchoolContext))
+ .ExcludeTypes(type => type.IsEnum)
+ .AlterType(type => type.IsEnum || Nullable.GetUnderlyingType(type)?.IsEnum == true, typeof(string))
+ .ShallowCopyForSameType(true)
+ .ForType(cfg => cfg.Ignore(it => it.Course))
+ .ForType(cfg => cfg.Ignore(it => it.Enrollments));
+ }
+
+ public static AdaptAttributeBuilder IgnoreNoModifyProperties(this AdaptAttributeBuilder builder)
+ {
+ return builder
+ .ForType(cfg => cfg.Ignore(it => it.Student));
}
}
}
diff --git a/src/Sample.CodeGen/Models/CourseMapper.g.cs b/src/Sample.CodeGen/Models/CourseMapper.g.cs
index dbeabc24..517f4f57 100644
--- a/src/Sample.CodeGen/Models/CourseMapper.g.cs
+++ b/src/Sample.CodeGen/Models/CourseMapper.g.cs
@@ -93,7 +93,7 @@ public static Course AdaptTo(this CourseMerge p15, Course p16)
if (p15.Credits != null)
{
- result.Credits = p15.Credits == null ? 0 : (int)p15.Credits;
+ result.Credits = (int)p15.Credits;
}
if (p15.Enrollments != null)
@@ -229,13 +229,38 @@ private static ICollection funcMain5(ICollection p1
while (enumerator.MoveNext())
{
EnrollmentMerge item = enumerator.Current;
- result.Add(item == null ? null : new Enrollment()
- {
- EnrollmentID = item.EnrollmentID == null ? 0 : (int)item.EnrollmentID,
- CourseID = item.CourseID == null ? 0 : (int)item.CourseID,
- StudentID = item.StudentID == null ? 0 : (int)item.StudentID,
- Grade = item.Grade == null ? null : (Grade?)Enum.Parse(item.Grade)
- });
+ result.Add(funcMain6(item));
+ }
+ return result;
+
+ }
+
+ private static Enrollment funcMain6(EnrollmentMerge p19)
+ {
+ if (p19 == null)
+ {
+ return null;
+ }
+ Enrollment result = new Enrollment();
+
+ if (p19.EnrollmentID != null)
+ {
+ result.EnrollmentID = (int)p19.EnrollmentID;
+ }
+
+ if (p19.CourseID != null)
+ {
+ result.CourseID = (int)p19.CourseID;
+ }
+
+ if (p19.StudentID != null)
+ {
+ result.StudentID = (int)p19.StudentID;
+ }
+
+ if (p19.Grade != null)
+ {
+ result.Grade = (Grade?)Enum.Parse(p19.Grade);
}
return result;
diff --git a/src/Sample.CodeGen/Models/PersonMapper.g.cs b/src/Sample.CodeGen/Models/PersonMapper.g.cs
deleted file mode 100644
index 6b24d4a4..00000000
--- a/src/Sample.CodeGen/Models/PersonMapper.g.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Sample.CodeGen.Models;
-
-namespace Sample.CodeGen.Models
-{
- public static partial class PersonMapper
- {
- public static Person AdaptToPerson(this Person p1)
- {
- return p1 == null ? null : new Person()
- {
- ID = p1.ID,
- LastName = p1.LastName,
- FirstMidName = p1.FirstMidName
- };
- }
- public static Person AdaptTo(this Person p2, Person p3)
- {
- if (p2 == null)
- {
- return null;
- }
- Person result = p3 ?? new Person();
-
- result.ID = p2.ID;
- result.LastName = p2.LastName;
- result.FirstMidName = p2.FirstMidName;
- return result;
-
- }
- }
-}
\ No newline at end of file
diff --git a/src/Sample.CodeGen/Models/StudentMapper.g.cs b/src/Sample.CodeGen/Models/StudentMapper.g.cs
index e134e264..07375b1c 100644
--- a/src/Sample.CodeGen/Models/StudentMapper.g.cs
+++ b/src/Sample.CodeGen/Models/StudentMapper.g.cs
@@ -83,7 +83,7 @@ public static Student AdaptTo(this StudentMerge p8, Student p9)
if (p8.EnrollmentDate != null)
{
- result.EnrollmentDate = p8.EnrollmentDate == null ? default(DateTime) : (DateTime)p8.EnrollmentDate;
+ result.EnrollmentDate = (DateTime)p8.EnrollmentDate;
}
return result;
diff --git a/src/Sample.CodeGen/Program.cs b/src/Sample.CodeGen/Program.cs
index d2ddb64a..9e3a49e7 100644
--- a/src/Sample.CodeGen/Program.cs
+++ b/src/Sample.CodeGen/Program.cs
@@ -4,7 +4,6 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Sample.CodeGen.Domains;
-using Sample.CodeGen.Models;
namespace Sample.CodeGen
{
diff --git a/src/Sample.CodeGen/Sample.CodeGen.csproj b/src/Sample.CodeGen/Sample.CodeGen.csproj
index faacd9eb..ba9848aa 100644
--- a/src/Sample.CodeGen/Sample.CodeGen.csproj
+++ b/src/Sample.CodeGen/Sample.CodeGen.csproj
@@ -6,7 +6,7 @@
-
+