diff --git a/src/Detached.Mappers.EntityFramework/Detached.Mappers.EntityFramework.csproj b/src/Detached.Mappers.EntityFramework/Detached.Mappers.EntityFramework.csproj
index 2ff1e25f..bd443b48 100644
--- a/src/Detached.Mappers.EntityFramework/Detached.Mappers.EntityFramework.csproj
+++ b/src/Detached.Mappers.EntityFramework/Detached.Mappers.EntityFramework.csproj
@@ -13,19 +13,23 @@
true
logo.png
- Detached.Mappers.EntityFramework Readme.MD
+ Detached.Mappers.EntityFramework
+ Readme.MD
+
+
+
diff --git a/src/Detached.Mappers.EntityFramework/TypeMappers/AggregationEntityTypeMapper.cs b/src/Detached.Mappers.EntityFramework/TypeMappers/AggregationEntityTypeMapper.cs
index d09fa813..dc71a1d6 100644
--- a/src/Detached.Mappers.EntityFramework/TypeMappers/AggregationEntityTypeMapper.cs
+++ b/src/Detached.Mappers.EntityFramework/TypeMappers/AggregationEntityTypeMapper.cs
@@ -4,7 +4,6 @@
namespace Detached.Mappers.EntityFramework.TypeMappers
{
public class AggregationEntityTypeMapper : EntityTypeMapper
- where TSource : class
where TTarget : class
where TKey : IEntityKey
{
diff --git a/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapper.cs b/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapper.cs
index b72a2816..d8a559b3 100644
--- a/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapper.cs
+++ b/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapper.cs
@@ -11,7 +11,6 @@
namespace Detached.Mappers.EntityFramework.TypeMappers
{
public abstract class EntityTypeMapper : TypeMapper
- where TSource : class
where TTarget : class
where TKey : IEntityKey
{
diff --git a/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapperFactory.cs b/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapperFactory.cs
index c8d721ee..f7f93f02 100644
--- a/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapperFactory.cs
+++ b/src/Detached.Mappers.EntityFramework/TypeMappers/EntityTypeMapperFactory.cs
@@ -10,8 +10,7 @@ public class EntityTypeMapperFactory : ITypeMapperFactory
{
public bool CanCreate(Mapper mapper, TypePair typePair)
{
- return typePair.SourceType.IsComplexOrEntity()
- && typePair.TargetType.IsEntity()
+ return typePair.TargetType.IsEntity()
&& typePair.TargetType.IsConcrete();
}
diff --git a/src/Detached.Mappers/Detached.Mappers.csproj b/src/Detached.Mappers/Detached.Mappers.csproj
index 1632e6bd..f4c1d8a7 100644
--- a/src/Detached.Mappers/Detached.Mappers.csproj
+++ b/src/Detached.Mappers/Detached.Mappers.csproj
@@ -20,6 +20,7 @@
+
diff --git a/src/Detached.Mappers/MapperOptions.cs b/src/Detached.Mappers/MapperOptions.cs
index 74701460..9621bbe1 100644
--- a/src/Detached.Mappers/MapperOptions.cs
+++ b/src/Detached.Mappers/MapperOptions.cs
@@ -13,6 +13,7 @@
using Detached.Mappers.TypeMappers.POCO.Nullable;
using Detached.Mappers.TypeMappers.POCO.Primitive;
using Detached.Mappers.TypePairs;
+using Detached.Mappers.TypePairs.Conventions;
using Detached.Mappers.Types;
using Detached.Mappers.Types.Class;
using Detached.Mappers.Types.Class.Builder;
@@ -78,8 +79,7 @@ public MapperOptions()
new NullableTypeMapperFactory(),
new InheritedTypeMapperFactory(),
new EntityCollectionTypeMapperFactory(),
- new EntityTypeMapperFactory(),
- new KeyToComplexTypeMapperFactory()
+ new EntityTypeMapperFactory()
};
ConcreteTypes = new Dictionary
@@ -111,7 +111,11 @@ public MapperOptions()
new NullableTypeBinder()
};
- PropertyNameConventions = new List();
+ MemberNameConventions = new List
+ {
+ new ForeignKeyMemberNameConvention(),
+ new DefaultMemberNameConvention()
+ };
}
public virtual HashSet Primitives { get; }
@@ -126,7 +130,7 @@ public MapperOptions()
public virtual List TypeConventions { get; }
- public virtual List PropertyNameConventions { get; }
+ public virtual List MemberNameConventions { get; }
public virtual Dictionary ConcreteTypes { get; }
diff --git a/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapper.cs b/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapper.cs
index 034eb09e..c6d89b63 100644
--- a/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapper.cs
+++ b/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapper.cs
@@ -6,7 +6,6 @@ namespace Detached.Mappers.TypeMappers.Entity.Collection
public class EntityCollectionTypeMapper : TypeMapper
where TSource : IEnumerable
where TTarget : ICollection
- where TSourceItem : class
where TTargetItem : class
where TKey : IEntityKey
{
diff --git a/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapperFactory.cs b/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapperFactory.cs
index e09e4141..576d6aa6 100644
--- a/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapperFactory.cs
+++ b/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityCollectionTypeMapperFactory.cs
@@ -18,7 +18,7 @@ public bool CanCreate(Mapper mapper, TypePair typePair)
IType sourceItemType = mapper.Options.GetType(typePair.TargetType.ItemClrType);
IType targetItemType = mapper.Options.GetType(typePair.TargetType.ItemClrType);
- return targetItemType.IsEntity() && sourceItemType.IsComplexOrEntity();
+ return targetItemType.IsEntity() && (sourceItemType.IsComplexOrEntity() || sourceItemType.IsPrimitive());
}
return false;
diff --git a/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityListTypeMapper.cs b/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityListTypeMapper.cs
index 76f3ff86..fd44b721 100644
--- a/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityListTypeMapper.cs
+++ b/src/Detached.Mappers/TypeMappers/Entity/Collection/EntityListTypeMapper.cs
@@ -6,7 +6,6 @@ namespace Detached.Mappers.TypeMappers.Entity.Collection
public class EntityListTypeMapper : TypeMapper
where TSource : IEnumerable
where TTarget : IList
- where TSourceItem : class
where TTargetItem : class
where TKey : IEntityKey
{
diff --git a/src/Detached.Mappers/TypePairs/Conventions/CamelCaseMemberNameConvention.cs b/src/Detached.Mappers/TypePairs/Conventions/CamelCaseMemberNameConvention.cs
new file mode 100644
index 00000000..24b44b39
--- /dev/null
+++ b/src/Detached.Mappers/TypePairs/Conventions/CamelCaseMemberNameConvention.cs
@@ -0,0 +1,12 @@
+using Detached.Mappers.Types;
+
+namespace Detached.Mappers.TypePairs.Conventions
+{
+ public class CamelCaseMemberNameConvention : IMemberNameConvention
+ {
+ public string GetSourceMemberName(string targetMemberName, IType sourceType, IType targetType, MapperOptions mapperOptions)
+ {
+ return char.ToLower(targetMemberName[0]) + targetMemberName.Substring(1);
+ }
+ }
+}
diff --git a/src/Detached.Mappers/TypePairs/Conventions/DefaultMemberNameConvention.cs b/src/Detached.Mappers/TypePairs/Conventions/DefaultMemberNameConvention.cs
new file mode 100644
index 00000000..7c6523ec
--- /dev/null
+++ b/src/Detached.Mappers/TypePairs/Conventions/DefaultMemberNameConvention.cs
@@ -0,0 +1,12 @@
+using Detached.Mappers.Types;
+
+namespace Detached.Mappers.TypePairs.Conventions
+{
+ public class DefaultMemberNameConvention : IMemberNameConvention
+ {
+ public string GetSourceMemberName(string targetMemberName, IType sourceType, IType targetType, MapperOptions mapperOptions)
+ {
+ return targetMemberName;
+ }
+ }
+}
diff --git a/src/Detached.Mappers/TypePairs/Conventions/ForeignKeyMemberNameConvention.cs b/src/Detached.Mappers/TypePairs/Conventions/ForeignKeyMemberNameConvention.cs
new file mode 100644
index 00000000..b5e1d612
--- /dev/null
+++ b/src/Detached.Mappers/TypePairs/Conventions/ForeignKeyMemberNameConvention.cs
@@ -0,0 +1,35 @@
+using Detached.Mappers.Types;
+using Humanizer;
+
+namespace Detached.Mappers.TypePairs.Conventions
+{
+ public class ForeignKeyMemberNameConvention : IMemberNameConvention
+ {
+ public string GetSourceMemberName(string targetMemberName, IType sourceType, IType targetType, MapperOptions mapperOptions)
+ {
+ string memberName = null;
+
+ if (targetType.IsEntity())
+ {
+ var key = targetType.GetKeyMember();
+
+ if (key != null)
+ {
+ var member = targetType.GetMember(targetMemberName);
+ var memberType = mapperOptions.GetType(member.ClrType);
+
+ if (memberType.IsCollection())
+ {
+ memberName = targetMemberName.Singularize(false) + key.Name.Pluralize(false);
+ }
+ else
+ {
+ memberName = targetMemberName + key.Name;
+ }
+ }
+ }
+
+ return memberName;
+ }
+ }
+}
diff --git a/src/Detached.Mappers/TypePairs/Conventions/IMemberNameConvention.cs b/src/Detached.Mappers/TypePairs/Conventions/IMemberNameConvention.cs
new file mode 100644
index 00000000..e079ed45
--- /dev/null
+++ b/src/Detached.Mappers/TypePairs/Conventions/IMemberNameConvention.cs
@@ -0,0 +1,9 @@
+using Detached.Mappers.Types;
+
+namespace Detached.Mappers.TypePairs.Conventions
+{
+ public interface IMemberNameConvention
+ {
+ string GetSourceMemberName(string targetMemberName, IType sourceType, IType targetType, MapperOptions mapperOptions);
+ }
+}
diff --git a/src/Detached.Mappers/TypePairs/ITypePairFactory.cs b/src/Detached.Mappers/TypePairs/ITypePairFactory.cs
index 6da27668..e498dd58 100644
--- a/src/Detached.Mappers/TypePairs/ITypePairFactory.cs
+++ b/src/Detached.Mappers/TypePairs/ITypePairFactory.cs
@@ -4,6 +4,6 @@ namespace Detached.Mappers.TypePairs
{
public interface ITypePairFactory
{
- TypePair Create(MapperOptions mapperOptions, IType sourceType, IType targetType, TypePairMember sourceMember);
+ TypePair Create(MapperOptions mapperOptions, IType sourceType, IType targetType, TypePairMember parentMember);
}
}
\ No newline at end of file
diff --git a/src/Detached.Mappers/TypePairs/TypePairFactory.cs b/src/Detached.Mappers/TypePairs/TypePairFactory.cs
index 969e327c..c5f88742 100644
--- a/src/Detached.Mappers/TypePairs/TypePairFactory.cs
+++ b/src/Detached.Mappers/TypePairs/TypePairFactory.cs
@@ -13,8 +13,6 @@ public TypePair Create(MapperOptions mapperOptions, IType sourceType, IType targ
if (memberNames != null)
{
- var keyMember = targetType.GetKeyMember();
-
foreach (string targetMemberName in memberNames)
{
ITypeMember targetMember = targetType.GetMember(targetMemberName);
@@ -26,18 +24,7 @@ public TypePair Create(MapperOptions mapperOptions, IType sourceType, IType targ
member.TargetType = targetType;
member.SourceType = sourceType;
member.TargetMember = targetMember;
-
- string sourceMemberName = GetSourcePropertyName(mapperOptions, sourceType, targetType, targetMemberName);
-
- ITypeMember sourceMember = sourceType.GetMember(sourceMemberName);
-
- if (sourceMember == null && keyMember != null)
- {
- string keyName = targetMemberName + keyMember.Name;
- sourceMember = sourceType.GetMember(keyName);
- }
-
- member.SourceMember = sourceMember;
+ member.SourceMember = GetSourceMember(targetMemberName, sourceType, targetType, mapperOptions);
typePair.Members.Add(targetMemberName, member);
}
@@ -47,14 +34,26 @@ public TypePair Create(MapperOptions mapperOptions, IType sourceType, IType targ
return typePair;
}
- public string GetSourcePropertyName(MapperOptions mapperOptions, IType sourceType, IType targetType, string memberName)
+ public ITypeMember GetSourceMember(string targetMemberName, IType sourceType, IType targetType, MapperOptions mapperOptions)
{
- for (int i = mapperOptions.PropertyNameConventions.Count - 1; i >= 0; i--)
+ ITypeMember result = null;
+
+ for (int i = mapperOptions.MemberNameConventions.Count - 1; i >= 0; i--)
{
- memberName = mapperOptions.PropertyNameConventions[i].GetSourcePropertyName(sourceType, targetType, memberName);
+ var convention = mapperOptions.MemberNameConventions[i];
+
+ var sourceMemberName = convention.GetSourceMemberName(targetMemberName, sourceType, targetType, mapperOptions);
+
+ if (sourceMemberName != null)
+ {
+ result = sourceType.GetMember(sourceMemberName);
+
+ if (result != null)
+ break;
+ }
}
- return memberName;
+ return result;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Detached.Mappers/TypePairs/TypePairMember.cs b/src/Detached.Mappers/TypePairs/TypePairMember.cs
index 8dd4815c..54fa88ff 100644
--- a/src/Detached.Mappers/TypePairs/TypePairMember.cs
+++ b/src/Detached.Mappers/TypePairs/TypePairMember.cs
@@ -14,5 +14,16 @@ public class TypePairMember
public ITypeMember TargetMember { get; set; }
public AnnotationCollection Annotations { get; set; } = new();
+
+ public override string ToString()
+ {
+ string sourceName = SourceMember == null
+ ? "null"
+ : SourceMember.Name + ": " + SourceMember.ClrType.GetFriendlyName();
+
+ string targetName = TargetMember.ClrType.GetFriendlyName();
+
+ return $"TypePairMember ({sourceName} -> {targetName})";
+ }
}
}
\ No newline at end of file
diff --git a/src/Detached.Mappers/Types/Class/ClassType.cs b/src/Detached.Mappers/Types/Class/ClassType.cs
index b88c3d98..8dab346d 100644
--- a/src/Detached.Mappers/Types/Class/ClassType.cs
+++ b/src/Detached.Mappers/Types/Class/ClassType.cs
@@ -38,6 +38,6 @@ public virtual Expression BuildNewExpression(Expression context, Expression disc
return Import(Constructor, context);
}
- public override string ToString() => $"{ClrType.GetFriendlyName()} (ClassType)";
+ public override string ToString() => $"ClassType ({ClrType.GetFriendlyName()})";
}
}
\ No newline at end of file
diff --git a/src/Detached.Mappers/Types/Class/ClassTypeMember.cs b/src/Detached.Mappers/Types/Class/ClassTypeMember.cs
index 373a1374..8e2a1376 100644
--- a/src/Detached.Mappers/Types/Class/ClassTypeMember.cs
+++ b/src/Detached.Mappers/Types/Class/ClassTypeMember.cs
@@ -47,6 +47,6 @@ public virtual Expression BuildTryGetExpression(Expression instance, Expression
return Import(TryGetter, instance, outVar, context);
}
- public override string ToString() => $"{Name} [{ClrType.GetFriendlyName()}] (MemberOptions)";
+ public override string ToString() => $"ClassTypeMember ({Name}: {ClrType.GetFriendlyName()})";
}
}
\ No newline at end of file
diff --git a/src/Detached.Mappers/Types/Conventions/CamelCasePropertyNameConvention.cs b/src/Detached.Mappers/Types/Conventions/CamelCasePropertyNameConvention.cs
deleted file mode 100644
index bf4d5e0f..00000000
--- a/src/Detached.Mappers/Types/Conventions/CamelCasePropertyNameConvention.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Detached.Mappers.Types.Conventions
-{
- public class CamelCasePropertyNameConvention : IPropertyNameConvention
- {
- public string GetSourcePropertyName(IType sourceType, IType targetType, string targetMemberName)
- {
- return char.ToLower(targetMemberName[0]) + targetMemberName.Substring(1);
- }
- }
-}
diff --git a/src/Detached.Mappers/Types/Conventions/IPropertyNameConvention.cs b/src/Detached.Mappers/Types/Conventions/IPropertyNameConvention.cs
deleted file mode 100644
index cc4ef0d6..00000000
--- a/src/Detached.Mappers/Types/Conventions/IPropertyNameConvention.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Detached.Mappers.Types.Conventions
-{
- public interface IPropertyNameConvention
- {
- string GetSourcePropertyName(IType sourceType, IType targetType, string targetMemberName);
- }
-}
diff --git a/test/Detached.Mappers.EntityFramework.Tests/KeyToEntityTests.cs b/test/Detached.Mappers.EntityFramework.Tests/ForeignKeyTests.cs
similarity index 74%
rename from test/Detached.Mappers.EntityFramework.Tests/KeyToEntityTests.cs
rename to test/Detached.Mappers.EntityFramework.Tests/ForeignKeyTests.cs
index 0423533b..a5b941ea 100644
--- a/test/Detached.Mappers.EntityFramework.Tests/KeyToEntityTests.cs
+++ b/test/Detached.Mappers.EntityFramework.Tests/ForeignKeyTests.cs
@@ -1,15 +1,16 @@
using Detached.Mappers.EntityFramework.Extensions;
using Detached.Mappers.EntityFramework.Tests.Fixture;
using Microsoft.EntityFrameworkCore;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
namespace Detached.Mappers.EntityFramework.Tests
{
- public class KeyToEntityTests
+ public class ForeignKeyTests
{
- //[Fact]
- public async Task map_key_to_entity()
+ [Fact]
+ public async Task map_fk_to_entity()
{
var dbContext = await TestDbContext.Create();
@@ -22,12 +23,20 @@ public async Task map_key_to_entity()
{
Id = 1,
Name = "test user",
- ChildId = 2
+ ChildId = 2,
+ ChildIds = new[] { 1, 2 }
});
Assert.NotNull(result.Child);
Assert.Equal(2, result.Child.Id);
Assert.Equal("Child 2", result.Child.Name);
+
+ Assert.NotNull(result.Children);
+ Assert.Equal(1, result.Children[0].Id);
+ Assert.Equal("Child 1", result.Children[0].Name);
+
+ Assert.Equal(2, result.Children[1].Id);
+ Assert.Equal("Child 2", result.Children[1].Name);
}
public class ParentEntity
@@ -37,6 +46,8 @@ public class ParentEntity
public string Name { get; set; }
public ChildEntity Child { get; set; }
+
+ public List Children { get; set; }
}
public class ChildEntity
diff --git a/test/Detached.Mappers.Json.Tests/JsonTests.cs b/test/Detached.Mappers.Json.Tests/JsonTests.cs
index 792db743..d3704e22 100644
--- a/test/Detached.Mappers.Json.Tests/JsonTests.cs
+++ b/test/Detached.Mappers.Json.Tests/JsonTests.cs
@@ -1,5 +1,5 @@
using Detached.Mappers.Json.Tests.Fixture;
-using Detached.Mappers.Types.Conventions;
+using Detached.Mappers.TypePairs.Conventions;
using System.Text.Json.Nodes;
using Xunit;
@@ -22,7 +22,7 @@ public void map_json_to_entity()
// create options. Json lib is a separate package to avoid adding System.Text.Json dependency to the main lib, call WithJson() to integrate.
MapperOptions mapperOptions = new MapperOptions().WithJson();
- mapperOptions.PropertyNameConventions.Add(new CamelCasePropertyNameConvention());
+ mapperOptions.MemberNameConventions.Add(new CamelCaseMemberNameConvention());
// create the mapper.
Mapper mapper = new Mapper(mapperOptions);