diff --git a/.gitignore b/.gitignore
index 396ef6a..9721219 100644
--- a/.gitignore
+++ b/.gitignore
@@ -192,3 +192,4 @@ ModelManifest.xml
*.GhostDoc.xml
/artifacts
*.lock.json
+/results
diff --git a/AutoMapper.Collection.sln b/AutoMapper.Collection.sln
index 8377a8b..d106b91 100644
--- a/AutoMapper.Collection.sln
+++ b/AutoMapper.Collection.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26430.6
+VisualStudioVersion = 15.0.27130.2010
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{19AAEE83-5EEC-4EAA-9CF7-16F8ED58B50E}"
ProjectSection(SolutionItems) = preProject
@@ -10,8 +10,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{19AAEE
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{578F2483-CF08-409D-A316-31BCB7C5D9D0}"
ProjectSection(SolutionItems) = preProject
+ Directory.Build.props = Directory.Build.props
global.json = global.json
README.md = README.md
+ version.props = version.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMapper.Collection", "src\AutoMapper.Collection\AutoMapper.Collection.csproj", "{37AD667A-8080-476C-88FD-20310AC7CAF3}"
@@ -54,4 +56,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D5C8CD86-1F3F-4D3A-8A90-5F3726E8F998}
+ EndGlobalSection
EndGlobal
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..a1b67b4
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,9 @@
+
+
+
+
+ 4.15.0
+ 15.5.0
+ 2.3.1
+
+
\ No newline at end of file
diff --git a/build.ps1 b/build.ps1
index 5ebf085..49b7a97 100644
--- a/build.ps1
+++ b/build.ps1
@@ -2,9 +2,8 @@ Framework '4.5.1x86'
properties {
$base_dir = resolve-path .
- $build_dir = "$base_dir\build"
$source_dir = "$base_dir\src"
- $result_dir = "$build_dir\results"
+ $result_dir = "$base_dir\results"
$artifacts_dir = "$base_dir\artifacts"
$global:config = "debug"
}
@@ -15,8 +14,8 @@ task local -depends init, compile, test
task ci -depends clean, release, local
task clean {
- rd "$source_dir\artifacts" -recurse -force -ErrorAction SilentlyContinue | out-null
- rd "$base_dir\build" -recurse -force -ErrorAction SilentlyContinue | out-null
+ rd "$artifacts_dir" -recurse -force -ErrorAction SilentlyContinue | out-null
+ rd "$result_dir" -recurse -force -ErrorAction SilentlyContinue | out-null
}
task init {
@@ -38,38 +37,25 @@ task compile -depends clean {
$buildParam = @{ $true = ""; $false = "--version-suffix=$buildSuffix"}[$tag -ne $NULL -and $revision -ne "local"]
$packageParam = @{ $true = ""; $false = "--version-suffix=$suffix"}[$tag -ne $NULL -and $revision -ne "local"]
-
+
echo "build: Tag is $tag"
echo "build: Package version suffix is $suffix"
echo "build: Build version suffix is $buildSuffix"
- # restore packages to get Fixie.Console.exe in packages folder
- exec { .\.nuget\NuGet.exe restore $base_dir\AutoMapper.Collection.sln }
-
# restore all project references (creating project.assets.json for each project)
- exec { dotnet restore $base_dir\AutoMapper.Collection.sln }
+ exec { dotnet restore $base_dir\AutoMapper.Collection.sln /nologo }
- exec { dotnet build $base_dir\AutoMapper.Collection.sln -c $config $buildParam /nologo }
+ exec { dotnet build $base_dir\AutoMapper.Collection.sln -c $config $buildParam /nologo --no-restore }
- exec { dotnet pack $source_dir\AutoMapper.Collection -c $config --include-symbols --no-build --output $artifacts_dir $packageParam /nologo}
-
- exec { dotnet pack $source_dir\AutoMapper.Collection.EntityFramework -c $config --include-symbols --no-build --output $artifacts_dir $packageParam /nologo}
-
- exec { dotnet pack $source_dir\AutoMapper.Collection.LinqToSQL -c $config --include-symbols --no-build --output $artifacts_dir $packageParam /nologo}
+ exec { dotnet pack $base_dir\AutoMapper.Collection.sln -c $config --include-symbols --no-build --no-restore --output $artifacts_dir $packageParam /nologo}
}
task test {
- $testRunners = @(gci $base_dir\packages -rec -filter Fixie.Console.exe)
- if ($testRunners.Length -ne 1)
- {
- throw "Expected to find 1 Fixie.Console.exe, but found $($testRunners.Length)."
- }
+ exec { dotnet test $source_dir\AutoMapper.Collection.Tests -c $config --no-build --no-restore --results-directory $result_dir --logger trx /nologo }
- $testRunner = $testRunners[0].FullName
+ exec { dotnet test $source_dir\AutoMapper.Collection.EntityFramework.Tests -c $config --no-build --no-restore --results-directory $result_dir --logger trx /nologo }
- exec { & $testRunner $source_dir\AutoMapper.Collection.Tests\bin\$config\AutoMapper.Collection.Tests.dll }
- exec { & $testRunner $source_dir\AutoMapper.Collection.EntityFramework.Tests\bin\$config\AutoMapper.Collection.EntityFramework.Tests.dll }
}
function Install-Dotnet
diff --git a/src/AutoMapper.Collection.EntityFramework.Tests/AssemblyInfo.cs b/src/AutoMapper.Collection.EntityFramework.Tests/AssemblyInfo.cs
new file mode 100644
index 0000000..7db8497
--- /dev/null
+++ b/src/AutoMapper.Collection.EntityFramework.Tests/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using Xunit;
+
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
\ No newline at end of file
diff --git a/src/AutoMapper.Collection.EntityFramework.Tests/AutoMapper.Collection.EntityFramework.Tests.csproj b/src/AutoMapper.Collection.EntityFramework.Tests/AutoMapper.Collection.EntityFramework.Tests.csproj
index da0d9be..31ec00b 100644
--- a/src/AutoMapper.Collection.EntityFramework.Tests/AutoMapper.Collection.EntityFramework.Tests.csproj
+++ b/src/AutoMapper.Collection.EntityFramework.Tests/AutoMapper.Collection.EntityFramework.Tests.csproj
@@ -1,99 +1,29 @@
-
-
-
+
+
- Debug
- AnyCPU
- {BDE127AB-AC3F-44DF-BC33-210DAFD12E15}
- Library
- Properties
- AutoMapper.Collection.EntityFramework.Tests
+ net461
AutoMapper.Collection.EntityFramework.Tests
- v4.5
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
+ false
+
-
- ..\..\packages\AutoMapper.6.2.1\lib\net45\AutoMapper.dll
-
-
- ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll
- True
-
-
- ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll
- True
-
-
- ..\..\packages\Fixie.1.0.2\lib\net45\Fixie.dll
- True
-
-
- ..\..\packages\FluentAssertions.4.15.0\lib\net45\FluentAssertions.dll
- True
-
-
- ..\..\packages\FluentAssertions.4.15.0\lib\net45\FluentAssertions.Core.dll
- True
-
-
-
-
-
- ..\..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll
- True
-
-
-
-
-
-
-
+
+
-
-
+
-
-
-
-
-
-
+
+
+
+
+
-
- {e0fd41fd-af5b-4bec-970f-8412e7b6c914}
- AutoMapper.Collection.EntityFramework
-
-
- {37ad667a-8080-476c-88fd-20310ac7caf3}
- AutoMapper.Collection
-
+
+
+
+
-
-
-
\ No newline at end of file
+
+
diff --git a/src/AutoMapper.Collection.EntityFramework.Tests/MapCollectionWithEqualityTests.cs b/src/AutoMapper.Collection.EntityFramework.Tests/MapCollectionWithEqualityTests.cs
index 96336b8..8a76cec 100644
--- a/src/AutoMapper.Collection.EntityFramework.Tests/MapCollectionWithEqualityTests.cs
+++ b/src/AutoMapper.Collection.EntityFramework.Tests/MapCollectionWithEqualityTests.cs
@@ -5,6 +5,7 @@
using AutoMapper.EntityFramework;
using AutoMapper.EquivalencyExpression;
using FluentAssertions;
+using Xunit;
namespace AutoMapper.Collection.EntityFramework.Tests
{
@@ -21,6 +22,7 @@ public MapCollectionWithEqualityTests()
});
}
+ [Fact]
public void Should_Keep_Existing_List()
{
var dtos = new List
@@ -38,6 +40,7 @@ public void Should_Keep_Existing_List()
Mapper.Map(dtos, items).Should().BeSameAs(items);
}
+ [Fact]
public void Should_Update_Existing_Item()
{
var dtos = new List
@@ -56,6 +59,7 @@ public void Should_Update_Existing_Item()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, cache.First());
}
+ [Fact]
public void Should_Work_With_Null_Destination()
{
var dtos = new List
@@ -67,6 +71,7 @@ public void Should_Work_With_Null_Destination()
Mapper.Map>(dtos).Should().HaveSameCount(dtos);
}
+ [Fact]
public void Should_Be_Instanced_Based()
{
Mapper.Reset();
@@ -92,6 +97,7 @@ public void Should_Be_Instanced_Based()
Mapper.Map(dtos, items.ToList()).Should().NotContain(cache.First());
}
+ //[Fact]
//public void Should_Persist_To_Update()
//{
// var db = new DB();
diff --git a/src/AutoMapper.Collection.EntityFramework.Tests/Properties/AssemblyInfo.cs b/src/AutoMapper.Collection.EntityFramework.Tests/Properties/AssemblyInfo.cs
deleted file mode 100644
index a436d2e..0000000
--- a/src/AutoMapper.Collection.EntityFramework.Tests/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("AutoMapper.Collection.EntityFramework.Tests")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("AutoMapper.Collection.EntityFramework.Tests")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("bde127ab-ac3f-44df-bc33-210dafd12e15")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/AutoMapper.Collection.EntityFramework.Tests/packages.config b/src/AutoMapper.Collection.EntityFramework.Tests/packages.config
deleted file mode 100644
index af1430c..0000000
--- a/src/AutoMapper.Collection.EntityFramework.Tests/packages.config
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/AutoMapper.Collection.EntityFramework/AutoMapper.Collection.EntityFramework.csproj b/src/AutoMapper.Collection.EntityFramework/AutoMapper.Collection.EntityFramework.csproj
index 8fe0cfa..2a772e9 100644
--- a/src/AutoMapper.Collection.EntityFramework/AutoMapper.Collection.EntityFramework.csproj
+++ b/src/AutoMapper.Collection.EntityFramework/AutoMapper.Collection.EntityFramework.csproj
@@ -2,7 +2,6 @@
Collection updating support for EntityFramework with AutoMapper. Extends DBSet<T> with Persist<TDto>().InsertUpdate(dto) and Persist<TDto>().Delete(dto). Will find the matching object and will Insert/Update/Delete.
- 3.1.3
Tyler Carlson
net45
AutoMapper.Collection.EntityFramework
@@ -21,6 +20,7 @@
+
diff --git a/src/AutoMapper.Collection.EntityFramework/Extensions.cs b/src/AutoMapper.Collection.EntityFramework/Extensions.cs
index efe4be1..4f632ae 100644
--- a/src/AutoMapper.Collection.EntityFramework/Extensions.cs
+++ b/src/AutoMapper.Collection.EntityFramework/Extensions.cs
@@ -2,7 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
-using AutoMapper.QueryableExtensions.Impl;
+using AutoMapper.Extensions.ExpressionMapping.Impl;
namespace AutoMapper.EntityFramework
{
diff --git a/src/AutoMapper.Collection.LinqToSQL/AutoMapper.Collection.LinqToSQL.csproj b/src/AutoMapper.Collection.LinqToSQL/AutoMapper.Collection.LinqToSQL.csproj
index 9f68ed4..64db2aa 100644
--- a/src/AutoMapper.Collection.LinqToSQL/AutoMapper.Collection.LinqToSQL.csproj
+++ b/src/AutoMapper.Collection.LinqToSQL/AutoMapper.Collection.LinqToSQL.csproj
@@ -2,7 +2,6 @@
Collection updating support for LinqToSQL with AutoMapper. Extends Table<T> with Persist<TDto>().InsertUpdate(dto) and Persist<TDto>().Delete(dto). Will find the matching object and will Insert/Update/Delete.
- 3.1.3
Tyler Carlson
net45
AutoMapper.Collection.LinqToSQL
@@ -20,6 +19,10 @@
+
+
+
+
diff --git a/src/AutoMapper.Collection.LinqToSQL/PersistenceExtensions.cs b/src/AutoMapper.Collection.LinqToSQL/PersistenceExtensions.cs
index 1d8d69d..d5ae665 100644
--- a/src/AutoMapper.Collection.LinqToSQL/PersistenceExtensions.cs
+++ b/src/AutoMapper.Collection.LinqToSQL/PersistenceExtensions.cs
@@ -2,7 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Data.Linq;
-using AutoMapper.QueryableExtensions.Impl;
+using AutoMapper.Extensions.ExpressionMapping.Impl;
namespace AutoMapper.Collection.LinqToSQL
{
diff --git a/src/AutoMapper.Collection.Tests/AssemblyInfo.cs b/src/AutoMapper.Collection.Tests/AssemblyInfo.cs
new file mode 100644
index 0000000..7db8497
--- /dev/null
+++ b/src/AutoMapper.Collection.Tests/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using Xunit;
+
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
\ No newline at end of file
diff --git a/src/AutoMapper.Collection.Tests/AutoMapper.Collection.Tests.csproj b/src/AutoMapper.Collection.Tests/AutoMapper.Collection.Tests.csproj
index 13c091d..ebc2b05 100644
--- a/src/AutoMapper.Collection.Tests/AutoMapper.Collection.Tests.csproj
+++ b/src/AutoMapper.Collection.Tests/AutoMapper.Collection.Tests.csproj
@@ -1,87 +1,30 @@
-
-
-
+
+
- Debug
- AnyCPU
- {2D3D34AD-6A0A-4382-9A2F-894F52D184A7}
- Library
- Properties
- AutoMapper.Collection
+ net461;netcoreapp1.1
AutoMapper.Collection.Tests
- v4.5
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
+ AutoMapper.Collection
+ false
+
-
- ..\..\packages\AutoMapper.6.2.1\lib\net45\AutoMapper.dll
-
-
- ..\..\packages\Fixie.1.0.2\lib\net45\Fixie.dll
- True
-
-
- ..\..\packages\FluentAssertions.4.15.0\lib\net45\FluentAssertions.dll
- True
-
-
- ..\..\packages\FluentAssertions.4.15.0\lib\net45\FluentAssertions.Core.dll
- True
-
-
-
-
- ..\..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll
- True
-
-
-
-
-
-
-
+
+
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
-
- {37ad667a-8080-476c-88fd-20310ac7caf3}
- AutoMapper.Collection
-
+
+
+
+
-
-
+
\ No newline at end of file
diff --git a/src/AutoMapper.Collection.Tests/InheritanceWithCollectionTests.cs b/src/AutoMapper.Collection.Tests/InheritanceWithCollectionTests.cs
new file mode 100644
index 0000000..b411a60
--- /dev/null
+++ b/src/AutoMapper.Collection.Tests/InheritanceWithCollectionTests.cs
@@ -0,0 +1,209 @@
+using AutoMapper.EquivalencyExpression;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Xunit;
+
+namespace AutoMapper.Collection
+{
+ public class InheritanceWithCollectionTests
+ {
+ private IMapper CreateMapper()
+ {
+ void Create(IMapperConfigurationExpression cfg)
+ {
+ cfg.ShouldMapProperty = propertyInfo => propertyInfo.GetMethod.IsPublic || propertyInfo.GetMethod.IsAssembly || propertyInfo.GetMethod.IsFamily || propertyInfo.GetMethod.IsPrivate;
+ cfg.AddCollectionMappers();
+
+ //DOMAIN --> EF
+ cfg.CreateMap()
+ .ForMember(rootEf => rootEf.Orders, opt => opt.ResolveUsing())
+ ;
+
+ //collection type
+ cfg.CreateMap()
+ .EqualityComparison((oo, dto) => BaseEquals(oo, dto))
+ ;
+
+ cfg.CreateMap()
+ .EqualityComparison((ood, ooe) => DerivedEquals(ood, ooe))
+ .IncludeBase()
+ ;
+
+ cfg.CreateMap()
+ .IncludeBase()
+ ;
+
+ //EF --> DOMAIN
+ cfg.CreateMap()
+ .ForMember(rootDomain => rootDomain.OnlineOrders, opt => opt.MapFrom(rootEf => rootEf.Orders.Where(orderEf => orderEf.GetType() == typeof(OnlineOrderEf))))
+ .ForMember(rootDomain => rootDomain.MailOrders, opt => opt.MapFrom(rootEf => rootEf.Orders.Where(orderEf => orderEf.GetType() == typeof(MailOrderEf))))
+ ;
+
+ cfg.CreateMap()
+ ;
+
+ cfg.CreateMap()
+ .IncludeBase()
+ ;
+
+ cfg.CreateMap()
+ .IncludeBase()
+ ;
+ }
+
+ var map = new MapperConfiguration(Create);
+ map.AssertConfigurationIsValid();
+ map.CompileMappings();
+
+ return map.CreateMapper();
+ }
+
+ [Fact]
+ public void Map_Should_ReturnOnlineOrderEf_When_ListIsOfTypeOrderEf()
+ {
+ var mapper = CreateMapper();
+
+ //arrange
+ var orderDomain = new OnlineOrderDomain { Id = "Id", Key = "Key" };
+ var rootDomain = new RootDomain { OnlineOrders = { orderDomain } };
+
+ //act
+ RootEf mappedRootEf = mapper.Map(rootDomain);
+
+ //assert
+ OrderEf orderEf = mappedRootEf.Orders[0];
+
+ orderEf.Should().BeOfType();
+ orderEf.Id.ShouldBeEquivalentTo(orderDomain.Id);
+
+ var onlineOrderEf = (OnlineOrderEf)orderEf;
+ onlineOrderEf.Key.ShouldBeEquivalentTo(orderDomain.Key);
+
+ // ------------------------------------------------------------- //
+
+ //arrange again
+ mappedRootEf.Orders.Add(new OnlineOrderEf { Id = "NewId" });
+
+ mapper.Map(mappedRootEf, rootDomain);
+
+ //assert again
+ rootDomain.OnlineOrders.Count.ShouldBeEquivalentTo(2);
+ rootDomain.OnlineOrders.Last().Should().BeOfType();
+
+ //Assert.AreSame(rootDomain.OnlineOrders.First(), orderDomain); that doesn't matter when we map from EF to Domain
+ }
+
+ [Fact]
+ public void Map_FromEfToDomain_And_AddAnOnlineOrderInTheDomainObject_And_ThenMapBackToEf_Should_UseTheSameReferenceInTheEfCollection()
+ {
+ var mapper = CreateMapper();
+
+ //arrange
+ var onlineOrderEf = new OnlineOrderEf { Id = "Id", Key = "Key" };
+ var mailOrderEf = new MailOrderEf { Id = "MailOrderId" };
+ var rootEf = new RootEf { Orders = { onlineOrderEf, mailOrderEf } };
+
+ //act
+ RootDomain mappedRootDomain = mapper.Map(rootEf);
+
+ //assert
+ OnlineOrderDomain onlineOrderDomain = mappedRootDomain.OnlineOrders[0];
+
+ onlineOrderDomain.Should().BeOfType();
+ onlineOrderEf.Id.ShouldBeEquivalentTo(onlineOrderEf.Id);
+
+ // IMPORTANT ASSERT ------------------------------------------------------------- IMPORTANT ASSERT //
+
+ //arrange again
+ mappedRootDomain.OnlineOrders.Add(new OnlineOrderDomain { Id = "NewOnlineOrderId", Key = "NewKey" });
+ mappedRootDomain.MailOrders.Add(new MailOrderDomain { Id = "NewMailOrderId", });
+ onlineOrderDomain.Id = "Hi";
+
+ //act again
+ mapper.Map(mappedRootDomain, rootEf);
+
+ //assert again
+ OrderEf existingMailOrderEf = rootEf.Orders.Single(orderEf => orderEf.Id == mailOrderEf.Id);
+ OrderEf existingOnlineOrderEf = rootEf.Orders.Single(orderEf => orderEf.Id == onlineOrderEf.Id);
+
+ OrderEf newOnlineOrderEf = rootEf.Orders.Single(orderEf => orderEf.Id == "NewOnlineOrderId");
+ OrderEf newMailOrderEf = rootEf.Orders.Single(orderEf => orderEf.Id == "NewMailOrderId");
+
+ rootEf.Orders.Count.ShouldBeEquivalentTo(4);
+ onlineOrderEf.Should().BeSameAs(existingOnlineOrderEf);
+ mailOrderEf.Should().BeSameAs(existingMailOrderEf);
+
+ newOnlineOrderEf.Should().BeOfType();
+ newMailOrderEf.Should().BeOfType();
+ }
+
+ private static bool BaseEquals(OrderDomain oo, OrderEf dto)
+ {
+ return oo.Id == dto.Id;
+ }
+
+ private static bool DerivedEquals(OnlineOrderDomain ood, OnlineOrderEf ooe)
+ {
+ return ood.Key == ooe.Key;
+ }
+
+ public class MailOrderDomain : OrderDomain
+ {
+ }
+
+ public class MailOrderEf : OrderEf
+ {
+ }
+
+ public class OnlineOrderDomain : OrderDomain
+ {
+ public string Key { get; set; }
+ }
+
+ public class OnlineOrderEf : OrderEf
+ {
+ public string Key { get; set; }
+ }
+
+ public abstract class OrderDomain
+ {
+ public string Id { get; set; }
+ }
+
+ public abstract class OrderEf
+ {
+ public string Id { get; set; }
+ }
+
+ public class RootDomain
+ {
+ public List OnlineOrders { get; set; } = new List();
+ public List MailOrders { get; set; } = new List();
+ }
+
+ public class RootEf
+ {
+ public List Orders { get; set; } = new List();
+ }
+
+ public class MergeDomainOrdersToEfOrdersValueResolver : IValueResolver>
+ {
+ public List Resolve(RootDomain source, RootEf destination, List destMember, ResolutionContext context)
+ {
+ var mappedOnlineOrders = new List(destination.Orders);
+ var mappedMailOrders = new List(destination.Orders);
+
+ context.Mapper.Map(source.OnlineOrders, mappedOnlineOrders, context);
+ context.Mapper.Map(source.MailOrders, mappedMailOrders, context);
+
+ var efOrders = mappedOnlineOrders.Union(mappedMailOrders).ToList();
+
+ return efOrders;
+ }
+ }
+ }
+}
diff --git a/src/AutoMapper.Collection.Tests/MapCollectionWithEqualityTests.cs b/src/AutoMapper.Collection.Tests/MapCollectionWithEqualityTests.cs
index 3d5de6d..48cbf9e 100644
--- a/src/AutoMapper.Collection.Tests/MapCollectionWithEqualityTests.cs
+++ b/src/AutoMapper.Collection.Tests/MapCollectionWithEqualityTests.cs
@@ -3,6 +3,7 @@
using System.Linq;
using AutoMapper.EquivalencyExpression;
using FluentAssertions;
+using Xunit;
namespace AutoMapper.Collection
{
@@ -18,6 +19,7 @@ public MapCollectionWithEqualityTests()
});
}
+ [Fact]
public void Should_Keep_Existing_List()
{
var dtos = new List
@@ -35,6 +37,7 @@ public void Should_Keep_Existing_List()
Mapper.Map(dtos, items).Should().BeSameAs(items);
}
+ [Fact]
public void Should_Update_Existing_Item()
{
var dtos = new List
@@ -52,6 +55,7 @@ public void Should_Update_Existing_Item()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Be_Fast_With_Large_Lists()
{
var dtos = new object[100000].Select((_, i) => new ThingDto { ID = i }).ToList();
@@ -61,6 +65,7 @@ public void Should_Be_Fast_With_Large_Lists()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Be_Fast_With_Large_Reversed_Lists()
{
var dtos = new object[100000].Select((_, i) => new ThingDto { ID = i }).ToList();
@@ -71,6 +76,7 @@ public void Should_Be_Fast_With_Large_Reversed_Lists()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Be_Fast_With_Large_Lists_MultiProperty_Mapping()
{
Mapper.Reset();
@@ -87,6 +93,7 @@ public void Should_Be_Fast_With_Large_Lists_MultiProperty_Mapping()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Be_Fast_With_Large_Lists_MultiProperty_Mapping_Cant_Extract()
{
Mapper.Reset();
@@ -103,6 +110,7 @@ public void Should_Be_Fast_With_Large_Lists_MultiProperty_Mapping_Cant_Extract()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Be_Fast_With_Large_Lists_Cant_Extract_Negative()
{
Mapper.Reset();
@@ -120,6 +128,7 @@ public void Should_Be_Fast_With_Large_Lists_Cant_Extract_Negative()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Be_Fast_With_Large_Lists_MultiProperty_Mapping_Cant_Extract_Negative()
{
Mapper.Reset();
@@ -137,6 +146,7 @@ public void Should_Be_Fast_With_Large_Lists_MultiProperty_Mapping_Cant_Extract_N
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Be_Fast_With_Large_Lists_SubObject()
{
Mapper.Reset();
@@ -153,7 +163,8 @@ public void Should_Be_Fast_With_Large_Lists_SubObject()
Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
- public void Should_Be_Fast_With_Large_Lists_SubObject_WrongCollectionType_Should_Throw()
+ [Fact]
+ public void Should_Be_Fast_With_Large_Lists_SubObject_switch_left_and_right_expression()
{
Mapper.Reset();
Mapper.Initialize(x =>
@@ -166,10 +177,10 @@ public void Should_Be_Fast_With_Large_Lists_SubObject_WrongCollectionType_Should
var items = new object[100000].Select((_, i) => new Thing { ID = i }).ToList();
- Action a = () => Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
- a.ShouldThrow().Where(x => x.Message.Contains(typeof(ThingSubDto).FullName) && x.Message.Contains(typeof(ThingDto).FullName));
+ Mapper.Map(dtos, items.ToList()).Should().HaveElementAt(0, items.First());
}
+ [Fact]
public void Should_Work_With_Conditionals()
{
Mapper.Reset();
@@ -207,6 +218,7 @@ public class ClientDto
}
+ [Fact]
public void Should_Work_With_Null_Destination()
{
var dtos = new List
@@ -218,6 +230,7 @@ public void Should_Work_With_Null_Destination()
Mapper.Map>(dtos).Should().HaveSameCount(dtos);
}
+ [Fact]
public void Should_Work_With_Comparing_String_Types()
{
Mapper.Reset();
@@ -297,6 +310,7 @@ public class SaleCharge
public decimal Value { get; set; }
}
+ [Fact]
public void Should_Be_Instanced_Based()
{
Mapper.Reset();
@@ -321,6 +335,7 @@ public void Should_Be_Instanced_Based()
Mapper.Map(dtos, items.ToList()).Should().NotContain(items.First());
}
+ [Fact]
public void Parent_Should_Be_Same_As_Root_Object()
{
var mapper = new MapperConfiguration(
diff --git a/src/AutoMapper.Collection.Tests/OptionsTests.cs b/src/AutoMapper.Collection.Tests/OptionsTests.cs
index 3bd17f0..f4b3634 100644
--- a/src/AutoMapper.Collection.Tests/OptionsTests.cs
+++ b/src/AutoMapper.Collection.Tests/OptionsTests.cs
@@ -2,11 +2,13 @@
using AutoMapper.Mappers;
using FluentAssertions;
using System.Collections.Generic;
+using Xunit;
namespace AutoMapper.Collection
{
public class OptionsTests
{
+ [Fact]
public void Should_Retain_Options_Passed_In_Map()
{
var collectionTestValue = 0;
diff --git a/src/AutoMapper.Collection.Tests/Properties/AssemblyInfo.cs b/src/AutoMapper.Collection.Tests/Properties/AssemblyInfo.cs
deleted file mode 100644
index da03948..0000000
--- a/src/AutoMapper.Collection.Tests/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("AutoMapper.Collection.Tests")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("AutoMapper.Collection.Tests")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("2d3d34ad-6a0a-4382-9a2f-894f52d184a7")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/AutoMapper.Collection.Tests/packages.config b/src/AutoMapper.Collection.Tests/packages.config
deleted file mode 100644
index b6dd6ba..0000000
--- a/src/AutoMapper.Collection.Tests/packages.config
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/AutoMapper.Collection/AutoMapper.Collection.csproj b/src/AutoMapper.Collection/AutoMapper.Collection.csproj
index 0677f43..dd76d77 100644
--- a/src/AutoMapper.Collection/AutoMapper.Collection.csproj
+++ b/src/AutoMapper.Collection/AutoMapper.Collection.csproj
@@ -2,9 +2,9 @@
Collection Add/Remove/Update support for AutoMapper. AutoMapper.Collection adds EqualityComparison Expressions for TypeMaps to determine if Source and Destination type are equivalent to each other when mapping collections.
- 3.1.4
+ 3.1.3
Tyler Carlson
- net45;netstandard1.1;netstandard1.3
+ netstandard2.0;netstandard1.3;net45
AutoMapper.Collection
AutoMapper.Collection
https://s3.amazonaws.com/automapper/icon.png
@@ -14,10 +14,11 @@
../Key.snk
true
true
+ 3.1.4
-
+
diff --git a/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpression.cs b/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpression.cs
index 7ef0346..fca2932 100644
--- a/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpression.cs
+++ b/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpression.cs
@@ -18,6 +18,11 @@ public int GetHashCode(object obj)
{
throw new Exception("How'd you get here");
}
+
+ public bool IsEquivalent(object source, object destination)
+ {
+ return false;
+ }
}
internal class EquivalentExpression : IEquivalentComparer
@@ -43,9 +48,22 @@ public EquivalentExpression(Expression> equivale
_destinationHashCodeFunc = members.Item2.GetHashCodeExpression(destinationParameter).Compile();
}
- public bool IsEquivalent(TSource source, TDestination destination)
+ public bool IsEquivalent(object source, object destination)
{
- return _equivalentFunc(source, destination);
+ var src = source as TSource;
+ var dest = destination as TDestination;
+
+ if (src == null && dest == null)
+ {
+ return true;
+ }
+
+ if (src == null || dest == null)
+ {
+ return false;
+ }
+
+ return _equivalentFunc(src, dest);
}
public Expression> ToSingleSourceExpression(TSource source)
diff --git a/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpressions.cs b/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpressions.cs
index 9393cf3..e403fd8 100644
--- a/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpressions.cs
+++ b/src/AutoMapper.Collection/EquivalencyExpression/EquivalentExpressions.cs
@@ -52,14 +52,37 @@ private static void InsertBefore(this IMapperConfigurationExpress
internal static IEquivalentComparer GetEquivalentExpression(this IConfigurationObjectMapper mapper, Type sourceType, Type destinationType)
{
var typeMap = mapper.ConfigurationProvider.ResolveTypeMap(sourceType, destinationType);
- return typeMap == null ? null : GetEquivalentExpression(mapper.ConfigurationProvider, typeMap);
+ if (typeMap == null)
+ {
+ return null;
+ }
+
+ var comparer = GetEquivalentExpression(mapper.ConfigurationProvider, typeMap);
+ if (comparer == null)
+ {
+ foreach (var item in typeMap.IncludedBaseTypes)
+ {
+ var baseTypeMap = mapper.ConfigurationProvider.ResolveTypeMap(item.SourceType, item.DestinationType);
+ if (baseTypeMap == null)
+ {
+ continue;
+ }
+
+ comparer = GetEquivalentExpression(mapper.ConfigurationProvider, baseTypeMap);
+ if (comparer != null)
+ {
+ break;
+ }
+ }
+ }
+ return comparer;
}
internal static IEquivalentComparer GetEquivalentExpression(IConfigurationProvider configurationProvider, TypeMap typeMap)
{
return EquivalentExpressionDictionary[configurationProvider].GetOrAdd(typeMap.Types,
tp =>
- GeneratePropertyMapsDictionary[configurationProvider].Select(_ =>_.GeneratePropertyMaps(typeMap).CreateEquivalentExpression()).FirstOrDefault(_ => _ != null));
+ GeneratePropertyMapsDictionary[configurationProvider].Select(_ => _.GeneratePropertyMaps(typeMap).CreateEquivalentExpression()).FirstOrDefault(_ => _ != null));
}
///
diff --git a/src/AutoMapper.Collection/EquivalencyExpression/IEquivalentComparer.cs b/src/AutoMapper.Collection/EquivalencyExpression/IEquivalentComparer.cs
index ee805a2..317fb30 100644
--- a/src/AutoMapper.Collection/EquivalencyExpression/IEquivalentComparer.cs
+++ b/src/AutoMapper.Collection/EquivalencyExpression/IEquivalentComparer.cs
@@ -6,11 +6,11 @@ namespace AutoMapper.EquivalencyExpression
public interface IEquivalentComparer
{
int GetHashCode(object obj);
+ bool IsEquivalent(object source, object destination);
}
public interface IEquivalentComparer : IEquivalentComparer
{
- bool IsEquivalent(TSource source, TDestination destination);
Expression> ToSingleSourceExpression(TSource destination);
}
}
\ No newline at end of file
diff --git a/src/AutoMapper.Collection/Mappers/EquivalentExpressionAddRemoveCollectionMapper.cs b/src/AutoMapper.Collection/Mappers/EquivalentExpressionAddRemoveCollectionMapper.cs
index 5b10a31..7911d37 100644
--- a/src/AutoMapper.Collection/Mappers/EquivalentExpressionAddRemoveCollectionMapper.cs
+++ b/src/AutoMapper.Collection/Mappers/EquivalentExpressionAddRemoveCollectionMapper.cs
@@ -14,7 +14,7 @@ public class EquivalentExpressionAddRemoveCollectionMapper : IConfigurationObjec
public IConfigurationProvider ConfigurationProvider { get; set; }
- public static TDestination Map(TSource source, TDestination destination, ResolutionContext context, IEquivalentComparer _equivalentComparer)
+ public static TDestination Map(TSource source, TDestination destination, ResolutionContext context, IEquivalentComparer equivalentComparer)
where TSource : IEnumerable
where TDestination : class, ICollection
{
@@ -23,17 +23,17 @@ public static TDestination Map _equivalentComparer.GetHashCode(x)).ToDictionary(x => x.Key, x => x.ToList());
+ var destList = destination.ToLookup(x => equivalentComparer.GetHashCode(x)).ToDictionary(x => x.Key, x => x.ToList());
var items = source.Select(x =>
{
- var sourceHash = _equivalentComparer.GetHashCode(x);
+ var sourceHash = equivalentComparer.GetHashCode(x);
var item = default(TDestinationItem);
List itemList;
if (destList.TryGetValue(sourceHash, out itemList))
{
- item = itemList.FirstOrDefault(dest => _equivalentComparer.IsEquivalent(x, dest));
+ item = itemList.FirstOrDefault(dest => equivalentComparer.IsEquivalent(x, dest));
if (item != null)
{
itemList.Remove(item);
@@ -70,17 +70,21 @@ public bool IsMatch(TypePair typePair)
&& typePair.DestinationType.IsCollectionType()
&& this.GetEquivalentExpression(TypeHelper.GetElementType(typePair.SourceType), TypeHelper.GetElementType(typePair.DestinationType)) != null;
}
-
+
public Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, PropertyMap propertyMap,
Expression sourceExpression, Expression destExpression, Expression contextExpression)
{
+ var sourceType = TypeHelper.GetElementType(sourceExpression.Type);
+ var destType = TypeHelper.GetElementType(destExpression.Type);
+
+ var method = MapMethodInfo.MakeGenericMethod(sourceExpression.Type, sourceType, destExpression.Type, destType);
+ var equivalencyExpression = this.GetEquivalentExpression(sourceType, destType);
+
+ var equivalencyExpressionConst = Constant(equivalencyExpression);
+ var map = Call(null, method, sourceExpression, destExpression, contextExpression, equivalencyExpressionConst);
+
var notNull = NotEqual(destExpression, Constant(null));
- var EquivalencyExpression = this.GetEquivalentExpression(TypeHelper.GetElementType(sourceExpression.Type), TypeHelper.GetElementType(destExpression.Type));
- var map = Call(null,
- MapMethodInfo.MakeGenericMethod(sourceExpression.Type, TypeHelper.GetElementType(sourceExpression.Type), destExpression.Type, TypeHelper.GetElementType(destExpression.Type)),
- sourceExpression, destExpression, contextExpression, Constant(EquivalencyExpression));
var collectionMap = CollectionMapper.MapExpression(configurationProvider, profileMap, propertyMap, sourceExpression, destExpression, contextExpression);
-
return Condition(notNull, map, Convert(collectionMap, destExpression.Type));
}
}
diff --git a/version.props b/version.props
new file mode 100644
index 0000000..4d47fba
--- /dev/null
+++ b/version.props
@@ -0,0 +1,5 @@
+
+
+ 3.1.4
+
+
\ No newline at end of file