diff --git a/src/Mapster.Tests/WhenMappingWithExplicitInheritance.cs b/src/Mapster.Tests/WhenMappingWithExplicitInheritance.cs index 442ed7e1..a33f8a8d 100644 --- a/src/Mapster.Tests/WhenMappingWithExplicitInheritance.cs +++ b/src/Mapster.Tests/WhenMappingWithExplicitInheritance.cs @@ -130,15 +130,77 @@ public void Invalid_Destination_Cast_Throws_Exception() } + [TestMethod] + public void InheritsLasyLoad__IsWork() + { + TypeAdapterConfig.NewConfig() + .Inherits() + .Compile(); + + TypeAdapterConfig.NewConfig() + .Inherits() + .Ignore(dest => dest.Name) + .Compile(); + + TypeAdapterConfig.NewConfig() + .Map(dest => dest.NumberDto, src => 42) + .Compile(); + + var source = new DerivedPoco + { + Id = new Guid(), + Name = "SourceName" + }; + + var dto = TypeAdapter.Adapt(source); + + dto.Id.ShouldBe(source.Id); + dto.Name.ShouldBe("SourceName"); // Inherits Ignore not work + dto.NumberDto.ShouldBe(0); // Inherits not work + + Setup(); // clean config + + TypeAdapterConfig.NewConfig() + .InheritsLazy() + .Compile(); + + TypeAdapterConfig.NewConfig() + .InheritsLazy() + .Ignore(dest => dest.Name) + .Compile(); + + TypeAdapterConfig.NewConfig() + .Map(dest => dest.NumberDto, src => 42) + .Compile(); + + dto = TypeAdapter.Adapt(source); + + dto.Id.ShouldBe(source.Id); + dto.Name.ShouldBeNull(); // InheritsLazy Ignore is work + dto.NumberDto.ShouldBe(42); // InheritsLazy is work + } + + #region TestMethod Classes - public class SimplePoco + public class RootPoco + { + public int Number { get; set; } + } + + public class RootDto + { + public int NumberDto { get; set; } + } + + + public class SimplePoco: RootPoco { public Guid Id { get; set; } public string Name { get; set; } } - public class SimpleDto + public class SimpleDto : RootDto { public Guid Id { get; set; } public string Name { get; set; } diff --git a/src/Mapster/Models/TypeTuple.cs b/src/Mapster/Models/TypeTuple.cs index e25a71fd..6faa9ec0 100644 --- a/src/Mapster/Models/TypeTuple.cs +++ b/src/Mapster/Models/TypeTuple.cs @@ -40,4 +40,50 @@ public TypeTuple(Type source, Type destination) Destination = destination; } } + + public class InheritsTypeTuple : IEquatable + { + public bool Equals(InheritsTypeTuple other) + { + return Source == other.Source && Destination == other.Destination; + } + + public override bool Equals(object obj) + { + if (!(obj is InheritsTypeTuple)) + return false; + return Equals((InheritsTypeTuple)obj); + } + + public override int GetHashCode() + { + return (Source.GetHashCode() << 16) ^ (Destination.GetHashCode() & 65535); + } + + public static bool operator ==(InheritsTypeTuple left, InheritsTypeTuple right) + { + return left.Equals(right); + } + + public static bool operator !=(InheritsTypeTuple left, InheritsTypeTuple right) + { + return !left.Equals(right); + } + + public Type Source { get; } + public Type Destination { get; } + public bool IsLoading { get; private set; } + + public void IsUploaded() + { + IsLoading = true; + } + + public InheritsTypeTuple(Type source, Type destination) + { + Source = source; + Destination = destination; + IsLoading = false; + } + } } diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index cb8ac881..562628c3 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -317,12 +317,41 @@ public Func GetMapFunction() } internal Delegate GetMapFunction(Type sourceType, Type destinationType) { + LoadInheritedRulesLazy(new TypeTuple(sourceType, destinationType)); + var key = new TypeTuple(sourceType, destinationType); if (!_mapDict.TryGetValue(key, out var del)) del = AddToHash(_mapDict, key, tuple => Compiler(CreateMapExpression(tuple, MapType.Map))); return del; } + private void LoadInheritedRulesLazy(TypeTuple types) + { + if (RuleMap.TryGetValue(types, out var rule)) + { + if (rule.Settings.InheritsTypeTuples.Count > 0) + { + LoadInheritedRules(rule, rule.Settings.InheritsTypeTuples); + } + } + } + private void LoadInheritedRules(TypeAdapterRule rule, IEnumerable inheritedTypes) + { + foreach (var typeTuple in inheritedTypes.Where(t => !t.IsLoading)) + { + if (RuleMap.TryGetValue(new TypeTuple(typeTuple.Source, typeTuple.Destination), out var parentRule)) + { + rule.LoadLasyInherits(parentRule); + typeTuple.IsUploaded(); + } + if (parentRule != null && parentRule.Settings.InheritsTypeTuples.Any()) + { + LoadInheritedRules(rule, parentRule.Settings.InheritsTypeTuples); + } + } + } + + private readonly ConcurrentDictionary _mapToTargetDict = new ConcurrentDictionary(); public Func GetMapToTargetFunction() { @@ -331,6 +360,9 @@ public Func GetMapToTargetFunction Compiler(CreateMapExpression(tuple, MapType.MapToTarget))); return del; @@ -346,6 +378,9 @@ internal Expression> GetProjectionExpression GetDynamicMapFunction(Type sourceType) { var key = new TypeTuple(sourceType, typeof(TDestination)); + + LoadInheritedRulesLazy(key); + if (!_dynamicMapDict.TryGetValue(key, out var del)) del = AddToHash(_dynamicMapDict, key, tuple => Compiler(CreateDynamicMapExpression(tuple))); return (Func)del; diff --git a/src/Mapster/TypeAdapterRule.cs b/src/Mapster/TypeAdapterRule.cs index 7e7f83ef..60203f6f 100644 --- a/src/Mapster/TypeAdapterRule.cs +++ b/src/Mapster/TypeAdapterRule.cs @@ -6,5 +6,10 @@ public class TypeAdapterRule { public Func Priority { get; set; } public TypeAdapterSettings Settings { get; set; } + + public void LoadLasyInherits(TypeAdapterRule rule) + { + this.Settings.Apply(rule.Settings); + } } } \ No newline at end of file diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index a0581845..dcfa5cc1 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -828,6 +828,26 @@ public TypeAdapterSetter Inherits InheritsLazy() + { + this.CheckCompiled(); + + Type baseSourceType = typeof(TBaseSource); + Type baseDestinationType = typeof(TBaseDestination); + + if (!baseSourceType.GetTypeInfo().IsAssignableFrom(typeof(TSource).GetTypeInfo())) + throw new InvalidCastException("In order to use inherits, TSource must be inherited from TBaseSource."); + + if (!baseDestinationType.GetTypeInfo().IsAssignableFrom(typeof(TDestination).GetTypeInfo())) + throw new InvalidCastException("In order to use inherits, TDestination must be inherited from TBaseDestination."); + + Settings.InheritsTypeTuples.Add(new InheritsTypeTuple(baseSourceType, baseDestinationType)); + + + return this; + } + + public TypeAdapterSetter Fork(Action action) { this.CheckCompiled(); diff --git a/src/Mapster/TypeAdapterSettings.cs b/src/Mapster/TypeAdapterSettings.cs index 818a09b5..c70d78f7 100644 --- a/src/Mapster/TypeAdapterSettings.cs +++ b/src/Mapster/TypeAdapterSettings.cs @@ -117,6 +117,12 @@ public List Resolvers { get => Get(nameof(Resolvers), () => new List()); } + + public HashSet InheritsTypeTuples + { + get => Get(nameof(InheritsTypeTuples), () => new HashSet()); + } + public List ExtraSources { get => Get(nameof(ExtraSources), () => new List());