diff --git a/.gitignore b/.gitignore index 9af4313..7a01dab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ Out .svn obj bin +BenchmarkDotNet.Artifacts .idea _ReSharper* *.sln.GhostDoc.xml @@ -14,3 +15,4 @@ _ReSharper* *.ncrunchproject /packages/*/* /Tools/*/* +/.vs diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe index 9ca6659..83ead7a 100644 Binary files a/.nuget/NuGet.exe and b/.nuget/NuGet.exe differ diff --git a/CONTRIBUTING.markdown b/CONTRIBUTING.markdown index 3c92d2e..cb7c3a2 100644 --- a/CONTRIBUTING.markdown +++ b/CONTRIBUTING.markdown @@ -1,9 +1,5 @@ ## Contributing -### Before you do anything else - - * Before reporting an issue or creating a pull request, please discuss it in the [Google Group](https://groups.google.com/group/tinymapper) - ### To contribute please follow these guidelines: * Fork the project diff --git a/GlobalAssemblyInfo.cs b/GlobalAssemblyInfo.cs index 16f11a4..97a032c 100644 --- a/GlobalAssemblyInfo.cs +++ b/GlobalAssemblyInfo.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34209 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/README.md b/README.md index f0c4539..f490cee 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ TinyMapper - a quick object mapper for .Net ====================================================== -[![Nuget downloads](http://img.shields.io/nuget/dt/tinymapper.svg)](https://www.nuget.org/packages/TinyMapper/) +[![Nuget downloads](https://img.shields.io/nuget/v/tinymapper.svg)](https://www.nuget.org/packages/TinyMapper/) [![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/TinyMapper/TinyMapper/blob/master/LICENSE) +[![GitHub license](https://img.shields.io/badge/first--timers--only-friendly-blue.svg)](http://www.firsttimersonly.com/) ## Performance Comparison @@ -51,7 +52,9 @@ var personDto = TinyMapper.Map(person); ``` `TinyMapper` supports the following platforms: -* .Net 3.0+ +* .Net 3.5+ +* [.NET Standard 1.3](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) +* .NET Core * Mono ## What to read @@ -63,10 +66,14 @@ var personDto = TinyMapper.Map(person); ## Contributors A big thanks to all of TinyMapper's contributors: + * [nzaugg](https://github.com/nzaugg) + * [Sdzeng](https://github.com/Sdzeng) * [iEmiya](https://github.com/iEmiya) * [lijaso](https://github.com/lijaso) * [nomailme](https://github.com/nomailme) * [Skaiol](https://github.com/Skaiol) * [Sufflavus](https://github.com/Sufflavus) * [qihangnet](https://github.com/qihangnet) - * [teknogecko](https://github.com/teknogecko) \ No newline at end of file + * [teknogecko](https://github.com/teknogecko) + * [Samtrion](https://github.com/Samtrion) + * [DerHulk](https://github.com/DerHulk) diff --git a/Site/index.html b/Site/index.html index 64ee322..bb0e8b9 100644 --- a/Site/index.html +++ b/Site/index.html @@ -229,7 +229,7 @@
Links
var data = { - labels: ["Handwritten", "TinyMapper", "AutoMapper.NET"], + labels: ["Handwritten", "TinyMapper v2.0.5", "AutoMapper.NET v4.0.4"], datasets: [ { label: "Complex object mapping", @@ -237,7 +237,7 @@
Links
strokeColor: "rgba(151,187,205,0.8)", highlightFill: "rgba(151,187,205,0.75)", highlightStroke: "rgba(151,187,205,1)", - data: [20,78,7818] + data: [21,81,4846] } ] }; diff --git a/Source/Benchmark/App.config b/Source/Benchmark/App.config deleted file mode 100644 index 5c73fc5..0000000 --- a/Source/Benchmark/App.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Source/Benchmark/Benchmark.csproj b/Source/Benchmark/Benchmark.csproj index 6a166af..a230109 100644 --- a/Source/Benchmark/Benchmark.csproj +++ b/Source/Benchmark/Benchmark.csproj @@ -1,120 +1,13 @@ - - - - - Debug - AnyCPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36} - Exe - Properties - Benchmark - Benchmark - v4.5 - 512 - ..\..\ - true - - - AnyCPU - true - full - false - ..\..\Out\Debug\Benchmark\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - ..\..\Out\Release\Benchmark\ - TRACE - prompt - 4 - - - bin\Release 4.0\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - bin\Release 3.5\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - bin\Release 3.0\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - - ..\..\packages\AutoMapper.3.3.0\lib\net40\AutoMapper.dll - - - ..\..\packages\AutoMapper.3.3.0\lib\net40\AutoMapper.Net4.dll - - - ..\..\packages\Nelibur.Sword.3.1.6\lib\net45\Nelibur.Sword.dll - - - - - - - - - - - - - - - - - - - - - - - {c7ebd676-248b-484a-a5cb-df879670652a} - TinyMapper - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + net46 + Debug;Release + exe + + + + + + + \ No newline at end of file diff --git a/Source/Benchmark/Benchmarks/Benchmark.cs b/Source/Benchmark/Benchmarks/Benchmark.cs deleted file mode 100644 index 8f7befa..0000000 --- a/Source/Benchmark/Benchmarks/Benchmark.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; - -namespace Benchmark.Benchmarks -{ - public abstract class Benchmark - { - protected readonly int _iterations; - - protected Benchmark(int iterations) - { - _iterations = iterations; - InitMappers(); - } - - public void Measure() - { - string inputInfo = MeasureMapperInputInfo(); - if (string.IsNullOrWhiteSpace(inputInfo)) - { - Console.WriteLine("Iterations: {0}", _iterations); - } - else - { - Console.WriteLine("Iterations: {0}, {1}", _iterations, inputInfo); - } - - TimeSpan handmade = MeasureHandmade(); - Console.WriteLine("Handmade: {0}ms", handmade.TotalMilliseconds); - - TimeSpan tinyMapper = MeasureTinyMapper(); - Console.WriteLine("TinyMapper: {0}ms", tinyMapper.TotalMilliseconds); - - TimeSpan autoMapper = MeasureAutoMapper(); - Console.WriteLine("AutoMapper: {0}ms", autoMapper.TotalMilliseconds); - - Console.WriteLine(Environment.NewLine); - } - - protected abstract void InitMappers(); - - protected abstract TimeSpan MeasureAutoMapper(); - protected abstract TimeSpan MeasureHandmade(); - - protected virtual string MeasureMapperInputInfo() - { - return string.Empty; - } - - protected abstract TimeSpan MeasureTinyMapper(); - } -} diff --git a/Source/Benchmark/Benchmarks/CollectionBenchmark.cs b/Source/Benchmark/Benchmarks/CollectionBenchmark.cs deleted file mode 100644 index eed7369..0000000 --- a/Source/Benchmark/Benchmarks/CollectionBenchmark.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using AutoMapper; -using Nelibur.ObjectMapper; -using Nelibur.Sword.Extensions; - -namespace Benchmark.Benchmarks -{ - public sealed class CollectionBenchmark : Benchmark - { - private const int CollectionLength = 100; - - public CollectionBenchmark(int iterations) : base(iterations) - { - } - - protected override void InitMappers() - { - TinyMapper.Bind(); - - Mapper.CreateMap(); - } - - protected override TimeSpan MeasureAutoMapper() - { - Source source = CreateSource(); - Mapper.Map(source); - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < _iterations; i++) - { - var target = Mapper.Map(source); - } - stopwatch.Stop(); - return stopwatch.Elapsed; - } - - protected override TimeSpan MeasureHandmade() - { - Source source = CreateSource(); - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < _iterations; i++) - { - var target = new Target(); - target = HandmadeMap(source, target); - } - - stopwatch.Stop(); - return stopwatch.Elapsed; - } - - protected override string MeasureMapperInputInfo() - { - return string.Format("CollectionLength: {0}", CollectionLength); - } - - protected override TimeSpan MeasureTinyMapper() - { - Source source = CreateSource(); - TinyMapper.Map(source); - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < _iterations; i++) - { - var target = TinyMapper.Map(source); - } - stopwatch.Stop(); - return stopwatch.Elapsed; - } - - private static Item CreateItem() - { - return new Item - { - FirstName = "John", - LastName = "Doe", - Nickname = "TinyMapper", - Email = "support@TinyMapper.net", - Short = 3, - Long = 10, - Int = 5, - Float = 4.9f, - Decimal = 4.0m, - DateTime = DateTime.Now, - Char = 'a', - Bool = true, - Byte = 0 - }; - } - - private static Source CreateSource() - { - var result = new Source(); - CollectionLength.Times(x => result.StringList.Add(Guid.NewGuid().ToString())); - CollectionLength.Times(x => result.ItemList.Add(CreateItem())); - return result; - } - - private static Target HandmadeMap(Source source, Target target) - { - target.StringList.AddRange(source.StringList); - source.ItemList.ForEach(x => target.ItemList.Add(HandmadeMap(x, new Item()))); - return target; - } - - private static Item HandmadeMap(Item source, Item target) - { - target.Bool = source.Bool; - target.Byte = source.Byte; - target.Char = source.Char; - target.DateTime = source.DateTime; - target.Decimal = source.Decimal; - target.Float = source.Float; - target.Int = source.Int; - target.Long = source.Long; - target.Short = source.Short; - target.FirstName = source.FirstName; - target.LastName = source.LastName; - target.Nickname = source.Nickname; - target.Email = source.Email; - return target; - } - - public class Item - { - public bool Bool { get; set; } - public byte Byte { get; set; } - public char Char { get; set; } - public DateTime DateTime { get; set; } - public decimal Decimal { get; set; } - public string Email { get; set; } - public string FirstName { get; set; } - public float Float { get; set; } - public int Int { get; set; } - public string LastName { get; set; } - public long Long { get; set; } - public string Nickname { get; set; } - public short Short { get; set; } - } - - public class Source - { - public Source() - { - ItemList = new List(); - StringList = new List(); - } - - public List ItemList { get; set; } - public List StringList { get; set; } - } - - public class Target - { - public Target() - { - ItemList = new List(); - StringList = new List(); - } - - public List ItemList { get; set; } - public List StringList { get; set; } - } - } -} diff --git a/Source/Benchmark/Benchmarks/PrimitiveTypeBenchmark.cs b/Source/Benchmark/Benchmarks/PrimitiveTypeBenchmark.cs deleted file mode 100644 index 5ac187b..0000000 --- a/Source/Benchmark/Benchmarks/PrimitiveTypeBenchmark.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Diagnostics; -using AutoMapper; -using Nelibur.ObjectMapper; - -namespace Benchmark.Benchmarks -{ - public sealed class PrimitiveTypeBenchmark : Benchmark - { - public PrimitiveTypeBenchmark(int iterations) : base(iterations) - { - } - - protected override void InitMappers() - { - TinyMapper.Bind(); - Mapper.CreateMap(); - } - - protected override TimeSpan MeasureAutoMapper() - { - Source source = CreateSource(); - Mapper.Map(source); - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < _iterations; i++) - { - var target = Mapper.Map(source); - } - stopwatch.Stop(); - return stopwatch.Elapsed; - } - - protected override TimeSpan MeasureHandmade() - { - Source source = CreateSource(); - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < _iterations; i++) - { - var target = new Target(); - target = HandmadeMap(source, target); - } - - stopwatch.Stop(); - return stopwatch.Elapsed; - } - - protected override TimeSpan MeasureTinyMapper() - { - Source source = CreateSource(); - TinyMapper.Map(source); - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < _iterations; i++) - { - var target = TinyMapper.Map(source); - } - stopwatch.Stop(); - return stopwatch.Elapsed; - } - - private static Source CreateSource() - { - return new Source - { - FirstName = "John", - LastName = "Doe", - Nickname = "TinyMapper", - Email = "support@TinyMapper.net", - Short = 3, - Long = 10, - Int = 5, - Float = 4.9f, - Decimal = 4.0m, - DateTime = DateTime.Now, - Char = 'a', - Bool = true, - Byte = 0 - }; - } - - private static Target HandmadeMap(Source source, Target target) - { - target.Bool = source.Bool; - target.Byte = source.Byte; - target.Char = source.Char; - target.DateTime = source.DateTime; - target.Decimal = source.Decimal; - target.Float = source.Float; - target.Int = source.Int; - target.Long = source.Long; - target.Short = source.Short; - target.FirstName = source.FirstName; - target.LastName = source.LastName; - target.Nickname = source.Nickname; - target.Email = source.Email; - return target; - } - - public class Source - { - public bool Bool { get; set; } - public byte Byte { get; set; } - public char Char { get; set; } - public DateTime DateTime { get; set; } - public decimal Decimal { get; set; } - public string Email { get; set; } - public string FirstName { get; set; } - public float Float { get; set; } - public int Int { get; set; } - public string LastName { get; set; } - public long Long { get; set; } - public string Nickname { get; set; } - public short Short { get; set; } - } - - public class Target - { - public bool Bool { get; set; } - public byte Byte { get; set; } - public char Char { get; set; } - public DateTime DateTime { get; set; } - public decimal Decimal { get; set; } - public string Email { get; set; } - public string FirstName { get; set; } - public float Float { get; set; } - public int Int { get; set; } - public string LastName { get; set; } - public long Long { get; set; } - public string Nickname { get; set; } - public short Short { get; set; } - } - } -} diff --git a/Source/Benchmark/CollectionBenchmark.cs b/Source/Benchmark/CollectionBenchmark.cs new file mode 100644 index 0000000..6fda347 --- /dev/null +++ b/Source/Benchmark/CollectionBenchmark.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using AutoMapper; +using BenchmarkDotNet.Attributes; +using Nelibur.ObjectMapper; + +namespace Benchmark +{ + public class CollectionBenchmark + { + private const int CollectionLength = 100; + private readonly SourceWithCollections _source = CreateSource(); + + private const int Iterations = 1; + + + public CollectionBenchmark() + { + InitTinyMapper(); + InitTinyAutoMapper(); + } + + private void InitTinyMapper() + { + TinyMapper.Bind(); + } + + private void InitTinyAutoMapper() + { + Mapper.Initialize(x => + { + x.CreateMap(); + x.CreateMap(); + }); + } + + [Benchmark] + public void CollectionMapping_AutoMapper() + { + for (int i = 0; i < Iterations; i++) + { + Mapper.Map(_source); + } + } + + [Benchmark] + public void CollectionMapping_TinyMapper() + { + for (int i = 0; i < Iterations; i++) + { + TinyMapper.Map(_source); + } + } + + [Benchmark] + public void CollectionMapping_Handwritten() + { + for (int i = 0; i < Iterations; i++) + { + HandwrittenMap(_source, new TargetWithCollections()); + } + } + + private static TargetWithCollections HandwrittenMap(SourceWithCollections source, TargetWithCollections target) + { + target.StringList.AddRange(source.StringList); + source.ItemList.ForEach(x => target.ItemList.Add(HandwrittenMap(x, new Item()))); + return target; + } + + private static Item HandwrittenMap(Item source, Item target) + { + target.Bool = source.Bool; + target.Byte = source.Byte; + target.Char = source.Char; + target.DateTime = source.DateTime; + target.Decimal = source.Decimal; + target.Float = source.Float; + target.Int = source.Int; + target.Long = source.Long; + target.Short = source.Short; + target.FirstName = source.FirstName; + target.LastName = source.LastName; + target.Nickname = source.Nickname; + target.Email = source.Email; + return target; + } + + private static SourceWithCollections CreateSource() + { + var result = new SourceWithCollections(); + + for (int i = 0; i < CollectionLength; i++) + { + result.StringList.Add(Guid.NewGuid().ToString()); + result.ItemList.Add(CreateItem()); + } + return result; + } + + private static Item CreateItem() + { + return new Item + { + FirstName = "John", + LastName = "Doe", + Nickname = "TinyMapper", + Email = "support@TinyMapper.net", + Short = 3, + Long = 10, + Int = 5, + Float = 4.9f, + Decimal = 4.0m, + DateTime = DateTime.Now, + Char = 'a', + Bool = true, + Byte = 0 + }; + } + } + + + public class SourceWithCollections + { + public SourceWithCollections() + { + ItemList = new List(); + StringList = new List(); + } + + public List ItemList { get; set; } + public List StringList { get; set; } + } + + + public class TargetWithCollections + { + public TargetWithCollections() + { + ItemList = new List(); + StringList = new List(); + } + + public List ItemList { get; set; } + public List StringList { get; set; } + } + + + public class Item + { + public bool Bool { get; set; } + public byte Byte { get; set; } + public char Char { get; set; } + public DateTime DateTime { get; set; } + public decimal Decimal { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public float Float { get; set; } + public int Int { get; set; } + public string LastName { get; set; } + public long Long { get; set; } + public string Nickname { get; set; } + public short Short { get; set; } + } +} diff --git a/Source/Benchmark/DataSource/PrimitiveTypeMapping.jpg b/Source/Benchmark/DataSource/PrimitiveTypeMapping.jpg index 04517fc..6b70272 100644 Binary files a/Source/Benchmark/DataSource/PrimitiveTypeMapping.jpg and b/Source/Benchmark/DataSource/PrimitiveTypeMapping.jpg differ diff --git a/Source/Benchmark/PrimitiveTypeBenchmark.cs b/Source/Benchmark/PrimitiveTypeBenchmark.cs new file mode 100644 index 0000000..0f1a401 --- /dev/null +++ b/Source/Benchmark/PrimitiveTypeBenchmark.cs @@ -0,0 +1,96 @@ +using System; +using AutoMapper; +using BenchmarkDotNet.Attributes; +using Nelibur.ObjectMapper; + +namespace Benchmark +{ + public class PrimitiveTypeBenchmark + { + private readonly SourceWithPrimitiveTypes _source = CreateSource(); + + public PrimitiveTypeBenchmark() + { + InitTinyMapper(); + InitTinyAutoMapper(); + } + + private void InitTinyMapper() + { + TinyMapper.Bind(); + } + + private void InitTinyAutoMapper() + { + Mapper.Initialize(x => x.CreateMap()); + } + + private static SourceWithPrimitiveTypes CreateSource() + { + return new SourceWithPrimitiveTypes + { + FirstName = "John", + LastName = "Doe", + Nickname = "TinyMapper", + Email = "support@TinyMapper.net", + Short = 3, + Long = 10, + Int = 5, + Float = 4.9f, + Decimal = 4.0m, + DateTime = DateTime.Now, + Char = 'a', + Bool = true, + Byte = 0 + }; + } + + [Benchmark] + public void BenchmarkTinyMapper() + { + TinyMapper.Map(_source); + } + + [Benchmark] + public void BenchmarkAutoMapper() + { + Mapper.Map(_source); + } + } + + + public sealed class SourceWithPrimitiveTypes + { + public bool Bool { get; set; } + public byte Byte { get; set; } + public char Char { get; set; } + public DateTime DateTime { get; set; } + public decimal Decimal { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public float Float { get; set; } + public int Int { get; set; } + public string LastName { get; set; } + public long Long { get; set; } + public string Nickname { get; set; } + public short Short { get; set; } + } + + + public sealed class TargetWithPrimitiveTypes + { + public bool Bool { get; set; } + public byte Byte { get; set; } + public char Char { get; set; } + public DateTime DateTime { get; set; } + public decimal Decimal { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public float Float { get; set; } + public int Int { get; set; } + public string LastName { get; set; } + public long Long { get; set; } + public string Nickname { get; set; } + public short Short { get; set; } + } +} diff --git a/Source/Benchmark/Program.cs b/Source/Benchmark/Program.cs index 4611f58..5162ae5 100644 --- a/Source/Benchmark/Program.cs +++ b/Source/Benchmark/Program.cs @@ -1,22 +1,16 @@ -using System; -using Benchmark.Benchmarks; - -namespace Benchmark -{ - internal class Program - { - private const int Iterations = 100000; - - private static void Main() - { - var primitiveTypeBenchmark = new PrimitiveTypeBenchmark(Iterations); - primitiveTypeBenchmark.Measure(); - - var collectionBenchmark = new CollectionBenchmark(Iterations); - collectionBenchmark.Measure(); - - Console.WriteLine("Press any key to Exit"); - Console.ReadLine(); - } - } -} +using System; +using BenchmarkDotNet.Running; + +namespace Benchmark +{ + public class Program + { + public static void Main() + { +// BenchmarkRunner.Run(); + BenchmarkRunner.Run(); + + Console.ReadKey(); + } + } +} diff --git a/Source/Benchmark/Properties/AssemblyInfo.cs b/Source/Benchmark/Properties/AssemblyInfo.cs deleted file mode 100644 index efeb241..0000000 --- a/Source/Benchmark/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Reflection; -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("Benchmark")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Benchmark")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[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("5b5db851-ab1f-4da4-83d8-22e08437c107")] - -// 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/Source/Benchmark/packages.config b/Source/Benchmark/packages.config deleted file mode 100644 index 51b3ae9..0000000 --- a/Source/Benchmark/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Source/BenchmarkInternal/Benchmark.cs b/Source/BenchmarkInternal/Benchmark.cs new file mode 100644 index 0000000..b1b87ec --- /dev/null +++ b/Source/BenchmarkInternal/Benchmark.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using Nelibur.ObjectMapper; + +namespace BenchmarkInternal +{ + public class Benchmark + { + private const int Iterations = 10; + + private readonly SourceTest _sourceTest = CreateSource(); + + public Benchmark() + { + TinyMapper.Bind(); + } + + [Benchmark] + public void TinyMapperCollectionMapping() + { + for (int i = 0; i < Iterations; i++) + { + TinyMapper.Map(_sourceTest); + } + } + + [Benchmark] + public void HandwrittenCollectionMapping() + { + for (int i = 0; i < Iterations; i++) + { + var result = new TargetTest + { + List = new List() + }; + result.List.AddRange(_sourceTest.List); + } + } + + private static SourceTest CreateSource() + { + var result = new SourceTest + { + List = new List() + }; + + for (int i = 0; i < 100; i++) + { + result.List.Add(i); + } + return result; + } + } + + public class SourceTest + { + public List List { get; set; } + } + + public class TargetTest + { + public List List { get; set; } + } +} diff --git a/Source/BenchmarkInternal/BenchmarkInternal.csproj b/Source/BenchmarkInternal/BenchmarkInternal.csproj new file mode 100644 index 0000000..2f113f6 --- /dev/null +++ b/Source/BenchmarkInternal/BenchmarkInternal.csproj @@ -0,0 +1,17 @@ + + + + net46 + Debug;Release + exe + + + + + + + + + + + \ No newline at end of file diff --git a/Source/BenchmarkInternal/Program.cs b/Source/BenchmarkInternal/Program.cs new file mode 100644 index 0000000..e98df54 --- /dev/null +++ b/Source/BenchmarkInternal/Program.cs @@ -0,0 +1,16 @@ +using System; +using BenchmarkDotNet.Running; + +namespace BenchmarkInternal +{ + class Program + { + static void Main(string[] args) + { +// BenchmarkRunner.Run(); + BenchmarkRunner.Run(); + + Console.ReadKey(); + } + } +} \ No newline at end of file diff --git a/Source/BenchmarkInternal/TypeConverters.cs b/Source/BenchmarkInternal/TypeConverters.cs new file mode 100644 index 0000000..89333a2 --- /dev/null +++ b/Source/BenchmarkInternal/TypeConverters.cs @@ -0,0 +1,22 @@ +using System.ComponentModel; +using BenchmarkDotNet.Attributes; + +namespace BenchmarkInternal +{ + public class TypeConverters + { + private readonly TypeConverter _intToString = TypeDescriptor.GetConverter(typeof(int)); + + [Benchmark] + public void IntToStringByConverter() + { + var result = _intToString.ConvertTo(1, typeof(string)); + } + + [Benchmark] + public void IntToStringByToString() + { + var result = 1.ToString(); + } + } +} diff --git a/Source/TestUnity/Assets/Plugins.meta b/Source/TestUnity/Assets/Plugins.meta new file mode 100644 index 0000000..c3a20a7 --- /dev/null +++ b/Source/TestUnity/Assets/Plugins.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: eb019e2211ce0314e9c388094a74984a +folderAsset: yes +timeCreated: 1509172083 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/TestUnity/Assets/Plugins/TinyMapper.dll b/Source/TestUnity/Assets/Plugins/TinyMapper.dll new file mode 100644 index 0000000..a921e77 Binary files /dev/null and b/Source/TestUnity/Assets/Plugins/TinyMapper.dll differ diff --git a/Source/TestUnity/Assets/Plugins/TinyMapper.dll.meta b/Source/TestUnity/Assets/Plugins/TinyMapper.dll.meta new file mode 100644 index 0000000..c0c8ed2 --- /dev/null +++ b/Source/TestUnity/Assets/Plugins/TinyMapper.dll.meta @@ -0,0 +1,25 @@ +fileFormatVersion: 2 +guid: dfe8367e91feaf448a68e495df9477de +timeCreated: 1509172107 +licenseType: Pro +PluginImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + isOverridable: 0 + platformData: + Any: + enabled: 1 + settings: {} + Editor: + enabled: 0 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/TestUnity/Assets/Scene.meta b/Source/TestUnity/Assets/Scene.meta new file mode 100644 index 0000000..31e4e0c --- /dev/null +++ b/Source/TestUnity/Assets/Scene.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6207deb034bc429408cf6cac46203817 +folderAsset: yes +timeCreated: 1509172093 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/TestUnity/Assets/Scene/TestScene.unity b/Source/TestUnity/Assets/Scene/TestScene.unity new file mode 100644 index 0000000..0e310af Binary files /dev/null and b/Source/TestUnity/Assets/Scene/TestScene.unity differ diff --git a/Source/TestUnity/Assets/Scene/TestScene.unity.meta b/Source/TestUnity/Assets/Scene/TestScene.unity.meta new file mode 100644 index 0000000..6da191c --- /dev/null +++ b/Source/TestUnity/Assets/Scene/TestScene.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b76b4b871849e654092465da6f2633ce +timeCreated: 1509172259 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/TestUnity/Assets/Script.meta b/Source/TestUnity/Assets/Script.meta new file mode 100644 index 0000000..2f1b240 --- /dev/null +++ b/Source/TestUnity/Assets/Script.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: bdc20e4cc6481ee4e861336c636cc5ef +folderAsset: yes +timeCreated: 1509172101 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/TestUnity/Assets/Script/MapTest.cs b/Source/TestUnity/Assets/Script/MapTest.cs new file mode 100644 index 0000000..b1acf9f --- /dev/null +++ b/Source/TestUnity/Assets/Script/MapTest.cs @@ -0,0 +1,48 @@ +using Nelibur.ObjectMapper; +using UnityEngine; + +namespace Assets.Script +{ + public class MapTest : MonoBehaviour + { + private void Start() + { + var testModel = new TestModel + { + Id = 1, + Name = "haha" + }; + TinyMapper.Bind(); + var dto = TinyMapper.Map(testModel); + Debug.LogFormat("TestModel Id[{0}] Name[{1}] => TestDto Id[{2}] Name[{3}]", testModel.Id, testModel.Name, dto.Id, dto.Name); + + var testStaticModel = new TestStaticModel(); + TinyMapper.Bind(); + var testDto = TinyMapper.Map(testStaticModel); + Debug.LogFormat("TestStaticModel Id[{0}] Name[{1}] => TestDto Id[{2}] Name[{3}]", TestStaticModel.Id, TestStaticModel.Name, testDto.Id, testDto.Name); + } + + private void Update() + { + + } + } + + public class TestModel + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class TestStaticModel + { + public static int Id = 1; + public static string Name = "test"; + } + + public class TestDto + { + public int Id { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Source/TestUnity/Assets/Script/MapTest.cs.meta b/Source/TestUnity/Assets/Script/MapTest.cs.meta new file mode 100644 index 0000000..6e96b7d --- /dev/null +++ b/Source/TestUnity/Assets/Script/MapTest.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e70ffad1896a8d5428223e0cbd4092de +timeCreated: 1509172126 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/TestUnity/ProjectSettings/AudioManager.asset b/Source/TestUnity/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..7bf2e21 Binary files /dev/null and b/Source/TestUnity/ProjectSettings/AudioManager.asset differ diff --git a/Source/TestUnity/ProjectSettings/ClusterInputManager.asset b/Source/TestUnity/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..0cb6ab1 Binary files /dev/null and b/Source/TestUnity/ProjectSettings/ClusterInputManager.asset differ diff --git a/Source/TestUnity/ProjectSettings/DynamicsManager.asset b/Source/TestUnity/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..eda3816 Binary files /dev/null and b/Source/TestUnity/ProjectSettings/DynamicsManager.asset differ diff --git a/Source/TestUnity/ProjectSettings/EditorBuildSettings.asset b/Source/TestUnity/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..c084adb Binary files /dev/null and b/Source/TestUnity/ProjectSettings/EditorBuildSettings.asset differ diff --git a/Source/TestUnity/ProjectSettings/EditorSettings.asset b/Source/TestUnity/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..82fe2fb Binary files /dev/null and b/Source/TestUnity/ProjectSettings/EditorSettings.asset differ diff --git a/Source/TestUnity/ProjectSettings/GraphicsSettings.asset b/Source/TestUnity/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..a528f5d Binary files /dev/null and b/Source/TestUnity/ProjectSettings/GraphicsSettings.asset differ diff --git a/Source/TestUnity/ProjectSettings/InputManager.asset b/Source/TestUnity/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..1a1131a Binary files /dev/null and b/Source/TestUnity/ProjectSettings/InputManager.asset differ diff --git a/Source/TestUnity/ProjectSettings/NavMeshAreas.asset b/Source/TestUnity/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..13eec1e Binary files /dev/null and b/Source/TestUnity/ProjectSettings/NavMeshAreas.asset differ diff --git a/Source/TestUnity/ProjectSettings/NetworkManager.asset b/Source/TestUnity/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..cdd1e69 Binary files /dev/null and b/Source/TestUnity/ProjectSettings/NetworkManager.asset differ diff --git a/Source/TestUnity/ProjectSettings/Physics2DSettings.asset b/Source/TestUnity/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..71676f8 Binary files /dev/null and b/Source/TestUnity/ProjectSettings/Physics2DSettings.asset differ diff --git a/Source/TestUnity/ProjectSettings/ProjectSettings.asset b/Source/TestUnity/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..51d5e12 Binary files /dev/null and b/Source/TestUnity/ProjectSettings/ProjectSettings.asset differ diff --git a/Source/TestUnity/ProjectSettings/ProjectVersion.txt b/Source/TestUnity/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..1049cc7 --- /dev/null +++ b/Source/TestUnity/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 5.5.2f1 diff --git a/Source/TestUnity/ProjectSettings/QualitySettings.asset b/Source/TestUnity/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..e62348b Binary files /dev/null and b/Source/TestUnity/ProjectSettings/QualitySettings.asset differ diff --git a/Source/TestUnity/ProjectSettings/TagManager.asset b/Source/TestUnity/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..3fa96fb Binary files /dev/null and b/Source/TestUnity/ProjectSettings/TagManager.asset differ diff --git a/Source/TestUnity/ProjectSettings/TimeManager.asset b/Source/TestUnity/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..851c9d2 Binary files /dev/null and b/Source/TestUnity/ProjectSettings/TimeManager.asset differ diff --git a/Source/TestUnity/ProjectSettings/UnityConnectSettings.asset b/Source/TestUnity/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..bc6433f Binary files /dev/null and b/Source/TestUnity/ProjectSettings/UnityConnectSettings.asset differ diff --git a/Source/TestUnity/TestUnity.csproj b/Source/TestUnity/TestUnity.csproj new file mode 100644 index 0000000..5439b59 --- /dev/null +++ b/Source/TestUnity/TestUnity.csproj @@ -0,0 +1,97 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {9922B5ED-E8DC-12B7-064F-D8AD31D63F0C} + Library + Assembly-CSharp + 512 + {E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + .NETFramework + v3.5 + Unity Subset v3.5 + + Game:1 + StandaloneWindows:5 + 5.5.2f1 + + 4 + + + pdbonly + false + Temp\UnityVS_bin\Debug\ + Temp\UnityVS_obj\Debug\ + prompt + 4 + DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_5_2;UNITY_5_5;UNITY_5;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_GENERICS;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITERENDERER_FLIPPING;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_RAKNET;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;INCLUDE_DYNAMIC_GI;INCLUDE_GI;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_PLAYMODE_TESTS_RUNNER;ENABLE_SCRIPTING_NEW_CSHARP_COMPILER;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_EVENT_QUEUE;ENABLE_CLUSTERINPUT;ENABLE_VIDEO;ENABLE_VR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;ENABLE_PROFILER;DEBUG;TRACE;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;UNITY_TEAM_LICENSE;ENABLE_VSTU;UNITY_PRO_LICENSE + false + + + pdbonly + false + Temp\UnityVS_bin\Release\ + Temp\UnityVS_obj\Release\ + prompt + 4 + TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_5_2;UNITY_5_5;UNITY_5;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_GENERICS;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITERENDERER_FLIPPING;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_RAKNET;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;INCLUDE_DYNAMIC_GI;INCLUDE_GI;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_PLAYMODE_TESTS_RUNNER;ENABLE_SCRIPTING_NEW_CSHARP_COMPILER;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_EVENT_QUEUE;ENABLE_CLUSTERINPUT;ENABLE_VIDEO;ENABLE_VR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;ENABLE_PROFILER;DEBUG;TRACE;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;UNITY_TEAM_LICENSE;ENABLE_VSTU;UNITY_PRO_LICENSE + false + + + + + + + + + + + + Library\UnityAssemblies\UnityEngine.dll + + + Library\UnityAssemblies\UnityEngine.UI.dll + + + Library\UnityAssemblies\UnityEngine.Networking.dll + + + Library\UnityAssemblies\UnityEngine.PlaymodeTestsRunner.dll + + + Library\UnityAssemblies\UnityEngine.Analytics.dll + + + Library\UnityAssemblies\UnityEngine.HoloLens.dll + + + Library\UnityAssemblies\UnityEngine.VR.dll + + + Library\UnityAssemblies\UnityEditor.dll + + + Library\UnityAssemblies\Mono.Cecil.dll + + + Library\UnityAssemblies\UnityEditor.iOS.Extensions.Xcode.dll + + + Library\UnityAssemblies\UnityEditor.iOS.Extensions.Xcode.dll + + + Library\UnityAssemblies\UnityEditor.iOS.Extensions.Common.dll + + + Assets\Plugins\TinyMapper.dll + + + + + + + + diff --git a/Source/TestUnity/TestUnity.sln b/Source/TestUnity/TestUnity.sln new file mode 100644 index 0000000..0a81fec --- /dev/null +++ b/Source/TestUnity/TestUnity.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2015 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUnity", "TestUnity.csproj", "{9922B5ED-E8DC-12B7-064F-D8AD31D63F0C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9922B5ED-E8DC-12B7-064F-D8AD31D63F0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9922B5ED-E8DC-12B7-064F-D8AD31D63F0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9922B5ED-E8DC-12B7-064F-D8AD31D63F0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9922B5ED-E8DC-12B7-064F-D8AD31D63F0C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/TinyMapper/Bindings/BindingConfig.cs b/Source/TinyMapper/Bindings/BindingConfig.cs index 3b4c0e2..3d5cff9 100644 --- a/Source/TinyMapper/Bindings/BindingConfig.cs +++ b/Source/TinyMapper/Bindings/BindingConfig.cs @@ -7,7 +7,8 @@ namespace Nelibur.ObjectMapper.Bindings { internal class BindingConfig { - private readonly Dictionary _bindFields = new Dictionary(); + private readonly Dictionary> _oneToOneBindFields = new Dictionary>(); + private readonly Dictionary> _bindFieldsPath = new Dictionary>(); private readonly Dictionary _bindTypes = new Dictionary(); private readonly Dictionary> _customTypeConverters = new Dictionary>(); private readonly HashSet _ignoreFields = new HashSet(); @@ -17,9 +18,32 @@ internal void BindConverter(string targetName, Func func) _customTypeConverters[targetName] = func; } - internal void BindFields(string sourceName, string targetName) + internal void BindFields(List sourcePath, List targetPath) { - _bindFields[sourceName] = targetName; + var bindingFieldPath = new BindingFieldPath(sourcePath, targetPath); + + if (!bindingFieldPath.HasPath) + { + if (_oneToOneBindFields.ContainsKey(bindingFieldPath.SourceHead)) + { + _oneToOneBindFields[bindingFieldPath.SourceHead].Add(bindingFieldPath.TargetHead); + } + else + { + _oneToOneBindFields[bindingFieldPath.SourceHead] = new List{ bindingFieldPath.TargetHead }; + } + } + else + { + if (_bindFieldsPath.ContainsKey(bindingFieldPath.SourceHead)) + { + _bindFieldsPath[bindingFieldPath.SourceHead].Add(bindingFieldPath); + } + else + { + _bindFieldsPath[bindingFieldPath.SourceHead] = new List { bindingFieldPath }; + } + } } internal void BindType(string targetName, Type value) @@ -27,18 +51,25 @@ internal void BindType(string targetName, Type value) _bindTypes[targetName] = value; } - internal Option GetBindField(string sourceName) + internal Option> GetBindField(string sourceName) + { + List result; + bool exists = _oneToOneBindFields.TryGetValue(sourceName, out result); + return new Option> (result, exists); + } + + internal Option> GetBindFieldPath(string fieldName) { - string result; - bool exsist = _bindFields.TryGetValue(sourceName, out result); - return new Option(result, exsist); + List result; + bool exists = _bindFieldsPath.TryGetValue(fieldName, out result); + return new Option>(result, exists); } internal Option GetBindType(string targetName) { Type result; - bool exsist = _bindTypes.TryGetValue(targetName, out result); - return new Option(result, exsist); + bool exists = _bindTypes.TryGetValue(targetName, out result); + return new Option(result, exists); } internal Option> GetCustomTypeConverter(string targetName) diff --git a/Source/TinyMapper/Bindings/BindingConfigAttributes.cs b/Source/TinyMapper/Bindings/BindingConfigAttributes.cs index e256fce..e11b48e 100644 --- a/Source/TinyMapper/Bindings/BindingConfigAttributes.cs +++ b/Source/TinyMapper/Bindings/BindingConfigAttributes.cs @@ -10,19 +10,20 @@ public IgnoreAttribute(Type targetType = null) TargetType = targetType; } - public Type TargetType { get; private set; } + public Type TargetType { get; } } + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public sealed class BindAttribute : Attribute { - public BindAttribute(string memberName, Type targetType = null) + public BindAttribute(string targetMemberName, Type targetType = null) { - MemberName = memberName; + MemberName = targetMemberName; TargetType = targetType; } - public Type TargetType { get; private set; } - public string MemberName { get; private set; } + public Type TargetType { get; } + public string MemberName { get; } } } diff --git a/Source/TinyMapper/Bindings/BindingConfigOf.cs b/Source/TinyMapper/Bindings/BindingConfigOf.cs index 219a38d..89c80a9 100644 --- a/Source/TinyMapper/Bindings/BindingConfigOf.cs +++ b/Source/TinyMapper/Bindings/BindingConfigOf.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq.Expressions; namespace Nelibur.ObjectMapper.Bindings @@ -7,22 +8,23 @@ internal sealed class BindingConfigOf : BindingConfig, IBindin { public void Bind(Expression> source, Expression> target) { - string sourceName = GetMemberInfo(source); - string targetName = GetMemberInfo(target); + List sourcePath = GetMemberInfoPath(source); + List targetPath = GetMemberInfoPath(target); - if (string.Equals(sourceName, targetName, StringComparison.OrdinalIgnoreCase)) + if (sourcePath.Count == 1 && targetPath.Count == 1 && + string.Equals(sourcePath[0], targetPath[0], StringComparison.Ordinal)) { return; } - BindFields(sourceName, targetName); + BindFields(sourcePath, targetPath); } - public void Bind(Expression> target, TField value) - { - Func func = x => value; - BindConverter(GetMemberInfo(target), func); - } + // public void Bind(Expression> target, TField value) + // { + // Func func = x => value; + // BindConverter(GetMemberInfo(target), func); + // } public void Bind(Expression> target, Type targetType) { @@ -54,5 +56,33 @@ private static string GetMemberInfo(Expression> expre } return member.Member.Name; } + + private static List GetMemberInfoPath(Expression> expression) + { + var member = expression.Body as MemberExpression; + if (member == null) + { + var unaryExpression = expression.Body as UnaryExpression; + if (unaryExpression != null) + { + member = unaryExpression.Operand as MemberExpression; + } + + if (member == null) + { + throw new ArgumentException("Expression is not a MemberExpression", nameof(expression)); + } + } + var result = new List(); + do + { + var resultMember = member.Member; + result.Add(resultMember.Name); + member = member.Expression as MemberExpression; + } + while (member != null); + result.Reverse(); + return result; + } } } diff --git a/Source/TinyMapper/Bindings/BindingFieldPath.cs b/Source/TinyMapper/Bindings/BindingFieldPath.cs new file mode 100644 index 0000000..001b94a --- /dev/null +++ b/Source/TinyMapper/Bindings/BindingFieldPath.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace Nelibur.ObjectMapper.Bindings +{ + internal sealed class BindingFieldPath + { + public BindingFieldPath(List sourcePath, List targetPath) + { + SourcePath = sourcePath; + TargetPath = targetPath; + HasPath = sourcePath.Count != 1 || targetPath.Count != 1; + SourceHead = sourcePath[0]; + TargetHead = targetPath[0]; + } + + public List SourcePath { get; } + public List TargetPath { get; } + public string SourceHead { get; } + public string TargetHead { get; } + public bool HasPath { get; } + } +} diff --git a/Source/TinyMapper/Bindings/IBindingConfig.cs b/Source/TinyMapper/Bindings/IBindingConfig.cs index 34ef5ab..0652dd6 100644 --- a/Source/TinyMapper/Bindings/IBindingConfig.cs +++ b/Source/TinyMapper/Bindings/IBindingConfig.cs @@ -6,8 +6,10 @@ namespace Nelibur.ObjectMapper.Bindings public interface IBindingConfig { void Bind(Expression> source, Expression> target); - void Bind(Expression> target, TField value); + + // void Bind(Expression> target, TField value); not working yet void Bind(Expression> target, Type targetType); + void Ignore(Expression> expression); } } diff --git a/Source/TinyMapper/CodeGenerators/CodeGenerator.cs b/Source/TinyMapper/CodeGenerators/CodeGenerator.cs index 3135438..60970fd 100644 --- a/Source/TinyMapper/CodeGenerators/CodeGenerator.cs +++ b/Source/TinyMapper/CodeGenerators/CodeGenerator.cs @@ -2,6 +2,7 @@ using System.Reflection; using System.Reflection.Emit; using Nelibur.ObjectMapper.CodeGenerators.Emitters; +using Nelibur.ObjectMapper.Core; using Nelibur.ObjectMapper.Core.Extensions; namespace Nelibur.ObjectMapper.CodeGenerators @@ -21,15 +22,15 @@ public CodeGenerator CastType(Type stackType, Type targetType) { return this; } - if (stackType.IsValueType == false && targetType == typeof(object)) + if (Helpers.IsValueType(stackType) == false && targetType == typeof(object)) { return this; } - if (stackType.IsValueType && !targetType.IsValueType) + if (Helpers.IsValueType(stackType) && !Helpers.IsValueType(targetType)) { _ilGenerator.Emit(OpCodes.Box, stackType); } - else if (!stackType.IsValueType && targetType.IsValueType) + else if (!Helpers.IsValueType(stackType) && Helpers.IsValueType(targetType)) { _ilGenerator.Emit(OpCodes.Unbox_Any, targetType); } @@ -45,31 +46,27 @@ public LocalBuilder DeclareLocal(Type type) return _ilGenerator.DeclareLocal(type); } - public CodeGenerator Emit(OpCode opCode) + public void Emit(OpCode opCode) { _ilGenerator.Emit(opCode); - return this; } - public CodeGenerator Emit(OpCode opCode, int value) + public void Emit(OpCode opCode, int value) { _ilGenerator.Emit(opCode, value); - return this; } - public CodeGenerator Emit(OpCode opCode, Type value) + public void Emit(OpCode opCode, Type value) { _ilGenerator.Emit(opCode, value); - return this; } - public CodeGenerator Emit(OpCode opCode, FieldInfo value) + public void Emit(OpCode opCode, FieldInfo value) { _ilGenerator.Emit(opCode, value); - return this; } - public CodeGenerator EmitCall(MethodInfo method, IEmitterType invocationObject, params IEmitterType[] arguments) + public void EmitCall(MethodInfo method, IEmitterType invocationObject, params IEmitterType[] arguments) { ParameterInfo[] actualArguments = method.GetParameters(); if (arguments.IsNull()) @@ -92,24 +89,22 @@ public CodeGenerator EmitCall(MethodInfo method, IEmitterType invocationObject, CastType(arguments[i].ObjectType, actualArguments[i].ParameterType); } EmitCall(method); - return this; } - public CodeGenerator EmitNewObject(ConstructorInfo ctor) + public void EmitNewObject(ConstructorInfo ctor) { _ilGenerator.Emit(OpCodes.Newobj, ctor); - return this; } private void EmitCall(MethodInfo method) { if (method.IsVirtual) { - _ilGenerator.EmitCall(OpCodes.Callvirt, method, Type.EmptyTypes); + _ilGenerator.Emit(OpCodes.Callvirt, method); } else { - _ilGenerator.EmitCall(OpCodes.Call, method, Type.EmptyTypes); + _ilGenerator.Emit(OpCodes.Call, method); } } } diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitArgument.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitArgument.cs index bb45bab..1de53c8 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitArgument.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitArgument.cs @@ -22,7 +22,7 @@ public EmitLoadArgument(Type type, int index) _index = index; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitArray.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitArray.cs index 1c89e68..7bf95da 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitArray.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitArray.cs @@ -23,7 +23,7 @@ public EmitLoadArray(IEmitterType array, int index) ObjectType = array.ObjectType.GetElementType(); } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitBox.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitBox.cs index a801922..ce6796a 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitBox.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitBox.cs @@ -1,5 +1,6 @@ using System; using System.Reflection.Emit; +using Nelibur.ObjectMapper.Core; namespace Nelibur.ObjectMapper.CodeGenerators.Emitters { @@ -13,13 +14,13 @@ private EmitBox(IEmitterType value) ObjectType = value.ObjectType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { _value.Emit(generator); - if (ObjectType.IsValueType) + if (Helpers.IsValueType(ObjectType)) { generator.Emit(OpCodes.Box, ObjectType); } diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitField.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitField.cs index 5befdb9..6d8063e 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitField.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitField.cs @@ -30,7 +30,7 @@ public EmitLoadField(IEmitterType source, FieldInfo field) ObjectType = field.FieldType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { @@ -54,7 +54,7 @@ public EmitStoreField(FieldInfo field, IEmitterType targetObject, IEmitterType v ObjectType = _field.FieldType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitLocal.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitLocal.cs index c8b17dd..47bc59c 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitLocal.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitLocal.cs @@ -22,7 +22,7 @@ public EmitLoadLocal(LocalBuilder localBuilder) ObjectType = localBuilder.LocalType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitLocalVariable.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitLocalVariable.cs index 47624f6..4a00c26 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitLocalVariable.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitLocalVariable.cs @@ -1,5 +1,6 @@ using System; using System.Reflection.Emit; +using Nelibur.ObjectMapper.Core; using Nelibur.ObjectMapper.Core.DataStructures; using Nelibur.ObjectMapper.Core.Extensions; @@ -15,11 +16,11 @@ private EmitLocalVariable(LocalBuilder localBuilder) ObjectType = localBuilder.LocalType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { - _localBuilder.Where(x => x.LocalType.IsValueType) + _localBuilder.Where(x => Helpers.IsValueType(x.LocalType)) .Do(x => generator.Emit(OpCodes.Ldloca, x.LocalIndex)) .Do(x => generator.Emit(OpCodes.Initobj, x.LocalType)); } diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitMethod.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitMethod.cs index 99afbc4..a1328fc 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitMethod.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitMethod.cs @@ -30,7 +30,7 @@ public EmitterCallMethod(MethodInfo method, IEmitterType invocationObject, param ObjectType = _method.ReturnType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitNewObj.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitNewObj.cs index 57f66a1..a2a4b5a 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitNewObj.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitNewObj.cs @@ -11,7 +11,7 @@ private EmitNewObj(Type objectType) ObjectType = objectType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitNull.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitNull.cs index 4216066..0877fee 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitNull.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitNull.cs @@ -10,7 +10,7 @@ private EmitNull() ObjectType = typeof(object); } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitProperty.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitProperty.cs index 7aa5603..3fb69ac 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitProperty.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitProperty.cs @@ -16,7 +16,7 @@ public static IEmitterType Store(PropertyInfo property, IEmitterType targetObjec } - private class EmitLoadProperty : IEmitterType + private sealed class EmitLoadProperty : IEmitterType { private readonly PropertyInfo _property; private readonly IEmitterType _source; @@ -28,7 +28,7 @@ public EmitLoadProperty(IEmitterType source, PropertyInfo property) ObjectType = property.PropertyType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { @@ -49,7 +49,7 @@ public EmitStoreProperty(PropertyInfo property, IEmitterType targetObject, IEmit ObjectType = _callMethod.ObjectType; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/CodeGenerators/Emitters/EmitReturn.cs b/Source/TinyMapper/CodeGenerators/Emitters/EmitReturn.cs index 705554f..1ae37b1 100644 --- a/Source/TinyMapper/CodeGenerators/Emitters/EmitReturn.cs +++ b/Source/TinyMapper/CodeGenerators/Emitters/EmitReturn.cs @@ -13,7 +13,7 @@ private EmitReturn(IEmitterType returnValue, Type returnType) _returnValue = returnValue; } - public Type ObjectType { get; private set; } + public Type ObjectType { get; } public void Emit(CodeGenerator generator) { diff --git a/Source/TinyMapper/Core/DataStructures/Option.cs b/Source/TinyMapper/Core/DataStructures/Option.cs index 3debb97..be2fc3e 100644 --- a/Source/TinyMapper/Core/DataStructures/Option.cs +++ b/Source/TinyMapper/Core/DataStructures/Option.cs @@ -3,35 +3,23 @@ namespace Nelibur.ObjectMapper.Core.DataStructures { /// - /// https://github.com/Nelibur/Nelibur. + /// https://github.com/Nelibur/Nelibur /// - internal sealed class Option + internal struct Option { - private static readonly Option _empty = new Option(default(T), false); - private readonly bool _hasValue; - public Option(T value, bool hasValue = true) { - _hasValue = hasValue; + HasValue = hasValue; Value = value; } - public static Option Empty - { - get { return _empty; } - } + public static Option Empty { get; } = new Option(default(T), false); - public bool HasNoValue - { - get { return !_hasValue; } - } + public bool HasNoValue => !HasValue; - public bool HasValue - { - get { return _hasValue; } - } + public bool HasValue { get; } - public T Value { get; private set; } + public T Value { get; } public Option Match(Func predicate, Action action) { diff --git a/Source/TinyMapper/Core/DataStructures/TypePair.cs b/Source/TinyMapper/Core/DataStructures/TypePair.cs index ff504fa..83a6aa6 100644 --- a/Source/TinyMapper/Core/DataStructures/TypePair.cs +++ b/Source/TinyMapper/Core/DataStructures/TypePair.cs @@ -39,43 +39,22 @@ public bool IsDeepCloneable } } - public bool IsEnumTypes - { - get { return Source.IsEnum && Target.IsEnum; } - } + public bool IsEnumTypes => Helpers.IsEnum(Source) && Helpers.IsEnum(Target); - public bool IsEnumerableTypes - { - get { return Source.IsIEnumerable() && Target.IsIEnumerable(); } - } + public bool IsEnumerableTypes => Source.IsIEnumerable() && Target.IsIEnumerable(); - public bool IsNullableToNotNullable - { - get { return Source.IsNullable() && Target.IsNullable() == false; } - } + public bool IsNullableToNotNullable => Source.IsNullable() && Target.IsNullable() == false; - public Type Source { get; private set; } - public Type Target { get; private set; } + public Type Source { get; } + public Type Target { get; } - private bool IsEqualTypes - { - get { return Source == Target; } - } + private bool IsEqualTypes => Source == Target; - private bool IsNullableTypes - { - get { return Source.IsNullable() && Target.IsNullable(); } - } + private bool IsNullableTypes => Source.IsNullable() && Target.IsNullable(); - private bool IsPrimitiveTypes - { - get { return Source.IsPrimitive && Target.IsPrimitive; } - } + private bool IsPrimitiveTypes => Helpers.IsPrimitive(Source) && Helpers.IsPrimitive(Target); - private bool IsValueTypes - { - get { return Source.IsValueType && Target.IsValueType; } - } + private bool IsValueTypes => Helpers.IsValueType(Source) && Helpers.IsValueType(Target); public static TypePair Create(Type source, Type target) { @@ -104,6 +83,7 @@ public override int GetHashCode() } } + // TODO Cache TypeConverters public bool HasTypeConverter() { TypeConverter fromConverter = TypeDescriptor.GetConverter(Source); diff --git a/Source/TinyMapper/Core/Error.cs b/Source/TinyMapper/Core/Error.cs index 6e2a38d..099aaa7 100644 --- a/Source/TinyMapper/Core/Error.cs +++ b/Source/TinyMapper/Core/Error.cs @@ -1,10 +1,9 @@ using System; -using System.Configuration; namespace Nelibur.ObjectMapper.Core { /// - /// https://github.com/Nelibur/Nelibur. + /// https://github.com/Nelibur/Nelibur /// internal static class Error { @@ -13,11 +12,6 @@ public static Exception ArgumentNull(string paramName) return new ArgumentNullException(paramName); } - public static Exception ConfigurationError(string message) - { - return new ConfigurationErrorsException(message); - } - public static Exception InvalidOperation(string message) { return new InvalidOperationException(message); diff --git a/Source/TinyMapper/Core/Extensions/DictionaryExtensions.cs b/Source/TinyMapper/Core/Extensions/DictionaryExtensions.cs index fcd8b9b..5d168bb 100644 --- a/Source/TinyMapper/Core/Extensions/DictionaryExtensions.cs +++ b/Source/TinyMapper/Core/Extensions/DictionaryExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Nelibur.ObjectMapper.Core.DataStructures; namespace Nelibur.ObjectMapper.Core.Extensions @@ -12,4 +13,4 @@ public static Option GetValue(this IDictionary(result, exists); } } -} \ No newline at end of file +} diff --git a/Source/TinyMapper/Core/Extensions/EnumerableExtensions.cs b/Source/TinyMapper/Core/Extensions/EnumerableExtensions.cs index f34da59..7020398 100644 --- a/Source/TinyMapper/Core/Extensions/EnumerableExtensions.cs +++ b/Source/TinyMapper/Core/Extensions/EnumerableExtensions.cs @@ -9,7 +9,8 @@ namespace Nelibur.ObjectMapper.Core.Extensions internal static class EnumerableExtensions { public static List ConvertAll( - this IEnumerable value, Func converter) + this IEnumerable value, + Func converter) { return value.Select(converter).ToList(); } @@ -73,7 +74,9 @@ public static void IterI(this IEnumerable value, Action action) /// Given function /// Exception handler action public static void IterSafe( - this IEnumerable value, Action action, Action exceptionHandler = null) + this IEnumerable value, + Action action, + Action exceptionHandler = null) { foreach (T item in value) { @@ -83,10 +86,7 @@ public static void IterSafe( } catch (Exception ex) { - if (exceptionHandler != null) - { - exceptionHandler(ex); - } + exceptionHandler?.Invoke(ex); } } } diff --git a/Source/TinyMapper/Core/Extensions/MemberInfoExtensions.cs b/Source/TinyMapper/Core/Extensions/MemberInfoExtensions.cs index 6f9134a..41635f8 100644 --- a/Source/TinyMapper/Core/Extensions/MemberInfoExtensions.cs +++ b/Source/TinyMapper/Core/Extensions/MemberInfoExtensions.cs @@ -11,7 +11,7 @@ internal static class MemberInfoExtensions public static Option GetAttribute(this MemberInfo value) where TAttribute : Attribute { - return Attribute.GetCustomAttributes(value) + return value.GetCustomAttributes(true) .FirstOrDefault(x => x is TAttribute) .ToType(); } @@ -19,7 +19,7 @@ public static Option GetAttribute(this MemberInfo value) public static List GetAttributes(this MemberInfo value) where TAttribute : Attribute { - return Attribute.GetCustomAttributes(value).OfType().ToList(); + return value.GetCustomAttributes(true).OfType().ToList(); } public static Type GetMemberType(this MemberInfo value) @@ -28,11 +28,11 @@ public static Type GetMemberType(this MemberInfo value) { return ((FieldInfo)value).FieldType; } - else if (value.IsProperty()) + if (value.IsProperty()) { return ((PropertyInfo)value).PropertyType; } - else if (value.IsMethod()) + if (value.IsMethod()) { return ((MethodInfo)value).ReturnType; } @@ -41,17 +41,30 @@ public static Type GetMemberType(this MemberInfo value) public static bool IsField(this MemberInfo value) { +#if COREFX + return value is FieldInfo; +#else return value.MemberType == MemberTypes.Field; +#endif } public static bool IsProperty(this MemberInfo value) { +#if COREFX + return value is PropertyInfo; +#else return value.MemberType == MemberTypes.Property; +#endif } private static bool IsMethod(this MemberInfo value) { +#if COREFX + return value is MethodInfo; +#else return value.MemberType == MemberTypes.Method; +#endif + } } } diff --git a/Source/TinyMapper/Core/Extensions/ObjectExtensions.cs b/Source/TinyMapper/Core/Extensions/ObjectExtensions.cs index df1f872..b2adfa4 100644 --- a/Source/TinyMapper/Core/Extensions/ObjectExtensions.cs +++ b/Source/TinyMapper/Core/Extensions/ObjectExtensions.cs @@ -4,7 +4,7 @@ namespace Nelibur.ObjectMapper.Core.Extensions { /// - /// https://github.com/Nelibur/Nelibur. + /// https://github.com/Nelibur/Nelibur /// internal static class ObjectExtensions { @@ -20,7 +20,7 @@ public static bool IsNull(this object obj) public static Option ToOption(this T value) { - if (typeof(T).IsValueType == false && ReferenceEquals(value, null)) + if (Helpers.IsValueType(typeof(T)) == false && ReferenceEquals(value, null)) { return Option.Empty; } diff --git a/Source/TinyMapper/Core/Extensions/OptionExtensions.cs b/Source/TinyMapper/Core/Extensions/OptionExtensions.cs index dd09c27..f12b270 100644 --- a/Source/TinyMapper/Core/Extensions/OptionExtensions.cs +++ b/Source/TinyMapper/Core/Extensions/OptionExtensions.cs @@ -4,7 +4,7 @@ namespace Nelibur.ObjectMapper.Core.Extensions { /// - /// https://github.com/Nelibur/Nelibur. + /// https://github.com/Nelibur/Nelibur /// internal static class OptionExtensions { diff --git a/Source/TinyMapper/Core/Extensions/TypeExtensions.cs b/Source/TinyMapper/Core/Extensions/TypeExtensions.cs index 2c7bfec..40d8cbb 100644 --- a/Source/TinyMapper/Core/Extensions/TypeExtensions.cs +++ b/Source/TinyMapper/Core/Extensions/TypeExtensions.cs @@ -14,11 +14,12 @@ public static Type GetCollectionItemType(this Type type) { return type.GetElementType(); } - else if (type.IsIEnumerableOf()) + if (Helpers.IsGenericType(type) && type.IsIEnumerableOf()) { return type.GetGenericArguments().First(); } - throw new NotSupportedException(); + + return typeof(object); } public static ConstructorInfo GetDefaultCtor(this Type type) @@ -49,7 +50,9 @@ public static bool HasDefaultCtor(this Type type) public static bool IsDictionaryOf(this Type type) { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>); + return Helpers.IsGenericType(type) && + (type.GetGenericTypeDefinition() == typeof(Dictionary<,>) || + type.GetGenericTypeDefinition() == typeof(IDictionary<,>)); } public static bool IsIEnumerable(this Type type) @@ -60,17 +63,23 @@ public static bool IsIEnumerable(this Type type) public static bool IsIEnumerableOf(this Type type) { return type.GetInterfaces() - .Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)); + .Any(x => Helpers.IsGenericType(x) && + x.GetGenericTypeDefinition() == typeof(IEnumerable<>) || + !Helpers.IsGenericType(x) && x == typeof(IEnumerable)); } public static bool IsListOf(this Type type) { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>); + return + Helpers.IsGenericType(type) && + (type.GetGenericTypeDefinition() == typeof(List<>) || + type.GetGenericTypeDefinition() == typeof(IList<>) || + type.GetGenericTypeDefinition() == typeof(ICollection<>)); } public static bool IsNullable(this Type type) { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + return Helpers.IsGenericType(type) && type.GetGenericTypeDefinition() == typeof(Nullable<>); } } } diff --git a/Source/TinyMapper/Core/Helpers.cs b/Source/TinyMapper/Core/Helpers.cs new file mode 100644 index 0000000..783c12f --- /dev/null +++ b/Source/TinyMapper/Core/Helpers.cs @@ -0,0 +1,66 @@ +using System; +using System.Reflection.Emit; +#if COREFX +using System.Reflection; +#endif + +namespace Nelibur.ObjectMapper.Core +{ + internal static class Helpers + { + internal static bool IsValueType(Type type) + { +#if COREFX + return type.GetTypeInfo().IsValueType; +#else + return type.IsValueType; +#endif + } + + internal static bool IsPrimitive(Type type) + { +#if COREFX + return type.GetTypeInfo().IsPrimitive; +#else + return type.IsPrimitive; +#endif + } + + internal static bool IsEnum(Type type) + { +#if COREFX + return type.GetTypeInfo().IsEnum; +#else + return type.IsEnum; +#endif + } + + internal static bool IsGenericType(Type type) + { +#if COREFX + return type.GetTypeInfo().IsGenericType; +#else + return type.IsGenericType; +#endif + } + + internal static Type CreateType(TypeBuilder typeBuilder) + { +#if COREFX + return typeBuilder.CreateTypeInfo().AsType(); +#else + return typeBuilder.CreateType(); +#endif + } + + internal static Type BaseType(Type type) + { +#if COREFX + return type.GetTypeInfo().BaseType; +#else + return type.BaseType; +#endif + } + + } +} \ No newline at end of file diff --git a/Source/TinyMapper/ITinyMapperConfig.cs b/Source/TinyMapper/ITinyMapperConfig.cs new file mode 100644 index 0000000..97641d0 --- /dev/null +++ b/Source/TinyMapper/ITinyMapperConfig.cs @@ -0,0 +1,21 @@ +using System; + +namespace Nelibur.ObjectMapper +{ + /// + /// Configuration for TinyMapper + /// + public interface ITinyMapperConfig + { + /// + /// Custom name matching function used for auto bindings + /// + /// Function to match names + void NameMatching(Func nameMatching); + + /// + /// Reset settings to default + /// + void Reset(); + } +} diff --git a/Source/TinyMapper/Mappers/Caches/MapperCache.cs b/Source/TinyMapper/Mappers/Caches/MapperCache.cs index 5a9b9c3..00b8a8f 100644 --- a/Source/TinyMapper/Mappers/Caches/MapperCache.cs +++ b/Source/TinyMapper/Mappers/Caches/MapperCache.cs @@ -10,10 +10,7 @@ internal sealed class MapperCache { private readonly Dictionary _cache = new Dictionary(); - public bool IsEmpty - { - get { return _cache.Count == 0; } - } + public bool IsEmpty => _cache.Count == 0; public List Mappers { @@ -25,7 +22,26 @@ public List Mappers } } - public MapperCacheItem Add(TypePair key, Mapper value) + public List MapperCacheItems => _cache.Values.ToList(); + + public MapperCacheItem AddStub(TypePair key) + { + if (_cache.ContainsKey(key)) + { + return _cache[key]; + } + + var mapperCacheItem = new MapperCacheItem { Id = GetId() }; + _cache[key] = mapperCacheItem; + return mapperCacheItem; + } + + public void ReplaceStub(TypePair key, Mapper mapper) + { + _cache[key].Mapper = mapper; + } + + public MapperCacheItem Add(TypePair key, Mapper mapper) { MapperCacheItem result; if (_cache.TryGetValue(key, out result)) @@ -35,12 +51,22 @@ public MapperCacheItem Add(TypePair key, Mapper value) result = new MapperCacheItem { Id = GetId(), - Mapper = value + Mapper = mapper }; _cache[key] = result; return result; } + public Option Get(TypePair key) + { + MapperCacheItem result; + if (_cache.TryGetValue(key, out result)) + { + return new Option(result); + } + return Option.Empty; + } + private int GetId() { return _cache.Count; diff --git a/Source/TinyMapper/Mappers/Classes/ClassMapper.cs b/Source/TinyMapper/Mappers/Classes/ClassMapper.cs index f671a25..f92199b 100644 --- a/Source/TinyMapper/Mappers/Classes/ClassMapper.cs +++ b/Source/TinyMapper/Mappers/Classes/ClassMapper.cs @@ -1,5 +1,4 @@ using System; -using Nelibur.ObjectMapper.Core.Extensions; namespace Nelibur.ObjectMapper.Mappers.Classes { @@ -14,7 +13,7 @@ protected virtual TTarget CreateTargetInstance() protected override TTarget MapCore(TSource source, TTarget target) { - if (target.IsNull()) + if (target == null) { target = CreateTargetInstance(); } diff --git a/Source/TinyMapper/Mappers/Classes/ClassMapperBuilder.cs b/Source/TinyMapper/Mappers/Classes/ClassMapperBuilder.cs index 6455bc4..a64e8d8 100644 --- a/Source/TinyMapper/Mappers/Classes/ClassMapperBuilder.cs +++ b/Source/TinyMapper/Mappers/Classes/ClassMapperBuilder.cs @@ -3,6 +3,7 @@ using System.Reflection.Emit; using Nelibur.ObjectMapper.CodeGenerators; using Nelibur.ObjectMapper.CodeGenerators.Emitters; +using Nelibur.ObjectMapper.Core; using Nelibur.ObjectMapper.Core.DataStructures; using Nelibur.ObjectMapper.Core.Extensions; using Nelibur.ObjectMapper.Mappers.Caches; @@ -12,32 +13,68 @@ namespace Nelibur.ObjectMapper.Mappers.Classes { internal sealed class ClassMapperBuilder : MapperBuilder { + private readonly MapperCache _mapperCache; private const string CreateTargetInstanceMethod = "CreateTargetInstance"; private const string MapClassMethod = "MapClass"; private readonly MappingMemberBuilder _mappingMemberBuilder; private readonly MemberMapper _memberMapper; - public ClassMapperBuilder(IMapperBuilderConfig config) : base(config) + public ClassMapperBuilder(MapperCache mapperCache, IMapperBuilderConfig config) : base(config) { - _memberMapper = new MemberMapper(config); + _mapperCache = mapperCache; + _memberMapper = new MemberMapper(mapperCache, config); _mappingMemberBuilder = new MappingMemberBuilder(config); } - protected override string ScopeName - { - get { return "ClassMappers"; } - } + protected override string ScopeName => "ClassMappers"; protected override Mapper BuildCore(TypePair typePair) { Type parentType = typeof(ClassMapper<,>).MakeGenericType(typePair.Source, typePair.Target); TypeBuilder typeBuilder = _assembly.DefineType(GetMapperFullName(), parentType); EmitCreateTargetInstance(typePair.Target, typeBuilder); + + MapperCacheItem rootMapperCacheItem = _mapperCache.AddStub(typePair); Option mappers = EmitMapClass(typePair, typeBuilder); - var result = (Mapper)Activator.CreateInstance(typeBuilder.CreateType()); - mappers.Do(x => result.AddMappers(x.Mappers)); - return result; + var rootMapper = (Mapper)Activator.CreateInstance(Helpers.CreateType(typeBuilder)); + + UpdateMappers(mappers, rootMapperCacheItem.Id, rootMapper); + + _mapperCache.ReplaceStub(typePair, rootMapper); + + mappers.Do(x => rootMapper.AddMappers(x.Mappers)); + + return rootMapper; + } + + private static void UpdateMappers(Option mappers, int rootMapperId, Mapper rootMapper) + { + if (mappers.HasValue) + { + var result = new List(); + foreach (var item in mappers.Value.MapperCacheItems) + { + if (item.Id != rootMapperId) + { + result.Add(item.Mapper); + } + else + { + result.Add(null); + } + } + result[rootMapperId] = rootMapper; + rootMapper.AddMappers(result); + foreach (var item in mappers.Value.MapperCacheItems) + { + if (item.Id == rootMapperId) + { + continue; + } + item.Mapper?.UpdateRootMapper(rootMapperId, rootMapper); + } + } } protected override Mapper BuildCore(TypePair parentTypePair, MappingMember mappingMember) @@ -55,7 +92,7 @@ private static void EmitCreateTargetInstance(Type targetType, TypeBuilder typeBu MethodBuilder methodBuilder = typeBuilder.DefineMethod(CreateTargetInstanceMethod, OverrideProtected, targetType, Type.EmptyTypes); var codeGenerator = new CodeGenerator(methodBuilder.GetILGenerator()); - IEmitterType result = targetType.IsValueType ? EmitValueType(targetType, codeGenerator) : EmitRefType(targetType); + IEmitterType result = Helpers.IsValueType(targetType) ? EmitValueType(targetType, codeGenerator) : EmitRefType(targetType); EmitReturn.Return(result, targetType).Emit(codeGenerator); } @@ -74,7 +111,9 @@ private static IEmitterType EmitValueType(Type type, CodeGenerator codeGenerator private Option EmitMapClass(TypePair typePair, TypeBuilder typeBuilder) { - MethodBuilder methodBuilder = typeBuilder.DefineMethod(MapClassMethod, OverrideProtected, typePair.Target, + MethodBuilder methodBuilder = typeBuilder.DefineMethod(MapClassMethod, + OverrideProtected, + typePair.Target, new[] { typePair.Source, typePair.Target }); var codeGenerator = new CodeGenerator(methodBuilder.GetILGenerator()); @@ -90,7 +129,7 @@ private Option EmitMapClass(TypePair typePair, TypeBuilder typeBuil private MemberEmitterDescription EmitMappingMembers(TypePair typePair) { - List members = _mappingMemberBuilder.Build(typePair); + List members = _mappingMemberBuilder.Build(typePair); MemberEmitterDescription result = _memberMapper.Build(typePair, members); return result; } diff --git a/Source/TinyMapper/Mappers/Classes/Members/MappingMember.cs b/Source/TinyMapper/Mappers/Classes/Members/MappingMember.cs index 1d5f3da..28b3ea8 100644 --- a/Source/TinyMapper/Mappers/Classes/Members/MappingMember.cs +++ b/Source/TinyMapper/Mappers/Classes/Members/MappingMember.cs @@ -1,17 +1,11 @@ using System; using System.Reflection; using Nelibur.ObjectMapper.Core.DataStructures; -using Nelibur.ObjectMapper.Core.Extensions; namespace Nelibur.ObjectMapper.Mappers.Classes.Members { internal sealed class MappingMember { - public MappingMember(MemberInfo source, MemberInfo target) - : this(source, target, new TypePair(source.GetMemberType(), target.GetMemberType())) - { - } - public MappingMember(MemberInfo source, MemberInfo target, TypePair typePair) { Source = source; @@ -19,8 +13,8 @@ public MappingMember(MemberInfo source, MemberInfo target, TypePair typePair) TypePair = typePair; } - public MemberInfo Source { get; private set; } - public MemberInfo Target { get; private set; } - public TypePair TypePair { get; private set; } + public MemberInfo Source { get; } + public MemberInfo Target { get; } + public TypePair TypePair { get; } } } diff --git a/Source/TinyMapper/Mappers/Classes/Members/MappingMemberBuilder.cs b/Source/TinyMapper/Mappers/Classes/Members/MappingMemberBuilder.cs index 8b30ced..6f30f05 100644 --- a/Source/TinyMapper/Mappers/Classes/Members/MappingMemberBuilder.cs +++ b/Source/TinyMapper/Mappers/Classes/Members/MappingMemberBuilder.cs @@ -17,26 +17,30 @@ public MappingMemberBuilder(IMapperBuilderConfig config) _config = config; } - public List Build(TypePair typePair) + public List Build(TypePair typePair) { return ParseMappingTypes(typePair); } - private static List GetPublicMembers(Type type) + private static MemberInfo[] GetPublicMembers(Type type) { - return type.GetMembers(BindingFlags.Instance | BindingFlags.Public) - .Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field) - .ToList(); + BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; + PropertyInfo[] properties = type.GetProperties(flags); + FieldInfo[] fields = type.GetFields(flags); + MemberInfo[] members = new MemberInfo[properties.Length + fields.Length]; + properties.CopyTo(members, 0); + fields.CopyTo(members, properties.Length); + return members; } private static List GetSourceMembers(Type sourceType) { var result = new List(); - List members = GetPublicMembers(sourceType); + MemberInfo[] members = GetPublicMembers(sourceType); foreach (MemberInfo member in members) { - if (member.MemberType == MemberTypes.Property) + if (member.IsProperty()) { MethodInfo method = ((PropertyInfo)member).GetGetMethod(); if (method.IsNull()) @@ -53,10 +57,10 @@ private static List GetTargetMembers(Type targetType) { var result = new List(); - List members = GetPublicMembers(targetType); + MemberInfo[] members = GetPublicMembers(targetType); foreach (MemberInfo member in members) { - if (member.MemberType == MemberTypes.Property) + if (member.IsProperty()) { MethodInfo method = ((PropertyInfo)member).GetSetMethod(); if (method.IsNull() || method.GetParameters().Length != 1) @@ -69,18 +73,13 @@ private static List GetTargetMembers(Type targetType) return result; } - private static bool Match(string valueA, string valueB) - { - return string.Equals(valueA, valueB, StringComparison.Ordinal); - } - - private string GetTargetName( + private List GetTargetName( Option bindingConfig, TypePair typePair, MemberInfo sourceMember, Dictionary targetBindings) { - Option targetName; + Option> targetName; List binds = sourceMember.GetAttributes(); BindAttribute bind = binds.FirstOrDefault(x => x.TargetType.IsNull()); if (bind.IsNull()) @@ -89,21 +88,20 @@ private string GetTargetName( } if (bind.IsNotNull()) { - targetName = new Option(bind.MemberName); + targetName = new Option>(new List { bind.MemberName } ); } else { targetName = bindingConfig.Map(x => x.GetBindField(sourceMember.Name)); if (targetName.HasNoValue) { - string targetMemberName; - if (targetBindings.TryGetValue(sourceMember.Name, out targetMemberName)) + if (targetBindings.TryGetValue(sourceMember.Name, out var targetMemberName)) { - targetName = new Option(targetMemberName); + targetName = new Option>(new List { targetMemberName }); } else { - targetName = new Option(sourceMember.Name); + targetName = new Option>(new List { sourceMember.Name }); } } } @@ -143,9 +141,22 @@ private bool IsIgnore(Option bindingConfig, TypePair typePair, Me return bindingConfig.Map(x => x.IsIgnoreSourceField(sourceMember.Name)).Value; } - private List ParseMappingTypes(TypePair typePair) + private List GetSourceMemberPath(List fieldPath, Type sourceType) { - var result = new List(); + var result = new List(); + var dummyType = sourceType; + foreach (var path in fieldPath) + { + var member = GetSourceMembers(dummyType).Single(x => string.Equals(x.Name, path, StringComparison.Ordinal)); + result.Add(member); + dummyType = member.GetMemberType(); + } + return result; + } + + private List ParseMappingTypes(TypePair typePair) + { + var result = new List(); List sourceMembers = GetSourceMembers(typePair.Source); List targetMembers = GetTargetMembers(typePair.Target); @@ -161,22 +172,48 @@ private List ParseMappingTypes(TypePair typePair) continue; } - string targetName = GetTargetName(bindingConfig, typePair, sourceMember, targetBindings); + List targetNames = GetTargetName(bindingConfig, typePair, sourceMember, targetBindings); - MemberInfo targetMember = targetMembers.FirstOrDefault(x => Match(x.Name, targetName)); - if (targetMember.IsNull()) - { - continue; - } - var concreteBindingType = bindingConfig.Map(x => x.GetBindType(targetName)); - if (concreteBindingType.HasValue) + foreach (var targetName in targetNames) { - var mappingTypePair = new TypePair(sourceMember.GetMemberType(), concreteBindingType.Value); - result.Add(new MappingMember(sourceMember, targetMember, mappingTypePair)); + MemberInfo targetMember = targetMembers.FirstOrDefault(x => _config.NameMatching(targetName, x.Name)); + if (targetMember.IsNull()) + { + result.AddRange(GetBindMappingMemberPath(typePair, bindingConfig, sourceMember)); + continue; + } + Option concreteBindingType = bindingConfig.Map(x => x.GetBindType(targetName)); + if (concreteBindingType.HasValue) + { + var mappingTypePair = new TypePair(sourceMember.GetMemberType(), concreteBindingType.Value); + result.Add(new MappingMemberPath(sourceMember, targetMember, mappingTypePair)); + } + else + { + result.Add(new MappingMemberPath(sourceMember, targetMember)); + } + + result.AddRange(GetBindMappingMemberPath(typePair, bindingConfig, sourceMember)); } - else + + } + return result; + } + + private List GetBindMappingMemberPath(TypePair typePair, Option bindingConfig, MemberInfo sourceMember) + { + var result = new List(); + + var bindFieldPath = bindingConfig.Map(x => x.GetBindFieldPath(sourceMember.Name)); + + if (bindFieldPath.HasValue) + { + foreach (BindingFieldPath item in bindFieldPath.Value) { - result.Add(new MappingMember(sourceMember, targetMember)); + var sourceMemberPath = GetSourceMemberPath(item.SourcePath, typePair.Source); + var targetMemberPath = GetSourceMemberPath(item.TargetPath, typePair.Target); + result.Add(new MappingMemberPath(sourceMemberPath, targetMemberPath)); + } } return result; diff --git a/Source/TinyMapper/Mappers/Classes/Members/MappingMemberPath.cs b/Source/TinyMapper/Mappers/Classes/Members/MappingMemberPath.cs new file mode 100644 index 0000000..9126745 --- /dev/null +++ b/Source/TinyMapper/Mappers/Classes/Members/MappingMemberPath.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Nelibur.ObjectMapper.Core.DataStructures; +using Nelibur.ObjectMapper.Core.Extensions; + +namespace Nelibur.ObjectMapper.Mappers.Classes.Members +{ + internal sealed class MappingMemberPath + { + public MappingMemberPath(List source, List target) + : this(source, target, new TypePair(source[source.Count - 1].GetMemberType(), target[target.Count - 1].GetMemberType())) + { + } + + public MappingMemberPath(MemberInfo source, MemberInfo target) + : this(new List { source }, new List { target }, new TypePair(source.GetMemberType(), target.GetMemberType())) + { + } + + public MappingMemberPath(MemberInfo source, MemberInfo target, TypePair typePair) + : this(new List { source }, new List { target }, typePair) + { + } + + public MappingMemberPath(List source, List target, TypePair typePair) + { + Source = source; + OneLevelSource = source.Count == 1; + OneLevelTarget = target.Count == 1; + Target = target; + TypePair = typePair; + Tail = new MappingMember(source[source.Count - 1], target[target.Count - 1], typePair); + Head = new MappingMember(source[0], target[0], new TypePair(source[0].GetMemberType(), target[0].GetMemberType())); + } + + public bool OneLevelSource { get; } + public bool OneLevelTarget { get; } + public List Source { get; } + public List Target { get; } + public TypePair TypePair { get; } + public MappingMember Tail { get; } + public MappingMember Head { get; } + } +} diff --git a/Source/TinyMapper/Mappers/Classes/Members/MemberEmitterDescription.cs b/Source/TinyMapper/Mappers/Classes/Members/MemberEmitterDescription.cs index 1e340ff..92cf5a1 100644 --- a/Source/TinyMapper/Mappers/Classes/Members/MemberEmitterDescription.cs +++ b/Source/TinyMapper/Mappers/Classes/Members/MemberEmitterDescription.cs @@ -14,7 +14,7 @@ public MemberEmitterDescription(IEmitter emitter, MapperCache mappers) MapperCache = new Option(mappers, mappers.IsEmpty); } - public IEmitter Emitter { get; private set; } + public IEmitter Emitter { get; } public Option MapperCache { get; private set; } public void AddMapper(MapperCache value) diff --git a/Source/TinyMapper/Mappers/Classes/Members/MemberMapper.cs b/Source/TinyMapper/Mappers/Classes/Members/MemberMapper.cs index 93de1d1..e550bab 100644 --- a/Source/TinyMapper/Mappers/Classes/Members/MemberMapper.cs +++ b/Source/TinyMapper/Mappers/Classes/Members/MemberMapper.cs @@ -11,18 +11,24 @@ namespace Nelibur.ObjectMapper.Mappers.Classes.Members internal sealed class MemberMapper { private readonly IMapperBuilderConfig _config; - private readonly MapperCache _mapperCache = new MapperCache(); + private readonly MapperCache _mapperCache; - public MemberMapper(IMapperBuilderConfig config) + + public MemberMapper(MapperCache mapperCache, IMapperBuilderConfig config) { + _mapperCache = mapperCache; _config = config; } - public MemberEmitterDescription Build(TypePair parentTypePair, List members) + public MemberEmitterDescription Build(TypePair parentTypePair, List members) { - var emitter = new EmitComposite(); - members.ForEach(x => emitter.Add(Build(parentTypePair, x))); - var result = new MemberEmitterDescription(emitter, _mapperCache); + var emitComposite = new EmitComposite(); + foreach (var path in members) + { + IEmitter emitter = Build(parentTypePair, path); + emitComposite.Add(emitter); + } + var result = new MemberEmitterDescription(emitComposite, _mapperCache); result.AddMapper(_mapperCache); return result; } @@ -47,23 +53,41 @@ private static IEmitterType StoreTargetObjectMember(MappingMember member, IEmitt return result; } - private IEmitter Build(TypePair parentTypePair, MappingMember member) + private IEmitter Build(TypePair parentTypePair, MappingMemberPath memberPath) { - IEmitterType sourceObject = EmitArgument.Load(member.TypePair.Source, 1); - IEmitterType targetObject = EmitArgument.Load(member.TypePair.Target, 2); - IEmitterType sourceMember = LoadMember(member.Source, sourceObject); - IEmitterType targetMember = LoadMember(member.Target, targetObject); + if (memberPath.OneLevelTarget) + { + var sourceObject = EmitArgument.Load(memberPath.TypePair.Source, 1); + var targetObject = EmitArgument.Load(memberPath.TypePair.Target, 2); - IEmitterType convertedMember = ConvertMember(parentTypePair, member, sourceMember, targetMember); + var sourceMember = LoadMember(memberPath.Source, sourceObject, memberPath.Source.Count); + var targetMember = LoadMember(memberPath.Target, targetObject, memberPath.Target.Count); - IEmitter result = StoreTargetObjectMember(member, targetObject, convertedMember); - return result; + IEmitterType convertedMember = ConvertMember(parentTypePair, memberPath.Tail, sourceMember, targetMember); + + IEmitter result = StoreTargetObjectMember(memberPath.Tail, targetObject, convertedMember); + return result; + } + else + { + var targetObject = EmitArgument.Load(memberPath.Head.TypePair.Target, 2); + var targetMember = LoadMember(memberPath.Target, targetObject, memberPath.Target.Count - 1); + + var sourceObject = EmitArgument.Load(memberPath.Head.TypePair.Source, 1); + var sourceMember = LoadMember(memberPath.Source, sourceObject, memberPath.Source.Count); + + IEmitterType convertedMember = ConvertMember(parentTypePair, memberPath.Tail, sourceMember, targetMember); + + IEmitter result = StoreTargetObjectMember(memberPath.Tail, targetMember, convertedMember); + return result; + } } private IEmitterType ConvertMember(TypePair parentTypePair, MappingMember member, IEmitterType sourceMemeber, IEmitterType targetMember) { - if (member.TypePair.IsDeepCloneable && _config.GetBindingConfig(parentTypePair).HasNoValue) + // if (member.TypePair.IsDeepCloneable && _config.GetBindingConfig(parentTypePair).HasNoValue) + if (member.TypePair.IsDeepCloneable) { return sourceMemeber; } @@ -76,10 +100,15 @@ private IEmitterType ConvertMember(TypePair parentTypePair, MappingMember member private MapperCacheItem CreateMapperCacheItem(TypePair parentTypePair, MappingMember mappingMember) { + var mapperCacheItemOption = _mapperCache.Get(mappingMember.TypePair); + if (mapperCacheItemOption.HasValue) + { + return mapperCacheItemOption.Value; + } + MapperBuilder mapperBuilder = _config.GetMapperBuilder(parentTypePair, mappingMember); Mapper mapper = mapperBuilder.Build(parentTypePair, mappingMember); MapperCacheItem mapperCacheItem = _mapperCache.Add(mappingMember.TypePair, mapper); - return mapperCacheItem; } @@ -88,6 +117,20 @@ private IEmitterType LoadField(IEmitterType source, FieldInfo field) return EmitField.Load(source, field); } + private IEmitterType LoadMember(List members, IEmitterType sourceObject, int loadLevel) + { + IEmitterType dummySource = sourceObject; + if (members.Count == 1) + { + return LoadMember(members[0], dummySource); + } + for (int i = 0; i < loadLevel; i++) + { + dummySource = LoadMember(members[i], dummySource); + } + return dummySource; + } + private IEmitterType LoadMember(MemberInfo member, IEmitterType sourceObject) { IEmitterType result = null; diff --git a/Source/TinyMapper/Mappers/Collections/CollectionMapper.cs b/Source/TinyMapper/Mappers/Collections/CollectionMapper.cs index 9d44b16..b72c9fd 100644 --- a/Source/TinyMapper/Mappers/Collections/CollectionMapper.cs +++ b/Source/TinyMapper/Mappers/Collections/CollectionMapper.cs @@ -6,6 +6,7 @@ namespace Nelibur.ObjectMapper.Mappers.Collections { internal abstract class CollectionMapper : MapperOf + where TTarget : class { protected virtual object ConvertItem(object item) { @@ -43,9 +44,9 @@ protected Array EnumerableToArrayTemplate(IEnumerable source) { var result = new TTargetItem[source.Count()]; int index = 0; - foreach (object item in source) + foreach (var item in source) { - result[index++] = ((TTargetItem)ConvertItem(item)); + result[index++] = (TTargetItem)ConvertItem(item); } return result; } @@ -55,16 +56,50 @@ protected virtual TTarget EnumerableToList(IEnumerable source) throw new NotImplementedException(); } + protected virtual TTarget EnumerableToArrayList(IEnumerable source) + { + var result = new ArrayList(); + + foreach (var item in source) + { + result.Add(ConvertItem(item)); + } + + return result as TTarget; + } + protected List EnumerableToListTemplate(IEnumerable source) { var result = new List(); - foreach (object item in source) + foreach (var item in source) { result.Add((TTargetItem)ConvertItem(item)); } return result; } + protected List EnumerableOfDeepCloneableToListTemplate(IEnumerable source) + { + var result = new List(); + result.AddRange((IEnumerable)source); + return result; + } + + protected virtual TTarget EnumerableToEnumerable(IEnumerable source) + { + IList result = null; + foreach (var item in source) + { + if (result == null) + { + result = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(item.GetType())); + } + + result.Add(ConvertItem(item)); + } + return result as TTarget; + } + protected override TTarget MapCore(TSource source, TTarget target) { Type targetType = typeof(TTarget); @@ -74,16 +109,25 @@ protected override TTarget MapCore(TSource source, TTarget target) { return EnumerableToList(enumerable); } - else if (targetType.IsArray) + if (targetType.IsArray) { return EnumerableToArray(enumerable); } - else if (typeof(TSource).IsDictionaryOf() && targetType.IsDictionaryOf()) + if (typeof(TSource).IsDictionaryOf() && targetType.IsDictionaryOf()) { return DictionaryToDictionary(enumerable); } - string errorMessage = string.Format("Not suppoerted From {0} To {1}", typeof(TSource).Name, targetType.Name); - throw new NotSupportedException(errorMessage); + if (targetType == typeof(ArrayList)) + { + return EnumerableToArrayList(enumerable); + } + if (targetType.IsIEnumerable()) + { + // Default Case + return EnumerableToEnumerable(enumerable); + } + + throw new NotSupportedException($"Not suppoerted From {typeof(TSource).Name} To {targetType.Name}"); } } } diff --git a/Source/TinyMapper/Mappers/Collections/CollectionMapperBuilder.cs b/Source/TinyMapper/Mappers/Collections/CollectionMapperBuilder.cs index b214307..1b05dd0 100644 --- a/Source/TinyMapper/Mappers/Collections/CollectionMapperBuilder.cs +++ b/Source/TinyMapper/Mappers/Collections/CollectionMapperBuilder.cs @@ -5,6 +5,7 @@ using System.Reflection.Emit; using Nelibur.ObjectMapper.CodeGenerators; using Nelibur.ObjectMapper.CodeGenerators.Emitters; +using Nelibur.ObjectMapper.Core; using Nelibur.ObjectMapper.Core.DataStructures; using Nelibur.ObjectMapper.Core.Extensions; using Nelibur.ObjectMapper.Mappers.Caches; @@ -14,6 +15,7 @@ namespace Nelibur.ObjectMapper.Mappers.Collections { internal sealed class CollectionMapperBuilder : MapperBuilder { + private readonly MapperCache _mapperCache; private const string ConvertItemKeyMethod = "ConvertItemKey"; private const string ConvertItemMethod = "ConvertItem"; private const string DictionaryToDictionaryMethod = "DictionaryToDictionary"; @@ -22,21 +24,22 @@ internal sealed class CollectionMapperBuilder : MapperBuilder private const string EnumerableToArrayTemplateMethod = "EnumerableToArrayTemplate"; private const string EnumerableToListMethod = "EnumerableToList"; private const string EnumerableToListTemplateMethod = "EnumerableToListTemplate"; - private readonly MapperCache _mapperCache = new MapperCache(); + private const string EnumerableOfDeepCloneableToListTemplateMethod = "EnumerableOfDeepCloneableToListTemplate"; - public CollectionMapperBuilder(IMapperBuilderConfig config) : base(config) + public CollectionMapperBuilder(MapperCache mapperCache, IMapperBuilderConfig config) : base(config) { + _mapperCache = mapperCache; } - protected override string ScopeName - { - get { return "CollectionMappers"; } - } + protected override string ScopeName => "CollectionMappers"; protected override Mapper BuildCore(TypePair typePair) { Type parentType = typeof(CollectionMapper<,>).MakeGenericType(typePair.Source, typePair.Target); TypeBuilder typeBuilder = _assembly.DefineType(GetMapperFullName(), parentType); + + _mapperCache.AddStub(typePair); + if (IsIEnumerableToList(typePair)) { EmitEnumerableToList(parentType, typeBuilder, typePair); @@ -49,9 +52,17 @@ protected override Mapper BuildCore(TypePair typePair) { EmitDictionaryToDictionary(parentType, typeBuilder, typePair); } - var result = (Mapper)Activator.CreateInstance(typeBuilder.CreateType()); - result.AddMappers(_mapperCache.Mappers); - return result; + else if (IsEnumerableToEnumerable(typePair)) + { + EmitEnumerableToEnumerable(parentType, typeBuilder, typePair); + } + + var rootMapper = (Mapper)Activator.CreateInstance(Helpers.CreateType(typeBuilder)); + + _mapperCache.ReplaceStub(typePair, rootMapper); + rootMapper.AddMappers(_mapperCache.Mappers); + + return rootMapper; } protected override Mapper BuildCore(TypePair parentTypePair, MappingMember mappingMember) @@ -79,8 +90,19 @@ private static bool IsIEnumerableToList(TypePair typePair) return typePair.Source.IsIEnumerable() && typePair.Target.IsListOf(); } + private bool IsEnumerableToEnumerable(TypePair typePair) + { + return typePair.Source.IsIEnumerable() && typePair.Target.IsIEnumerable(); + } + private MapperCacheItem CreateMapperCacheItem(TypePair typePair) { + var mapperCacheItemOption = _mapperCache.Get(typePair); + if (mapperCacheItemOption.HasValue) + { + return mapperCacheItemOption.Value; + } + MapperBuilder mapperBuilder = GetMapperBuilder(typePair); Mapper mapper = mapperBuilder.Build(typePair); MapperCacheItem mapperCacheItem = _mapperCache.Add(typePair, mapper); @@ -106,8 +128,12 @@ private void EmitDictionaryToDictionary(Type parentType, TypeBuilder typeBuilder EmitDictionaryToTarget(parentType, typeBuilder, typePair, DictionaryToDictionaryMethod, DictionaryToDictionaryTemplateMethod); } - private void EmitDictionaryToTarget(Type parentType, TypeBuilder typeBuilder, TypePair typePair, - string methodName, string templateMethodName) + private void EmitDictionaryToTarget( + Type parentType, + TypeBuilder typeBuilder, + TypePair typePair, + string methodName, + string templateMethodName) { MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, OverrideProtected, typePair.Target, new[] { typeof(IEnumerable) }); @@ -126,25 +152,48 @@ private void EmitDictionaryToTarget(Type parentType, TypeBuilder typeBuilder, Ty private void EmitEnumerableToArray(Type parentType, TypeBuilder typeBuilder, TypePair typePair) { - EmitEnumerableToTarget(parentType, typeBuilder, typePair, EnumerableToArrayMethod, EnumerableToArrayTemplateMethod); + var collectionItemTypePair = GetCollectionItemTypePair(typePair); + + EmitEnumerableToTarget(parentType, typeBuilder, typePair, collectionItemTypePair, EnumerableToArrayMethod, EnumerableToArrayTemplateMethod); } private void EmitEnumerableToList(Type parentType, TypeBuilder typeBuilder, TypePair typePair) { - EmitEnumerableToTarget(parentType, typeBuilder, typePair, EnumerableToListMethod, EnumerableToListTemplateMethod); + var collectionItemTypePair = GetCollectionItemTypePair(typePair); + var templateMethod = collectionItemTypePair.IsDeepCloneable ? EnumerableOfDeepCloneableToListTemplateMethod : EnumerableToListTemplateMethod; + + EmitEnumerableToTarget(parentType, typeBuilder, typePair, collectionItemTypePair, EnumerableToListMethod, templateMethod); } - private void EmitEnumerableToTarget(Type parentType, TypeBuilder typeBuilder, TypePair typePair, - string methodName, string templateMethodName) + private void EmitEnumerableToEnumerable(Type parentType, TypeBuilder typeBuilder, TypePair typePair) { - MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, OverrideProtected, typePair.Target, new[] { typeof(IEnumerable) }); + var collectionItemTypePair = GetCollectionItemTypePair(typePair); + var templateMethod = collectionItemTypePair.IsDeepCloneable ? EnumerableOfDeepCloneableToListTemplateMethod : EnumerableToListTemplateMethod; + + EmitEnumerableToTarget(parentType, typeBuilder, typePair, collectionItemTypePair, EnumerableToListMethod, templateMethod); + } + private static TypePair GetCollectionItemTypePair(TypePair typePair) + { Type sourceItemType = typePair.Source.GetCollectionItemType(); Type targetItemType = typePair.Target.GetCollectionItemType(); - EmitConvertItem(typeBuilder, new TypePair(sourceItemType, targetItemType)); + return new TypePair(sourceItemType, targetItemType); + } + + private void EmitEnumerableToTarget( + Type parentType, + TypeBuilder typeBuilder, + TypePair typePair, + TypePair collectionItemTypePair, + string methodName, + string templateMethodName) + { + MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, OverrideProtected, typePair.Target, new[] { typeof(IEnumerable) }); + + EmitConvertItem(typeBuilder, collectionItemTypePair); - MethodInfo methodTemplate = parentType.GetGenericMethod(templateMethodName, targetItemType); + MethodInfo methodTemplate = parentType.GetGenericMethod(templateMethodName, collectionItemTypePair.Target); IEmitterType returnValue = EmitMethod.Call(methodTemplate, EmitThis.Load(parentType), EmitArgument.Load(typeof(IEnumerable), 1)); EmitReturn.Return(returnValue).Emit(new CodeGenerator(methodBuilder.GetILGenerator())); diff --git a/Source/TinyMapper/Mappers/IMapperBuilderConfig.cs b/Source/TinyMapper/Mappers/IMapperBuilderConfig.cs index b198a14..e7a75e5 100644 --- a/Source/TinyMapper/Mappers/IMapperBuilderConfig.cs +++ b/Source/TinyMapper/Mappers/IMapperBuilderConfig.cs @@ -9,6 +9,7 @@ namespace Nelibur.ObjectMapper.Mappers internal interface IMapperBuilderConfig { IDynamicAssembly Assembly { get; } + Func NameMatching { get; } Option GetBindingConfig(TypePair typePair); MapperBuilder GetMapperBuilder(TypePair typePair); MapperBuilder GetMapperBuilder(TypePair parentTypePair, MappingMember mappingMember); diff --git a/Source/TinyMapper/Mappers/Mapper.cs b/Source/TinyMapper/Mappers/Mapper.cs index 1cd5993..ce5c734 100644 --- a/Source/TinyMapper/Mappers/Mapper.cs +++ b/Source/TinyMapper/Mappers/Mapper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Nelibur.ObjectMapper.Mappers { @@ -9,11 +10,31 @@ internal abstract class Mapper public const string MappersFieldName = "_mappers"; protected Mapper[] _mappers; - public void AddMappers(List mappers) + public void AddMappers(IEnumerable mappers) { _mappers = mappers.ToArray(); } + public void UpdateRootMapper(int mapperId, Mapper mapper) + { + if (_mappers == null) + { + return; + } + + for (int i = 0; i < _mappers.Length; i++) + { + if (i == mapperId) + { + if (_mappers[i] == null) + { + _mappers[i] = mapper; + } + return; + } + } + } + public object Map(object source, object target = null) { return MapCore(source, target); diff --git a/Source/TinyMapper/Mappers/MapperBuilder.cs b/Source/TinyMapper/Mappers/MapperBuilder.cs index 1894824..291566e 100644 --- a/Source/TinyMapper/Mappers/MapperBuilder.cs +++ b/Source/TinyMapper/Mappers/MapperBuilder.cs @@ -9,9 +9,9 @@ namespace Nelibur.ObjectMapper.Mappers internal abstract class MapperBuilder { protected const MethodAttributes OverrideProtected = MethodAttributes.Family | MethodAttributes.Virtual; + private const string AssemblyName = "DynamicTinyMapper"; protected readonly IDynamicAssembly _assembly; protected readonly IMapperBuilderConfig _config; - private const string AssemblyName = "DynamicTinyMapper"; protected MapperBuilder(IMapperBuilderConfig config) { @@ -47,7 +47,7 @@ protected MapperBuilder GetMapperBuilder(TypePair typePair) protected string GetMapperFullName() { string random = Guid.NewGuid().ToString("N"); - return string.Format("{0}.{1}.Mapper{2}", AssemblyName, ScopeName, random); + return $"{AssemblyName}.{ScopeName}.Mapper{random}"; } protected abstract bool IsSupportedCore(TypePair typePair); diff --git a/Source/TinyMapper/Mappers/TargetMapperBuilder.cs b/Source/TinyMapper/Mappers/TargetMapperBuilder.cs index 2ad2587..02534ae 100644 --- a/Source/TinyMapper/Mappers/TargetMapperBuilder.cs +++ b/Source/TinyMapper/Mappers/TargetMapperBuilder.cs @@ -3,6 +3,7 @@ using Nelibur.ObjectMapper.Bindings; using Nelibur.ObjectMapper.Core.DataStructures; using Nelibur.ObjectMapper.Core.Extensions; +using Nelibur.ObjectMapper.Mappers.Caches; using Nelibur.ObjectMapper.Mappers.Classes; using Nelibur.ObjectMapper.Mappers.Classes.Members; using Nelibur.ObjectMapper.Mappers.Collections; @@ -14,6 +15,8 @@ namespace Nelibur.ObjectMapper.Mappers { internal sealed class TargetMapperBuilder : IMapperBuilderConfig { + public static readonly Func DefaultNameMatching = (source, target) => string.Equals(source, target, StringComparison.Ordinal); + private readonly Dictionary _bindingConfigs = new Dictionary(); private readonly ClassMapperBuilder _classMapperBuilder; private readonly CollectionMapperBuilder _collectionMapperBuilder; @@ -24,26 +27,18 @@ public TargetMapperBuilder(IDynamicAssembly assembly) { Assembly = assembly; - _classMapperBuilder = new ClassMapperBuilder(this); - _collectionMapperBuilder = new CollectionMapperBuilder(this); + var mapperCache = new MapperCache(); + _classMapperBuilder = new ClassMapperBuilder(mapperCache, this); + _collectionMapperBuilder = new CollectionMapperBuilder(mapperCache, this); _convertibleTypeMapperBuilder = new ConvertibleTypeMapperBuilder(this); _customTypeMapperBuilder = new CustomTypeMapperBuilder(this); - } - public IDynamicAssembly Assembly { get; private set; } - - public Mapper Build(TypePair typePair, BindingConfig bindingConfig) - { - _bindingConfigs[typePair] = bindingConfig; - return Build(typePair); + NameMatching = DefaultNameMatching; } - public Mapper Build(TypePair typePair) - { - MapperBuilder mapperBuilder = GetMapperBuilder(typePair); - Mapper mapper = mapperBuilder.Build(typePair); - return mapper; - } + public Func NameMatching { get; private set; } + + public IDynamicAssembly Assembly { get; } public Option GetBindingConfig(TypePair typePair) { @@ -65,6 +60,24 @@ public MapperBuilder GetMapperBuilder(TypePair typePair) return GetTypeMapperBuilder(typePair); } + public void SetNameMatching(Func nameMatching) + { + NameMatching = nameMatching; + } + + public Mapper Build(TypePair typePair, BindingConfig bindingConfig) + { + _bindingConfigs[typePair] = bindingConfig; + return Build(typePair); + } + + public Mapper Build(TypePair typePair) + { + MapperBuilder mapperBuilder = GetTypeMapperBuilder(typePair); + Mapper mapper = mapperBuilder.Build(typePair); + return mapper; + } + private MapperBuilder GetTypeMapperBuilder(TypePair typePair) { if (_convertibleTypeMapperBuilder.IsSupported(typePair)) diff --git a/Source/TinyMapper/Mappers/Types/Convertible/ConvertibleTypeMapperBuilder.cs b/Source/TinyMapper/Mappers/Types/Convertible/ConvertibleTypeMapperBuilder.cs index 7b23b1e..10e431a 100644 --- a/Source/TinyMapper/Mappers/Types/Convertible/ConvertibleTypeMapperBuilder.cs +++ b/Source/TinyMapper/Mappers/Types/Convertible/ConvertibleTypeMapperBuilder.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using Nelibur.ObjectMapper.Core; using Nelibur.ObjectMapper.Core.DataStructures; using Nelibur.ObjectMapper.Core.Extensions; using Nelibur.ObjectMapper.Mappers.Classes.Members; @@ -14,10 +15,7 @@ public ConvertibleTypeMapperBuilder(IMapperBuilderConfig config) : base(config) { } - protected override string ScopeName - { - get { return "ConvertibleTypeMappers"; } - } + protected override string ScopeName => "ConvertibleTypeMappers"; protected override Mapper BuildCore(TypePair typePair) { @@ -44,20 +42,21 @@ private static Option> ConvertEnum(TypePair pair) return result.ToOption(); } - if (pair.Target.IsEnum) + if (Helpers.IsEnum(pair.Target)) { - if (pair.Source.IsEnum == false) + if (Helpers.IsEnum(pair.Source) == false) { if (pair.Source == typeof(string)) { result = x => Enum.Parse(pair.Target, x.ToString()); + return result.ToOption(); } } result = x => Enum.ToObject(pair.Target, Convert.ChangeType(x, Enum.GetUnderlyingType(pair.Target))); return result.ToOption(); } - if (pair.Source.IsEnum) + if (Helpers.IsEnum(pair.Source)) { result = x => Convert.ChangeType(x, pair.Target); return result.ToOption(); @@ -105,10 +104,10 @@ private static Func GetConverter(TypePair pair) private bool IsSupportedType(Type value) { - return value.IsPrimitive + return Helpers.IsPrimitive(value) || value == typeof(string) || value == typeof(Guid) - || value.IsEnum + || Helpers.IsEnum(value) || value == typeof(decimal) || value.IsNullable() && IsSupportedType(Nullable.GetUnderlyingType(value)); } diff --git a/Source/TinyMapper/Mappers/Types/Custom/CustomTypeMapperBuilder.cs b/Source/TinyMapper/Mappers/Types/Custom/CustomTypeMapperBuilder.cs index 33553e0..5df08ae 100644 --- a/Source/TinyMapper/Mappers/Types/Custom/CustomTypeMapperBuilder.cs +++ b/Source/TinyMapper/Mappers/Types/Custom/CustomTypeMapperBuilder.cs @@ -11,10 +11,7 @@ public CustomTypeMapperBuilder(IMapperBuilderConfig config) : base(config) { } - protected override string ScopeName - { - get { return "CustomTypeMapper"; } - } + protected override string ScopeName => "CustomTypeMapper"; public bool IsSupported(TypePair parentTypePair, MappingMember mappingMember) { diff --git a/Source/TinyMapper/Properties/AssemblyInfo.cs b/Source/TinyMapper/Properties/AssemblyInfo.cs index e96ca9d..b6e0313 100644 --- a/Source/TinyMapper/Properties/AssemblyInfo.cs +++ b/Source/TinyMapper/Properties/AssemblyInfo.cs @@ -8,9 +8,6 @@ using System.Runtime.InteropServices; using Nelibur.ObjectMapper.Reflection; -[assembly: AssemblyTitle("TinyMapper")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyCompany("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible diff --git a/Source/TinyMapper/Reflection/DynamicAssemblyBuilder.cs b/Source/TinyMapper/Reflection/DynamicAssemblyBuilder.cs index fee3807..8a727f5 100644 --- a/Source/TinyMapper/Reflection/DynamicAssemblyBuilder.cs +++ b/Source/TinyMapper/Reflection/DynamicAssemblyBuilder.cs @@ -7,15 +7,18 @@ namespace Nelibur.ObjectMapper.Reflection internal class DynamicAssemblyBuilder { internal const string AssemblyName = "DynamicTinyMapper"; +#if !COREFX // private const string AssemblyNameFileName = AssemblyName + ".dll"; - private static readonly DynamicAssembly _dynamicAssembly = new DynamicAssembly(); // private static AssemblyBuilder _assemblyBuilder; +#endif + private static readonly DynamicAssembly _dynamicAssembly = new DynamicAssembly(); public static IDynamicAssembly Get() { return _dynamicAssembly; } + private sealed class DynamicAssembly : IDynamicAssembly { private readonly ModuleBuilder _moduleBuilder; @@ -23,11 +26,19 @@ private sealed class DynamicAssembly : IDynamicAssembly public DynamicAssembly() { var assemblyName = new AssemblyName(AssemblyName); + +#if COREFX + AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + _moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); + +#else AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); -// _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); +// _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); _moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); -// _moduleBuilder = _assemblyBuilder.DefineDynamicModule(assemblyName.Name, AssemblyNameFileName, true); +// _moduleBuilder = _assemblyBuilder.DefineDynamicModule(assemblyName.Name, AssemblyNameFileName, true); +#endif + } public TypeBuilder DefineType(string typeName, Type parentType) @@ -37,7 +48,9 @@ public TypeBuilder DefineType(string typeName, Type parentType) public void Save() { +#if !COREFX // _assemblyBuilder.Save(AssemblyNameFileName); +#endif } } } diff --git a/Source/TinyMapper/TinyMapper.cs b/Source/TinyMapper/TinyMapper.cs index 6548726..fbde9ab 100644 --- a/Source/TinyMapper/TinyMapper.cs +++ b/Source/TinyMapper/TinyMapper.cs @@ -1,83 +1,248 @@ -using System; -using System.Collections.Generic; -using Nelibur.ObjectMapper.Bindings; -using Nelibur.ObjectMapper.Core; -using Nelibur.ObjectMapper.Core.DataStructures; -using Nelibur.ObjectMapper.Core.Extensions; -using Nelibur.ObjectMapper.Mappers; -using Nelibur.ObjectMapper.Reflection; - -namespace Nelibur.ObjectMapper -{ - public static class TinyMapper - { - private static readonly Dictionary _mappers = new Dictionary(); - private static readonly TargetMapperBuilder _targetMapperBuilder; - - static TinyMapper() - { - IDynamicAssembly assembly = DynamicAssemblyBuilder.Get(); - _targetMapperBuilder = new TargetMapperBuilder(assembly); - } - - public static void Bind() - { - TypePair typePair = TypePair.Create(); - - _mappers[typePair] = _targetMapperBuilder.Build(typePair); - } - - public static void Bind(Action> config) - { - TypePair typePair = TypePair.Create(); - - var bindingConfig = new BindingConfigOf(); - config(bindingConfig); - - _mappers[typePair] = _targetMapperBuilder.Build(typePair, bindingConfig); - } - - public static TTarget Map(TSource source, TTarget target = default(TTarget)) - { - TypePair typePair = TypePair.Create(); - - Mapper mapper = GetMapper(typePair); - var result = (TTarget)mapper.Map(source, target); - - return result; - } - - /// - /// Maps the specified source to type. - /// - /// The type of the target. - /// The source value. - /// Value - /// For mapping nullable type use method. - public static TTarget Map(object source) - { - if (source.IsNull()) - { - throw Error.ArgumentNull("source, for mapping nullable type use Map method"); - } - - TypePair typePair = TypePair.Create(source.GetType(), typeof(TTarget)); - - Mapper mapper = GetMapper(typePair); - var result = (TTarget)mapper.Map(source); - - return result; - } - - private static Mapper GetMapper(TypePair typePair) - { - Mapper mapper; - if (_mappers.TryGetValue(typePair, out mapper) == false) - { - mapper = _targetMapperBuilder.Build(typePair); - _mappers[typePair] = mapper; - } - return mapper; - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +#if COREFX +using System.Reflection; +#endif +using System.Threading; +using Nelibur.ObjectMapper.Bindings; +using Nelibur.ObjectMapper.Core; +using Nelibur.ObjectMapper.Core.DataStructures; +using Nelibur.ObjectMapper.Core.Extensions; +using Nelibur.ObjectMapper.Mappers; +using Nelibur.ObjectMapper.Reflection; + +namespace Nelibur.ObjectMapper +{ + /// + /// TinyMapper is an object to object mapper for .NET. The main advantage is performance. + /// TinyMapper allows easily map object to object, i.e. properties or fields from one object to another. + /// + public static class TinyMapper + { + private static readonly Dictionary _mappers = new Dictionary(); + private static readonly TargetMapperBuilder _targetMapperBuilder; + private static readonly TinyMapperConfig _config; + private static readonly object _mappersLock = new object(); + + static TinyMapper() + { + IDynamicAssembly assembly = DynamicAssemblyBuilder.Get(); + _targetMapperBuilder = new TargetMapperBuilder(assembly); + _config = new TinyMapperConfig(_targetMapperBuilder); + } + + /// + /// Create a one-way mapping between Source and Target types. + /// + /// Source type. + /// Target type. + /// The method is thread safe. + public static void Bind() + { + TypePair typePair = TypePair.Create(); + lock (_mappersLock) + { + _mappers[typePair] = _targetMapperBuilder.Build(typePair); + } + } + + /// + /// Create a one-way mapping between Source and Target types. + /// + /// Source type. + /// Target type. + /// The method is thread safe. + public static void Bind(Type sourceType, Type targetType) + { + if (sourceType == null) + { + throw new ArgumentNullException(nameof(sourceType)); + } + if (targetType == null) + { + throw new ArgumentNullException(nameof(targetType)); + } + TypePair typePair = TypePair.Create(sourceType, targetType); + lock (_mappersLock) + { + _mappers[typePair] = _targetMapperBuilder.Build(typePair); + } + } + + /// + /// Create a one-way mapping between Source and Target types. + /// + /// Source type. + /// Target type. + /// BindingConfig for custom binding. + /// The method is thread safe. + public static void Bind(Action> config) + { + TypePair typePair = TypePair.Create(); + + var bindingConfig = new BindingConfigOf(); + config(bindingConfig); + + lock (_mappersLock) + { + _mappers[typePair] = _targetMapperBuilder.Build(typePair, bindingConfig); + } + } + + /// + /// Find out if a binding exists from Source to Target. + /// + /// Source type. + /// Target type. + /// True if exists, otherwise - False. + /// The method is thread safe. + public static bool BindingExists() + { + TypePair typePair = TypePair.Create(); + lock (_mappersLock) + { + return _mappers.ContainsKey(typePair); + } + } + + /// + /// Maps the source to Target type. + /// The method can be called in parallel to Map methods, but cannot be called in parallel to Bind method. + /// + /// Source type. + /// Target type. + /// Source object. + /// Target object. + /// Mapped object. + public static TTarget Map(TSource source, TTarget target = default(TTarget)) + { + TypePair typePair = TypePair.Create(); + + Mapper mapper = GetMapper(typePair); + var result = (TTarget)mapper.Map(source, target); + + return result; + } + + /// + /// Maps the source to Target type. + /// The method can be called in parallel to Map methods, but cannot be called in parallel to Bind method. + /// + /// Source type. + /// Target type. + /// Source object. + /// Target object. + /// Mapped object. + public static object Map(Type sourceType, Type targetType, object source, object target = null) + { + TypePair typePair = TypePair.Create(sourceType, targetType); + + Mapper mapper = GetMapper(typePair); + var result = mapper.Map(source, target); + + return result; + } + + /// + /// Configure the Mapper. + /// + /// Lambda to provide config settings + public static void Config(Action config) + { + config(_config); + } + + /// + /// Maps the source to Target type. + /// The method can be called in parallel to Map methods, but cannot be called in parallel to Bind method. + /// + /// Target type. + /// Source object [Not null]. + /// Mapped object. The method can be called in parallel to Map methods, but cannot be called in parallel to Bind method. + public static TTarget Map(object source) + { + if (source.IsNull()) + { + throw Error.ArgumentNull("Source cannot be null. Use TinyMapper.Map method instead."); + } + + TypePair typePair = TypePair.Create(source.GetType(), typeof(TTarget)); + + Mapper mapper = GetMapper(typePair); + var result = (TTarget)mapper.Map(source); + + return result; + } + + [SuppressMessage("ReSharper", "All")] + private static Mapper GetMapper(TypePair typePair) + { + Mapper mapper; + + if (_mappers.TryGetValue(typePair, out mapper) == false) + { + throw new TinyMapperException($"No binding found for '{typePair.Source.Name}' to '{typePair.Target.Name}'. " + + $"Call TinyMapper.Bind<{typePair.Source.Name}, {typePair.Target.Name}>()"); + } + +// _mappersLock.EnterUpgradeableReadLock(); +// try +// { +// if (_mappers.TryGetValue(typePair, out mapper) == false) +// { +// if (_config.EnablePolymorphicMapping && (mapper = GetPolymorphicMapping(typePair)) != null) +// { +// return mapper; +// } +// else if (_config.EnableAutoBinding) +// { +// mapper = _targetMapperBuilder.Build(typePair); +// _mappersLock.EnterWriteLock(); +// try +// { +// _mappers[typePair] = mapper; +// } +// finally +// { +// _mappersLock.ExitWriteLock(); +// } +// } +// else +// { +// throw new TinyMapperException($"Unable to find a binding for type '{typePair.Source?.Name}' to '{typePair.Target?.Name}'."); +// } +// } +// } +// finally +// { +// _mappersLock.ExitUpgradeableReadLock(); +// } + + return mapper; + } + + //Note: Lock should already be acquired for the mapper +// private static Mapper GetPolymorphicMapping(TypePair types) +// { +// // Walk the polymorphic heirarchy until we find a mapping match +// Type source = types.Source; +// +// do +// { +// Mapper result; +// foreach (Type iface in source.GetInterfaces()) +// { +// if (_mappers.TryGetValue(TypePair.Create(iface, types.Target), out result)) +// return result; +// } +// +// if (_mappers.TryGetValue(TypePair.Create(source, types.Target), out result)) +// return result; +// } +// while ((source = Helpers.BaseType(source)) != null); +// +// return null; +// } + } +} diff --git a/Source/TinyMapper/TinyMapper.csproj b/Source/TinyMapper/TinyMapper.csproj index 48ad9c7..83f6075 100644 --- a/Source/TinyMapper/TinyMapper.csproj +++ b/Source/TinyMapper/TinyMapper.csproj @@ -1,141 +1,47 @@ - - - - - Debug - AnyCPU - {C7EBD676-248B-484A-A5CB-DF879670652A} - Library - Properties - Nelibur.ObjectMapper - TinyMapper - v4.5 - 512 - - - true - full - false - ..\..\Out\Debug\TinyMapper\4.5\ - DEBUG;TRACE - prompt - 4 - true - - - pdbonly - true - ..\..\Out\Release\TinyMapper\4.5\ - TRACE - prompt - 4 - true - - - ..\..\Out\Release\TinyMapper\4.0\ - TRACE - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - v4.0 - - - ..\..\Out\Release\TinyMapper\3.5\ - TRACE - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - v3.5 - - - ..\..\Out\Release\TinyMapper\3.0\ - TRACE - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - v3.5 - - - - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Nelibur.ObjectMapper + net35;net40;netstandard1.3 + + net + Debug;Release;VS + Sergey Morenko + Sergey Morenko + TinyMapper - a tiny and quick object mapper for .Net. + https://github.com/TinyMapper/TinyMapper/blob/master/LICENSE + http://www.gravatar.com/avatar/cfffd78dd57d31992a0052c6786cdc3d + TinyMapper;Mapper + git + https://github.com/TinyMapper/TinyMapper + http://tinymapper.net/ + false + 3.0.2-beta + 3.0.1.0 + 3.0.1.0 + True + 3.0.3 + + + + true + true + + + $(TargetFrameworks) + True + + + COREFX + core + + + + + + + + + + + \ No newline at end of file diff --git a/Source/TinyMapper/TinyMapperConfig.cs b/Source/TinyMapper/TinyMapperConfig.cs new file mode 100644 index 0000000..d553432 --- /dev/null +++ b/Source/TinyMapper/TinyMapperConfig.cs @@ -0,0 +1,29 @@ +using System; +using Nelibur.ObjectMapper.Mappers; + +namespace Nelibur.ObjectMapper +{ + internal sealed class TinyMapperConfig : ITinyMapperConfig + { + private readonly TargetMapperBuilder _targetMapperBuilder; + + public TinyMapperConfig(TargetMapperBuilder targetMapperBuilder) + { + _targetMapperBuilder = targetMapperBuilder ?? throw new ArgumentNullException(); + } + + public void NameMatching(Func nameMatching) + { + if (nameMatching == null) + { + throw new ArgumentNullException(); + } + _targetMapperBuilder.SetNameMatching(nameMatching); + } + + public void Reset() + { + _targetMapperBuilder.SetNameMatching(TargetMapperBuilder.DefaultNameMatching); + } + } +} diff --git a/Source/TinyMapper/TinyMapperException.cs b/Source/TinyMapper/TinyMapperException.cs new file mode 100644 index 0000000..9e65078 --- /dev/null +++ b/Source/TinyMapper/TinyMapperException.cs @@ -0,0 +1,33 @@ +using System; +#if !COREFX +using System.Runtime.Serialization; +#endif + +namespace Nelibur.ObjectMapper +{ + /// + /// Exception during mapping or binding + /// +#if !COREFX + [Serializable] +#endif + public class TinyMapperException : Exception + { + public TinyMapperException() + { + } + + public TinyMapperException(string message) : base(message) + { + } + + public TinyMapperException(string message, Exception innerException) : base(message, innerException) + { + } +#if !COREFX + protected TinyMapperException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } +} diff --git a/Source/UnitTests/Core/Extensions/TypeExtensionsTests.cs b/Source/UnitTests/Core/Extensions/TypeExtensionsTests.cs index d2f0ae5..edb0586 100644 --- a/Source/UnitTests/Core/Extensions/TypeExtensionsTests.cs +++ b/Source/UnitTests/Core/Extensions/TypeExtensionsTests.cs @@ -4,7 +4,6 @@ using System.IO; using Nelibur.ObjectMapper.Core.Extensions; using Xunit; -using Xunit.Extensions; namespace UnitTests.Core.Extensions { @@ -32,7 +31,7 @@ public void IsDictionaryOf_Types_True(Type type, bool result) [InlineData(typeof(List), true)] [InlineData(typeof(int[]), true)] - [InlineData(typeof(ArrayList), false)] + [InlineData(typeof(ArrayList), true)] [InlineData(typeof(int), false)] [Theory] public void IsIEnumerableOf_Types_True(Type type, bool result) diff --git a/Source/UnitTests/ForTests.cs b/Source/UnitTests/ForTests.cs index cac0731..090129b 100644 --- a/Source/UnitTests/ForTests.cs +++ b/Source/UnitTests/ForTests.cs @@ -6,51 +6,53 @@ namespace UnitTests { public sealed class ForTests { + [Fact] public void Test() { - TinyMapper.Bind(config => config.Bind(target => target.Value, "MyValue")); + var source = new TestStaticModel(); + +// TinyMapper.Bind(); + var dto = Map(source); + // OR - var source = new Source(); - var result = TinyMapper.Map(source); + dto = source.Map(); } - [Fact] - public void Test_Area() + private TTarget Map(TSource source) { - TinyMapper.Bind(config => config.Bind(source => source.Area.Name, target => target.AreaName)); - - var actual = new SourceArea + if (!TinyMapper.BindingExists()) { - Area = new Area { Name = "MyName" } - }; - - var result = TinyMapper.Map(actual); + TinyMapper.Bind(); + } + return TinyMapper.Map(source); } + } - public class Area - { - public string Name { get; set; } - } - public class Source + public static class MyExtensions + { + public static TTarget Map(this TSource source) { - public string Value { get; set; } + if (!TinyMapper.BindingExists()) + { + TinyMapper.Bind(); + } + return TinyMapper.Map(source); } + } - public class SourceArea - { - public Area Area { get; set; } - } - public class Target - { - public string Value { get; set; } - } + public class TestStaticModel + { + public static string Name = "test"; + public static int Id = 1; + } - public class TargetArea - { - public string AreaName { get; set; } - } + + public class TestDto + { + public int Id { get; set; } + public string Name { get; set; } } } diff --git a/Source/UnitTests/Mappers/Classes/ClassMapperTests.cs b/Source/UnitTests/Mappers/Classes/ClassMapperTests.cs index b25e87f..d88b7d7 100644 --- a/Source/UnitTests/Mappers/Classes/ClassMapperTests.cs +++ b/Source/UnitTests/Mappers/Classes/ClassMapperTests.cs @@ -1,6 +1,7 @@ using System; using Nelibur.ObjectMapper.Core.DataStructures; using Nelibur.ObjectMapper.Mappers; +using Nelibur.ObjectMapper.Mappers.Caches; using Nelibur.ObjectMapper.Mappers.Classes; using Xunit; @@ -11,10 +12,10 @@ public sealed class ClassMapperTests [Fact] public void Map_PrimitiveField_Success() { - var builder = new ClassMapperBuilder(new MappingBuilderConfigStub()); - Mapper mapper = builder.Build(new TypePair(typeof(FiledSource), typeof(FiledTarget))); + var builder = new ClassMapperBuilder(new MapperCache(), new MappingBuilderConfigStub()); + Mapper mapper = builder.Build(new TypePair(typeof(SourceWithFields), typeof(TargetWithFields))); - var source = new FiledSource + var source = new SourceWithFields { Bool = true, Byte = 0, @@ -30,7 +31,7 @@ public void Map_PrimitiveField_Success() Ushort = 7 }; - var actual = (FiledTarget)mapper.Map(source); + var actual = (TargetWithFields)mapper.Map(source); Assert.Equal(source.Bool, actual.Bool); Assert.Equal(source.Byte, actual.Byte); @@ -49,10 +50,10 @@ public void Map_PrimitiveField_Success() [Fact] public void Map_PrimitiveProperty_Success() { - var builder = new ClassMapperBuilder(new MappingBuilderConfigStub()); - Mapper mapper = builder.Build(new TypePair(typeof(PropertySource), typeof(PropertyTarget))); + var builder = new ClassMapperBuilder(new MapperCache(), new MappingBuilderConfigStub()); + Mapper mapper = builder.Build(new TypePair(typeof(SourceWithProperties), typeof(TargetWithProperties))); - var source = new PropertySource + var source = new SourceWithProperties { Bool = true, Byte = 0, @@ -72,7 +73,7 @@ public void Map_PrimitiveProperty_Success() DateTimeNullable1 = new DateTime(2020, 2, 4) }; - var actual = (PropertyTarget)mapper.Map(source); + var actual = (TargetWithProperties)mapper.Map(source); Assert.Equal(source.Bool, actual.Bool); Assert.Equal(source.Byte, actual.Byte); @@ -92,7 +93,7 @@ public void Map_PrimitiveProperty_Success() Assert.Equal(source.DateTimeNullable1, actual.DateTimeNullable1); } - public class FiledSource + public class SourceWithFields { public bool Bool; public byte Byte; @@ -108,7 +109,7 @@ public class FiledSource public ushort Ushort; } - public class FiledTarget + public class TargetWithFields { public bool Bool; public byte Byte; @@ -124,7 +125,7 @@ public class FiledTarget public ushort Ushort; } - public class PropertySource + public class SourceWithProperties { public bool Bool { get; set; } public byte Byte { get; set; } @@ -144,7 +145,7 @@ public class PropertySource public ushort Ushort { get; set; } } - public class PropertyTarget + public class TargetWithProperties { public bool Bool { get; set; } public byte Byte { get; set; } diff --git a/Source/UnitTests/Mappers/MappingMembers/MappingMemberBuilderTests.cs b/Source/UnitTests/Mappers/MappingMembers/MappingMemberBuilderTests.cs index 9bb33bc..f18d55c 100644 --- a/Source/UnitTests/Mappers/MappingMembers/MappingMemberBuilderTests.cs +++ b/Source/UnitTests/Mappers/MappingMembers/MappingMemberBuilderTests.cs @@ -13,7 +13,7 @@ public sealed class MappingMemberBuilderTests public void Buid_Recursion_Success() { var mappingMemberBuilder = new MappingMemberBuilder(new MappingBuilderConfigStub()); - List members = mappingMemberBuilder.Build(new TypePair(typeof(Source1), typeof(Target1))); + List members = mappingMemberBuilder.Build(new TypePair(typeof(SourceWithRecursion), typeof(TargetWithRecursion))); Assert.Equal(2, members.Count); } @@ -21,7 +21,7 @@ public void Buid_Recursion_Success() public void Build_CommonFileds_Success() { var mappingMemberBuilder = new MappingMemberBuilder(new MappingBuilderConfigStub()); - List members = mappingMemberBuilder.Build(new TypePair(typeof(Source2), typeof(Target2))); + List members = mappingMemberBuilder.Build(new TypePair(typeof(SourceSimple), typeof(TargetSimple))); Assert.Equal(2, members.Count); } @@ -29,41 +29,39 @@ public void Build_CommonFileds_Success() public void Build_IgnoreProperty_Success() { var bindingConfig = new BindingConfig(); - bindingConfig.IgnoreSourceField("Id"); + bindingConfig.IgnoreSourceField(nameof(SourceWithRecursion.Id)); var mappingMemberBuilder = new MappingMemberBuilder(new MappingBuilderConfigStub(bindingConfig)); - List members = mappingMemberBuilder.Build(new TypePair(typeof(Source1), typeof(Target1))); + List members = mappingMemberBuilder.Build(new TypePair(typeof(SourceWithRecursion), typeof(TargetWithRecursion))); Assert.Equal(1, members.Count); } - public class Source1 + public class SourceWithRecursion { - public Target1 Class { get; set; } + public TargetWithRecursion Class { get; set; } public int Id { get; set; } } - public class Target1 + public class TargetWithRecursion { - public Source1 Class { get; set; } + public SourceWithRecursion Class { get; set; } public int Id { get; set; } } - public class Source2 + public class SourceSimple { - private string _private; public int Int { get; set; } public long Long { get; set; } public string String { get; set; } } - public class Target2 + public class TargetSimple { - private string _private; public int Int { get; set; } public long Long { get; set; } public string String1 { get; set; } diff --git a/Source/UnitTests/Mappers/Types/PrimitiveTypeMapperTests.cs b/Source/UnitTests/Mappers/Types/PrimitiveTypeMapperTests.cs index ca6d500..f7f8da3 100644 --- a/Source/UnitTests/Mappers/Types/PrimitiveTypeMapperTests.cs +++ b/Source/UnitTests/Mappers/Types/PrimitiveTypeMapperTests.cs @@ -3,7 +3,6 @@ using Nelibur.ObjectMapper.Mappers; using Nelibur.ObjectMapper.Mappers.Types.Convertible; using Xunit; -using Xunit.Extensions; namespace UnitTests.Mappers.Types { @@ -32,7 +31,7 @@ public void Map_Enum_Success(object source, object expected) Mapper mapper = builder.Build(new TypePair(typeof(EnumA), typeof(EnumB))); var actual = (EnumB)mapper.Map(source); - Assert.Equal((EnumB)(expected), actual); + Assert.Equal((EnumB)expected, actual); } [InlineData(typeof(bool), typeof(bool), true, true)] diff --git a/Source/UnitTests/MappingBuilderConfigStub.cs b/Source/UnitTests/MappingBuilderConfigStub.cs index adea0d6..858b062 100644 --- a/Source/UnitTests/MappingBuilderConfigStub.cs +++ b/Source/UnitTests/MappingBuilderConfigStub.cs @@ -21,10 +21,9 @@ public MappingBuilderConfigStub(BindingConfig bindingConfig) _bindingConfig = bindingConfig.ToOption(); } - public IDynamicAssembly Assembly - { - get { return DynamicAssemblyBuilder.Get(); } - } + public IDynamicAssembly Assembly => DynamicAssemblyBuilder.Get(); + + public Func NameMatching => TargetMapperBuilder.DefaultNameMatching; public Option GetBindingConfig(TypePair typePair) { diff --git a/Source/UnitTests/Mappings/Attributes/MappingWithAttributesTests.cs b/Source/UnitTests/Mappings/Attributes/MappingWithAttributesTests.cs index 4988853..d6d3ddf 100644 --- a/Source/UnitTests/Mappings/Attributes/MappingWithAttributesTests.cs +++ b/Source/UnitTests/Mappings/Attributes/MappingWithAttributesTests.cs @@ -8,225 +8,125 @@ namespace UnitTests.Mappings.Attributes public sealed class MappingWithAttributesTests { [Fact] - public void Map_Success() + public void Map_WithAttributes_Success() { - // use custom attributes: 'IgnoreAttribute' and 'BindAttribute' - TinyMapper.Bind(config => - { -// config.Ignore(x => x.DateTime); -// config.Bind(from => from.LegacyString, to => to.LatestString); -// config.Bind(from => from.SealedString, to => to.ProtectedString); - }); + TinyMapper.Bind(); + + SourceWithIgnore source = CreateSourceWithIgnore(); + var actual = TinyMapper.Map(source); - Source source = CreateSource(); - var actual = TinyMapper.Map(source); + Assert.Equal(default(DateTime), actual.DateTime); + Assert.Equal(source.FirstName, actual.FirstName); + Assert.Equal(source.LegacyString, actual.LatestString); + Assert.Equal(default(string), actual.ProtectedString); + } - Assert.Equal(actual.DateTime, default(DateTime)); - Assert.Equal(actual.LatestString, source.LegacyString); - Assert.Equal(actual.ProtectedString, source.SealedString); + private static SourceWithIgnore CreateSourceWithIgnore() + { + return new SourceWithIgnore + { + FirstName = "John", + DateTime = DateTime.Now, + LegacyString = "LegacyString", + SealedString = "SealedString" + }; } [Fact] - public void Map_Back_Success() + public void Map_WithTargetSubset_Success() { - TinyMapper.Bind(); + TinyMapper.Bind(); + TinyMapper.Bind(); - Target target = CreateTarget(); - var actual = TinyMapper.Map(target); + SourceWithSubset source = CreateSourceWithSubset(); + var target1 = TinyMapper.Map(source); - Assert.Equal(actual.DateTime, target.DateTime); - Assert.Equal(actual.FirstName, target.FirstName); - Assert.Equal(actual.LegacyString, target.LatestString); - Assert.Equal(actual.SealedString, target.ProtectedString); - } + Assert.Equal(default(DateTime), target1.DateTime); + Assert.Equal(source.FirstName, target1.FirstName); + Assert.Equal(source.SourceForTarget1and2, target1.LatestString); + Assert.Equal(source.SourceForTarget1, target1.SourceString); + Assert.NotEqual(source.SourceForTarget2, target1.SourceString); - private static Source CreateSource() - { - return new Source - { - DateTime = DateTime.Now, - LegacyString = "legacy field", - SealedString = "sealed field, we don't change legacy code", - }; + var target2 = TinyMapper.Map(source); + + Assert.Equal(source.DateTime, target2.DateTime); + Assert.Equal(source.FirstName, target2.FirstName); + Assert.Equal(source.SourceForTarget1and2, target2.LatestString); + Assert.NotEqual(source.SourceForTarget1, target2.SourceString); + Assert.Equal(source.SourceForTarget2, target2.SourceString); } - private static Target CreateTarget() + private static SourceWithSubset CreateSourceWithSubset() { - return new Target + return new SourceWithSubset { + FirstName = "John", DateTime = DateTime.Now, - LatestString = "legacy field", - ProtectedString = "sealed field, we don't change legacy code", + SourceForTarget1and2 = "SourceForTarget1and2", + SourceForTarget1 = "SourceForTarget1", + SourceForTarget2 = "SourceForTarget2" }; } - public class Source + public class SourceWithIgnore { - [Ignore] // ignore set 'Target' field 'DateTime', by set 'Source' field 'DateTime' + [Ignore] public DateTime DateTime { get; set; } + public string FirstName { get; set; } - [Bind("LatestString")] // set to 'Target' field 'LatestString' + + [Bind(nameof(TargetWithIgnore.LatestString))] public string LegacyString { get; set; } + public string SealedString { get; set; } } - public class Target + + public class TargetWithIgnore { public DateTime DateTime { get; set; } public string FirstName { get; set; } public string LatestString { get; set; } - [Bind("SealedString")] // set from 'Source' field 'SealedString' public string ProtectedString { get; set; } } - [Fact] - public void Map_WithType_Success() - { - TinyMapper.Bind(); - TinyMapper.Bind(); - - Source1 source = CreateSource1(); - var target1 = TinyMapper.Map(source); - - Assert.Equal(target1.DateTime, default(DateTime)); - Assert.Equal(target1.FirstName, source.FirstName); - - Assert.Equal(target1.LatestString, source.LegacyString); - Assert.Equal(target1.SourceString, source.LegacyTarget1String); - Assert.NotEqual(target1.SourceString, source.LegacyTarget2String); - Assert.Equal(target1.ProtectedString, source.SealedString); - Assert.Equal(target1.TargetString, source.SealedTarget1String); - Assert.NotEqual(target1.TargetString, source.SealedTarget2String); - - var target2 = TinyMapper.Map(source); - - Assert.Equal(target2.DateTime, source.DateTime); - Assert.Equal(target2.FirstName, source.FirstName); - - Assert.Equal(target2.LatestString, source.LegacyString); - Assert.NotEqual(target2.SourceString, source.LegacyTarget1String); - Assert.Equal(target2.SourceString, source.LegacyTarget2String); - - Assert.Equal(target2.ProtectedString, source.SealedString); - Assert.NotEqual(target2.TargetString, source.SealedTarget1String); - Assert.Equal(target2.TargetString, source.SealedTarget2String); - } - - [Fact] - public void Map_WithType_Back_Success() + public class SourceWithSubset { - TinyMapper.Bind(); - TinyMapper.Bind(); - - Target1 target1 = CreateTarget1(); - var actual = TinyMapper.Map(target1); - - Assert.Equal(actual.DateTime, target1.DateTime); - Assert.Equal(actual.FirstName, target1.FirstName); - Assert.Equal(actual.LegacyString, target1.LatestString); - Assert.Equal(actual.LegacyTarget1String, target1.SourceString); - Assert.Null(actual.LegacyTarget2String); - Assert.Equal(actual.SealedString, target1.ProtectedString); - Assert.Equal(actual.SealedTarget1String, target1.TargetString); - Assert.Null(actual.SealedTarget2String); - - Target2 target2 = CreateTarget2(); - actual = TinyMapper.Map(target2); - - Assert.Equal(actual.DateTime, target2.DateTime); - Assert.Equal(actual.FirstName, target2.FirstName); - Assert.Equal(actual.LegacyString, target2.LatestString); - Assert.Null(actual.LegacyTarget1String); - Assert.Equal(actual.LegacyTarget2String, target2.SourceString); - Assert.Equal(actual.SealedString, target2.ProtectedString); - Assert.Null(actual.SealedTarget1String); - Assert.Equal(actual.SealedTarget2String, target2.TargetString); - } + /// + /// Ignore map for , but not for + /// + [Ignore(typeof(TargetSubset1))] + public DateTime DateTime { get; set; } - private static Source1 CreateSource1() - { - return new Source1 - { - FirstName = "John", - DateTime = DateTime.Now, - LegacyString = "legacy field", - LegacyTarget1String = "custom field only for 'Target1'", - LegacyTarget2String = "custom field only for 'Target2'", - SealedString = "sealed field, we don't change legacy code", - SealedTarget1String = "custom field only for 'Target1', see [Bind]", - SealedTarget2String = "custom field only for 'Target2', see [Bind]", - }; - } + public string FirstName { get; set; } - private static Target1 CreateTarget1() - { - return new Target1 - { - FirstName = "John", - DateTime = DateTime.Now, - LatestString = "legacy field", - SourceString = "custom field only for 'Target1'", - ProtectedString = "sealed field, we don't change legacy code", - TargetString = "custom field only for 'Target1', see [Bind]", - }; - } - - private static Target2 CreateTarget2() - { - return new Target2 - { - FirstName = "John", - DateTime = DateTime.Now, - LatestString = "legacy field", - SourceString = "custom field only for 'Target1'", - ProtectedString = "sealed field, we don't change legacy code", - TargetString = "custom field only for 'Target1', see [Bind]", - }; - } + [Bind("LatestString")] + public string SourceForTarget1and2 { get; set; } - public class Source1 - { - [Ignore(typeof(Target1))] // ignore set 'Target1' field 'DateTime', but not 'Target2' - public DateTime DateTime { get; set; } - public string FirstName { get; set; } - - [Bind("LatestString")] // set to 'Target1' and 'Target2' field 'LatestString' - public string LegacyString { get; set; } - [Bind("SourceString", typeof(Target1))] // set to 'Target1' field 'SourceString' - public string LegacyTarget1String { get; set; } - [Bind("SourceString", typeof(Target2))] // set to 'Target2' field 'SourceString' - public string LegacyTarget2String { get; set; } + [Bind(nameof(TargetSubset1.SourceString), typeof(TargetSubset1))] + public string SourceForTarget1 { get; set; } - public string SealedString { get; set; } - public string SealedTarget1String { get; set; } - public string SealedTarget2String { get; set; } + [Bind(nameof(TargetSubset2.SourceString), typeof(TargetSubset2))] + public string SourceForTarget2 { get; set; } } - public class Target1 + + public class TargetSubset1 { public DateTime DateTime { get; set; } public string FirstName { get; set; } public string LatestString { get; set; } public string SourceString { get; set; } - - [Bind("SealedString")] // set from 'Source1' field 'SealedString' - public string ProtectedString { get; set; } - [Bind("SealedTarget1String", typeof(Source1))] // set from 'Source1' field 'SealedTarget1String' - public string TargetString { get; set; } } - public class Target2 + + public class TargetSubset2 { public DateTime DateTime { get; set; } public string FirstName { get; set; } public string LatestString { get; set; } public string SourceString { get; set; } - - [Bind("SealedString")] // set from 'Source1' field 'SealedString' - public string ProtectedString { get; set; } - [Bind("SealedTarget2String", typeof(Source1))] // set from 'Source1' field 'SealedTarget2String' - public string TargetString { get; set; } } } } diff --git a/Source/UnitTests/Mappings/Attributes/MappingWithGenericTypeTests.cs b/Source/UnitTests/Mappings/Attributes/MappingWithGenericTypeTests.cs new file mode 100644 index 0000000..bfaa6cb --- /dev/null +++ b/Source/UnitTests/Mappings/Attributes/MappingWithGenericTypeTests.cs @@ -0,0 +1,72 @@ +#define VS2015 + +using Nelibur.ObjectMapper; +using Nelibur.ObjectMapper.Bindings; +using Xunit; + +namespace UnitTests.Mappings.Attributes +{ + public sealed class MappingWithGenericTypeTests + { + interface IEntity + { + T Key { get; set; } + } + + + public abstract class Entity : IEntity + { + public T Key { get; set; } + } + + + public class SourceDto + { + [Bind(nameof(Target.Key), typeof(Target))] + public long Id { get; set; } + } + + + public class Target : Entity + { + } + + + private static SourceDto CreateSource() + { + return new SourceDto + { + Id = 23, + }; + } + + private static Target CreateTarget() + { + return new Target + { + Key = 23, + }; + } + + [Fact] + public void Map_WithType_Success() + { + TinyMapper.Bind(); + + SourceDto source = CreateSource(); + var target = TinyMapper.Map(source); + + Assert.Equal(target.Key, source.Id); + } + + [Fact] + public void Map_WithType_Back_Success() + { + TinyMapper.Bind(); + Target target = CreateTarget(); + var source = TinyMapper.Map(target); + + Assert.Equal(source.Id, target.Key); + } + } +} \ No newline at end of file diff --git a/Source/UnitTests/Mappings/Collections/CollectionMappingTests.cs b/Source/UnitTests/Mappings/Collections/CollectionMappingTests.cs index 699b239..b5953ec 100644 --- a/Source/UnitTests/Mappings/Collections/CollectionMappingTests.cs +++ b/Source/UnitTests/Mappings/Collections/CollectionMappingTests.cs @@ -1,183 +1,305 @@ -using System; -using System.Collections.Generic; -using Nelibur.ObjectMapper; -using Xunit; - -namespace UnitTests.Mappings.Collections -{ - public sealed class CollectionMappingTests - { - [Fact] - public void Map_Collections_Success() - { - TinyMapper.Bind(); - - var source = new Source1 - { - Items = new List - { - new Item1 - { - Int = 1, - String = "2", - List = new List { 1, 2, 3 }, - Bools = new[] { true, false } - }, - new Item1 - { - Int = 2, - String = "3", - List = new List { 2, 3 }, - Bools = new[] { false, false } - } - } - }; - - var actual = TinyMapper.Map(source); - - Assert.Equal(source.Items.Count, actual.Items.Count); - Assert.Equal(source.Items1, actual.Items1); - - for (int i = 0; i < source.Items.Count; i++) - { - Item1 expectedItem = source.Items[i]; - Item2 actualItem = actual.Items[i]; - - Assert.Equal(expectedItem.Bools, actualItem.Bools); - Assert.Equal(expectedItem.Int, actualItem.Int); - Assert.Equal(expectedItem.List, actualItem.List); - Assert.Equal(expectedItem.String, actualItem.String); - } - } - - [Fact] - public void Map_DifferentCollections_Success() - { - TinyMapper.Bind(); - - var source = new Person - { - Contacts = new List - { - new Contact - { - Int = 1, - String = "2" - } - } - }; - - var actual = TinyMapper.Map(source); - - Assert.Equal(source.Contacts.Count, actual.Contacts.Count); - for (int i = 0; i < source.Contacts.Count; i++) - { - Contact expectedItem = source.Contacts[i]; - ContactDto actualItem = actual.Contacts[i]; - - Assert.Equal(expectedItem.Int, actualItem.Int); - Assert.Equal(expectedItem.String, actualItem.String); - } - } - - [Fact] - public void Map_InterfaceToCollection_Success() - { - TinyMapper.Bind(); - - var source = new Source3 - { - List = new List { 1, 2, 3 } - }; - - var actual = TinyMapper.Map(source); - Assert.Equal(source.List, actual.List); - } - - [Fact] - public void Map_NullCollection_Success() - { - var source = new Source2 - { - Int = 1 - }; - - var actual = TinyMapper.Map(source); - - Assert.Equal(source.Ints, actual.Ints); - Assert.Equal(source.Int, actual.Int); - } - - public class Contact - { - public int Int { get; set; } - public string String { get; set; } - } - - public class ContactDto - { - public int Int { get; set; } - public string String { get; set; } - } - - public sealed class Item1 - { - public bool[] Bools { get; set; } - public int Int { get; set; } - public List List { get; set; } - public string String { get; set; } - } - - public sealed class Item2 - { - public bool[] Bools { get; set; } - public int Int { get; set; } - public List List { get; set; } - public string String { get; set; } - } - - public class Person - { - public List Contacts { get; set; } - } - - public class PersonDto - { - public List Contacts { get; set; } - } - - public class Source1 - { - public IList Items { get; set; } - public List Items1 { get; set; } - } - - public class Source2 - { - public int Int { get; set; } - public List Ints { get; set; } - } - - public class Source3 - { - public IReadOnlyList List { get; set; } - } - - public class Target1 - { - public List Items { get; set; } - public List Items1 { get; set; } - } - - public class Target2 - { - public int Int { get; set; } - public List Ints { get; set; } - } - - public class Target3 - { - public List List { get; set; } - } - } -} +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using Nelibur.ObjectMapper; +using Nelibur.ObjectMapper.Reflection; +using Xunit; + +namespace UnitTests.Mappings.Collections +{ + public sealed class CollectionMappingTests + { + [Fact] + public void Map_Collections_Success() + { + TinyMapper.Bind(); + + var source = new Source1 + { + Items = new List + { + new Item1 + { + Int = 1, + String = "2", + List = new List { 1, 2, 3 }, + Bools = new[] { true, false } + }, + new Item1 + { + Int = 2, + String = "3", + List = new List { 2, 3 }, + Bools = new[] { false, false } + } + } + }; +// DynamicAssemblyBuilder.Get().Save(); + var actual = TinyMapper.Map(source); + + Assert.Equal(source.Items.Count, actual.Items.Count); + Assert.Equal(source.Items1, actual.Items1); + + for (int i = 0; i < source.Items.Count; i++) + { + Item1 expectedItem = source.Items[i]; + Item2 actualItem = actual.Items[i]; + + Assert.Equal(expectedItem.Bools, actualItem.Bools); + Assert.Equal(expectedItem.Int, actualItem.Int); + Assert.Equal(expectedItem.List, actualItem.List); + Assert.Equal(expectedItem.String, actualItem.String); + } + } + + [Fact] + public void Map_DifferentCollections_Success() + { + TinyMapper.Bind(); + + var source = new Person + { + Contacts = new List + { + new Contact + { + Int = 1, + String = "2" + } + } + }; + + var actual = TinyMapper.Map(source); + + Assert.Equal(source.Contacts.Count, actual.Contacts.Count); + for (int i = 0; i < source.Contacts.Count; i++) + { + Contact expectedItem = source.Contacts[i]; + ContactDto actualItem = actual.Contacts[i]; + + Assert.Equal(expectedItem.Int, actualItem.Int); + Assert.Equal(expectedItem.String, actualItem.String); + } + } + + [Fact] + public void Map_InterfaceToCollection_Success() + { + TinyMapper.Bind(); + + var source = new Source3 + { + List = new List { 1, 2, 3 } + }; + + var actual = TinyMapper.Map(source); + Assert.Equal(source.List, actual.List); + } + + [Fact] + public void Map_NullCollection_Success() + { + var source = new Source2 + { + Int = 1 + }; + + TinyMapper.Bind(); + + var actual = TinyMapper.Map(source); + + Assert.Equal(source.Ints, actual.Ints); + Assert.Equal(source.Int, actual.Int); + } + + [Fact] + public void Map_IEnumerable_T_Success() + { + var target = new PersonComplex + { + Emails = new[] {"none1@none.com", "none2@none.com"}, + FirstName = "John", + LastName = "Doe" + }; + + TinyMapper.Bind(); + + var actual = TinyMapper.Map(target); + + Assert.Equal(target.FirstName, actual.FirstName); + Assert.Equal(target.LastName, actual.LastName); + Assert.Equal(target.Emails, actual.Emails); + } + + [Fact] + public void Map_IEnumerable_Success() + { + var target = new PersonComplex + { + Emails = new[] { "none1@none.com", "none2@none.com" }, + FirstName = "John", + LastName = "Doe" + }; + + TinyMapper.Bind(); + + var actual = TinyMapper.Map(target); + + Assert.Equal(target.FirstName, actual.FirstName); + Assert.Equal(target.LastName, actual.LastName); + Assert.Equal(target.Emails, actual.Emails.Cast()); + } + + [Fact] + public void Map_To_IEnumerable_T_Success() + { + var target = new PersonComplex3 + { + Emails = new ArrayList(new [] { "none1@none.com", "none2@none.com" }), + FirstName = "John", + LastName = "Doe" + }; + + TinyMapper.Bind(); + + var actual = TinyMapper.Map(target); + + Assert.Equal(target.FirstName, actual.FirstName); + Assert.Equal(target.LastName, actual.LastName); + Assert.Equal(target.Emails.Cast(), actual.Emails); + } + + [Fact] + public void Map_To_IEnumerable_Success() + { + var target = new PersonComplex + { + Emails = new[] { "none1@none.com", "none2@none.com" }, + FirstName = "John", + LastName = "Doe" + }; + + TinyMapper.Bind(); + + var actual = TinyMapper.Map(target); + + Assert.Equal(target.FirstName, actual.FirstName); + Assert.Equal(target.LastName, actual.LastName); + Assert.Equal(target.Emails, actual.Emails.Cast()); + } + + public class Contact + { + public int Int { get; set; } + public string String { get; set; } + } + + public class ContactDto + { + public int Int { get; set; } + public string String { get; set; } + } + + public sealed class Item1 + { + public bool[] Bools { get; set; } + public int Int { get; set; } + public List List { get; set; } + public string String { get; set; } + } + + public sealed class Item2 + { + public bool[] Bools { get; set; } + public int Int { get; set; } + public List List { get; set; } + public string String { get; set; } + } + + public class Person + { + public List Contacts { get; set; } + } + + public class PersonDto + { + public List Contacts { get; set; } + } + + public class PersonComplex + { + public IEnumerable Emails { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class PersonComplexTarget + { + public IList Emails { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class PersonComplexTarget4 + { + public IEnumerable Emails { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class PersonComplexTarget2 + { + public ArrayList Emails { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class PersonComplex3 + { + public ArrayList Emails { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class PersonComplexTarget3 + { + public IEnumerable Emails { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class Source1 + { + public IList Items { get; set; } + public List Items1 { get; set; } + } + + public class Source2 + { + public int Int { get; set; } + public List Ints { get; set; } + } + + public class Source3 + { + public IReadOnlyList List { get; set; } + } + + public class Target1 + { + public List Items { get; set; } + public List Items1 { get; set; } + } + + public class Target2 + { + public int Int { get; set; } + public List Ints { get; set; } + } + + public class Target3 + { + public List List { get; set; } + } + } +} diff --git a/Source/UnitTests/Mappings/Collections/DictionaryMappingTests.cs b/Source/UnitTests/Mappings/Collections/DictionaryMappingTests.cs index c44c907..1a1b5c8 100644 --- a/Source/UnitTests/Mappings/Collections/DictionaryMappingTests.cs +++ b/Source/UnitTests/Mappings/Collections/DictionaryMappingTests.cs @@ -1,85 +1,131 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Nelibur.ObjectMapper; -using Xunit; - -namespace UnitTests.Mappings.Collections -{ - public sealed class DictionaryMappingTests - { - [Fact] - public void Map_Dictionary_Success() - { - TinyMapper.Bind(); - - var source = new Source1 - { - Id = Guid.NewGuid(), - Dictionary = new Dictionary { { "Key1", 1 }, { "Key2", 2 } } - }; - - var target = TinyMapper.Map(source); - - Assert.Equal(source.Id, target.Id); - Assert.Equal(source.Dictionary, target.Dictionary); - } - - [Fact] - public void Map_DifferentKeyDictionary_Success() - { - TinyMapper.Bind(); - TinyMapper.Bind(); - - var source = new Source2 - { - Id = Guid.NewGuid(), - Dictionary = new Dictionary - { - { new ItemKeySource { Id = Guid.NewGuid() }, 1 }, - { new ItemKeySource { Id = Guid.NewGuid() }, 2 }, - } - }; - - var target = TinyMapper.Map(source); - - Assert.Equal(source.Id, target.Id); - Assert.Equal(source.Dictionary.Keys.Select(x => x.Id), target.Dictionary.Keys.Select(x => x.Id)); - Assert.Equal(source.Dictionary.Values, target.Dictionary.Values); - } - - public class ItemKeySource - { - public Guid Id { get; set; } - } - - public class ItemKeyTarget - { - public Guid Id { get; set; } - } - - public class Source1 - { - public Dictionary Dictionary { get; set; } - public Guid Id { get; set; } - } - - public class Source2 - { - public Dictionary Dictionary { get; set; } - public Guid Id { get; set; } - } - - public class Target1 - { - public Dictionary Dictionary { get; set; } - public Guid Id { get; set; } - } - - public class Target2 - { - public Dictionary Dictionary { get; set; } - public Guid Id { get; set; } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Nelibur.ObjectMapper; +using Xunit; + +namespace UnitTests.Mappings.Collections +{ + public sealed class DictionaryMappingTests + { + [Fact] + public void Map_Dictionary_Success() + { + TinyMapper.Bind(); + + var source = new Source1 + { + Id = Guid.NewGuid(), + Dictionary = new Dictionary { { "Key1", 1 }, { "Key2", 2 } } + }; + + var target = TinyMapper.Map(source); + + Assert.Equal(source.Id, target.Id); + Assert.Equal(source.Dictionary, target.Dictionary); + } + + [Fact] + public void Map_IDictionary_Success() + { + TinyMapper.Bind(); + + var source = new Source3 + { + Id = Guid.NewGuid(), + Dictionary = new Dictionary { { "Key1", 1 }, { "Key2", 2 } } + }; + + var target = TinyMapper.Map(source); + + Assert.Equal(source.Id, target.Id); + Assert.Equal(source.Dictionary, target.Dictionary); + } + + [Fact] + public void Map_IDictionary_Target_Success() + { + TinyMapper.Bind(); + + var source = new Source1 + { + Id = Guid.NewGuid(), + Dictionary = new Dictionary { { "Key1", 1 }, { "Key2", 2 } } + }; + + var target = TinyMapper.Map(source); + + Assert.Equal(source.Id, target.Id); + Assert.Equal(source.Dictionary, target.Dictionary); + } + + [Fact] + public void Map_DifferentKeyDictionary_Success() + { + TinyMapper.Bind(); + TinyMapper.Bind(); + + var source = new Source2 + { + Id = Guid.NewGuid(), + Dictionary = new Dictionary + { + { new ItemKeySource { Id = Guid.NewGuid() }, 1 }, + { new ItemKeySource { Id = Guid.NewGuid() }, 2 }, + } + }; + + var target = TinyMapper.Map(source); + + Assert.Equal(source.Id, target.Id); + Assert.Equal(source.Dictionary.Keys.Select(x => x.Id), target.Dictionary.Keys.Select(x => x.Id)); + Assert.Equal(source.Dictionary.Values, target.Dictionary.Values); + } + + public class ItemKeySource + { + public Guid Id { get; set; } + } + + public class ItemKeyTarget + { + public Guid Id { get; set; } + } + + public class Source1 + { + public Dictionary Dictionary { get; set; } + public Guid Id { get; set; } + } + + public class Source2 + { + public Dictionary Dictionary { get; set; } + public Guid Id { get; set; } + } + + public class Source3 + { + public IDictionary Dictionary { get; set; } + public Guid Id { get; set; } + } + + public class Target1 + { + public Dictionary Dictionary { get; set; } + public Guid Id { get; set; } + } + + public class Target2 + { + public Dictionary Dictionary { get; set; } + public Guid Id { get; set; } + } + + public class Target3 + { + public IDictionary Dictionary { get; set; } + public Guid Id { get; set; } + } + } +} diff --git a/Source/UnitTests/Mappings/MapWithCircularReferences.cs b/Source/UnitTests/Mappings/MapWithCircularReferences.cs new file mode 100644 index 0000000..379b841 --- /dev/null +++ b/Source/UnitTests/Mappings/MapWithCircularReferences.cs @@ -0,0 +1,90 @@ +using Nelibur.ObjectMapper; +using Xunit; + +namespace UnitTests.Mappings +{ + public class MapWithCircularReferences + { + [Fact] + public void Map_Node_CircularReferences_Success() + { + var source = new Node + { + Id = "1", + Next = new Node + { + Id = "2", + Next = new Node + { + Id = "3", + Child = new[] + { + new Node + { + Id = " 123 1" + }, + new Node + { + Id = "123 2" + } + } + + } + }, + Child = new[] + { + new Node + { + Id = "1 1" + }, + new Node + { + Id = "1 2" + } + } + }; + + TinyMapper.Bind(); + + var target = TinyMapper.Map(source); + + Assert.Equal(source.Id, target.Id); + Assert.Equal(source.Next.Id, target.Next.Id); + Assert.Equal(source.Next.Next.Id, target.Next.Next.Id); + Assert.Equal(source.Next.Next.Id, target.Next.Next.Id); + + Assert.Equal(source.Next.Next.Child, target.Next.Next.Child); + + Assert.Equal(source.Child, target.Child); + } + + } + + public class Node + { + public string Id; + public Node Next; + public Node[] Child; + + protected bool Equals(Node other) + { + return string.Equals(Id, other.Id); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((Node)obj); + } + + public override int GetHashCode() + { + return (Id != null ? Id.GetHashCode() : 0); + } + } +} diff --git a/Source/UnitTests/Mappings/MapWithCustomBindTests.cs b/Source/UnitTests/Mappings/MapWithCustomBindTests.cs new file mode 100644 index 0000000..51750a8 --- /dev/null +++ b/Source/UnitTests/Mappings/MapWithCustomBindTests.cs @@ -0,0 +1,32 @@ +using System; +using Xunit; + +namespace UnitTests.Mappings +{ + public sealed class MapWithCustomBindTests + { + [Fact] + public void Map_CustomBind_Success() + { + // TinyMapper.Bind(config => config.Bind(target => target.Name, "John")); + // + // var source = new SourceCustom(); + // var result = TinyMapper.Map(source); + // + // Assert.Equal("John", result.Name); + // Assert.True(string.IsNullOrWhiteSpace(source.Name)); + } + + + public class SourceCustom + { + public string Name { get; set; } + } + + + public class TargetCustom + { + public string Name { get; set; } + } + } +} diff --git a/Source/UnitTests/Mappings/MapWithStaticFields.cs b/Source/UnitTests/Mappings/MapWithStaticFields.cs new file mode 100644 index 0000000..dda8145 --- /dev/null +++ b/Source/UnitTests/Mappings/MapWithStaticFields.cs @@ -0,0 +1,33 @@ +using Nelibur.ObjectMapper; +using Xunit; + +namespace UnitTests.Mappings +{ + public class MapWithStaticFields + { + [Fact] + public void MapStaticFields() + { + var source = new SourceStatic(); + + TinyMapper.Bind(); + var actual = TinyMapper.Map(source); + + Assert.Equal(SourceStatic.Id, actual.Id); + Assert.Equal(SourceStatic.Name, actual.Name); + } + } + + public class SourceStatic + { + public static string Name = "test"; + public static int Id = 1; + } + + + public class TargetDto + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/Source/UnitTests/Mappings/MappingWithComplexConfigTests.cs b/Source/UnitTests/Mappings/MappingWithComplexConfigTests.cs new file mode 100644 index 0000000..167733e --- /dev/null +++ b/Source/UnitTests/Mappings/MappingWithComplexConfigTests.cs @@ -0,0 +1,96 @@ +using Nelibur.ObjectMapper; +using Xunit; + +namespace UnitTests.Mappings +{ + public class MappingWithComplexConfigTests + { + [Fact] + public void Map_ComplexBind_Success() + { + TinyMapper.Bind( + config => + { + config.Bind(source => source.Address.Street, target => target.Street); + config.Bind(source => source.Address.Phone, target => target.Phone); + } + ); + + var dto = new PersonDto + { + Address = new AddressDto + { + Street = "Street", + Phone = "123123" + }, + Code = "Code", + Identity = 1, + Name = "Alex" + }; + + var person = TinyMapper.Map(dto); + + Assert.Equal(dto.Identity, person.Identity); + Assert.Equal(dto.Code, person.Code); + Assert.Equal(dto.Name, person.Name); + Assert.Equal(dto.Address.Street, person.Street); + Assert.Equal(dto.Address.Phone, person.Phone); + } + + [Fact] + public void Map_ComplexBind_Success1() + { + TinyMapper.Bind( + config => + { + config.Bind(source => source.Address.Street, target => target.Street); + config.Bind(source => source.Address.Phone, target => target.Phone); + } + ); + + var dto = new PersonDto + { + Address = new AddressDto + { + Street = "Street", + Phone = "123123" + }, + Code = "Code", + Identity = 1, + Name = "Alex" + }; + + var person = (Person)TinyMapper.Map(typeof(PersonDto), typeof(Person), dto); + + Assert.Equal(dto.Identity, person.Identity); + Assert.Equal(dto.Code, person.Code); + Assert.Equal(dto.Name, person.Name); + Assert.Equal(dto.Address.Street, person.Street); + Assert.Equal(dto.Address.Phone, person.Phone); + } + + + public class Person + { + public int Identity { get; set; } + public string Code { get; set; } + public string Name { get; set; } + public string Street { get; set; } + public string Phone { get; set; } + } + + public class PersonDto + { + public int Identity { get; set; } + public string Code { get; set; } + public string Name { get; set; } + public AddressDto Address { get; set; } + } + + public class AddressDto + { + public string Street { get; set; } + public string Phone { get; set; } + } + } +} diff --git a/Source/UnitTests/Mappings/MappingWithConfigTests.cs b/Source/UnitTests/Mappings/MappingWithConfigTests.cs index 304e2f9..f57b780 100644 --- a/Source/UnitTests/Mappings/MappingWithConfigTests.cs +++ b/Source/UnitTests/Mappings/MappingWithConfigTests.cs @@ -25,6 +25,7 @@ public void Map_Bind_Success() Byte = 5, Int = 9, String = "Test", + DateTime = DateTime.Now, SourceItem = new SourceItem { Id = Guid.NewGuid() } }; @@ -34,9 +35,30 @@ public void Map_Bind_Success() Assert.Equal(source.String, actual.MyString); Assert.Equal(source.Byte, actual.Byte); Assert.Equal(source.Int, actual.MyInt); + Assert.Equal(source.DateTime, actual.DateTime); Assert.Equal(source.SourceItem.Id, actual.TargetItem.Id); } + [Fact] + public void Map_BindCaseSensitive_Success() + { + TinyMapper.Bind(); + + TinyMapper.Bind(config => + { + config.Bind(from => from.CaseSensitive, to => to.Casesensitive); + }); + + var source = new Source1 + { + CaseSensitive = "CaseSensitive" + }; + + var actual = TinyMapper.Map(source); + + Assert.Equal(source.CaseSensitive, actual.Casesensitive); + } + [Fact] public void Map_ConcreteType_Success() { @@ -82,12 +104,43 @@ public void Map_Ignore_Success() Assert.Equal(0, actual.MyInt); Assert.Null(actual.TargetItem); } + + [Fact] + public void Map_MultiTarget() + { + TinyMapper.Bind(config => + { + config.Bind(s => s.CName, t => t.C2Name1); + config.Bind(s => s.CName, t => t.C2Name2); + }); + + var result = TinyMapper.Map(new SourceName + { + CName = "7788" + }); + + Assert.Equal("7788", result.C2Name1); + Assert.Equal("7788", result.C2Name2); + } + + public class SourceName + { + public string CName { get; set; } + } +    + public class TargetName + { + public string C2Name1 { get; set; } + public string C2Name2 { get; set; } + } public class Source1 { public bool Bool { get; set; } public byte Byte { get; set; } public int Int { get; set; } + public DateTime? DateTime { get; set; } + public string CaseSensitive { get; set; } public SourceItem SourceItem { get; set; } public string String { get; set; } @@ -109,7 +162,9 @@ public class Target1 public bool Bool { get; set; } public byte Byte { get; set; } public int MyInt { get; set; } + public DateTime? DateTime { get; set; } public string MyString { get; set; } + public string Casesensitive { get; set; } public TargetItem TargetItem { get; set; } } diff --git a/Source/UnitTests/Mappings/TypeConverters/ConvertibleTypeMappingTests.cs b/Source/UnitTests/Mappings/TypeConverters/ConvertibleTypeMappingTests.cs index c46154f..35a54e5 100644 --- a/Source/UnitTests/Mappings/TypeConverters/ConvertibleTypeMappingTests.cs +++ b/Source/UnitTests/Mappings/TypeConverters/ConvertibleTypeMappingTests.cs @@ -1,4 +1,5 @@ -using System; +#if !COREFX +using System; using System.ComponentModel; using System.Globalization; using Nelibur.ObjectMapper; @@ -58,3 +59,4 @@ public class Target1 } } } +#endif diff --git a/Source/UnitTests/Properties/AssemblyInfo.cs b/Source/UnitTests/Properties/AssemblyInfo.cs index 5c3b46f..e063b09 100644 --- a/Source/UnitTests/Properties/AssemblyInfo.cs +++ b/Source/UnitTests/Properties/AssemblyInfo.cs @@ -1,16 +1,12 @@ using System; using System.Reflection; using System.Runtime.InteropServices; +using Xunit; // 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("UnitTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("UnitTests")] [assembly: AssemblyCopyright("Copyright © 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -36,5 +32,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: CollectionBehavior(DisableTestParallelization = true)] + diff --git a/Source/UnitTests/Snippets/DynamicMethodSnippet.cs b/Source/UnitTests/Snippets/DynamicMethodSnippet.cs index 9d98165..fdf53a1 100644 --- a/Source/UnitTests/Snippets/DynamicMethodSnippet.cs +++ b/Source/UnitTests/Snippets/DynamicMethodSnippet.cs @@ -1,4 +1,5 @@ -using System; +#if !COREFX +using System; using System.Reflection.Emit; using Xunit; @@ -71,3 +72,4 @@ private static T Cast(object value) } } } +#endif diff --git a/Source/UnitTests/Snippets/ForeachSnippet.cs b/Source/UnitTests/Snippets/ForeachSnippet.cs index 3944e04..cb96f5d 100644 --- a/Source/UnitTests/Snippets/ForeachSnippet.cs +++ b/Source/UnitTests/Snippets/ForeachSnippet.cs @@ -1,4 +1,5 @@ -using System; +#if !COREFX +using System; using System.Collections; using System.Collections.Generic; using System.Reflection; @@ -9,7 +10,7 @@ namespace UnitTests.Snippets { public sealed class ForeachSnippet { - [Fact] + [Fact(Skip ="Not ready")] public void Snippet() { var assemblyName = new AssemblyName("Test"); @@ -103,3 +104,4 @@ public abstract class ForeachBase // } //} } +#endif diff --git a/Source/UnitTests/Snippets/TypeConverters/DictionaryConverterSnippet.cs b/Source/UnitTests/Snippets/TypeConverters/DictionaryConverterSnippet.cs index bf24b02..ef989ad 100644 --- a/Source/UnitTests/Snippets/TypeConverters/DictionaryConverterSnippet.cs +++ b/Source/UnitTests/Snippets/TypeConverters/DictionaryConverterSnippet.cs @@ -1,4 +1,5 @@ -using System; +#if !COREFX +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -62,3 +63,4 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c } } } +#endif \ No newline at end of file diff --git a/Source/UnitTests/Snippets/TypeConverters/TypeConverterSnippet.cs b/Source/UnitTests/Snippets/TypeConverters/TypeConverterSnippet.cs index 539acab..3b23bc9 100644 --- a/Source/UnitTests/Snippets/TypeConverters/TypeConverterSnippet.cs +++ b/Source/UnitTests/Snippets/TypeConverters/TypeConverterSnippet.cs @@ -1,4 +1,5 @@ -using System; +#if !COREFX +using System; using System.ComponentModel; using System.Globalization; using Xunit; @@ -55,3 +56,4 @@ private class TargetClass } } } +#endif diff --git a/Source/UnitTests/Snippets/TypeConverters/YesNoConverterSnippet.cs b/Source/UnitTests/Snippets/TypeConverters/YesNoConverterSnippet.cs index db285bd..8d0c0db 100644 --- a/Source/UnitTests/Snippets/TypeConverters/YesNoConverterSnippet.cs +++ b/Source/UnitTests/Snippets/TypeConverters/YesNoConverterSnippet.cs @@ -1,4 +1,5 @@ -using System; +#if !COREFX +using System; using System.ComponentModel; using System.Globalization; using Xunit; @@ -64,3 +65,4 @@ private static bool ConvertYesNo(string yesNoValue) } } } +#endif diff --git a/Source/UnitTests/TinyMapperConfigTests.cs b/Source/UnitTests/TinyMapperConfigTests.cs new file mode 100644 index 0000000..8b536a7 --- /dev/null +++ b/Source/UnitTests/TinyMapperConfigTests.cs @@ -0,0 +1,116 @@ +#if !COREFX +using System; +using Nelibur.ObjectMapper; +using Xunit; + +namespace UnitTests +{ + public class TinyMapperConfigTests : IDisposable + { + private static int index; + private readonly AppDomain TestDomain; + + public TinyMapperConfigTests() + { + var name = string.Concat($"TestDomainFor{typeof(TinyMapperConfigTests).Name}.Nr_", ++index); + + TestDomain = AppDomain.CreateDomain(name, + AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation); + } + + public void Dispose() + { + if (TestDomain != null) + { + AppDomain.Unload(TestDomain); + } + } + + [Fact(DisplayName = "Checks if the Name-Matching is used.")] + public void CustomeNameMatching_Success() + { + TestDomain.DoCallBack(() => { + + TinyMapper.Config(config => + { + config.NameMatching((x, y) => string.Equals(x + "1", y, StringComparison.OrdinalIgnoreCase)); + }); + + TinyMapper.Bind(); + var sourceCustom = new SourceCustom { Custom = "Hello", Default = "Default" }; + var targetCustom = TinyMapper.Map(sourceCustom); + + Assert.Equal(sourceCustom.Custom, targetCustom.Custom1); + Assert.True(string.IsNullOrEmpty(targetCustom.Default)); + + TinyMapper.Config(config => + { + config.Reset(); + }); + TinyMapper.Bind(); + + var sourceDefault = new SourceDefault { Custom = "Hello", Default = "Default" }; + var targetDefault = TinyMapper.Map(sourceDefault); + + Assert.True(string.IsNullOrEmpty(targetDefault.Custom1)); + Assert.Equal(sourceDefault.Default, targetDefault.Default); + + }); + } + + [Fact(DisplayName = "Checks if the Name-Matching is used isolated.")] + public void CustomeNameMatching_IsIsolated() + { + TestDomain.DoCallBack(() => { + + TinyMapper.Config(config => + { + config.NameMatching((x, y) => string.Equals(x + "1", y, StringComparison.OrdinalIgnoreCase)); + + TinyMapper.Bind(); + var sourceCustomOtherDomain = new SourceCustom { Custom = "Hello", Default = "Default" }; + var targetCustomOtherDomain = TinyMapper.Map(sourceCustomOtherDomain); + + Assert.Equal(sourceCustomOtherDomain.Custom, targetCustomOtherDomain.Custom1); + Assert.True(string.IsNullOrEmpty(targetCustomOtherDomain.Default)); + }); + }); + + TinyMapper.Bind(); + var sourceCustom = new SourceCustom { Custom = "Hello", Default = "Default" }; + var targetCustom = TinyMapper.Map(sourceCustom); + + TinyMapper.Bind(); + var sourceDefault = new SourceDefault { Custom = "Hello", Default = "Default" }; + var targetDefault = TinyMapper.Map(sourceDefault); + + Assert.True(string.IsNullOrEmpty(targetDefault.Custom1)); + Assert.Equal(sourceDefault.Default, targetDefault.Default); + } + + public class SourceCustom + { + public string Custom { get; set; } + public string Default { get; set; } + } + + public class TargetCustom + { + public string Custom1 { get; set; } + public string Default { get; set; } + } + + public class SourceDefault + { + public string Custom { get; set; } + public string Default { get; set; } + } + + public class TargetDefault + { + public string Custom1 { get; set; } + public string Default { get; set; } + } + } +} +#endif diff --git a/Source/UnitTests/UnitTests.csproj b/Source/UnitTests/UnitTests.csproj index aed1e3e..4c4ac34 100644 --- a/Source/UnitTests/UnitTests.csproj +++ b/Source/UnitTests/UnitTests.csproj @@ -1,129 +1,36 @@ - - - - - Debug - AnyCPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE} - Library - Properties - UnitTests - UnitTests - v4.5 - 512 - 692d7b07 - ..\..\ - true - - - true - full - false - ..\..\Out\Debug\UnitTests\ - DEBUG;TRACE - prompt - 4 - true - - - pdbonly - true - ..\..\Out\Release\UnitTests\ - TRACE - prompt - 4 - - - bin\Release 4.0\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\Release 3.5\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\Release 3.0\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - - - - - - - - False - ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - False - ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - {c7ebd676-248b-484a-a5cb-df879670652a} - TinyMapper - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - + + + net452;netcoreapp1.1 + Debug;Release;VS + + + $(TargetFrameworks) + + + COREFX + core + + + COREFX + core + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UnitTests/packages.config b/Source/UnitTests/packages.config index 67a23e7..206fcd5 100644 --- a/Source/UnitTests/packages.config +++ b/Source/UnitTests/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/TinyMapper.sln b/TinyMapper.sln index 2ab0509..150b00e 100644 --- a/TinyMapper.sln +++ b/TinyMapper.sln @@ -1,62 +1,52 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.16 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "Source\UnitTests\UnitTests.csproj", "{0537D08C-3A73-4F2A-866B-2219E6A336DE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyMapper", "Source\TinyMapper\TinyMapper.csproj", "{C8ECF61C-DD30-4CF1-8DC2-612AE6CA35DE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyMapper", "Source\TinyMapper\TinyMapper.csproj", "{C7EBD676-248B-484A-A5CB-DF879670652A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "Source\UnitTests\UnitTests.csproj", "{B330A99A-C110-4536-9266-BD41C5953B92}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Source\Benchmark\Benchmark.csproj", "{49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmark", "Source\Benchmark\Benchmark.csproj", "{DBE499CD-3B4A-4373-8AD4-19BE39CAD94A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{9CA061E1-4791-4FE1-B50F-00FA012CE3C6}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkInternal", "Source\BenchmarkInternal\BenchmarkInternal.csproj", "{9764E625-069A-429D-929E-E1A5FD58EB93}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Release 3.0|Any CPU = Release 3.0|Any CPU - Release 3.5|Any CPU = Release 3.5|Any CPU - Release 4.0|Any CPU = Release 4.0|Any CPU Release|Any CPU = Release|Any CPU + VS|Any CPU = VS|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release 3.0|Any CPU.ActiveCfg = Release 3.0|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release 3.0|Any CPU.Build.0 = Release 3.0|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release 3.5|Any CPU.ActiveCfg = Release 3.5|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release 3.5|Any CPU.Build.0 = Release 3.5|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release 4.0|Any CPU.ActiveCfg = Release 4.0|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release 4.0|Any CPU.Build.0 = Release 4.0|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0537D08C-3A73-4F2A-866B-2219E6A336DE}.Release|Any CPU.Build.0 = Release|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release 3.0|Any CPU.ActiveCfg = Release 3.0|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release 3.0|Any CPU.Build.0 = Release 3.0|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release 3.5|Any CPU.ActiveCfg = Release 3.5|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release 3.5|Any CPU.Build.0 = Release 3.5|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release 4.0|Any CPU.ActiveCfg = Release 4.0|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release 4.0|Any CPU.Build.0 = Release 4.0|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C7EBD676-248B-484A-A5CB-DF879670652A}.Release|Any CPU.Build.0 = Release|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release 3.0|Any CPU.ActiveCfg = Release 3.0|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release 3.0|Any CPU.Build.0 = Release 3.0|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release 3.5|Any CPU.ActiveCfg = Release 3.5|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release 3.5|Any CPU.Build.0 = Release 3.5|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release 4.0|Any CPU.ActiveCfg = Release 4.0|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release 4.0|Any CPU.Build.0 = Release 4.0|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {49058BB5-61A5-4FE3-B64F-B1F9FD5DCB36}.Release|Any CPU.Build.0 = Release|Any CPU + {C8ECF61C-DD30-4CF1-8DC2-612AE6CA35DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8ECF61C-DD30-4CF1-8DC2-612AE6CA35DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8ECF61C-DD30-4CF1-8DC2-612AE6CA35DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8ECF61C-DD30-4CF1-8DC2-612AE6CA35DE}.Release|Any CPU.Build.0 = Release|Any CPU + {C8ECF61C-DD30-4CF1-8DC2-612AE6CA35DE}.VS|Any CPU.ActiveCfg = VS|Any CPU + {C8ECF61C-DD30-4CF1-8DC2-612AE6CA35DE}.VS|Any CPU.Build.0 = VS|Any CPU + {B330A99A-C110-4536-9266-BD41C5953B92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B330A99A-C110-4536-9266-BD41C5953B92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B330A99A-C110-4536-9266-BD41C5953B92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B330A99A-C110-4536-9266-BD41C5953B92}.Release|Any CPU.Build.0 = Release|Any CPU + {B330A99A-C110-4536-9266-BD41C5953B92}.VS|Any CPU.ActiveCfg = VS|Any CPU + {B330A99A-C110-4536-9266-BD41C5953B92}.VS|Any CPU.Build.0 = VS|Any CPU + {DBE499CD-3B4A-4373-8AD4-19BE39CAD94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBE499CD-3B4A-4373-8AD4-19BE39CAD94A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBE499CD-3B4A-4373-8AD4-19BE39CAD94A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBE499CD-3B4A-4373-8AD4-19BE39CAD94A}.Release|Any CPU.Build.0 = Release|Any CPU + {DBE499CD-3B4A-4373-8AD4-19BE39CAD94A}.VS|Any CPU.ActiveCfg = Release|Any CPU + {DBE499CD-3B4A-4373-8AD4-19BE39CAD94A}.VS|Any CPU.Build.0 = Release|Any CPU + {9764E625-069A-429D-929E-E1A5FD58EB93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9764E625-069A-429D-929E-E1A5FD58EB93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9764E625-069A-429D-929E-E1A5FD58EB93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9764E625-069A-429D-929E-E1A5FD58EB93}.Release|Any CPU.Build.0 = Release|Any CPU + {9764E625-069A-429D-929E-E1A5FD58EB93}.VS|Any CPU.ActiveCfg = Debug|Any CPU + {9764E625-069A-429D-929E-E1A5FD58EB93}.VS|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {23A06143-949A-438D-A11C-F984A12D611F} + EndGlobalSection EndGlobal diff --git a/TinyMapper.sln.DotSettings b/TinyMapper.sln.DotSettings index 3ca61f6..bd1a431 100644 --- a/TinyMapper.sln.DotSettings +++ b/TinyMapper.sln.DotSettings @@ -86,7 +86,10 @@ 1 1 True + NEVER False + NEVER + NEVER True LINE_BREAK False @@ -100,7 +103,8 @@ CHOP_ALWAYS True True - <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> + <?xml version="1.0" encoding="utf-16"?> +<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> <TypePattern DisplayName="COM interfaces or structs"> <TypePattern.Match> <Or> @@ -111,77 +115,67 @@ <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> </Or> </And> - <HasAttribute Name="System.Runtime.InteropServices.StructLayoutAttribute" /> + <Kind Is="Struct" /> </Or> </TypePattern.Match> </TypePattern> - <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> <TypePattern.Match> <And> <Kind Is="Class" /> - <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="true" /> - <HasAttribute Name="NUnit.Framework.TestCaseFixtureAttribute" Inherited="true" /> + <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TestCaseFixtureAttribute" Inherited="True" /> </And> </TypePattern.Match> - <Entry DisplayName="Setup/Teardown Methods"> <Entry.Match> <And> <Kind Is="Method" /> <Or> - <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="true" /> - <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="true" /> - <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="true" /> - <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="true" /> + <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> </Or> </And> </Entry.Match> </Entry> - <Entry DisplayName="All other members" /> - - <Entry DisplayName="Test Methods" Priority="100"> + <Entry Priority="100" DisplayName="Test Methods"> <Entry.Match> <And> <Kind Is="Method" /> - <HasAttribute Name="NUnit.Framework.TestAttribute" Inherited="false" /> + <HasAttribute Name="NUnit.Framework.TestAttribute" /> </And> </Entry.Match> - <Entry.SortBy> <Name /> </Entry.SortBy> </Entry> </TypePattern> - <TypePattern DisplayName="Default Pattern"> - <Entry DisplayName="Public Delegates" Priority="100"> + <Entry Priority="100" DisplayName="Public Delegates"> <Entry.Match> <And> <Access Is="Public" /> <Kind Is="Delegate" /> </And> </Entry.Match> - <Entry.SortBy> <Name /> </Entry.SortBy> </Entry> - - <Entry DisplayName="Public Enums" Priority="100"> + <Entry Priority="100" DisplayName="Public Enums"> <Entry.Match> <And> <Access Is="Public" /> <Kind Is="Enum" /> </And> </Entry.Match> - <Entry.SortBy> <Name /> </Entry.SortBy> </Entry> - <Entry DisplayName="Static Fields and Constants"> <Entry.Match> <Or> @@ -192,17 +186,10 @@ </And> </Or> </Entry.Match> - <Entry.SortBy> - <Kind> - <Kind.Order> - <DeclarationKind>Constant</DeclarationKind> - <DeclarationKind>Field</DeclarationKind> - </Kind.Order> - </Kind> + <Kind Order="Constant Field" /> </Entry.SortBy> </Entry> - <Entry DisplayName="Fields"> <Entry.Match> <And> @@ -212,23 +199,19 @@ </Not> </And> </Entry.Match> - <Entry.SortBy> <Readonly /> <Name /> </Entry.SortBy> </Entry> - <Entry DisplayName="Constructors"> <Entry.Match> <Kind Is="Constructor" /> </Entry.Match> - <Entry.SortBy> - <Static/> + <Static /> </Entry.SortBy> </Entry> - <Entry DisplayName="Properties, Indexers"> <Entry.Match> <Or> @@ -237,30 +220,25 @@ </Or> </Entry.Match> </Entry> - - <Entry DisplayName="Interface Implementations" Priority="100"> + <Entry Priority="100" DisplayName="Interface Implementations"> <Entry.Match> <And> <Kind Is="Member" /> <ImplementsInterface /> </And> </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="true" /> + <ImplementsInterface Immediate="True" /> </Entry.SortBy> </Entry> - <Entry DisplayName="All other members" /> - <Entry DisplayName="Nested Types"> <Entry.Match> <Kind Is="Type" /> </Entry.Match> </Entry> </TypePattern> -</Patterns> - +</Patterns> <?xml version="1.0" encoding="utf-8" ?> <!-- @@ -557,8 +535,17 @@ II.2.12 <HandlesEvent /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> $object$_On$event$ + True True + True + True + True + True True + True + True True True - True \ No newline at end of file + True + True + True \ No newline at end of file diff --git a/Tools/packages.config b/Tools/packages.config index 8767f4e..3d0349c 100644 --- a/Tools/packages.config +++ b/Tools/packages.config @@ -1,4 +1,4 @@ - + \ No newline at end of file