Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 64 additions & 2 deletions src/Mapster.Tests/WhenMappingWithExplicitInheritance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,77 @@ public void Invalid_Destination_Cast_Throws_Exception()

}

[TestMethod]
public void InheritsLasyLoad__IsWork()
{
TypeAdapterConfig<DerivedPoco, DerivedDto>.NewConfig()
.Inherits<SimplePoco, SimpleDto>()
.Compile();

TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig()
.Inherits<RootPoco, RootDto>()
.Ignore(dest => dest.Name)
.Compile();

TypeAdapterConfig<RootPoco, RootDto>.NewConfig()
.Map(dest => dest.NumberDto, src => 42)
.Compile();

var source = new DerivedPoco
{
Id = new Guid(),
Name = "SourceName"
};

var dto = TypeAdapter.Adapt<DerivedDto>(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<DerivedPoco, DerivedDto>.NewConfig()
.InheritsLazy<SimplePoco, SimpleDto>()
.Compile();

TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig()
.InheritsLazy<RootPoco, RootDto>()
.Ignore(dest => dest.Name)
.Compile();

TypeAdapterConfig<RootPoco, RootDto>.NewConfig()
.Map(dest => dest.NumberDto, src => 42)
.Compile();

dto = TypeAdapter.Adapt<DerivedDto>(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; }
Expand Down
46 changes: 46 additions & 0 deletions src/Mapster/Models/TypeTuple.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,50 @@ public TypeTuple(Type source, Type destination)
Destination = destination;
}
}

public class InheritsTypeTuple : IEquatable<InheritsTypeTuple>
{
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;
}
}
}
38 changes: 38 additions & 0 deletions src/Mapster/TypeAdapterConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,41 @@ public Func<TSource, TDestination> GetMapFunction<TSource, TDestination>()
}
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<InheritsTypeTuple> 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<TypeTuple, Delegate> _mapToTargetDict = new ConcurrentDictionary<TypeTuple, Delegate>();
public Func<TSource, TDestination, TDestination> GetMapToTargetFunction<TSource, TDestination>()
{
Expand All @@ -331,6 +360,9 @@ public Func<TSource, TDestination, TDestination> GetMapToTargetFunction<TSource,
internal Delegate GetMapToTargetFunction(Type sourceType, Type destinationType)
{
var key = new TypeTuple(sourceType, destinationType);

LoadInheritedRulesLazy(key);

if (!_mapToTargetDict.TryGetValue(key, out var del))
del = AddToHash(_mapToTargetDict, key, tuple => Compiler(CreateMapExpression(tuple, MapType.MapToTarget)));
return del;
Expand All @@ -346,6 +378,9 @@ internal Expression<Func<TSource, TDestination>> GetProjectionExpression<TSource
internal MethodCallExpression GetProjectionCallExpression(Type sourceType, Type destinationType)
{
var key = new TypeTuple(sourceType, destinationType);

LoadInheritedRulesLazy(key);

if (!_projectionDict.TryGetValue(key, out var del))
del = AddToHash(_projectionDict, key, CreateProjectionCallExpression);
return del;
Expand All @@ -355,6 +390,9 @@ internal MethodCallExpression GetProjectionCallExpression(Type sourceType, Type
public Func<object, TDestination> GetDynamicMapFunction<TDestination>(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<object, TDestination>)del;
Expand Down
5 changes: 5 additions & 0 deletions src/Mapster/TypeAdapterRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ public class TypeAdapterRule
{
public Func<PreCompileArgument, int?> Priority { get; set; }
public TypeAdapterSettings Settings { get; set; }

public void LoadLasyInherits(TypeAdapterRule rule)
{
this.Settings.Apply(rule.Settings);
}
}
}
20 changes: 20 additions & 0 deletions src/Mapster/TypeAdapterSetter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,26 @@ public TypeAdapterSetter<TSource, TDestination> Inherits<TBaseSource, TBaseDesti
return this;
}

public TypeAdapterSetter<TSource, TDestination> InheritsLazy<TBaseSource, TBaseDestination>()
{
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<TSource, TDestination> Fork(Action<TypeAdapterConfig> action)
{
this.CheckCompiled();
Expand Down
6 changes: 6 additions & 0 deletions src/Mapster/TypeAdapterSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ public List<InvokerModel> Resolvers
{
get => Get(nameof(Resolvers), () => new List<InvokerModel>());
}

public HashSet<InheritsTypeTuple> InheritsTypeTuples
{
get => Get(nameof(InheritsTypeTuples), () => new HashSet<InheritsTypeTuple>());
}

public List<object> ExtraSources
{
get => Get(nameof(ExtraSources), () => new List<object>());
Expand Down
Loading