From a83c2c348b48bf04e57424013bc7322386548c46 Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 20 Feb 2014 12:38:02 -0900 Subject: [PATCH 1/4] initial commit of nested property support. --- .../Core/Mocks/MockComplexPerson.cs | 25 +++++ .../Data/Tasks/BuildComplexTypedTest.cs | 64 +++++++++++++ .../Data/Tasks/BuildMappingsTest.cs | 34 +++++++ app/Simpler.Tests/Simpler.Tests.csproj | 3 + app/Simpler/Data/Tasks/BuildMappings.cs | 86 +++++++++++++++++ app/Simpler/Data/Tasks/BuildObjects.cs | 25 ++++- app/Simpler/Data/Tasks/BuildTyped.cs | 93 ++++++++++++++----- app/Simpler/Simpler.csproj | 3 +- 8 files changed, 302 insertions(+), 31 deletions(-) create mode 100644 app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs create mode 100644 app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs create mode 100644 app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs create mode 100644 app/Simpler/Data/Tasks/BuildMappings.cs diff --git a/app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs b/app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs new file mode 100644 index 0000000..e68e88d --- /dev/null +++ b/app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Simpler.Tests.Core.Mocks +{ + public class MockComplexPerson + { + public string Name { get; set; } + public int? Age { get; set; } + public MockPet Pet { get; set; } + public MockVechile[] Vechiles { get; set; } + public MockEnum MockEnum { get; set; } + } + + public class MockPet + { + public string Name { get; set; } + public int? Age { get; set; } + } + + public class MockVechile + { + public string Make { get; set; } + public string Model { get; set; } + } +} diff --git a/app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs b/app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs new file mode 100644 index 0000000..cce9990 --- /dev/null +++ b/app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using NUnit.Framework; +using Simpler.Data.Tasks; +using Moq; +using System.Data; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.Tasks +{ + [TestFixture] + public class BuildComplexTypedTest + { + static IDataReader SetupReader() + { + var table = new DataTable(); + table.Columns.Add("Name", Type.GetType("System.String")); + table.Columns.Add("Age", Type.GetType("System.Int32")); + table.Columns.Add("PetName", Type.GetType("System.String")); + table.Columns.Add("PetAge", Type.GetType("System.Int32")); + table.Columns.Add("Vechiles0Make", Type.GetType("System.String")); + table.Columns.Add("Vechiles0Model", Type.GetType("System.String")); + table.Columns.Add("Vechiles1Make", Type.GetType("System.String")); + table.Columns.Add("Vechiles1Model", Type.GetType("System.String")); + table.Rows.Add(new object[] { "John Doe", "21", "Fiddo", "2", "Dodge", "Durango", "Dodge", "Durango" }); + table.Rows.Add(new object[] { "Jane Doe", "19", "Spot", "3", "Jeep", "Wrangler", "Jeep", "Wrangler" }); + return table.CreateDataReader(); + } + + [Test] + public void should_return_an_object_for_each_record_returned_by_the_select_command() + { + // Arrange + var task = Task.New>(); + task.In.Reader = SetupReader(); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.Objects.Count(), Is.EqualTo(2)); + Assert.That(task.Out.Objects[0].Name, Is.EqualTo("John Doe")); + Assert.That(task.Out.Objects[1].Name, Is.EqualTo("Jane Doe")); + } + + [Test] + public void should_return_nested_complex_objects() + { + // Arrange + var task = Task.New>(); + task.In.Reader = SetupReader(); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.Objects.Count(), Is.EqualTo(2)); + Assert.That(task.Out.Objects[0].Pet.Name, Is.EqualTo("Fiddo")); + Assert.That(task.Out.Objects[1].Pet.Name, Is.EqualTo("Spot")); + Assert.That(task.Out.Objects[0].Vechiles[0].Make, Is.EqualTo("Dodge")); + Assert.That(task.Out.Objects[1].Vechiles[1].Make, Is.EqualTo("Jeep")); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs b/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs new file mode 100644 index 0000000..93ddff1 --- /dev/null +++ b/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Simpler.Data.Tasks; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.Tasks +{ + [TestFixture] + public class BuildMappingsTest + { + [Test] + public void should_return_an_object_for_each_record_returned_by_the_select_command() + { + + // Arrange + var task = Task.New(); + task.In.ColumnNames = new Dictionary + { + { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 2}, {"Vechiles0Make", 2}, + {"Vechiles0Model", 2}, {"Vechiles1Make", 2}, {"Vechiles1Model", 2} + }; + task.In.RootType = typeof (MockComplexPerson); + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.ObjectMapping.Count(), Is.EqualTo(4)); + Assert.That(task.Out.ObjectMapping["Pet"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.ObjectMapping["Vechiles"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.ObjectMapping["Vechiles"].Children["0"].Children.Count(), Is.EqualTo(2)); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Simpler.Tests.csproj b/app/Simpler.Tests/Simpler.Tests.csproj index c03d0ac..a5d76f4 100644 --- a/app/Simpler.Tests/Simpler.Tests.csproj +++ b/app/Simpler.Tests/Simpler.Tests.csproj @@ -51,6 +51,7 @@ + @@ -74,6 +75,8 @@ + + diff --git a/app/Simpler/Data/Tasks/BuildMappings.cs b/app/Simpler/Data/Tasks/BuildMappings.cs new file mode 100644 index 0000000..3cc2ac9 --- /dev/null +++ b/app/Simpler/Data/Tasks/BuildMappings.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Simpler.Data.Tasks +{ + public class BuildMappings : InOutTask + { + public class Input + { + public Dictionary ColumnNames { get; set; } + public Type RootType { get; set; } + } + + public class Output + { + public Dictionary ObjectMapping { get; set; } + } + + public class ObjectMapping + { + public PropertyInfo PropertyInfo { get; set; } + public Type PropertyType { get; set; } + public int? Column { get; set; } + public Dictionary Children = new Dictionary(); + } + + public void ProcessColumnName(Dictionary objectMapping, Type currentType, string columnName, string path = "") + { + var currentColumnName = columnName.Substring(path.Length); + if (currentType.IsArray) + { + //find if there is an index + var index = new String(currentColumnName.TakeWhile(Char.IsDigit).ToArray()); + if (!objectMapping.ContainsKey(index)) + { + objectMapping[index] = new ObjectMapping { PropertyType = currentType}; + } + + ProcessColumnName(objectMapping[index].Children, currentType.GetElementType(), columnName, path += index); + return; + } + + var propertyInfo = currentType.GetProperty(currentColumnName); + if (propertyInfo != null) + { + if (!objectMapping.ContainsKey(currentColumnName)) + { + objectMapping[currentColumnName] = new ObjectMapping { PropertyInfo = propertyInfo, PropertyType = currentType, Column = In.ColumnNames[columnName] }; + } + return; + } + + var complexPropertyInfo = currentType.GetProperties().FirstOrDefault(x => currentColumnName.StartsWith(x.Name)); + if (complexPropertyInfo != null) + { + if (!objectMapping.ContainsKey(complexPropertyInfo.Name)) + { + objectMapping[complexPropertyInfo.Name] = new ObjectMapping { PropertyInfo = complexPropertyInfo, PropertyType = currentType }; + } + + ProcessColumnName(objectMapping[complexPropertyInfo.Name].Children, complexPropertyInfo.PropertyType, columnName, path += complexPropertyInfo.Name); + return; + } + + //throw an exception mapping not found + } + + public void CreateMapping(Dictionary rootMapping, Type currentType) + { + var sortedColumnNames = In.ColumnNames.OrderBy(s => s.Key); + foreach (var columnName in sortedColumnNames) + { + ProcessColumnName(rootMapping, currentType, columnName.Key); + } + } + + public override void Execute() + { + var root = new Dictionary(); + CreateMapping(root, In.RootType); + Out.ObjectMapping = root; + } + } +} diff --git a/app/Simpler/Data/Tasks/BuildObjects.cs b/app/Simpler/Data/Tasks/BuildObjects.cs index 2cf58d9..e2218d5 100644 --- a/app/Simpler/Data/Tasks/BuildObjects.cs +++ b/app/Simpler/Data/Tasks/BuildObjects.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Linq; namespace Simpler.Data.Tasks { @@ -16,15 +17,16 @@ public class Output public T[] Objects { get; set; } } + public BuildMappings BuildMappings { get; set; } public BuildTyped BuildTyped { get; set; } public BuildDynamic BuildDynamic { get; set; } public override void Execute() { - Func buildObject; + Func, T> buildObject; if (typeof (T).FullName == "System.Object") { - buildObject = reader => + buildObject = (reader, map) => { BuildDynamic.In.DataRecord = reader; BuildDynamic.Execute(); @@ -33,8 +35,9 @@ public override void Execute() } else { - buildObject = reader => + buildObject = (reader, map) => { + BuildTyped.In.Map = map; BuildTyped.In.DataRecord = reader; BuildTyped.Execute(); return BuildTyped.Out.Object; @@ -42,10 +45,22 @@ public override void Execute() } var objectList = new List(); - while (In.Reader.Read()) + In.Reader.Read(); + + var columns = new Dictionary(); + for (var i = 0; i < In.Reader.FieldCount; i++) { - objectList.Add(buildObject(In.Reader)); + columns.Add(In.Reader.GetName(i), i); } + BuildMappings.In.ColumnNames = columns; + BuildMappings.In.RootType = typeof (T); + BuildMappings.Execute(); + + do + { + objectList.Add(buildObject(In.Reader, BuildMappings.Out.ObjectMapping)); + } while (In.Reader.Read()); + Out.Objects = objectList.ToArray(); } } diff --git a/app/Simpler/Data/Tasks/BuildTyped.cs b/app/Simpler/Data/Tasks/BuildTyped.cs index 523e686..844a2d9 100644 --- a/app/Simpler/Data/Tasks/BuildTyped.cs +++ b/app/Simpler/Data/Tasks/BuildTyped.cs @@ -1,13 +1,19 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Data; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; namespace Simpler.Data.Tasks { - public class BuildTyped : InOutTask.Input, BuildTyped.Output> + public class BuildTyped : InOutTask.Input, BuildTyped.Output> { public class Input { public IDataRecord DataRecord { get; set; } + public Dictionary Map { get; set; } } public class Output @@ -15,41 +21,78 @@ public class Output public T Object { get; set; } } - public override void Execute() + public void Parse(Dictionary objectMappings, object instance) { - Out.Object = (T)Activator.CreateInstance(typeof(T)); - var objectType = typeof(T); - - for (var i = 0; i < In.DataRecord.FieldCount; i++) + var instanceType = instance.GetType(); + foreach (var objectMapping in objectMappings) { - var columnName = In.DataRecord.GetName(i); - var propertyInfo = objectType.GetProperty(columnName); + var propertyInfo = instanceType.GetProperty(objectMapping.Key); + object propertyInstance; + Type propertyType; + if (propertyInfo != null) + { + propertyInstance = propertyInfo.GetValue(instance, null); + propertyType = propertyInfo.PropertyType; + } + else if (instanceType.IsArray) + { + var array = (Array)instance; + var index = int.Parse(objectMapping.Key); + propertyInstance = array.GetValue(index); + propertyType = instanceType.GetElementType(); + } + else + { + continue; + } + + if (propertyInstance == null) + { + if (propertyType.IsArray) + { + propertyInstance = Activator.CreateInstance(propertyType, new object[] { objectMapping.Value.Children.Count }); + } + else if (objectMapping.Value.Column == null) + { + propertyInstance = Activator.CreateInstance(propertyType, null); + } + else + { + var value = In.DataRecord.GetValue((int) objectMapping.Value.Column); - Check.That(propertyInfo != null, - "The DataRecord contains column '{0}' that is not a property of the '{1}' class.", - columnName, - objectType.FullName); + if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof (Nullable<>)) + { + propertyType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + } - var columnValue = In.DataRecord[columnName]; - if (columnValue.GetType() != typeof(DBNull)) - { - var propertyType = propertyInfo.PropertyType; + if (propertyType.IsEnum) + { + value = Enum.Parse(propertyType, value.ToString()); + } - if (propertyType.IsEnum) - { - propertyInfo.SetValue(Out.Object,Enum.Parse(propertyType,columnValue.ToString()),null); - continue; + propertyInstance = Convert.ChangeType(value, propertyType); } - if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + if (instanceType.IsArray) { - propertyType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + ((Array) instance).SetValue(propertyInstance, int.Parse(objectMapping.Key)); + } + else + { + propertyInfo.SetValue(instance, propertyInstance, null); } - columnValue = Convert.ChangeType(columnValue, propertyType); - propertyInfo.SetValue(Out.Object, columnValue, null); + } + Parse(objectMapping.Value.Children, propertyInstance); } } + + public override void Execute() + { + var instance = (T) Activator.CreateInstance(typeof (T)); + Parse(In.Map, instance); + Out.Object = instance; + } } -} +} \ No newline at end of file diff --git a/app/Simpler/Simpler.csproj b/app/Simpler/Simpler.csproj index 32f9920..0cf910f 100644 --- a/app/Simpler/Simpler.csproj +++ b/app/Simpler/Simpler.csproj @@ -56,6 +56,8 @@ + + @@ -65,7 +67,6 @@ - From b93d009d7f15b45a6c091ad9704bf25c5ddc443e Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 21 Feb 2014 14:14:55 -0900 Subject: [PATCH 2/4] Refactored the ObjectMapping Moved all the reflection logic into ObjectMapping Combined BuildDynamic and BuildTyped into BuildObject Fixed failing tests Added Additional tests --- app/Simpler.Tests/Core/Mocks/MockPerson.cs | 1 + .../Data/Tasks/BuildDynamicTest.cs | 45 ++++++++- .../Data/Tasks/BuildMappingsTest.cs | 89 ++++++++++++++++- .../Data/Tasks/BuildObjectsTest.cs | 14 +-- .../Data/Tasks/BuildTypedTest.cs | 51 ++++++---- app/Simpler/Data/Mappings/ObjectMapping.cs | 58 +++++++++++ .../Mappings/ObjectMappingArrayChildNode.cs | 46 +++++++++ .../Data/Mappings/ObjectMappingArrayNode.cs | 17 ++++ .../Mappings/ObjectMappingDynamicChildNode.cs | 29 ++++++ .../Data/Mappings/ObjectMappingDynamicNode.cs | 18 ++++ .../Data/Mappings/ObjectMappingNode.cs | 19 ++++ .../Data/Mappings/ObjectMappingObjectNode.cs | 53 ++++++++++ .../Data/Mappings/ObjectMappingRoot.cs | 14 +++ app/Simpler/Data/Tasks/BuildDynamic.cs | 35 ------- app/Simpler/Data/Tasks/BuildMappings.cs | 91 +++++++++-------- app/Simpler/Data/Tasks/BuildObject.cs | 67 +++++++++++++ app/Simpler/Data/Tasks/BuildObjects.cs | 31 ++---- app/Simpler/Data/Tasks/BuildTyped.cs | 98 ------------------- app/Simpler/Simpler.csproj | 11 ++- 19 files changed, 555 insertions(+), 232 deletions(-) create mode 100644 app/Simpler/Data/Mappings/ObjectMapping.cs create mode 100644 app/Simpler/Data/Mappings/ObjectMappingArrayChildNode.cs create mode 100644 app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs create mode 100644 app/Simpler/Data/Mappings/ObjectMappingDynamicChildNode.cs create mode 100644 app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs create mode 100644 app/Simpler/Data/Mappings/ObjectMappingNode.cs create mode 100644 app/Simpler/Data/Mappings/ObjectMappingObjectNode.cs create mode 100644 app/Simpler/Data/Mappings/ObjectMappingRoot.cs delete mode 100644 app/Simpler/Data/Tasks/BuildDynamic.cs create mode 100644 app/Simpler/Data/Tasks/BuildObject.cs delete mode 100644 app/Simpler/Data/Tasks/BuildTyped.cs diff --git a/app/Simpler.Tests/Core/Mocks/MockPerson.cs b/app/Simpler.Tests/Core/Mocks/MockPerson.cs index 7bd338a..cc6fa17 100644 --- a/app/Simpler.Tests/Core/Mocks/MockPerson.cs +++ b/app/Simpler.Tests/Core/Mocks/MockPerson.cs @@ -5,5 +5,6 @@ public class MockPerson public string Name { get; set; } public int? Age { get; set; } public MockEnum MockEnum { get; set; } + public dynamic Stuff { get; set; } } } diff --git a/app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs b/app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs index 3cf13ba..3893e94 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs @@ -1,7 +1,9 @@ -using NUnit.Framework; +using System.Collections.Generic; +using NUnit.Framework; using Simpler.Data.Tasks; using System.Data; using Moq; +using Simpler.Tests.Core.Mocks; namespace Simpler.Tests.Data.Tasks { @@ -12,15 +14,19 @@ public class BuildDynamicTest public void should_populate_dynamic_object_using_all_columns_in_the_data_record() { // Arrange - var task = Task.New(); + var task = Task.New>(); var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(2); mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("Name"); - mockDataRecord.Setup(dataRecord => dataRecord["Name"]).Returns("John Doe"); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(0)).Returns("John Doe"); mockDataRecord.Setup(dataRecord => dataRecord.GetName(1)).Returns("Age"); - mockDataRecord.Setup(dataRecord => dataRecord["Age"]).Returns(21); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(1)).Returns(21); task.In.DataRecord = mockDataRecord.Object; + var mapTask = Task.New(); + mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "Age", 1 } }; + mapTask.Execute(); + task.In.ObjectMapping = mapTask.Out.ObjectMapping; // Act task.Execute(); @@ -29,5 +35,36 @@ public void should_populate_dynamic_object_using_all_columns_in_the_data_record( Assert.That(task.Out.Object.Name, Is.EqualTo("John Doe")); Assert.That(task.Out.Object.Age, Is.EqualTo(21)); } + + [Test] + public void should_populate_nested_dynamic_objects_using_all_columns_in_the_data_record() + { + // Arrange + var task = Task.New>(); + + var mockDataRecord = new Mock(); + mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(3); + mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("Name"); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(0)).Returns("John Doe"); + mockDataRecord.Setup(dataRecord => dataRecord.GetName(1)).Returns("Age"); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(1)).Returns(21); + mockDataRecord.Setup(dataRecord => dataRecord.GetName(2)).Returns("StuffCake"); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(2)).Returns("TheCakeIsALie"); + task.In.DataRecord = mockDataRecord.Object; + var mapTask = Task.New(); + mapTask.In.RootType = typeof (MockPerson); + mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "Age", 1 }, {"StuffCake", 2 } }; + mapTask.Execute(); + task.In.ObjectMapping = mapTask.Out.ObjectMapping; + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.Object.Stuff.Cake, Is.EqualTo("TheCakeIsALie")); + Assert.That(task.Out.Object.Name, Is.EqualTo("John Doe")); + Assert.That(task.Out.Object.Age, Is.EqualTo(21)); + } + } } \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs b/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs index 93ddff1..3dec108 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using Simpler.Data; using Simpler.Data.Tasks; using Simpler.Tests.Core.Mocks; @@ -12,13 +13,12 @@ public class BuildMappingsTest [Test] public void should_return_an_object_for_each_record_returned_by_the_select_command() { - // Arrange var task = Task.New(); task.In.ColumnNames = new Dictionary { - { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 2}, {"Vechiles0Make", 2}, - {"Vechiles0Model", 2}, {"Vechiles1Make", 2}, {"Vechiles1Model", 2} + { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, + {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} }; task.In.RootType = typeof (MockComplexPerson); // Act @@ -28,7 +28,88 @@ public void should_return_an_object_for_each_record_returned_by_the_select_comma Assert.That(task.Out.ObjectMapping.Count(), Is.EqualTo(4)); Assert.That(task.Out.ObjectMapping["Pet"].Children.Count(), Is.EqualTo(2)); Assert.That(task.Out.ObjectMapping["Vechiles"].Children.Count(), Is.EqualTo(2)); - Assert.That(task.Out.ObjectMapping["Vechiles"].Children["0"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.ObjectMapping["Vechiles"]["0"].Children.Count(), Is.EqualTo(2)); + } + + [Test] + public void should_return_an_type_for_each_record_returned_by_the_select_command() + { + // Arrange + var task = Task.New(); + task.In.ColumnNames = new Dictionary + { + { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, + {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} + }; + task.In.RootType = typeof(MockComplexPerson); + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.ObjectMapping["Name"].PropertyType, Is.EqualTo(typeof(string))); + Assert.That(task.Out.ObjectMapping["Age"].PropertyType, Is.EqualTo(typeof(int?))); + Assert.That(task.Out.ObjectMapping["Pet"]["Name"].PropertyType, Is.EqualTo(typeof(string))); + Assert.That(task.Out.ObjectMapping["Pet"]["Age"].PropertyType, Is.EqualTo(typeof(int?))); + Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Model"].PropertyType, Is.EqualTo(typeof(string))); + Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Make"].PropertyType, Is.EqualTo(typeof(string))); + Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Model"].PropertyType, Is.EqualTo(typeof(string))); + Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Make"].PropertyType, Is.EqualTo(typeof(string))); + + Assert.That(task.Out.ObjectMapping.PropertyType, Is.EqualTo(typeof(MockComplexPerson))); + Assert.That(task.Out.ObjectMapping["Pet"].PropertyType, Is.EqualTo(typeof(MockPet))); + Assert.That(task.Out.ObjectMapping["Vechiles"]["0"].PropertyType, Is.EqualTo(typeof(MockVechile))); + Assert.That(task.Out.ObjectMapping["Vechiles"]["1"].PropertyType, Is.EqualTo(typeof(MockVechile))); + } + + [Test] + public void should_return_contain_column_indexs_for_each_record_returned_by_the_select_command() + { + // Arrange + var task = Task.New(); + task.In.ColumnNames = new Dictionary + { + { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, + {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} + }; + task.In.RootType = typeof(MockComplexPerson); + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.ObjectMapping["Name"].ColumnIndex, Is.EqualTo(0)); + Assert.That(task.Out.ObjectMapping["Age"].ColumnIndex, Is.EqualTo(1)); + Assert.That(task.Out.ObjectMapping["Pet"]["Name"].ColumnIndex, Is.EqualTo(2)); + Assert.That(task.Out.ObjectMapping["Pet"]["Age"].ColumnIndex, Is.EqualTo(3)); + Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Make"].ColumnIndex, Is.EqualTo(4)); + Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Model"].ColumnIndex, Is.EqualTo(5)); + Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Make"].ColumnIndex, Is.EqualTo(6)); + Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Model"].ColumnIndex, Is.EqualTo(7)); + + Assert.That(task.Out.ObjectMapping["Pet"].ColumnIndex, Is.Null); + Assert.That(task.Out.ObjectMapping["Vechiles"].ColumnIndex, Is.Null); + Assert.That(task.Out.ObjectMapping["Vechiles"]["0"].ColumnIndex, Is.Null); + Assert.That(task.Out.ObjectMapping["Vechiles"]["1"].ColumnIndex, Is.Null); + } + + [Test] + public void should_return_property_info_for_each_record_returned_by_the_select_command() + { + // Arrange + var task = Task.New(); + task.In.ColumnNames = new Dictionary + { + { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, + {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} + }; + task.In.RootType = typeof(MockComplexPerson); + // Act + task.Execute(); + + // Assert + Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Name"]).PropertyInfo, Is.EqualTo(typeof(MockComplexPerson).GetProperty("Name"))); + Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Age"]).PropertyInfo, Is.EqualTo(typeof(MockComplexPerson).GetProperty("Age"))); + Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Pet"]["Age"]).PropertyInfo, Is.EqualTo(typeof(MockPet).GetProperty("Age"))); + Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Pet"]["Name"]).PropertyInfo, Is.EqualTo(typeof(MockPet).GetProperty("Name"))); } } } \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs b/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs index 2027f79..046f5e3 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs @@ -44,15 +44,13 @@ public void should_build_typed_objects_if_given_strong_type() var task = Task.New>(); task.In.Reader = SetupReader(); - task.BuildTyped = Fake.Task>(bt => bt.Out.Object = new MockPerson()); - task.BuildDynamic = Fake.Task(); + task.BuildObject = Fake.Task>(bt => bt.Out.Object = new MockPerson()); // Act task.Execute(); // Assert - Assert.That(task.BuildTyped.Stats.ExecuteCount, Is.GreaterThan(0)); - Assert.That(task.BuildDynamic.Stats.ExecuteCount, Is.EqualTo(0)); + Assert.That(task.BuildObject.Stats.ExecuteCount, Is.GreaterThan(0)); } [Test] @@ -62,15 +60,13 @@ public void should_build_dynamic_objects_if_given_dynamic_type() var task = Task.New>(); task.In.Reader = SetupReader(); - task.BuildTyped = Fake.Task>(); - task.BuildDynamic = Fake.Task(bd => bd.Out.Object = new MockPerson()); - + task.BuildObject = Fake.Task>(bt => bt.Out.Object = new MockPerson()); + // Act task.Execute(); // Assert - Assert.That(task.BuildTyped.Stats.ExecuteCount, Is.EqualTo(0)); - Assert.That(task.BuildDynamic.Stats.ExecuteCount, Is.GreaterThan(0)); + Assert.That(task.BuildObject.Stats.ExecuteCount, Is.GreaterThan(0)); } } } \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs b/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs index ff0c7ad..daf827c 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using System.Collections.Generic; +using NUnit.Framework; using Simpler.Data.Tasks; using System.Data; using Moq; @@ -13,7 +14,12 @@ public class BuildTypedTest public void should_build_an_instance_of_given_type() { // Arrange - var task = Task.New>(); + var task = Task.New>(); + var mapTask = Task.New(); + mapTask.In.RootType = typeof(MockPerson); + mapTask.In.ColumnNames = new Dictionary { { "Name", 0 } }; + mapTask.Execute(); + task.In.ObjectMapping = mapTask.Out.ObjectMapping; var mockDataRecord = new Mock(); task.In.DataRecord = mockDataRecord.Object; @@ -29,14 +35,19 @@ public void should_build_an_instance_of_given_type() public void should_populate_typed_object_using_all_columns_in_the_data_record() { // Arrange - var task = Task.New>(); + var task = Task.New>(); + var mapTask = Task.New(); + mapTask.In.RootType = typeof(MockPerson); + mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "Age", 1 } }; + mapTask.Execute(); + task.In.ObjectMapping = mapTask.Out.ObjectMapping; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(2); mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("Name"); - mockDataRecord.Setup(dataRecord => dataRecord["Name"]).Returns("John Doe"); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(0)).Returns("John Doe"); mockDataRecord.Setup(dataRecord => dataRecord.GetName(1)).Returns("Age"); - mockDataRecord.Setup(dataRecord => dataRecord["Age"]).Returns(21); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(1)).Returns(21); task.In.DataRecord = mockDataRecord.Object; // Act @@ -51,28 +62,29 @@ public void should_populate_typed_object_using_all_columns_in_the_data_record() public void should_throw_exception_if_a_data_record_column_is_not_a_property_of_the_object_class() { // Arrange - var task = Task.New>(); - - var mockDataRecord = new Mock(); - mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(1); - mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("SomeOtherColumn"); - mockDataRecord.Setup(dataRecord => dataRecord["SomeOtherColumn"]).Returns("whatever"); - task.In.DataRecord = mockDataRecord.Object; + var mapTask = Task.New(); + mapTask.In.RootType = typeof(MockPerson); + mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "TheCakeIsALie", 1 } }; // Act & Assert - Assert.Throws(typeof(CheckException), task.Execute); + Assert.Throws(typeof(CheckException), mapTask.Execute); } [Test] public void should_allow_object_to_have_properties_that_dont_have_matching_columns_in_the_data_record() { // Arrange - var task = Task.New>(); + var task = Task.New>(); + var mapTask = Task.New(); + mapTask.In.RootType = typeof(MockPerson); + mapTask.In.ColumnNames = new Dictionary{ { "Name", 0 } }; + mapTask.Execute(); + task.In.ObjectMapping = mapTask.Out.ObjectMapping; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(1); mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("Name"); - mockDataRecord.Setup(dataRecord => dataRecord["Name"]).Returns("John Doe"); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(0)).Returns("John Doe"); task.In.DataRecord = mockDataRecord.Object; @@ -88,12 +100,17 @@ public void should_allow_object_to_have_properties_that_dont_have_matching_colum public void should_build_enum_properties() { // Arrange - var task = Task.New>(); + var task = Task.New>(); + var mapTask = Task.New(); + mapTask.In.RootType = typeof(MockPerson); + mapTask.In.ColumnNames = new Dictionary { { "MockEnum", 0 } }; + mapTask.Execute(); + task.In.ObjectMapping = mapTask.Out.ObjectMapping; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(1); mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("MockEnum"); - mockDataRecord.Setup(dataRecord => dataRecord["MockEnum"]).Returns("One"); + mockDataRecord.Setup(dataRecord => dataRecord.GetValue(0)).Returns("One"); task.In.DataRecord = mockDataRecord.Object; // Act diff --git a/app/Simpler/Data/Mappings/ObjectMapping.cs b/app/Simpler/Data/Mappings/ObjectMapping.cs new file mode 100644 index 0000000..df62172 --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMapping.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public abstract class ObjectMapping : IEnumerable + { + public Type PropertyType { get; set; } + public List Children { get; set; } + + public ObjectMapping() + { + Children = new List(); + } + + public bool ContainsColumn(string columnName) + { + return Children.Any(x => x.Name == columnName); + } + + public int Count() + { + return Children.Count; + } + + public IEnumerator GetEnumerator() + { + return Children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public ObjectMappingNode this[string index] + { + get { return Children.FirstOrDefault(x => x.Name == index); } + set + { + var listIndex = Children.FindIndex(x => x.Name == index); + value.Name = index; + if (listIndex == -1) + { + Children.Add(value); + } + else + { + Children[listIndex] = value; + } + } + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingArrayChildNode.cs b/app/Simpler/Data/Mappings/ObjectMappingArrayChildNode.cs new file mode 100644 index 0000000..08dec67 --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMappingArrayChildNode.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public class ObjectMappingArrayChildNode : ObjectMappingNode + { + public override object CreateInstance(object value = null) + { + if (value == null) + { + return Activator.CreateInstance(PropertyType); + } + + //I think this can be moved into a constructor + var propertyType = PropertyType; + if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + propertyType = Nullable.GetUnderlyingType(propertyType); + } + //end of thought + + if (propertyType.IsEnum) + { + value = Enum.Parse(propertyType, value.ToString()); + } + + return Convert.ChangeType(value, propertyType); + } + + public override void SetValue(object instance, object value) + { + //this int parse can be moved into a constructor + ((Array)instance).SetValue(CreateInstance(value), int.Parse(Name)); + } + + public override object GetValue(object instance) + { + return ((Array) instance).GetValue(int.Parse(Name)); + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs b/app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs new file mode 100644 index 0000000..ad76084 --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public class ObjectMappingArrayNode : ObjectMappingObjectNode + { + public override object CreateInstance(object value = null) + { + return Activator.CreateInstance(PropertyType, new object[] { Children.Count }); + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingDynamicChildNode.cs b/app/Simpler/Data/Mappings/ObjectMappingDynamicChildNode.cs new file mode 100644 index 0000000..278afae --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMappingDynamicChildNode.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public class ObjectMappingDynamicChildNode : ObjectMappingNode + { + public override object CreateInstance(object value = null) + { + return value; + } + + public override void SetValue(dynamic instance, object value) + { + var instanceValue = CreateInstance(value); + ((ExpandoObject)instance as IDictionary)[Name] = instanceValue; + } + + public override object GetValue(dynamic instance) + { + return ((ExpandoObject)instance as IDictionary)[Name]; + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs b/app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs new file mode 100644 index 0000000..f8930be --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public class ObjectMappingDynamicNode : ObjectMappingObjectNode + { + public override object CreateInstance(object value = null) + { + return new ExpandoObject(); + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingNode.cs b/app/Simpler/Data/Mappings/ObjectMappingNode.cs new file mode 100644 index 0000000..81b3e60 --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMappingNode.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public abstract class ObjectMappingNode : ObjectMapping + { + public string Name { get; set; } + public int? ColumnIndex { get; set; } + + public abstract object CreateInstance(object value = null); + public abstract void SetValue(object instance, object value); + public abstract object GetValue(object instance); + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingObjectNode.cs b/app/Simpler/Data/Mappings/ObjectMappingObjectNode.cs new file mode 100644 index 0000000..af57c8f --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMappingObjectNode.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public class ObjectMappingObjectNode : ObjectMappingNode + { + public PropertyInfo PropertyInfo { get; set; } + + public override object CreateInstance(object value = null) + { + var propertyType = PropertyType; + if (value == null && propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + return null; + } + + if (value == null) + { + return Activator.CreateInstance(PropertyType, null); + } + + //I think this can be moved into a constructor + if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + propertyType = Nullable.GetUnderlyingType(propertyType); + } + //end of thought + + if (propertyType.IsEnum) + { + value = Enum.Parse(propertyType, value.ToString()); + } + + return Convert.ChangeType(value, propertyType); + } + + public override void SetValue(object instance, object value) + { + var instanceValue = CreateInstance(value); + PropertyInfo.SetValue(instance, instanceValue, null); + } + + public override object GetValue(object instance) + { + return PropertyInfo.GetValue(instance, null); + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingRoot.cs b/app/Simpler/Data/Mappings/ObjectMappingRoot.cs new file mode 100644 index 0000000..4100ca2 --- /dev/null +++ b/app/Simpler/Data/Mappings/ObjectMappingRoot.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Simpler.Data +{ + public class ObjectMappingRoot : ObjectMapping + { + + } +} diff --git a/app/Simpler/Data/Tasks/BuildDynamic.cs b/app/Simpler/Data/Tasks/BuildDynamic.cs deleted file mode 100644 index f01ffe3..0000000 --- a/app/Simpler/Data/Tasks/BuildDynamic.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Dynamic; - -namespace Simpler.Data.Tasks -{ - public class BuildDynamic : InOutTask - { - public class Input - { - public IDataRecord DataRecord { get; set; } - } - - public class Output - { - public dynamic Object { get; set; } - } - - public override void Execute() - { - Out.Object = new ExpandoObject(); - var dictionary = Out.Object as IDictionary; - for (var i = 0; i < In.DataRecord.FieldCount; i++) - { - var columnName = In.DataRecord.GetName(i); - var columnValue = In.DataRecord[columnName]; - if (columnValue.GetType() != typeof(DBNull)) - { - dictionary.Add(columnName, columnValue); - } - } - } - } -} diff --git a/app/Simpler/Data/Tasks/BuildMappings.cs b/app/Simpler/Data/Tasks/BuildMappings.cs index 3cc2ac9..3ae7908 100644 --- a/app/Simpler/Data/Tasks/BuildMappings.cs +++ b/app/Simpler/Data/Tasks/BuildMappings.cs @@ -15,71 +15,84 @@ public class Input public class Output { - public Dictionary ObjectMapping { get; set; } + public ObjectMapping ObjectMapping { get; set; } } - public class ObjectMapping + public void ProcessColumnName(ObjectMapping objectMapping, string columnName, string path = "") { - public PropertyInfo PropertyInfo { get; set; } - public Type PropertyType { get; set; } - public int? Column { get; set; } - public Dictionary Children = new Dictionary(); - } - - public void ProcessColumnName(Dictionary objectMapping, Type currentType, string columnName, string path = "") - { - var currentColumnName = columnName.Substring(path.Length); - if (currentType.IsArray) + var remainingPath = columnName.Substring(path.Length); + //check if we have reached the end of the path + if (remainingPath.Length == 0) { - //find if there is an index - var index = new String(currentColumnName.TakeWhile(Char.IsDigit).ToArray()); - if (!objectMapping.ContainsKey(index)) - { - objectMapping[index] = new ObjectMapping { PropertyType = currentType}; - } + ((ObjectMappingNode)objectMapping).ColumnIndex = In.ColumnNames[columnName]; + return; + } - ProcessColumnName(objectMapping[index].Children, currentType.GetElementType(), columnName, path += index); + //if the parent is a dyanmic no need to look at properties just set it + if (objectMapping.PropertyType == null || objectMapping.PropertyType.FullName == "System.Object") + { + objectMapping[remainingPath] = new ObjectMappingDynamicChildNode(); + ProcessColumnName(objectMapping[remainingPath], columnName, path + remainingPath); return; } - var propertyInfo = currentType.GetProperty(currentColumnName); - if (propertyInfo != null) + //if the parent is an array we need to pull the index off and create a node. + if (objectMapping.PropertyType.IsArray) { - if (!objectMapping.ContainsKey(currentColumnName)) + //pull the index off the remainingPath + var index = new String(remainingPath.TakeWhile(Char.IsDigit).ToArray()); + + //create the node if it doesn't exist + if (!objectMapping.ContainsColumn(index)) { - objectMapping[currentColumnName] = new ObjectMapping { PropertyInfo = propertyInfo, PropertyType = currentType, Column = In.ColumnNames[columnName] }; + objectMapping[index] = new ObjectMappingArrayChildNode + { + PropertyType = objectMapping.PropertyType.GetElementType() + }; } + + ProcessColumnName(objectMapping[index], columnName, path + index); return; } - var complexPropertyInfo = currentType.GetProperties().FirstOrDefault(x => currentColumnName.StartsWith(x.Name)); - if (complexPropertyInfo != null) + //attempt to find an exact match + var propertyInfo = objectMapping.PropertyType.GetProperty(remainingPath); + if (propertyInfo == null) + { + //if we can't find an exact match find a property that starts with the remmaining path + propertyInfo = objectMapping.PropertyType.GetProperties().FirstOrDefault(x => remainingPath.StartsWith(x.Name)); + } + + Check.That(propertyInfo != null, "The DataRecord contains column '{0}' to a property or nested property.", columnName); + + //check if it already exisits + if (!objectMapping.ContainsColumn(propertyInfo.Name)) { - if (!objectMapping.ContainsKey(complexPropertyInfo.Name)) + if (propertyInfo.PropertyType.FullName == "System.Object") { - objectMapping[complexPropertyInfo.Name] = new ObjectMapping { PropertyInfo = complexPropertyInfo, PropertyType = currentType }; + objectMapping[propertyInfo.Name] = new ObjectMappingDynamicNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType }; + } + else if (propertyInfo.PropertyType.IsArray) + { + objectMapping[propertyInfo.Name] = new ObjectMappingArrayNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType }; + } + else + { + objectMapping[propertyInfo.Name] = new ObjectMappingObjectNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType}; } - - ProcessColumnName(objectMapping[complexPropertyInfo.Name].Children, complexPropertyInfo.PropertyType, columnName, path += complexPropertyInfo.Name); - return; } - //throw an exception mapping not found + ProcessColumnName(objectMapping[propertyInfo.Name], columnName, path + propertyInfo.Name); } - public void CreateMapping(Dictionary rootMapping, Type currentType) + public override void Execute() { + var root = new ObjectMappingRoot { PropertyType = In.RootType }; var sortedColumnNames = In.ColumnNames.OrderBy(s => s.Key); foreach (var columnName in sortedColumnNames) { - ProcessColumnName(rootMapping, currentType, columnName.Key); + ProcessColumnName(root, columnName.Key); } - } - - public override void Execute() - { - var root = new Dictionary(); - CreateMapping(root, In.RootType); Out.ObjectMapping = root; } } diff --git a/app/Simpler/Data/Tasks/BuildObject.cs b/app/Simpler/Data/Tasks/BuildObject.cs new file mode 100644 index 0000000..9f7d210 --- /dev/null +++ b/app/Simpler/Data/Tasks/BuildObject.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Dynamic; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace Simpler.Data.Tasks +{ + public class BuildObject : InOutTask.Input, BuildObject.Output> + { + public class Input + { + public IDataRecord DataRecord { get; set; } + public ObjectMapping ObjectMapping { get; set; } + } + + public class Output + { + public T Object { get; set; } + } + + public void Parse(ObjectMappingNode objectMapping, object instance) + { + if (objectMapping.ColumnIndex == null) + { + objectMapping.SetValue(instance, null); + } + else + { + var value = In.DataRecord.GetValue((int)objectMapping.ColumnIndex); + if (value != null && value.GetType() != typeof(DBNull)) + { + objectMapping.SetValue(instance, value); + } + } + + var childInstance = objectMapping.GetValue(instance); + + foreach (var children in objectMapping) + { + Parse(children, childInstance); + } + } + + public override void Execute() + { + object instance; + if (typeof (T).FullName == "System.Object") + { + instance = new ExpandoObject(); + } + else + { + instance = Activator.CreateInstance(typeof(T)); + } + + foreach (var objectMapping in In.ObjectMapping) + { + Parse(objectMapping, instance); + } + Out.Object = (T)instance; + } + } +} \ No newline at end of file diff --git a/app/Simpler/Data/Tasks/BuildObjects.cs b/app/Simpler/Data/Tasks/BuildObjects.cs index e2218d5..e487fbf 100644 --- a/app/Simpler/Data/Tasks/BuildObjects.cs +++ b/app/Simpler/Data/Tasks/BuildObjects.cs @@ -18,33 +18,13 @@ public class Output } public BuildMappings BuildMappings { get; set; } - public BuildTyped BuildTyped { get; set; } - public BuildDynamic BuildDynamic { get; set; } + public BuildObject BuildObject { get; set; } public override void Execute() { - Func, T> buildObject; - if (typeof (T).FullName == "System.Object") - { - buildObject = (reader, map) => - { - BuildDynamic.In.DataRecord = reader; - BuildDynamic.Execute(); - return BuildDynamic.Out.Object; - }; - } - else - { - buildObject = (reader, map) => - { - BuildTyped.In.Map = map; - BuildTyped.In.DataRecord = reader; - BuildTyped.Execute(); - return BuildTyped.Out.Object; - }; - } - var objectList = new List(); + + //read the first record off and determine the column mappings In.Reader.Read(); var columns = new Dictionary(); @@ -58,7 +38,10 @@ public override void Execute() do { - objectList.Add(buildObject(In.Reader, BuildMappings.Out.ObjectMapping)); + BuildObject.In.ObjectMapping = BuildMappings.Out.ObjectMapping; + BuildObject.In.DataRecord = In.Reader; + BuildObject.Execute(); + objectList.Add(BuildObject.Out.Object); } while (In.Reader.Read()); Out.Objects = objectList.ToArray(); diff --git a/app/Simpler/Data/Tasks/BuildTyped.cs b/app/Simpler/Data/Tasks/BuildTyped.cs deleted file mode 100644 index 844a2d9..0000000 --- a/app/Simpler/Data/Tasks/BuildTyped.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; - -namespace Simpler.Data.Tasks -{ - public class BuildTyped : InOutTask.Input, BuildTyped.Output> - { - public class Input - { - public IDataRecord DataRecord { get; set; } - public Dictionary Map { get; set; } - } - - public class Output - { - public T Object { get; set; } - } - - public void Parse(Dictionary objectMappings, object instance) - { - var instanceType = instance.GetType(); - foreach (var objectMapping in objectMappings) - { - var propertyInfo = instanceType.GetProperty(objectMapping.Key); - object propertyInstance; - Type propertyType; - if (propertyInfo != null) - { - propertyInstance = propertyInfo.GetValue(instance, null); - propertyType = propertyInfo.PropertyType; - } - else if (instanceType.IsArray) - { - var array = (Array)instance; - var index = int.Parse(objectMapping.Key); - propertyInstance = array.GetValue(index); - propertyType = instanceType.GetElementType(); - } - else - { - continue; - } - - if (propertyInstance == null) - { - if (propertyType.IsArray) - { - propertyInstance = Activator.CreateInstance(propertyType, new object[] { objectMapping.Value.Children.Count }); - } - else if (objectMapping.Value.Column == null) - { - propertyInstance = Activator.CreateInstance(propertyType, null); - } - else - { - var value = In.DataRecord.GetValue((int) objectMapping.Value.Column); - - if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof (Nullable<>)) - { - propertyType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); - } - - if (propertyType.IsEnum) - { - value = Enum.Parse(propertyType, value.ToString()); - } - - propertyInstance = Convert.ChangeType(value, propertyType); - } - - if (instanceType.IsArray) - { - ((Array) instance).SetValue(propertyInstance, int.Parse(objectMapping.Key)); - } - else - { - propertyInfo.SetValue(instance, propertyInstance, null); - } - - - } - Parse(objectMapping.Value.Children, propertyInstance); - } - } - - public override void Execute() - { - var instance = (T) Activator.CreateInstance(typeof (T)); - Parse(In.Map, instance); - Out.Object = instance; - } - } -} \ No newline at end of file diff --git a/app/Simpler/Simpler.csproj b/app/Simpler/Simpler.csproj index 0cf910f..779ef95 100644 --- a/app/Simpler/Simpler.csproj +++ b/app/Simpler/Simpler.csproj @@ -52,11 +52,18 @@ + + + + + + + + - - + From 3bc51b1710b6d2112be133994a63f418e77c3c1e Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 18 Mar 2014 12:06:21 -0800 Subject: [PATCH 3/4] Updated naming conventions. Added more unit tests. --- .../Core/Mocks/MockComplexPerson.cs | 25 --- app/Simpler.Tests/Core/Mocks/MockPerson.cs | 4 +- app/Simpler.Tests/Core/Mocks/MockPet.cs | 8 + app/Simpler.Tests/Core/Mocks/MockVehicle.cs | 8 + .../PropertyParseTreeArrayChildNodeTest.cs | 63 +++++++ .../PropertyParseTreeArrayNodeTest.cs | 66 +++++++ .../PropertyParseTreeDynamicChildNodeTest.cs | 65 +++++++ .../PropertyParseTreeDynamicNodeTest.cs | 68 ++++++++ .../PropertyParseTreeObjectNodeTest.cs | 65 +++++++ .../PropertyParseTreeRootNodeTest.cs | 26 +++ .../Data/Tasks/BuildComplexTypedTest.cs | 64 ------- .../Data/Tasks/BuildDynamicTest.cs | 70 -------- .../Data/Tasks/BuildMappingsTest.cs | 115 ------------ .../Data/Tasks/BuildObjectTest.cs | 163 ++++++++++++++++++ .../Data/Tasks/BuildObjectsTest.cs | 50 ++---- .../Data/Tasks/BuildPropertyParseTreeTest.cs | 131 ++++++++++++++ .../Data/Tasks/BuildTypedTest.cs | 38 ++-- app/Simpler.Tests/Data/Tasks/FetchTest.cs | 76 -------- .../Data/Tasks/ParseColumnTest.cs | 137 +++++++++++++++ app/Simpler.Tests/Simpler.Tests.csproj | 17 +- app/Simpler/Data/Mappings/ObjectMapping.cs | 58 ------- .../Data/Mappings/ObjectMappingArrayNode.cs | 17 -- .../Data/Mappings/ObjectMappingDynamicNode.cs | 18 -- .../Data/Mappings/ObjectMappingNode.cs | 19 -- .../Data/Mappings/ObjectMappingRoot.cs | 14 -- .../PropertyParseTree/PropertyParseTree.cs | 70 ++++++++ .../PropertyParseTreeArrayChildNode.cs} | 16 +- .../PropertyParseTreeArrayNode.cs | 15 ++ .../PropertyParseTreeDynamicChildNode.cs} | 15 +- .../PropertyParseTreeDynamicNode.cs | 15 ++ .../PropertyParseTreeNode.cs | 32 ++++ .../PropertyParseTreeObjectNode.cs} | 18 +- .../PropertyParseTreeRootNode.cs | 21 +++ app/Simpler/Data/Tasks/BuildMappings.cs | 99 ----------- app/Simpler/Data/Tasks/BuildObject.cs | 48 +++--- app/Simpler/Data/Tasks/BuildObjects.cs | 19 +- .../Data/Tasks/BuildPropertyParseTree.cs | 40 +++++ app/Simpler/Data/Tasks/FindColumns.cs | 33 ++++ app/Simpler/Data/Tasks/ParseColumn.cs | 105 +++++++++++ app/Simpler/Simpler.csproj | 20 ++- 40 files changed, 1246 insertions(+), 705 deletions(-) delete mode 100644 app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs create mode 100644 app/Simpler.Tests/Core/Mocks/MockPet.cs create mode 100644 app/Simpler.Tests/Core/Mocks/MockVehicle.cs create mode 100644 app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs create mode 100644 app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayNodeTest.cs create mode 100644 app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs create mode 100644 app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs create mode 100644 app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeObjectNodeTest.cs create mode 100644 app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs delete mode 100644 app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs delete mode 100644 app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs delete mode 100644 app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs create mode 100644 app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs create mode 100644 app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs delete mode 100644 app/Simpler.Tests/Data/Tasks/FetchTest.cs create mode 100644 app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs delete mode 100644 app/Simpler/Data/Mappings/ObjectMapping.cs delete mode 100644 app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs delete mode 100644 app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs delete mode 100644 app/Simpler/Data/Mappings/ObjectMappingNode.cs delete mode 100644 app/Simpler/Data/Mappings/ObjectMappingRoot.cs create mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs rename app/Simpler/Data/{Mappings/ObjectMappingArrayChildNode.cs => PropertyParseTree/PropertyParseTreeArrayChildNode.cs} (73%) create mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayNode.cs rename app/Simpler/Data/{Mappings/ObjectMappingDynamicChildNode.cs => PropertyParseTree/PropertyParseTreeDynamicChildNode.cs} (61%) create mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicNode.cs create mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs rename app/Simpler/Data/{Mappings/ObjectMappingObjectNode.cs => PropertyParseTree/PropertyParseTreeObjectNode.cs} (79%) create mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs delete mode 100644 app/Simpler/Data/Tasks/BuildMappings.cs create mode 100644 app/Simpler/Data/Tasks/BuildPropertyParseTree.cs create mode 100644 app/Simpler/Data/Tasks/FindColumns.cs create mode 100644 app/Simpler/Data/Tasks/ParseColumn.cs diff --git a/app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs b/app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs deleted file mode 100644 index e68e88d..0000000 --- a/app/Simpler.Tests/Core/Mocks/MockComplexPerson.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; - -namespace Simpler.Tests.Core.Mocks -{ - public class MockComplexPerson - { - public string Name { get; set; } - public int? Age { get; set; } - public MockPet Pet { get; set; } - public MockVechile[] Vechiles { get; set; } - public MockEnum MockEnum { get; set; } - } - - public class MockPet - { - public string Name { get; set; } - public int? Age { get; set; } - } - - public class MockVechile - { - public string Make { get; set; } - public string Model { get; set; } - } -} diff --git a/app/Simpler.Tests/Core/Mocks/MockPerson.cs b/app/Simpler.Tests/Core/Mocks/MockPerson.cs index cc6fa17..a97ec1d 100644 --- a/app/Simpler.Tests/Core/Mocks/MockPerson.cs +++ b/app/Simpler.Tests/Core/Mocks/MockPerson.cs @@ -4,7 +4,9 @@ public class MockPerson { public string Name { get; set; } public int? Age { get; set; } + public MockPet Pet { get; set; } + public MockVehicle[] Vehicles { get; set; } public MockEnum MockEnum { get; set; } - public dynamic Stuff { get; set; } + public dynamic Other { get; set; } } } diff --git a/app/Simpler.Tests/Core/Mocks/MockPet.cs b/app/Simpler.Tests/Core/Mocks/MockPet.cs new file mode 100644 index 0000000..dd441bd --- /dev/null +++ b/app/Simpler.Tests/Core/Mocks/MockPet.cs @@ -0,0 +1,8 @@ +namespace Simpler.Tests.Core.Mocks +{ + public class MockPet + { + public string Name { get; set; } + public int? Age { get; set; } + } +} diff --git a/app/Simpler.Tests/Core/Mocks/MockVehicle.cs b/app/Simpler.Tests/Core/Mocks/MockVehicle.cs new file mode 100644 index 0000000..c2167a3 --- /dev/null +++ b/app/Simpler.Tests/Core/Mocks/MockVehicle.cs @@ -0,0 +1,8 @@ +namespace Simpler.Tests.Core.Mocks +{ + public class MockVehicle + { + public string Make { get; set; } + public string Model { get; set; } + } +} diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs new file mode 100644 index 0000000..5f9a218 --- /dev/null +++ b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs @@ -0,0 +1,63 @@ +using NUnit.Framework; +using Simpler.Data.PropertyParseTree; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.PropertyParseTree +{ + [TestFixture] + public class PropertyParseTreeArrayChildNodeTest + { + [Test] + public void should_set_value_for_array_child_node() + { + //arrange + var array = new MockPet[1]; + var propertyParseTreeArrayChildNode = new PropertyParseTreeArrayChildNode + { + Name = "0", + PropertyType = typeof (MockPet) + }; + + // Act + propertyParseTreeArrayChildNode.SetValue(array, null); + + // Assert + Assert.That(array[0], Is.Not.Null); + } + + [Test] + public void should_get_value_for_array_child_node() + { + //arrange + var array = new [] { new MockPet {Name = "Doug"} }; + var propertyParseTreeArrayChildNode = new PropertyParseTreeArrayChildNode + { + Name = "0", + PropertyType = typeof (MockPet) + }; + + // Act + var value = (MockPet)propertyParseTreeArrayChildNode.GetValue(array); + + // Assert + Assert.That(value, Is.Not.Null); + Assert.That(value.Name, Is.EqualTo("Doug")); + } + + [Test] + public void should_create_object_for_array_child_node() + { + //arrange + var propertyParseTreeArrayChildNode = new PropertyParseTreeArrayChildNode + { + PropertyType = typeof (MockPet) + }; + + // Act + var value = propertyParseTreeArrayChildNode.CreateObject(null); + + // Assert + Assert.That(value, Is.TypeOf(typeof(MockPet))); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayNodeTest.cs b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayNodeTest.cs new file mode 100644 index 0000000..8da7d3e --- /dev/null +++ b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayNodeTest.cs @@ -0,0 +1,66 @@ +using NUnit.Framework; +using Simpler.Data.PropertyParseTree; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.PropertyParseTree +{ + [TestFixture] + public class PropertyParseTreeArrayNodeTest + { + [Test] + public void should_set_value_for_array_node() + { + //arrange + var obj = new MockPerson(); + var propertyParseTreeArrayNode = new PropertyParseTreeArrayNode + { + Name = "Vehicles", + PropertyInfo = obj.GetType().GetProperty("Vehicles"), + PropertyType = typeof (MockVehicle[]) + }; + + // Act + propertyParseTreeArrayNode.SetValue(obj, null); + + // Assert + Assert.That(obj.Vehicles, Is.Not.Null); + } + + [Test] + public void should_get_value_for_array() + { + //arrange + var obj = new MockPerson {Vehicles = new MockVehicle[0]}; + var propertyParseTreeArrayNode = new PropertyParseTreeArrayNode + { + Name = "Vehicles", + PropertyInfo = obj.GetType().GetProperty("Vehicles"), + PropertyType = typeof (MockVehicle[]) + }; + + // Act + var value = propertyParseTreeArrayNode.GetValue(obj); + + // Assert + Assert.That(value, Is.Not.Null); + Assert.That(value, Is.EqualTo(obj.Vehicles)); + } + + [Test] + public void should_create_object_for_array() + { + //arrange + var propertyParseTreeArrayNode = new PropertyParseTreeArrayNode + { + PropertyType = typeof (MockVehicle[]) + }; + + // Act + var value = propertyParseTreeArrayNode.CreateObject(null); + + // Assert + Assert.That(value, Is.Not.Null); + Assert.That(value, Is.TypeOf(typeof(MockVehicle[]))); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs new file mode 100644 index 0000000..045f6de --- /dev/null +++ b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs @@ -0,0 +1,65 @@ +using System.Dynamic; +using NUnit.Framework; +using Simpler.Data.PropertyParseTree; + +namespace Simpler.Tests.Data.PropertyParseTree +{ + [TestFixture] + public class PropertyParseTreeDynamicChildNodeTest + { + [Test] + public void should_set_value_for_dynamic_node() + { + //arrange + dynamic obj = new ExpandoObject(); + var propertyParseTreeArrayNode = new PropertyParseTreeDynamicChildNode() + { + Name = "City", + PropertyType = typeof (string) + }; + + // Act + propertyParseTreeArrayNode.SetValue(obj, "Anchorage"); + + // Assert + Assert.That(obj.City, Is.EqualTo("Anchorage")); + } + + [Test] + public void should_get_value_for_dynamic_node() + { + //arrange + dynamic obj = new ExpandoObject(); + obj.City = "Anchorage"; + var propertyParseTreeArrayNode = new PropertyParseTreeDynamicChildNode() + { + Name = "City", + PropertyType = typeof(string) + }; + + // Act + var value = propertyParseTreeArrayNode.GetValue(obj); + + // Assert + Assert.That(value, Is.Not.Null); + Assert.That(value, Is.EqualTo(obj.City)); + } + + [Test] + public void should_create_object_for_dynamic_node() + { + //arrange + var propertyParseTreeArrayNode = new PropertyParseTreeDynamicChildNode() + { + Name = "City", + PropertyType = typeof(string) + }; + + // Act + var value = propertyParseTreeArrayNode.CreateObject("Anchorage"); + + // Assert + Assert.That(value, Is.EqualTo("Anchorage")); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs new file mode 100644 index 0000000..b4d870b --- /dev/null +++ b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs @@ -0,0 +1,68 @@ +using System.Dynamic; +using NUnit.Framework; +using Simpler.Data.PropertyParseTree; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.PropertyParseTree +{ + [TestFixture] + public class PropertyParseTreeDynamicNodeTest + { + [Test] + public void should_set_value_for_dynamic_node() + { + //arrange + var obj = new MockPerson(); + var propertyParseTreeArrayNode = new PropertyParseTreeDynamicNode() + { + Name = "Other", + PropertyInfo = obj.GetType().GetProperty("Other"), + PropertyType = typeof (object) + }; + + // Act + propertyParseTreeArrayNode.SetValue(obj, null); + + // Assert + Assert.That(obj.Other, Is.Not.Null); + } + + [Test] + public void should_get_value_for_dynamic_node() + { + //arrange + var obj = new MockPerson{Other = new ExpandoObject()}; + var propertyParseTreeArrayNode = new PropertyParseTreeDynamicNode() + { + Name = "Other", + PropertyInfo = obj.GetType().GetProperty("Other"), + PropertyType = typeof(object) + }; + + // Act + var value = propertyParseTreeArrayNode.GetValue(obj); + + // Assert + Assert.That(value, Is.Not.Null); + Assert.That(value, Is.EqualTo(obj.Other)); + } + + [Test] + public void should_create_object_for_dynamic_node() + { + //arrange + var propertyParseTreeArrayNode = new PropertyParseTreeDynamicNode() + { + Name = "Other", + PropertyType = typeof(object) + }; + + // Act + var value = propertyParseTreeArrayNode.CreateObject(); + + // Assert + Assert.That(value, Is.Not.Null); + Assert.That(value, Is.TypeOf(typeof(ExpandoObject))); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeObjectNodeTest.cs b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeObjectNodeTest.cs new file mode 100644 index 0000000..ae79b58 --- /dev/null +++ b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeObjectNodeTest.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; +using Simpler.Data.PropertyParseTree; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.PropertyParseTree +{ + [TestFixture] + public class PropertyParseTreeObjectNodeTest + { + [Test] + public void should_set_value_for_object_node() + { + //arrange + var obj = new MockPerson(); + var propertyParseTreeObjectNode = new PropertyParseTreeObjectNode() + { + Name = "Name", + PropertyInfo = obj.GetType().GetProperty("Name"), + PropertyType = typeof(string) + }; + + // Act + propertyParseTreeObjectNode.SetValue(obj, "Richard"); + + // Assert + Assert.That(obj.Name, Is.EqualTo("Richard")); + } + + [Test] + public void should_get_value_for_object_node() + { + //arrange + var obj = new MockPerson{ Name = "Richard" }; + var propertyParseTreeObjectNode = new PropertyParseTreeObjectNode() + { + Name = "Name", + PropertyInfo = obj.GetType().GetProperty("Name"), + PropertyType = typeof(string) + }; + + // Act + var value = propertyParseTreeObjectNode.GetValue(obj); + + // Assert + Assert.That(value, Is.EqualTo("Richard")); + } + + [Test] + public void should_create_object_for_object_node() + { + //arrange + var propertyParseTreeObjectNode = new PropertyParseTreeObjectNode() + { + Name = "Name", + PropertyType = typeof(string) + }; + + // Act + var value = propertyParseTreeObjectNode.CreateObject("Richard"); + + // Assert + Assert.That(value, Is.EqualTo("Richard")); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs new file mode 100644 index 0000000..847da55 --- /dev/null +++ b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs @@ -0,0 +1,26 @@ +using NUnit.Framework; +using Simpler.Data.PropertyParseTree; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.PropertyParseTree +{ + [TestFixture] + public class PropertyParseTreeRootNodeTest + { + [Test] + public void should_create_object_for_object_node() + { + //arrange + var propertyParseTreeRootNode = new PropertyParseTreeRootNode() + { + PropertyType = typeof(MockPerson) + }; + + // Act + var value = propertyParseTreeRootNode.CreateObject(); + + // Assert + Assert.That(value, Is.TypeOf(typeof(MockPerson))); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs b/app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs deleted file mode 100644 index cce9990..0000000 --- a/app/Simpler.Tests/Data/Tasks/BuildComplexTypedTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Simpler.Data.Tasks; -using Moq; -using System.Data; -using Simpler.Tests.Core.Mocks; - -namespace Simpler.Tests.Data.Tasks -{ - [TestFixture] - public class BuildComplexTypedTest - { - static IDataReader SetupReader() - { - var table = new DataTable(); - table.Columns.Add("Name", Type.GetType("System.String")); - table.Columns.Add("Age", Type.GetType("System.Int32")); - table.Columns.Add("PetName", Type.GetType("System.String")); - table.Columns.Add("PetAge", Type.GetType("System.Int32")); - table.Columns.Add("Vechiles0Make", Type.GetType("System.String")); - table.Columns.Add("Vechiles0Model", Type.GetType("System.String")); - table.Columns.Add("Vechiles1Make", Type.GetType("System.String")); - table.Columns.Add("Vechiles1Model", Type.GetType("System.String")); - table.Rows.Add(new object[] { "John Doe", "21", "Fiddo", "2", "Dodge", "Durango", "Dodge", "Durango" }); - table.Rows.Add(new object[] { "Jane Doe", "19", "Spot", "3", "Jeep", "Wrangler", "Jeep", "Wrangler" }); - return table.CreateDataReader(); - } - - [Test] - public void should_return_an_object_for_each_record_returned_by_the_select_command() - { - // Arrange - var task = Task.New>(); - task.In.Reader = SetupReader(); - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.Objects.Count(), Is.EqualTo(2)); - Assert.That(task.Out.Objects[0].Name, Is.EqualTo("John Doe")); - Assert.That(task.Out.Objects[1].Name, Is.EqualTo("Jane Doe")); - } - - [Test] - public void should_return_nested_complex_objects() - { - // Arrange - var task = Task.New>(); - task.In.Reader = SetupReader(); - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.Objects.Count(), Is.EqualTo(2)); - Assert.That(task.Out.Objects[0].Pet.Name, Is.EqualTo("Fiddo")); - Assert.That(task.Out.Objects[1].Pet.Name, Is.EqualTo("Spot")); - Assert.That(task.Out.Objects[0].Vechiles[0].Make, Is.EqualTo("Dodge")); - Assert.That(task.Out.Objects[1].Vechiles[1].Make, Is.EqualTo("Jeep")); - } - } -} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs b/app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs deleted file mode 100644 index 3893e94..0000000 --- a/app/Simpler.Tests/Data/Tasks/BuildDynamicTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Collections.Generic; -using NUnit.Framework; -using Simpler.Data.Tasks; -using System.Data; -using Moq; -using Simpler.Tests.Core.Mocks; - -namespace Simpler.Tests.Data.Tasks -{ - [TestFixture] - public class BuildDynamicTest - { - [Test] - public void should_populate_dynamic_object_using_all_columns_in_the_data_record() - { - // Arrange - var task = Task.New>(); - - var mockDataRecord = new Mock(); - mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(2); - mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("Name"); - mockDataRecord.Setup(dataRecord => dataRecord.GetValue(0)).Returns("John Doe"); - mockDataRecord.Setup(dataRecord => dataRecord.GetName(1)).Returns("Age"); - mockDataRecord.Setup(dataRecord => dataRecord.GetValue(1)).Returns(21); - task.In.DataRecord = mockDataRecord.Object; - var mapTask = Task.New(); - mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "Age", 1 } }; - mapTask.Execute(); - task.In.ObjectMapping = mapTask.Out.ObjectMapping; - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.Object.Name, Is.EqualTo("John Doe")); - Assert.That(task.Out.Object.Age, Is.EqualTo(21)); - } - - [Test] - public void should_populate_nested_dynamic_objects_using_all_columns_in_the_data_record() - { - // Arrange - var task = Task.New>(); - - var mockDataRecord = new Mock(); - mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(3); - mockDataRecord.Setup(dataRecord => dataRecord.GetName(0)).Returns("Name"); - mockDataRecord.Setup(dataRecord => dataRecord.GetValue(0)).Returns("John Doe"); - mockDataRecord.Setup(dataRecord => dataRecord.GetName(1)).Returns("Age"); - mockDataRecord.Setup(dataRecord => dataRecord.GetValue(1)).Returns(21); - mockDataRecord.Setup(dataRecord => dataRecord.GetName(2)).Returns("StuffCake"); - mockDataRecord.Setup(dataRecord => dataRecord.GetValue(2)).Returns("TheCakeIsALie"); - task.In.DataRecord = mockDataRecord.Object; - var mapTask = Task.New(); - mapTask.In.RootType = typeof (MockPerson); - mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "Age", 1 }, {"StuffCake", 2 } }; - mapTask.Execute(); - task.In.ObjectMapping = mapTask.Out.ObjectMapping; - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.Object.Stuff.Cake, Is.EqualTo("TheCakeIsALie")); - Assert.That(task.Out.Object.Name, Is.EqualTo("John Doe")); - Assert.That(task.Out.Object.Age, Is.EqualTo(21)); - } - - } -} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs b/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs deleted file mode 100644 index 3dec108..0000000 --- a/app/Simpler.Tests/Data/Tasks/BuildMappingsTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using Simpler.Data; -using Simpler.Data.Tasks; -using Simpler.Tests.Core.Mocks; - -namespace Simpler.Tests.Data.Tasks -{ - [TestFixture] - public class BuildMappingsTest - { - [Test] - public void should_return_an_object_for_each_record_returned_by_the_select_command() - { - // Arrange - var task = Task.New(); - task.In.ColumnNames = new Dictionary - { - { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, - {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} - }; - task.In.RootType = typeof (MockComplexPerson); - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.ObjectMapping.Count(), Is.EqualTo(4)); - Assert.That(task.Out.ObjectMapping["Pet"].Children.Count(), Is.EqualTo(2)); - Assert.That(task.Out.ObjectMapping["Vechiles"].Children.Count(), Is.EqualTo(2)); - Assert.That(task.Out.ObjectMapping["Vechiles"]["0"].Children.Count(), Is.EqualTo(2)); - } - - [Test] - public void should_return_an_type_for_each_record_returned_by_the_select_command() - { - // Arrange - var task = Task.New(); - task.In.ColumnNames = new Dictionary - { - { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, - {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} - }; - task.In.RootType = typeof(MockComplexPerson); - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.ObjectMapping["Name"].PropertyType, Is.EqualTo(typeof(string))); - Assert.That(task.Out.ObjectMapping["Age"].PropertyType, Is.EqualTo(typeof(int?))); - Assert.That(task.Out.ObjectMapping["Pet"]["Name"].PropertyType, Is.EqualTo(typeof(string))); - Assert.That(task.Out.ObjectMapping["Pet"]["Age"].PropertyType, Is.EqualTo(typeof(int?))); - Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Model"].PropertyType, Is.EqualTo(typeof(string))); - Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Make"].PropertyType, Is.EqualTo(typeof(string))); - Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Model"].PropertyType, Is.EqualTo(typeof(string))); - Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Make"].PropertyType, Is.EqualTo(typeof(string))); - - Assert.That(task.Out.ObjectMapping.PropertyType, Is.EqualTo(typeof(MockComplexPerson))); - Assert.That(task.Out.ObjectMapping["Pet"].PropertyType, Is.EqualTo(typeof(MockPet))); - Assert.That(task.Out.ObjectMapping["Vechiles"]["0"].PropertyType, Is.EqualTo(typeof(MockVechile))); - Assert.That(task.Out.ObjectMapping["Vechiles"]["1"].PropertyType, Is.EqualTo(typeof(MockVechile))); - } - - [Test] - public void should_return_contain_column_indexs_for_each_record_returned_by_the_select_command() - { - // Arrange - var task = Task.New(); - task.In.ColumnNames = new Dictionary - { - { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, - {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} - }; - task.In.RootType = typeof(MockComplexPerson); - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.ObjectMapping["Name"].ColumnIndex, Is.EqualTo(0)); - Assert.That(task.Out.ObjectMapping["Age"].ColumnIndex, Is.EqualTo(1)); - Assert.That(task.Out.ObjectMapping["Pet"]["Name"].ColumnIndex, Is.EqualTo(2)); - Assert.That(task.Out.ObjectMapping["Pet"]["Age"].ColumnIndex, Is.EqualTo(3)); - Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Make"].ColumnIndex, Is.EqualTo(4)); - Assert.That(task.Out.ObjectMapping["Vechiles"]["0"]["Model"].ColumnIndex, Is.EqualTo(5)); - Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Make"].ColumnIndex, Is.EqualTo(6)); - Assert.That(task.Out.ObjectMapping["Vechiles"]["1"]["Model"].ColumnIndex, Is.EqualTo(7)); - - Assert.That(task.Out.ObjectMapping["Pet"].ColumnIndex, Is.Null); - Assert.That(task.Out.ObjectMapping["Vechiles"].ColumnIndex, Is.Null); - Assert.That(task.Out.ObjectMapping["Vechiles"]["0"].ColumnIndex, Is.Null); - Assert.That(task.Out.ObjectMapping["Vechiles"]["1"].ColumnIndex, Is.Null); - } - - [Test] - public void should_return_property_info_for_each_record_returned_by_the_select_command() - { - // Arrange - var task = Task.New(); - task.In.ColumnNames = new Dictionary - { - { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vechiles0Make", 4}, - {"Vechiles0Model", 5}, {"Vechiles1Make", 6}, {"Vechiles1Model", 7} - }; - task.In.RootType = typeof(MockComplexPerson); - // Act - task.Execute(); - - // Assert - Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Name"]).PropertyInfo, Is.EqualTo(typeof(MockComplexPerson).GetProperty("Name"))); - Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Age"]).PropertyInfo, Is.EqualTo(typeof(MockComplexPerson).GetProperty("Age"))); - Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Pet"]["Age"]).PropertyInfo, Is.EqualTo(typeof(MockPet).GetProperty("Age"))); - Assert.That(((ObjectMappingObjectNode)task.Out.ObjectMapping["Pet"]["Name"]).PropertyInfo, Is.EqualTo(typeof(MockPet).GetProperty("Name"))); - } - } -} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs b/app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs new file mode 100644 index 0000000..91166d9 --- /dev/null +++ b/app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using NUnit.Framework; +using Simpler.Data.Tasks; +using Moq; +using System.Data; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.Tasks +{ + [TestFixture] + public class BuildObjectTest + { + public static Simpler.Data.PropertyParseTree.PropertyParseTree get_parse_tree_from_data_table(DataTable table, Type type) + { + var buildPropertyParseTree = new BuildPropertyParseTree(); + buildPropertyParseTree.In.Columns = table.Columns.Cast().Select((x, i) => new { x.ColumnName, i }).ToDictionary(x => x.ColumnName, x => x.i); + buildPropertyParseTree.In.InitialType = type; + buildPropertyParseTree.Execute(); + return buildPropertyParseTree.Out.PropertyParseTree; + } + + [Test] + public void should_create_root_typed_object() + { + //Arrange + var table = new DataTable(); + table.Columns.Add("Name", Type.GetType("System.String")); + table.Rows.Add(new object[] { "John Doe" }); + + var dataReader = table.CreateDataReader(); + dataReader.Read(); + + var task = Task.New>(); + task.In.DataRecord = dataReader; + task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + + //Act + task.Execute(); + + //Assert + Assert.That(task.Out.Object, Is.Not.Null); + Assert.That(task.Out.Object, Is.TypeOf(typeof(MockPerson))); + } + + [Test] + public void should_create_root_dynamic_object() + { + //Arrange + var table = new DataTable(); + table.Columns.Add("Name", Type.GetType("System.String")); + table.Rows.Add(new object[] { "John Doe" }); + + var dataReader = table.CreateDataReader(); + dataReader.Read(); + + var task = Task.New>(); + task.In.DataRecord = dataReader; + task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(object)); + + //Act + task.Execute(); + + //Assert + Assert.That(task.Out.Object, Is.Not.Null); + Assert.That(task.Out.Object, Is.TypeOf(typeof(ExpandoObject))); + } + + [Test] + public void should_assign_simple_properties() + { + //Arrange + var table = new DataTable(); + table.Columns.Add("Name", Type.GetType("System.String")); + table.Rows.Add(new object[] { "John Doe" }); + + var dataReader = table.CreateDataReader(); + dataReader.Read(); + + var task = Task.New>(); + task.In.DataRecord = dataReader; + task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + + //Act + task.Execute(); + + //Assert + Assert.That(task.Out.Object, Is.Not.Null); + Assert.That(task.Out.Object.Name, Is.EqualTo("John Doe")); + } + + [Test] + public void should_assign_nested_properties() + { + //Arrange + var table = new DataTable(); + table.Columns.Add("PetName", Type.GetType("System.String")); + table.Rows.Add(new object[] { "Spot" }); + + var dataReader = table.CreateDataReader(); + dataReader.Read(); + + var task = Task.New>(); + task.In.DataRecord = dataReader; + task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + + //Act + task.Execute(); + + //Assert + Assert.That(task.Out.Object, Is.Not.Null); + Assert.That(task.Out.Object.Pet.Name, Is.EqualTo("Spot")); + } + + [Test] + public void should_assign_array_properties() + { + //Arrange + var table = new DataTable(); + table.Columns.Add("Vehicles0Make", Type.GetType("System.String")); + table.Rows.Add(new object[] { "Jeep" }); + + var dataReader = table.CreateDataReader(); + dataReader.Read(); + + var task = Task.New>(); + task.In.DataRecord = dataReader; + task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + + //Act + task.Execute(); + + //Assert + Assert.That(task.Out.Object, Is.Not.Null); + Assert.That(task.Out.Object.Vehicles[0].Make, Is.EqualTo("Jeep")); + } + + [Test] + public void should_assign_dynamic_properties() + { + //Arrange + var table = new DataTable(); + table.Columns.Add("OtherCity", Type.GetType("System.String")); + table.Rows.Add(new object[] { "Anchorage" }); + + var dataReader = table.CreateDataReader(); + dataReader.Read(); + + var task = Task.New>(); + task.In.DataRecord = dataReader; + task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + + //Act + task.Execute(); + + //Assert + Assert.That(task.Out.Object, Is.Not.Null); + Assert.That(task.Out.Object.Other.City, Is.EqualTo("Anchorage")); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs b/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs index 046f5e3..501558c 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildObjectsTest.cs @@ -22,11 +22,20 @@ static IDataReader SetupReader() } [Test] - public void should_return_an_object_for_each_record_returned_by_the_select_command() + public void should_return_an_object_for_each_record() { // Arrange var task = Task.New>(); - task.In.Reader = SetupReader(); + + var table = new DataTable(); + table.Columns.Add("Name", Type.GetType("System.String")); + table.Columns.Add("Age", Type.GetType("System.Int32")); + table.Columns.Add("PetName", Type.GetType("System.String")); + table.Columns.Add("Vehicles0Make", Type.GetType("System.String")); + table.Rows.Add(new object[] { "John Doe", "21", "Doug", "Dodge" }); + table.Rows.Add(new object[] { "Jane Doe", "19", "Spot", "Jeep" }); + + task.In.Reader = table.CreateDataReader(); // Act task.Execute(); @@ -34,39 +43,14 @@ public void should_return_an_object_for_each_record_returned_by_the_select_comma // Assert Assert.That(task.Out.Objects.Count(), Is.EqualTo(2)); Assert.That(task.Out.Objects[0].Name, Is.EqualTo("John Doe")); + Assert.That(task.Out.Objects[0].Age, Is.EqualTo(21)); + Assert.That(task.Out.Objects[0].Pet.Name, Is.EqualTo("Doug")); + Assert.That(task.Out.Objects[0].Vehicles[0].Make, Is.EqualTo("Dodge")); Assert.That(task.Out.Objects[1].Name, Is.EqualTo("Jane Doe")); + Assert.That(task.Out.Objects[1].Age, Is.EqualTo(19)); + Assert.That(task.Out.Objects[1].Pet.Name, Is.EqualTo("Spot")); + Assert.That(task.Out.Objects[1].Vehicles[0].Make, Is.EqualTo("Jeep")); } - [Test] - public void should_build_typed_objects_if_given_strong_type() - { - // Arrange - var task = Task.New>(); - task.In.Reader = SetupReader(); - - task.BuildObject = Fake.Task>(bt => bt.Out.Object = new MockPerson()); - - // Act - task.Execute(); - - // Assert - Assert.That(task.BuildObject.Stats.ExecuteCount, Is.GreaterThan(0)); - } - - [Test] - public void should_build_dynamic_objects_if_given_dynamic_type() - { - // Arrange - var task = Task.New>(); - task.In.Reader = SetupReader(); - - task.BuildObject = Fake.Task>(bt => bt.Out.Object = new MockPerson()); - - // Act - task.Execute(); - - // Assert - Assert.That(task.BuildObject.Stats.ExecuteCount, Is.GreaterThan(0)); - } } } \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs b/app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs new file mode 100644 index 0000000..534fbc5 --- /dev/null +++ b/app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs @@ -0,0 +1,131 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Simpler.Data.Tasks; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.Tasks +{ + [TestFixture] + public class BuildPropertyParseTreeTest + { + [Test] + public void should_build_a_tree_for_primitives() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + { "Name", 0 }, { "Age", 1 } + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Age"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree_for_nested_objects() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + {"PetName", 0}, {"PetAge", 1} + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyParseTree["Pet"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Pet"]["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Pet"]["Age"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree_for_arrays() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + {"Vehicles0Make", 0}, {"Vehicles0Model", 1}, {"Vehicles1Make", 2}, {"Vehicles1Model", 3} + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyParseTree["Vehicles"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Model"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Model"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree_for_dynamics() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + {"OtherCity", 0}, {"OtherState", 1} + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyParseTree["Other"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Other"]["State"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Other"]["City"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vehicles0Make", 4}, + {"Vehicles0Model", 5}, {"Vehicles1Make", 6}, {"Vehicles1Model", 7}, {"OtherCity", 8} + }; + task.In.InitialType = typeof(MockPerson); + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(5)); + Assert.That(task.Out.PropertyParseTree["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Age"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Pet"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Pet"]["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Pet"]["Age"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Vehicles"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Model"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"].Nodes.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Model"], Is.Not.Null); + Assert.That(task.Out.PropertyParseTree["Other"].Nodes.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyParseTree["Other"]["City"], Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs b/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs index daf827c..3a4c1e7 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs @@ -15,11 +15,11 @@ public void should_build_an_instance_of_given_type() { // Arrange var task = Task.New>(); - var mapTask = Task.New(); - mapTask.In.RootType = typeof(MockPerson); - mapTask.In.ColumnNames = new Dictionary { { "Name", 0 } }; + var mapTask = Task.New(); + mapTask.In.InitialType = typeof(MockPerson); + mapTask.In.Columns = new Dictionary { { "Name", 0 } }; mapTask.Execute(); - task.In.ObjectMapping = mapTask.Out.ObjectMapping; + task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; var mockDataRecord = new Mock(); task.In.DataRecord = mockDataRecord.Object; @@ -36,11 +36,11 @@ public void should_populate_typed_object_using_all_columns_in_the_data_record() { // Arrange var task = Task.New>(); - var mapTask = Task.New(); - mapTask.In.RootType = typeof(MockPerson); - mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "Age", 1 } }; + var mapTask = Task.New(); + mapTask.In.InitialType = typeof(MockPerson); + mapTask.In.Columns = new Dictionary { { "Name", 0 }, { "Age", 1 } }; mapTask.Execute(); - task.In.ObjectMapping = mapTask.Out.ObjectMapping; + task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(2); @@ -62,9 +62,9 @@ public void should_populate_typed_object_using_all_columns_in_the_data_record() public void should_throw_exception_if_a_data_record_column_is_not_a_property_of_the_object_class() { // Arrange - var mapTask = Task.New(); - mapTask.In.RootType = typeof(MockPerson); - mapTask.In.ColumnNames = new Dictionary { { "Name", 0 }, { "TheCakeIsALie", 1 } }; + var mapTask = Task.New(); + mapTask.In.InitialType = typeof(MockPerson); + mapTask.In.Columns = new Dictionary { { "Name", 0 }, { "TheCakeIsALie", 1 } }; // Act & Assert Assert.Throws(typeof(CheckException), mapTask.Execute); @@ -75,11 +75,11 @@ public void should_allow_object_to_have_properties_that_dont_have_matching_colum { // Arrange var task = Task.New>(); - var mapTask = Task.New(); - mapTask.In.RootType = typeof(MockPerson); - mapTask.In.ColumnNames = new Dictionary{ { "Name", 0 } }; + var mapTask = Task.New(); + mapTask.In.InitialType = typeof(MockPerson); + mapTask.In.Columns = new Dictionary { { "Name", 0 } }; mapTask.Execute(); - task.In.ObjectMapping = mapTask.Out.ObjectMapping; + task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(1); @@ -101,11 +101,11 @@ public void should_build_enum_properties() { // Arrange var task = Task.New>(); - var mapTask = Task.New(); - mapTask.In.RootType = typeof(MockPerson); - mapTask.In.ColumnNames = new Dictionary { { "MockEnum", 0 } }; + var mapTask = Task.New(); + mapTask.In.InitialType = typeof(MockPerson); + mapTask.In.Columns = new Dictionary { { "MockEnum", 0 } }; mapTask.Execute(); - task.In.ObjectMapping = mapTask.Out.ObjectMapping; + task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(1); diff --git a/app/Simpler.Tests/Data/Tasks/FetchTest.cs b/app/Simpler.Tests/Data/Tasks/FetchTest.cs deleted file mode 100644 index e263008..0000000 --- a/app/Simpler.Tests/Data/Tasks/FetchTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Simpler.Data.Tasks; -using Moq; -using System.Data; -using Simpler.Tests.Core.Mocks; - -namespace Simpler.Tests.Data.Tasks -{ - [TestFixture] - public class FetchTest - { - static IDataReader SetupReader() - { - var table = new DataTable(); - table.Columns.Add("Name", Type.GetType("System.String")); - table.Columns.Add("Age", Type.GetType("System.Int32")); - table.Rows.Add(new object[] { "John Doe", "21" }); - table.Rows.Add(new object[] { "Jane Doe", "19" }); - return table.CreateDataReader(); - } - - [Test] - public void should_return_an_object_for_each_record_returned_by_the_select_command() - { - // Arrange - var task = Task.New>(); - task.In.Reader = SetupReader(); - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.ObjectsFetched.Count(), Is.EqualTo(2)); - Assert.That(task.Out.ObjectsFetched[0].Name, Is.EqualTo("John Doe")); - Assert.That(task.Out.ObjectsFetched[1].Name, Is.EqualTo("Jane Doe")); - } - - [Test] - public void should_build_typed_objects_if_given_strong_type() - { - // Arrange - var task = Task.New>(); - task.In.Reader = SetupReader(); - - task.BuildTyped = Fake.Task>(bt => bt.Out.Object = new MockObject()); - task.BuildDynamic = Fake.Task(); - - // Act - task.Execute(); - - // Assert - Assert.That(task.BuildTyped.Stats.ExecuteCount, Is.GreaterThan(0)); - Assert.That(task.BuildDynamic.Stats.ExecuteCount, Is.EqualTo(0)); - } - - [Test] - public void should_build_dynamic_objects_if_given_dynamic_type() - { - // Arrange - var task = Task.New>(); - task.In.Reader = SetupReader(); - - task.BuildTyped = Fake.Task>(); - task.BuildDynamic = Fake.Task(bd => bd.Out.Object = new MockObject()); - - // Act - task.Execute(); - - // Assert - Assert.That(task.BuildTyped.Stats.ExecuteCount, Is.EqualTo(0)); - Assert.That(task.BuildDynamic.Stats.ExecuteCount, Is.GreaterThan(0)); - } - } -} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs b/app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs new file mode 100644 index 0000000..3ca87cc --- /dev/null +++ b/app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs @@ -0,0 +1,137 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Simpler.Data; +using Simpler.Data.PropertyParseTree; +using Simpler.Data.Tasks; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.Tasks +{ + [TestFixture] + public class ParseColumnTest + { + [Test] + public void should_throw_exception_if_column_name_does_not_match_any_properties() + { + //Arrange + var task = Task.New(); + task.In.PropertyParseTree = new PropertyParseTreeRootNode + { + PropertyType = typeof(MockPerson) + }; + task.In.ColumnName = "TheCakeIsALie"; + + //Act + Assert.Throws(() => task.Execute()); + } + + [Test] + public void should_throw_exception_if_column_name_is_only_a_partial_match() + { + //Arrange + var task = Task.New(); + task.In.PropertyParseTree = new PropertyParseTreeRootNode + { + PropertyType = typeof(MockPerson) + }; + task.In.ColumnName = "PetTheCakeIsALie"; + + //Act + Assert.Throws(() => task.Execute()); + } + + [Test] + public void should_parse_column_names() + { + //Arrange + var task = Task.New(); + task.In.PropertyParseTree = new PropertyParseTreeRootNode + { + PropertyType = typeof (MockPerson) + }; + task.In.ColumnName = "Name"; + + //Act + task.Execute(); + + //Assert + Assert.That(task.In.PropertyParseTree["Name"], Is.Not.Null); + + //Check the node types + Assert.That(task.In.PropertyParseTree["Name"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); + } + + [Test] + public void should_parse_column_names_with_nested_objects() + { + //Arrange + var task = Task.New(); + task.In.PropertyParseTree = new PropertyParseTreeRootNode + { + PropertyType = typeof(MockPerson) + }; + task.In.ColumnName = "PetName"; + + //Act + task.Execute(); + + //Assert + Assert.That(task.In.PropertyParseTree["Pet"], Is.Not.Null); + Assert.That(task.In.PropertyParseTree["Pet"]["Name"], Is.Not.Null); + + //Check the node types + Assert.That(task.In.PropertyParseTree["Pet"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); + Assert.That(task.In.PropertyParseTree["Pet"]["Name"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); + } + + [Test] + public void should_parse_column_names_with_arrays() + { + //Arrange + var task = Task.New(); + task.In.PropertyParseTree = new PropertyParseTreeRootNode + { + PropertyType = typeof(MockPerson) + }; + task.In.ColumnName = "Vehicles0Make"; + + //Act + task.Execute(); + + //Assert + Assert.That(task.In.PropertyParseTree["Vehicles"], Is.Not.Null); + Assert.That(task.In.PropertyParseTree["Vehicles"]["0"], Is.Not.Null); + Assert.That(task.In.PropertyParseTree["Vehicles"]["0"]["Make"], Is.Not.Null); + + //Check the node types + Assert.That(task.In.PropertyParseTree["Vehicles"], Is.TypeOf(typeof(PropertyParseTreeArrayNode))); + Assert.That(task.In.PropertyParseTree["Vehicles"]["0"], Is.TypeOf(typeof(PropertyParseTreeArrayChildNode))); + Assert.That(task.In.PropertyParseTree["Vehicles"]["0"]["Make"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); + } + + [Test] + public void should_parse_column_names_that_are_dynamic() + { + //Arrange + var task = Task.New(); + task.In.PropertyParseTree = new PropertyParseTreeRootNode + { + PropertyType = typeof(MockPerson) + }; + task.In.ColumnName = "OtherTheLastManStanding"; + + //Act + task.Execute(); + + //Assert + Assert.That(task.In.PropertyParseTree["Other"], Is.Not.Null); + Assert.That(task.In.PropertyParseTree["Other"]["TheLastManStanding"], Is.Not.Null); + + //Check the node types + Assert.That(task.In.PropertyParseTree["Other"], Is.TypeOf(typeof(PropertyParseTreeDynamicNode))); + Assert.That(task.In.PropertyParseTree["Other"]["TheLastManStanding"], Is.TypeOf(typeof(PropertyParseTreeDynamicChildNode))); + } + + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Simpler.Tests.csproj b/app/Simpler.Tests/Simpler.Tests.csproj index a5d76f4..47abbbc 100644 --- a/app/Simpler.Tests/Simpler.Tests.csproj +++ b/app/Simpler.Tests/Simpler.Tests.csproj @@ -51,9 +51,10 @@ - - + + + @@ -66,17 +67,23 @@ + + + + + + - - - + + + diff --git a/app/Simpler/Data/Mappings/ObjectMapping.cs b/app/Simpler/Data/Mappings/ObjectMapping.cs deleted file mode 100644 index df62172..0000000 --- a/app/Simpler/Data/Mappings/ObjectMapping.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Simpler.Data -{ - public abstract class ObjectMapping : IEnumerable - { - public Type PropertyType { get; set; } - public List Children { get; set; } - - public ObjectMapping() - { - Children = new List(); - } - - public bool ContainsColumn(string columnName) - { - return Children.Any(x => x.Name == columnName); - } - - public int Count() - { - return Children.Count; - } - - public IEnumerator GetEnumerator() - { - return Children.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public ObjectMappingNode this[string index] - { - get { return Children.FirstOrDefault(x => x.Name == index); } - set - { - var listIndex = Children.FindIndex(x => x.Name == index); - value.Name = index; - if (listIndex == -1) - { - Children.Add(value); - } - else - { - Children[listIndex] = value; - } - } - } - } -} diff --git a/app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs b/app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs deleted file mode 100644 index ad76084..0000000 --- a/app/Simpler/Data/Mappings/ObjectMappingArrayNode.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Simpler.Data -{ - public class ObjectMappingArrayNode : ObjectMappingObjectNode - { - public override object CreateInstance(object value = null) - { - return Activator.CreateInstance(PropertyType, new object[] { Children.Count }); - } - } -} diff --git a/app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs b/app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs deleted file mode 100644 index f8930be..0000000 --- a/app/Simpler/Data/Mappings/ObjectMappingDynamicNode.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Simpler.Data -{ - public class ObjectMappingDynamicNode : ObjectMappingObjectNode - { - public override object CreateInstance(object value = null) - { - return new ExpandoObject(); - } - } -} diff --git a/app/Simpler/Data/Mappings/ObjectMappingNode.cs b/app/Simpler/Data/Mappings/ObjectMappingNode.cs deleted file mode 100644 index 81b3e60..0000000 --- a/app/Simpler/Data/Mappings/ObjectMappingNode.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Simpler.Data -{ - public abstract class ObjectMappingNode : ObjectMapping - { - public string Name { get; set; } - public int? ColumnIndex { get; set; } - - public abstract object CreateInstance(object value = null); - public abstract void SetValue(object instance, object value); - public abstract object GetValue(object instance); - } -} diff --git a/app/Simpler/Data/Mappings/ObjectMappingRoot.cs b/app/Simpler/Data/Mappings/ObjectMappingRoot.cs deleted file mode 100644 index 4100ca2..0000000 --- a/app/Simpler/Data/Mappings/ObjectMappingRoot.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Simpler.Data -{ - public class ObjectMappingRoot : ObjectMapping - { - - } -} diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs new file mode 100644 index 0000000..6f14cc9 --- /dev/null +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Simpler.Data.PropertyParseTree +{ + /// + /// A hierarchical collection of properties that can reference columns in a data reader, each represented by a PropertyParseTreeNode. + /// + public abstract class PropertyParseTree + { + /// + /// The property type of the current node + /// + public Type PropertyType { get; set; } + + + /// + /// A List of PropertyParseTreeNode that represents the property parse tree nodes assigned to the current property parse tree node. + /// + public List Nodes { get; set; } + + /// + /// Creates an instance of the specified type using the constructor that best matches the specified parameters. + /// + /// The default value for the newly created object + /// A reference to the newly created object. + public abstract object CreateObject(object value = null); + + /// + /// Initializes a new instance of the PropertyParseTree class. + /// + public PropertyParseTree() + { + Nodes = new List(); + } + + public PropertyParseTreeNode this[string index] + { + get + { + return Nodes.FirstOrDefault(x => x.Name == index); + } + + set + { + var listIndex = Nodes.FindIndex(x => x.Name == index); + value.Name = index; + if (listIndex == -1) + { + Nodes.Add(value); + } + else + { + Nodes[listIndex] = value; + } + } + } + + /// + /// + /// + public bool IsDynamicProperty { + get + { + return PropertyType.FullName == "System.Object"; + } + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingArrayChildNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayChildNode.cs similarity index 73% rename from app/Simpler/Data/Mappings/ObjectMappingArrayChildNode.cs rename to app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayChildNode.cs index 08dec67..9552a9d 100644 --- a/app/Simpler/Data/Mappings/ObjectMappingArrayChildNode.cs +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayChildNode.cs @@ -1,15 +1,13 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -namespace Simpler.Data +namespace Simpler.Data.PropertyParseTree { - public class ObjectMappingArrayChildNode : ObjectMappingNode + /// + /// + /// + public class PropertyParseTreeArrayChildNode : PropertyParseTreeNode { - public override object CreateInstance(object value = null) + public override object CreateObject(object value = null) { if (value == null) { @@ -35,7 +33,7 @@ public override object CreateInstance(object value = null) public override void SetValue(object instance, object value) { //this int parse can be moved into a constructor - ((Array)instance).SetValue(CreateInstance(value), int.Parse(Name)); + ((Array)instance).SetValue(CreateObject(value), int.Parse(Name)); } public override object GetValue(object instance) diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayNode.cs new file mode 100644 index 0000000..8d3212f --- /dev/null +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayNode.cs @@ -0,0 +1,15 @@ +using System; + +namespace Simpler.Data.PropertyParseTree +{ + /// + /// + /// + public class PropertyParseTreeArrayNode : PropertyParseTreeObjectNode + { + public override object CreateObject(object value = null) + { + return Activator.CreateInstance(PropertyType, new object[] { Nodes.Count }); + } + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingDynamicChildNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicChildNode.cs similarity index 61% rename from app/Simpler/Data/Mappings/ObjectMappingDynamicChildNode.cs rename to app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicChildNode.cs index 278afae..69ad861 100644 --- a/app/Simpler/Data/Mappings/ObjectMappingDynamicChildNode.cs +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicChildNode.cs @@ -1,23 +1,22 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Dynamic; -using System.Linq; -using System.Reflection; -using System.Text; -namespace Simpler.Data +namespace Simpler.Data.PropertyParseTree { - public class ObjectMappingDynamicChildNode : ObjectMappingNode + /// + /// + /// + public class PropertyParseTreeDynamicChildNode : PropertyParseTreeNode { - public override object CreateInstance(object value = null) + public override object CreateObject(object value = null) { return value; } public override void SetValue(dynamic instance, object value) { - var instanceValue = CreateInstance(value); + var instanceValue = CreateObject(value); ((ExpandoObject)instance as IDictionary)[Name] = instanceValue; } diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicNode.cs new file mode 100644 index 0000000..ee4c2b5 --- /dev/null +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicNode.cs @@ -0,0 +1,15 @@ +using System.Dynamic; + +namespace Simpler.Data.PropertyParseTree +{ + /// + /// + /// + public class PropertyParseTreeDynamicNode : PropertyParseTreeObjectNode + { + public override object CreateObject(object value = null) + { + return new ExpandoObject(); + } + } +} diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs new file mode 100644 index 0000000..47cfb92 --- /dev/null +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs @@ -0,0 +1,32 @@ +namespace Simpler.Data.PropertyParseTree +{ + /// + /// + /// + public abstract class PropertyParseTreeNode : PropertyParseTree + { + /// + /// the name of the column from the data reader this node references. + /// + public string Name { get; set; } + + /// + /// The index of the column from the data reader this node references. + /// + public int? Index { get; set; } + + /// + /// Sets the property value of a specified object. + /// + /// The object whose property value will be set. + /// The new property value. + public abstract void SetValue(object obj, object value); + + /// + /// Returns the property value of a specified object. + /// + /// The object whose property value will be returned. + /// The property value of the specified object. + public abstract object GetValue(object obj); + } +} diff --git a/app/Simpler/Data/Mappings/ObjectMappingObjectNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeObjectNode.cs similarity index 79% rename from app/Simpler/Data/Mappings/ObjectMappingObjectNode.cs rename to app/Simpler/Data/PropertyParseTree/PropertyParseTreeObjectNode.cs index af57c8f..925be53 100644 --- a/app/Simpler/Data/Mappings/ObjectMappingObjectNode.cs +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeObjectNode.cs @@ -1,17 +1,19 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -namespace Simpler.Data +namespace Simpler.Data.PropertyParseTree { - public class ObjectMappingObjectNode : ObjectMappingNode + /// + /// + /// + public class PropertyParseTreeObjectNode : PropertyParseTreeNode { + /// + /// + /// public PropertyInfo PropertyInfo { get; set; } - public override object CreateInstance(object value = null) + public override object CreateObject(object value = null) { var propertyType = PropertyType; if (value == null && propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) @@ -41,7 +43,7 @@ public override object CreateInstance(object value = null) public override void SetValue(object instance, object value) { - var instanceValue = CreateInstance(value); + var instanceValue = CreateObject(value); PropertyInfo.SetValue(instance, instanceValue, null); } diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs new file mode 100644 index 0000000..a479e89 --- /dev/null +++ b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs @@ -0,0 +1,21 @@ +using System; +using System.Dynamic; + +namespace Simpler.Data.PropertyParseTree +{ + /// + /// + /// + public class PropertyParseTreeRootNode : PropertyParseTree + { + public override object CreateObject(object value = null) + { + if (IsDynamicProperty) + { + return new ExpandoObject(); + } + + return Activator.CreateInstance(PropertyType); + } + } +} \ No newline at end of file diff --git a/app/Simpler/Data/Tasks/BuildMappings.cs b/app/Simpler/Data/Tasks/BuildMappings.cs deleted file mode 100644 index 3ae7908..0000000 --- a/app/Simpler/Data/Tasks/BuildMappings.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Simpler.Data.Tasks -{ - public class BuildMappings : InOutTask - { - public class Input - { - public Dictionary ColumnNames { get; set; } - public Type RootType { get; set; } - } - - public class Output - { - public ObjectMapping ObjectMapping { get; set; } - } - - public void ProcessColumnName(ObjectMapping objectMapping, string columnName, string path = "") - { - var remainingPath = columnName.Substring(path.Length); - //check if we have reached the end of the path - if (remainingPath.Length == 0) - { - ((ObjectMappingNode)objectMapping).ColumnIndex = In.ColumnNames[columnName]; - return; - } - - //if the parent is a dyanmic no need to look at properties just set it - if (objectMapping.PropertyType == null || objectMapping.PropertyType.FullName == "System.Object") - { - objectMapping[remainingPath] = new ObjectMappingDynamicChildNode(); - ProcessColumnName(objectMapping[remainingPath], columnName, path + remainingPath); - return; - } - - //if the parent is an array we need to pull the index off and create a node. - if (objectMapping.PropertyType.IsArray) - { - //pull the index off the remainingPath - var index = new String(remainingPath.TakeWhile(Char.IsDigit).ToArray()); - - //create the node if it doesn't exist - if (!objectMapping.ContainsColumn(index)) - { - objectMapping[index] = new ObjectMappingArrayChildNode - { - PropertyType = objectMapping.PropertyType.GetElementType() - }; - } - - ProcessColumnName(objectMapping[index], columnName, path + index); - return; - } - - //attempt to find an exact match - var propertyInfo = objectMapping.PropertyType.GetProperty(remainingPath); - if (propertyInfo == null) - { - //if we can't find an exact match find a property that starts with the remmaining path - propertyInfo = objectMapping.PropertyType.GetProperties().FirstOrDefault(x => remainingPath.StartsWith(x.Name)); - } - - Check.That(propertyInfo != null, "The DataRecord contains column '{0}' to a property or nested property.", columnName); - - //check if it already exisits - if (!objectMapping.ContainsColumn(propertyInfo.Name)) - { - if (propertyInfo.PropertyType.FullName == "System.Object") - { - objectMapping[propertyInfo.Name] = new ObjectMappingDynamicNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType }; - } - else if (propertyInfo.PropertyType.IsArray) - { - objectMapping[propertyInfo.Name] = new ObjectMappingArrayNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType }; - } - else - { - objectMapping[propertyInfo.Name] = new ObjectMappingObjectNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType}; - } - } - - ProcessColumnName(objectMapping[propertyInfo.Name], columnName, path + propertyInfo.Name); - } - - public override void Execute() - { - var root = new ObjectMappingRoot { PropertyType = In.RootType }; - var sortedColumnNames = In.ColumnNames.OrderBy(s => s.Key); - foreach (var columnName in sortedColumnNames) - { - ProcessColumnName(root, columnName.Key); - } - Out.ObjectMapping = root; - } - } -} diff --git a/app/Simpler/Data/Tasks/BuildObject.cs b/app/Simpler/Data/Tasks/BuildObject.cs index 9f7d210..eba408a 100644 --- a/app/Simpler/Data/Tasks/BuildObject.cs +++ b/app/Simpler/Data/Tasks/BuildObject.cs @@ -1,11 +1,7 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.Data; using System.Dynamic; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; +using Simpler.Data.PropertyParseTree; namespace Simpler.Data.Tasks { @@ -14,7 +10,7 @@ public class BuildObject : InOutTask.Input, BuildObject.Out public class Input { public IDataRecord DataRecord { get; set; } - public ObjectMapping ObjectMapping { get; set; } + public PropertyParseTree.PropertyParseTree PropertyParseTree { get; set; } } public class Output @@ -22,46 +18,42 @@ public class Output public T Object { get; set; } } - public void Parse(ObjectMappingNode objectMapping, object instance) + public void Parse(PropertyParseTreeNode treeNode, object obj) { - if (objectMapping.ColumnIndex == null) + //if the treeNode does not have a index then just set the value to the instance. + if (treeNode.Index == null) { - objectMapping.SetValue(instance, null); + treeNode.SetValue(obj, null); } else { - var value = In.DataRecord.GetValue((int)objectMapping.ColumnIndex); + //get the value from the data reader and set the value + var value = In.DataRecord.GetValue((int)treeNode.Index); if (value != null && value.GetType() != typeof(DBNull)) { - objectMapping.SetValue(instance, value); + treeNode.SetValue(obj, value); } } + + //get the newly created object + var childObj = treeNode.GetValue(obj); - var childInstance = objectMapping.GetValue(instance); - - foreach (var children in objectMapping) + //continue down the parse tree + foreach (var childNode in treeNode.Nodes) { - Parse(children, childInstance); + Parse(childNode, childObj); } } public override void Execute() { - object instance; - if (typeof (T).FullName == "System.Object") - { - instance = new ExpandoObject(); - } - else - { - instance = Activator.CreateInstance(typeof(T)); - } - - foreach (var objectMapping in In.ObjectMapping) + //create the root object + var obj = In.PropertyParseTree.CreateObject(); + foreach (var node in In.PropertyParseTree.Nodes) { - Parse(objectMapping, instance); + Parse(node, obj); } - Out.Object = (T)instance; + Out.Object = (T)obj; } } } \ No newline at end of file diff --git a/app/Simpler/Data/Tasks/BuildObjects.cs b/app/Simpler/Data/Tasks/BuildObjects.cs index e487fbf..97c6515 100644 --- a/app/Simpler/Data/Tasks/BuildObjects.cs +++ b/app/Simpler/Data/Tasks/BuildObjects.cs @@ -17,7 +17,8 @@ public class Output public T[] Objects { get; set; } } - public BuildMappings BuildMappings { get; set; } + public FindColumns FindColumns { get; set; } + public BuildPropertyParseTree BuildPropertyParseTree { get; set; } public BuildObject BuildObject { get; set; } public override void Execute() @@ -27,18 +28,16 @@ public override void Execute() //read the first record off and determine the column mappings In.Reader.Read(); - var columns = new Dictionary(); - for (var i = 0; i < In.Reader.FieldCount; i++) - { - columns.Add(In.Reader.GetName(i), i); - } - BuildMappings.In.ColumnNames = columns; - BuildMappings.In.RootType = typeof (T); - BuildMappings.Execute(); + FindColumns.In.Reader = In.Reader; + FindColumns.Execute(); + + BuildPropertyParseTree.In.Columns = FindColumns.Out.Columns; + BuildPropertyParseTree.In.InitialType = typeof (T); + BuildPropertyParseTree.Execute(); do { - BuildObject.In.ObjectMapping = BuildMappings.Out.ObjectMapping; + BuildObject.In.PropertyParseTree = BuildPropertyParseTree.Out.PropertyParseTree; BuildObject.In.DataRecord = In.Reader; BuildObject.Execute(); objectList.Add(BuildObject.Out.Object); diff --git a/app/Simpler/Data/Tasks/BuildPropertyParseTree.cs b/app/Simpler/Data/Tasks/BuildPropertyParseTree.cs new file mode 100644 index 0000000..ff8d086 --- /dev/null +++ b/app/Simpler/Data/Tasks/BuildPropertyParseTree.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection; +using Simpler.Data.PropertyParseTree; + +namespace Simpler.Data.Tasks +{ + public class BuildPropertyParseTree : InOutTask + { + public class Input + { + public Dictionary Columns { get; set; } + public Type InitialType { get; set; } + } + + public class Output + { + public PropertyParseTree.PropertyParseTree PropertyParseTree { get; set; } + } + + public override void Execute() + { + var parseColumn = new ParseColumn(); + + parseColumn.In.PropertyParseTree = new PropertyParseTreeRootNode { PropertyType = In.InitialType }; + + //order by the longest column names first + var columns = In.Columns.OrderByDescending(x => x.Key.Length); + foreach (var column in columns) + { + parseColumn.In.ColumnName = column.Key; + parseColumn.In.ColumnIndex = column.Value; + parseColumn.Execute(); + } + Out.PropertyParseTree = parseColumn.In.PropertyParseTree; + } + } +} diff --git a/app/Simpler/Data/Tasks/FindColumns.cs b/app/Simpler/Data/Tasks/FindColumns.cs new file mode 100644 index 0000000..4608180 --- /dev/null +++ b/app/Simpler/Data/Tasks/FindColumns.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection; +using Simpler.Data.PropertyParseTree; + +namespace Simpler.Data.Tasks +{ + public class FindColumns : InOutTask + { + public class Input + { + public IDataReader Reader { get; set; } + } + + public class Output + { + public Dictionary Columns { get; set; } + } + + public override void Execute() + { + Out.Columns = new Dictionary(); + for (var i = 0; i < In.Reader.FieldCount; i++) + { + var columnName = In.Reader.GetName(i); + Check.That(!Out.Columns.ContainsKey(columnName), "The DataRecord contains a duplicate column '{0}'.", columnName); + Out.Columns[columnName] = i; + } + } + } +} diff --git a/app/Simpler/Data/Tasks/ParseColumn.cs b/app/Simpler/Data/Tasks/ParseColumn.cs new file mode 100644 index 0000000..bd62dbd --- /dev/null +++ b/app/Simpler/Data/Tasks/ParseColumn.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Simpler.Data.PropertyParseTree; + +namespace Simpler.Data.Tasks +{ + public class ParseColumn : InTask + { + public class Input + { + public int ColumnIndex { get; set; } + public string ColumnName { get; set; } + public PropertyParseTree.PropertyParseTree PropertyParseTree { get; set; } + } + + private void ParseColumnName(PropertyParseTree.PropertyParseTree propertyParseTree, string path = "") + { + var remainingPath = In.ColumnName.Substring(path.Length); + //check if we have reached the end of the path + if (remainingPath.Length == 0) + { + ((PropertyParseTreeNode) propertyParseTree).Index = In.ColumnIndex; + return; + } + + var propertyType = propertyParseTree.PropertyType; + + //if the parent is a dyanmic no need to look at properties just set it + if (propertyType == null || propertyType.FullName == "System.Object") + { + propertyParseTree[remainingPath] = new PropertyParseTreeDynamicChildNode(); + ParseColumnName(propertyParseTree[remainingPath], path + remainingPath); + return; + } + + //if the parent is an array we need to pull the index off and create a treeNode. + if (propertyType.IsArray) + { + //pull the index off the remainingPath + var index = new String(remainingPath.TakeWhile(Char.IsDigit).ToArray()); + + //create the treeNode if it doesn't exist + if(propertyParseTree.Nodes.All(x => x.Name != index)) + { + propertyParseTree[index] = new PropertyParseTreeArrayChildNode + { + PropertyType = propertyType.GetElementType() + }; + } + + ParseColumnName(propertyParseTree[index], path + index); + return; + } + + //attempt to find an exact match + var propertyInfo = propertyType.GetProperty(remainingPath); + if (propertyInfo == null) + { + //if we can't find an exact match find a property that starts with the remmaining path + propertyInfo = propertyType.GetProperties().FirstOrDefault(x => remainingPath.StartsWith(x.Name)); + } + + Check.That(propertyInfo != null, "The DataRecord contains a column '{0}' that does not match a property or nested property.", In.ColumnName); + + //check if the treeNode already exisits in the parse tree + if (!propertyParseTree.Nodes.Any(x => x.Name == propertyInfo.Name)) + { + //determine the type of the treeNode and add it to the parse tree + if (propertyInfo.PropertyType.FullName == "System.Object") + { + propertyParseTree[propertyInfo.Name] = new PropertyParseTreeDynamicNode + { + PropertyInfo = propertyInfo, + PropertyType = propertyInfo.PropertyType + }; + } + else if (propertyInfo.PropertyType.IsArray) + { + propertyParseTree[propertyInfo.Name] = new PropertyParseTreeArrayNode + { + PropertyInfo = propertyInfo, + PropertyType = propertyInfo.PropertyType + }; + } + else + { + propertyParseTree[propertyInfo.Name] = new PropertyParseTreeObjectNode + { + PropertyInfo = propertyInfo, + PropertyType = propertyInfo.PropertyType + }; + } + } + + ParseColumnName(propertyParseTree[propertyInfo.Name], path + propertyInfo.Name); + } + + public override void Execute() + { + ParseColumnName(In.PropertyParseTree); + } + } +} diff --git a/app/Simpler/Simpler.csproj b/app/Simpler/Simpler.csproj index 779ef95..2566135 100644 --- a/app/Simpler/Simpler.csproj +++ b/app/Simpler/Simpler.csproj @@ -52,19 +52,21 @@ - - - - - - - - + + + + + + + + - + + + From 6eb1b1caf6eb83ff85b3b1bc29b7c7c1df13d61b Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 27 Mar 2014 12:17:20 -0800 Subject: [PATCH 4/4] More renaming. --- .../ArrayElementNodeTest.cs} | 12 +- .../ArrayNodeTest.cs} | 10 +- .../DynamicNodeTest.cs} | 10 +- .../DynamicPropertyNodeTest.cs} | 10 +- .../ObjectNodeTest.cs} | 10 +- .../PropertyParseTreeRootNodeTest.cs | 26 ---- .../Data/Tasks/BuildObjectTest.cs | 18 +-- .../Data/Tasks/BuildPropertyMappingTree.cs | 131 ++++++++++++++++++ .../Data/Tasks/BuildPropertyParseTreeTest.cs | 131 ------------------ .../Data/Tasks/BuildTypedTest.cs | 18 +-- .../Data/Tasks/ParseColumnTest.cs | 46 +++--- app/Simpler.Tests/Simpler.Tests.csproj | 13 +- .../Data/PropertyMappingTree/AbstractNode.cs | 95 +++++++++++++ .../ArrayElementNode.cs} | 4 +- .../ArrayNode.cs} | 6 +- .../DynamicNode.cs} | 4 +- .../DynamicPropertyNode.cs} | 4 +- .../ObjectNode.cs} | 4 +- .../PropertyParseTree/PropertyParseTree.cs | 70 ---------- .../PropertyParseTreeNode.cs | 32 ----- .../PropertyParseTreeRootNode.cs | 21 --- app/Simpler/Data/Tasks/BuildObject.cs | 24 ++-- app/Simpler/Data/Tasks/BuildObjects.cs | 10 +- ...rseTree.cs => BuildPropertyMappingTree.cs} | 17 ++- app/Simpler/Data/Tasks/FindColumns.cs | 2 +- app/Simpler/Data/Tasks/ParseColumn.cs | 46 +++--- app/Simpler/Simpler.csproj | 16 +-- 27 files changed, 369 insertions(+), 421 deletions(-) rename app/Simpler.Tests/Data/{PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs => PropertyMappingTree/ArrayElementNodeTest.cs} (78%) rename app/Simpler.Tests/Data/{PropertyParseTree/PropertyParseTreeArrayNodeTest.cs => PropertyMappingTree/ArrayNodeTest.cs} (84%) rename app/Simpler.Tests/Data/{PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs => PropertyMappingTree/DynamicNodeTest.cs} (83%) rename app/Simpler.Tests/Data/{PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs => PropertyMappingTree/DynamicPropertyNodeTest.cs} (81%) rename app/Simpler.Tests/Data/{PropertyParseTree/PropertyParseTreeObjectNodeTest.cs => PropertyMappingTree/ObjectNodeTest.cs} (82%) delete mode 100644 app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs create mode 100644 app/Simpler.Tests/Data/Tasks/BuildPropertyMappingTree.cs delete mode 100644 app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs create mode 100644 app/Simpler/Data/PropertyMappingTree/AbstractNode.cs rename app/Simpler/Data/{PropertyParseTree/PropertyParseTreeArrayChildNode.cs => PropertyMappingTree/ArrayElementNode.cs} (91%) rename app/Simpler/Data/{PropertyParseTree/PropertyParseTreeArrayNode.cs => PropertyMappingTree/ArrayNode.cs} (62%) rename app/Simpler/Data/{PropertyParseTree/PropertyParseTreeDynamicNode.cs => PropertyMappingTree/DynamicNode.cs} (64%) rename app/Simpler/Data/{PropertyParseTree/PropertyParseTreeDynamicChildNode.cs => PropertyMappingTree/DynamicPropertyNode.cs} (84%) rename app/Simpler/Data/{PropertyParseTree/PropertyParseTreeObjectNode.cs => PropertyMappingTree/ObjectNode.cs} (93%) delete mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs delete mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs delete mode 100644 app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs rename app/Simpler/Data/Tasks/{BuildPropertyParseTree.cs => BuildPropertyMappingTree.cs} (58%) diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs b/app/Simpler.Tests/Data/PropertyMappingTree/ArrayElementNodeTest.cs similarity index 78% rename from app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs rename to app/Simpler.Tests/Data/PropertyMappingTree/ArrayElementNodeTest.cs index 5f9a218..93735ff 100644 --- a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayChildNodeTest.cs +++ b/app/Simpler.Tests/Data/PropertyMappingTree/ArrayElementNodeTest.cs @@ -1,18 +1,18 @@ using NUnit.Framework; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; using Simpler.Tests.Core.Mocks; -namespace Simpler.Tests.Data.PropertyParseTree +namespace Simpler.Tests.Data.PropertyMappingTree { [TestFixture] - public class PropertyParseTreeArrayChildNodeTest + public class ArrayElementNodeTest { [Test] public void should_set_value_for_array_child_node() { //arrange var array = new MockPet[1]; - var propertyParseTreeArrayChildNode = new PropertyParseTreeArrayChildNode + var propertyParseTreeArrayChildNode = new ArrayElementNode { Name = "0", PropertyType = typeof (MockPet) @@ -30,7 +30,7 @@ public void should_get_value_for_array_child_node() { //arrange var array = new [] { new MockPet {Name = "Doug"} }; - var propertyParseTreeArrayChildNode = new PropertyParseTreeArrayChildNode + var propertyParseTreeArrayChildNode = new ArrayElementNode { Name = "0", PropertyType = typeof (MockPet) @@ -48,7 +48,7 @@ public void should_get_value_for_array_child_node() public void should_create_object_for_array_child_node() { //arrange - var propertyParseTreeArrayChildNode = new PropertyParseTreeArrayChildNode + var propertyParseTreeArrayChildNode = new ArrayElementNode { PropertyType = typeof (MockPet) }; diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayNodeTest.cs b/app/Simpler.Tests/Data/PropertyMappingTree/ArrayNodeTest.cs similarity index 84% rename from app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayNodeTest.cs rename to app/Simpler.Tests/Data/PropertyMappingTree/ArrayNodeTest.cs index 8da7d3e..013ee08 100644 --- a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeArrayNodeTest.cs +++ b/app/Simpler.Tests/Data/PropertyMappingTree/ArrayNodeTest.cs @@ -1,18 +1,18 @@ using NUnit.Framework; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; using Simpler.Tests.Core.Mocks; namespace Simpler.Tests.Data.PropertyParseTree { [TestFixture] - public class PropertyParseTreeArrayNodeTest + public class ArrayNodeTest { [Test] public void should_set_value_for_array_node() { //arrange var obj = new MockPerson(); - var propertyParseTreeArrayNode = new PropertyParseTreeArrayNode + var propertyParseTreeArrayNode = new ArrayNode { Name = "Vehicles", PropertyInfo = obj.GetType().GetProperty("Vehicles"), @@ -31,7 +31,7 @@ public void should_get_value_for_array() { //arrange var obj = new MockPerson {Vehicles = new MockVehicle[0]}; - var propertyParseTreeArrayNode = new PropertyParseTreeArrayNode + var propertyParseTreeArrayNode = new ArrayNode { Name = "Vehicles", PropertyInfo = obj.GetType().GetProperty("Vehicles"), @@ -50,7 +50,7 @@ public void should_get_value_for_array() public void should_create_object_for_array() { //arrange - var propertyParseTreeArrayNode = new PropertyParseTreeArrayNode + var propertyParseTreeArrayNode = new ArrayNode { PropertyType = typeof (MockVehicle[]) }; diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs b/app/Simpler.Tests/Data/PropertyMappingTree/DynamicNodeTest.cs similarity index 83% rename from app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs rename to app/Simpler.Tests/Data/PropertyMappingTree/DynamicNodeTest.cs index b4d870b..0a5fe11 100644 --- a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicNodeTest.cs +++ b/app/Simpler.Tests/Data/PropertyMappingTree/DynamicNodeTest.cs @@ -1,19 +1,19 @@ using System.Dynamic; using NUnit.Framework; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; using Simpler.Tests.Core.Mocks; namespace Simpler.Tests.Data.PropertyParseTree { [TestFixture] - public class PropertyParseTreeDynamicNodeTest + public class DynamicNodeTest { [Test] public void should_set_value_for_dynamic_node() { //arrange var obj = new MockPerson(); - var propertyParseTreeArrayNode = new PropertyParseTreeDynamicNode() + var propertyParseTreeArrayNode = new DynamicNode() { Name = "Other", PropertyInfo = obj.GetType().GetProperty("Other"), @@ -32,7 +32,7 @@ public void should_get_value_for_dynamic_node() { //arrange var obj = new MockPerson{Other = new ExpandoObject()}; - var propertyParseTreeArrayNode = new PropertyParseTreeDynamicNode() + var propertyParseTreeArrayNode = new DynamicNode() { Name = "Other", PropertyInfo = obj.GetType().GetProperty("Other"), @@ -51,7 +51,7 @@ public void should_get_value_for_dynamic_node() public void should_create_object_for_dynamic_node() { //arrange - var propertyParseTreeArrayNode = new PropertyParseTreeDynamicNode() + var propertyParseTreeArrayNode = new DynamicNode() { Name = "Other", PropertyType = typeof(object) diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs b/app/Simpler.Tests/Data/PropertyMappingTree/DynamicPropertyNodeTest.cs similarity index 81% rename from app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs rename to app/Simpler.Tests/Data/PropertyMappingTree/DynamicPropertyNodeTest.cs index 045f6de..91c5d12 100644 --- a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeDynamicChildNodeTest.cs +++ b/app/Simpler.Tests/Data/PropertyMappingTree/DynamicPropertyNodeTest.cs @@ -1,18 +1,18 @@ using System.Dynamic; using NUnit.Framework; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; namespace Simpler.Tests.Data.PropertyParseTree { [TestFixture] - public class PropertyParseTreeDynamicChildNodeTest + public class DynamicPropertyNodeTest { [Test] public void should_set_value_for_dynamic_node() { //arrange dynamic obj = new ExpandoObject(); - var propertyParseTreeArrayNode = new PropertyParseTreeDynamicChildNode() + var propertyParseTreeArrayNode = new DynamicPropertyNode() { Name = "City", PropertyType = typeof (string) @@ -31,7 +31,7 @@ public void should_get_value_for_dynamic_node() //arrange dynamic obj = new ExpandoObject(); obj.City = "Anchorage"; - var propertyParseTreeArrayNode = new PropertyParseTreeDynamicChildNode() + var propertyParseTreeArrayNode = new DynamicPropertyNode() { Name = "City", PropertyType = typeof(string) @@ -49,7 +49,7 @@ public void should_get_value_for_dynamic_node() public void should_create_object_for_dynamic_node() { //arrange - var propertyParseTreeArrayNode = new PropertyParseTreeDynamicChildNode() + var propertyParseTreeArrayNode = new DynamicPropertyNode() { Name = "City", PropertyType = typeof(string) diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeObjectNodeTest.cs b/app/Simpler.Tests/Data/PropertyMappingTree/ObjectNodeTest.cs similarity index 82% rename from app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeObjectNodeTest.cs rename to app/Simpler.Tests/Data/PropertyMappingTree/ObjectNodeTest.cs index ae79b58..e29e92e 100644 --- a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeObjectNodeTest.cs +++ b/app/Simpler.Tests/Data/PropertyMappingTree/ObjectNodeTest.cs @@ -1,18 +1,18 @@ using NUnit.Framework; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; using Simpler.Tests.Core.Mocks; namespace Simpler.Tests.Data.PropertyParseTree { [TestFixture] - public class PropertyParseTreeObjectNodeTest + public class ObjectNodeTest { [Test] public void should_set_value_for_object_node() { //arrange var obj = new MockPerson(); - var propertyParseTreeObjectNode = new PropertyParseTreeObjectNode() + var propertyParseTreeObjectNode = new ObjectNode() { Name = "Name", PropertyInfo = obj.GetType().GetProperty("Name"), @@ -31,7 +31,7 @@ public void should_get_value_for_object_node() { //arrange var obj = new MockPerson{ Name = "Richard" }; - var propertyParseTreeObjectNode = new PropertyParseTreeObjectNode() + var propertyParseTreeObjectNode = new ObjectNode() { Name = "Name", PropertyInfo = obj.GetType().GetProperty("Name"), @@ -49,7 +49,7 @@ public void should_get_value_for_object_node() public void should_create_object_for_object_node() { //arrange - var propertyParseTreeObjectNode = new PropertyParseTreeObjectNode() + var propertyParseTreeObjectNode = new ObjectNode() { Name = "Name", PropertyType = typeof(string) diff --git a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs b/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs deleted file mode 100644 index 847da55..0000000 --- a/app/Simpler.Tests/Data/PropertyParseTree/PropertyParseTreeRootNodeTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using NUnit.Framework; -using Simpler.Data.PropertyParseTree; -using Simpler.Tests.Core.Mocks; - -namespace Simpler.Tests.Data.PropertyParseTree -{ - [TestFixture] - public class PropertyParseTreeRootNodeTest - { - [Test] - public void should_create_object_for_object_node() - { - //arrange - var propertyParseTreeRootNode = new PropertyParseTreeRootNode() - { - PropertyType = typeof(MockPerson) - }; - - // Act - var value = propertyParseTreeRootNode.CreateObject(); - - // Assert - Assert.That(value, Is.TypeOf(typeof(MockPerson))); - } - } -} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs b/app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs index 91166d9..2ccad22 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildObjectTest.cs @@ -13,13 +13,13 @@ namespace Simpler.Tests.Data.Tasks [TestFixture] public class BuildObjectTest { - public static Simpler.Data.PropertyParseTree.PropertyParseTree get_parse_tree_from_data_table(DataTable table, Type type) + public static Simpler.Data.PropertyMappingTree.AbstractNode get_parse_tree_from_data_table(DataTable table, Type type) { - var buildPropertyParseTree = new BuildPropertyParseTree(); + var buildPropertyParseTree = new Simpler.Data.Tasks.BuildPropertyMappingTree(); buildPropertyParseTree.In.Columns = table.Columns.Cast().Select((x, i) => new { x.ColumnName, i }).ToDictionary(x => x.ColumnName, x => x.i); buildPropertyParseTree.In.InitialType = type; buildPropertyParseTree.Execute(); - return buildPropertyParseTree.Out.PropertyParseTree; + return buildPropertyParseTree.Out.PropertyMappingTree; } [Test] @@ -35,7 +35,7 @@ public void should_create_root_typed_object() var task = Task.New>(); task.In.DataRecord = dataReader; - task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + task.In.PropertyParse = get_parse_tree_from_data_table(table, typeof(MockPerson)); //Act task.Execute(); @@ -58,7 +58,7 @@ public void should_create_root_dynamic_object() var task = Task.New>(); task.In.DataRecord = dataReader; - task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(object)); + task.In.PropertyParse = get_parse_tree_from_data_table(table, typeof(object)); //Act task.Execute(); @@ -81,7 +81,7 @@ public void should_assign_simple_properties() var task = Task.New>(); task.In.DataRecord = dataReader; - task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + task.In.PropertyParse = get_parse_tree_from_data_table(table, typeof(MockPerson)); //Act task.Execute(); @@ -104,7 +104,7 @@ public void should_assign_nested_properties() var task = Task.New>(); task.In.DataRecord = dataReader; - task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + task.In.PropertyParse = get_parse_tree_from_data_table(table, typeof(MockPerson)); //Act task.Execute(); @@ -127,7 +127,7 @@ public void should_assign_array_properties() var task = Task.New>(); task.In.DataRecord = dataReader; - task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + task.In.PropertyParse = get_parse_tree_from_data_table(table, typeof(MockPerson)); //Act task.Execute(); @@ -150,7 +150,7 @@ public void should_assign_dynamic_properties() var task = Task.New>(); task.In.DataRecord = dataReader; - task.In.PropertyParseTree = get_parse_tree_from_data_table(table, typeof(MockPerson)); + task.In.PropertyParse = get_parse_tree_from_data_table(table, typeof(MockPerson)); //Act task.Execute(); diff --git a/app/Simpler.Tests/Data/Tasks/BuildPropertyMappingTree.cs b/app/Simpler.Tests/Data/Tasks/BuildPropertyMappingTree.cs new file mode 100644 index 0000000..007be8a --- /dev/null +++ b/app/Simpler.Tests/Data/Tasks/BuildPropertyMappingTree.cs @@ -0,0 +1,131 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Simpler.Data.Tasks; +using Simpler.Tests.Core.Mocks; + +namespace Simpler.Tests.Data.Tasks +{ + [TestFixture] + public class BuildPropertyMappingTree + { + [Test] + public void should_build_a_tree_for_primitives() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + { "Name", 0 }, { "Age", 1 } + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyMappingTree.Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Age"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree_for_nested_objects() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + {"PetName", 0}, {"PetAge", 1} + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyMappingTree.Children.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyMappingTree["Pet"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Pet"]["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Pet"]["Age"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree_for_arrays() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + {"Vehicles0Make", 0}, {"Vehicles0Model", 1}, {"Vehicles1Make", 2}, {"Vehicles1Model", 3} + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyMappingTree.Children.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyMappingTree["Vehicles"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["0"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["0"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["0"]["Model"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["1"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["1"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["1"]["Model"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree_for_dynamics() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + {"OtherCity", 0}, {"OtherState", 1} + }; + task.In.InitialType = typeof(MockPerson); + + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyMappingTree.Children.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyMappingTree["Other"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Other"]["State"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Other"]["City"], Is.Not.Null); + } + + [Test] + public void should_build_a_tree() + { + // Arrange + var task = Task.New(); + task.In.Columns = new Dictionary + { + { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vehicles0Make", 4}, + {"Vehicles0Model", 5}, {"Vehicles1Make", 6}, {"Vehicles1Model", 7}, {"OtherCity", 8} + }; + task.In.InitialType = typeof(MockPerson); + // Act + task.Execute(); + + // Assert + Assert.That(task.Out.PropertyMappingTree.Children.Count(), Is.EqualTo(5)); + Assert.That(task.Out.PropertyMappingTree["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Age"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Pet"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Pet"]["Name"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Pet"]["Age"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Vehicles"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["0"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["0"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["0"]["Model"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["1"].Children.Count(), Is.EqualTo(2)); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["1"]["Make"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Vehicles"]["1"]["Model"], Is.Not.Null); + Assert.That(task.Out.PropertyMappingTree["Other"].Children.Count(), Is.EqualTo(1)); + Assert.That(task.Out.PropertyMappingTree["Other"]["City"], Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs b/app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs deleted file mode 100644 index 534fbc5..0000000 --- a/app/Simpler.Tests/Data/Tasks/BuildPropertyParseTreeTest.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using Simpler.Data.Tasks; -using Simpler.Tests.Core.Mocks; - -namespace Simpler.Tests.Data.Tasks -{ - [TestFixture] - public class BuildPropertyParseTreeTest - { - [Test] - public void should_build_a_tree_for_primitives() - { - // Arrange - var task = Task.New(); - task.In.Columns = new Dictionary - { - { "Name", 0 }, { "Age", 1 } - }; - task.In.InitialType = typeof(MockPerson); - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Name"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Age"], Is.Not.Null); - } - - [Test] - public void should_build_a_tree_for_nested_objects() - { - // Arrange - var task = Task.New(); - task.In.Columns = new Dictionary - { - {"PetName", 0}, {"PetAge", 1} - }; - task.In.InitialType = typeof(MockPerson); - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(1)); - Assert.That(task.Out.PropertyParseTree["Pet"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Pet"]["Name"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Pet"]["Age"], Is.Not.Null); - } - - [Test] - public void should_build_a_tree_for_arrays() - { - // Arrange - var task = Task.New(); - task.In.Columns = new Dictionary - { - {"Vehicles0Make", 0}, {"Vehicles0Model", 1}, {"Vehicles1Make", 2}, {"Vehicles1Model", 3} - }; - task.In.InitialType = typeof(MockPerson); - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(1)); - Assert.That(task.Out.PropertyParseTree["Vehicles"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Make"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Model"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Make"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Model"], Is.Not.Null); - } - - [Test] - public void should_build_a_tree_for_dynamics() - { - // Arrange - var task = Task.New(); - task.In.Columns = new Dictionary - { - {"OtherCity", 0}, {"OtherState", 1} - }; - task.In.InitialType = typeof(MockPerson); - - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(1)); - Assert.That(task.Out.PropertyParseTree["Other"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Other"]["State"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Other"]["City"], Is.Not.Null); - } - - [Test] - public void should_build_a_tree() - { - // Arrange - var task = Task.New(); - task.In.Columns = new Dictionary - { - { "Name", 0 }, { "Age", 1 }, {"PetName" , 2}, {"PetAge", 3}, {"Vehicles0Make", 4}, - {"Vehicles0Model", 5}, {"Vehicles1Make", 6}, {"Vehicles1Model", 7}, {"OtherCity", 8} - }; - task.In.InitialType = typeof(MockPerson); - // Act - task.Execute(); - - // Assert - Assert.That(task.Out.PropertyParseTree.Nodes.Count(), Is.EqualTo(5)); - Assert.That(task.Out.PropertyParseTree["Name"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Age"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Pet"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Pet"]["Name"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Pet"]["Age"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Vehicles"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Make"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["0"]["Model"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"].Nodes.Count(), Is.EqualTo(2)); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Make"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Vehicles"]["1"]["Model"], Is.Not.Null); - Assert.That(task.Out.PropertyParseTree["Other"].Nodes.Count(), Is.EqualTo(1)); - Assert.That(task.Out.PropertyParseTree["Other"]["City"], Is.Not.Null); - } - } -} \ No newline at end of file diff --git a/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs b/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs index 3a4c1e7..fa8cabd 100644 --- a/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs +++ b/app/Simpler.Tests/Data/Tasks/BuildTypedTest.cs @@ -15,11 +15,11 @@ public void should_build_an_instance_of_given_type() { // Arrange var task = Task.New>(); - var mapTask = Task.New(); + var mapTask = Task.New(); mapTask.In.InitialType = typeof(MockPerson); mapTask.In.Columns = new Dictionary { { "Name", 0 } }; mapTask.Execute(); - task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; + task.In.PropertyParse = mapTask.Out.PropertyMappingTree; var mockDataRecord = new Mock(); task.In.DataRecord = mockDataRecord.Object; @@ -36,11 +36,11 @@ public void should_populate_typed_object_using_all_columns_in_the_data_record() { // Arrange var task = Task.New>(); - var mapTask = Task.New(); + var mapTask = Task.New(); mapTask.In.InitialType = typeof(MockPerson); mapTask.In.Columns = new Dictionary { { "Name", 0 }, { "Age", 1 } }; mapTask.Execute(); - task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; + task.In.PropertyParse = mapTask.Out.PropertyMappingTree; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(2); @@ -62,7 +62,7 @@ public void should_populate_typed_object_using_all_columns_in_the_data_record() public void should_throw_exception_if_a_data_record_column_is_not_a_property_of_the_object_class() { // Arrange - var mapTask = Task.New(); + var mapTask = Task.New(); mapTask.In.InitialType = typeof(MockPerson); mapTask.In.Columns = new Dictionary { { "Name", 0 }, { "TheCakeIsALie", 1 } }; @@ -75,11 +75,11 @@ public void should_allow_object_to_have_properties_that_dont_have_matching_colum { // Arrange var task = Task.New>(); - var mapTask = Task.New(); + var mapTask = Task.New(); mapTask.In.InitialType = typeof(MockPerson); mapTask.In.Columns = new Dictionary { { "Name", 0 } }; mapTask.Execute(); - task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; + task.In.PropertyParse = mapTask.Out.PropertyMappingTree; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(1); @@ -101,11 +101,11 @@ public void should_build_enum_properties() { // Arrange var task = Task.New>(); - var mapTask = Task.New(); + var mapTask = Task.New(); mapTask.In.InitialType = typeof(MockPerson); mapTask.In.Columns = new Dictionary { { "MockEnum", 0 } }; mapTask.Execute(); - task.In.PropertyParseTree = mapTask.Out.PropertyParseTree; + task.In.PropertyParse = mapTask.Out.PropertyMappingTree; var mockDataRecord = new Mock(); mockDataRecord.Setup(dataRecord => dataRecord.FieldCount).Returns(1); diff --git a/app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs b/app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs index 3ca87cc..81227a1 100644 --- a/app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs +++ b/app/Simpler.Tests/Data/Tasks/ParseColumnTest.cs @@ -2,7 +2,7 @@ using System.Linq; using NUnit.Framework; using Simpler.Data; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; using Simpler.Data.Tasks; using Simpler.Tests.Core.Mocks; @@ -16,7 +16,7 @@ public void should_throw_exception_if_column_name_does_not_match_any_properties( { //Arrange var task = Task.New(); - task.In.PropertyParseTree = new PropertyParseTreeRootNode + task.In.RootNode = new ObjectNode { PropertyType = typeof(MockPerson) }; @@ -31,7 +31,7 @@ public void should_throw_exception_if_column_name_is_only_a_partial_match() { //Arrange var task = Task.New(); - task.In.PropertyParseTree = new PropertyParseTreeRootNode + task.In.RootNode = new ObjectNode { PropertyType = typeof(MockPerson) }; @@ -46,7 +46,7 @@ public void should_parse_column_names() { //Arrange var task = Task.New(); - task.In.PropertyParseTree = new PropertyParseTreeRootNode + task.In.RootNode = new ObjectNode { PropertyType = typeof (MockPerson) }; @@ -56,10 +56,10 @@ public void should_parse_column_names() task.Execute(); //Assert - Assert.That(task.In.PropertyParseTree["Name"], Is.Not.Null); + Assert.That(task.In.RootNode["Name"], Is.Not.Null); //Check the node types - Assert.That(task.In.PropertyParseTree["Name"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); + Assert.That(task.In.RootNode["Name"], Is.TypeOf(typeof(ObjectNode))); } [Test] @@ -67,7 +67,7 @@ public void should_parse_column_names_with_nested_objects() { //Arrange var task = Task.New(); - task.In.PropertyParseTree = new PropertyParseTreeRootNode + task.In.RootNode = new ObjectNode { PropertyType = typeof(MockPerson) }; @@ -77,12 +77,12 @@ public void should_parse_column_names_with_nested_objects() task.Execute(); //Assert - Assert.That(task.In.PropertyParseTree["Pet"], Is.Not.Null); - Assert.That(task.In.PropertyParseTree["Pet"]["Name"], Is.Not.Null); + Assert.That(task.In.RootNode["Pet"], Is.Not.Null); + Assert.That(task.In.RootNode["Pet"]["Name"], Is.Not.Null); //Check the node types - Assert.That(task.In.PropertyParseTree["Pet"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); - Assert.That(task.In.PropertyParseTree["Pet"]["Name"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); + Assert.That(task.In.RootNode["Pet"], Is.TypeOf(typeof(ObjectNode))); + Assert.That(task.In.RootNode["Pet"]["Name"], Is.TypeOf(typeof(ObjectNode))); } [Test] @@ -90,7 +90,7 @@ public void should_parse_column_names_with_arrays() { //Arrange var task = Task.New(); - task.In.PropertyParseTree = new PropertyParseTreeRootNode + task.In.RootNode = new ObjectNode { PropertyType = typeof(MockPerson) }; @@ -100,14 +100,14 @@ public void should_parse_column_names_with_arrays() task.Execute(); //Assert - Assert.That(task.In.PropertyParseTree["Vehicles"], Is.Not.Null); - Assert.That(task.In.PropertyParseTree["Vehicles"]["0"], Is.Not.Null); - Assert.That(task.In.PropertyParseTree["Vehicles"]["0"]["Make"], Is.Not.Null); + Assert.That(task.In.RootNode["Vehicles"], Is.Not.Null); + Assert.That(task.In.RootNode["Vehicles"]["0"], Is.Not.Null); + Assert.That(task.In.RootNode["Vehicles"]["0"]["Make"], Is.Not.Null); //Check the node types - Assert.That(task.In.PropertyParseTree["Vehicles"], Is.TypeOf(typeof(PropertyParseTreeArrayNode))); - Assert.That(task.In.PropertyParseTree["Vehicles"]["0"], Is.TypeOf(typeof(PropertyParseTreeArrayChildNode))); - Assert.That(task.In.PropertyParseTree["Vehicles"]["0"]["Make"], Is.TypeOf(typeof(PropertyParseTreeObjectNode))); + Assert.That(task.In.RootNode["Vehicles"], Is.TypeOf(typeof(ArrayNode))); + Assert.That(task.In.RootNode["Vehicles"]["0"], Is.TypeOf(typeof(ArrayElementNode))); + Assert.That(task.In.RootNode["Vehicles"]["0"]["Make"], Is.TypeOf(typeof(ObjectNode))); } [Test] @@ -115,7 +115,7 @@ public void should_parse_column_names_that_are_dynamic() { //Arrange var task = Task.New(); - task.In.PropertyParseTree = new PropertyParseTreeRootNode + task.In.RootNode = new ObjectNode { PropertyType = typeof(MockPerson) }; @@ -125,12 +125,12 @@ public void should_parse_column_names_that_are_dynamic() task.Execute(); //Assert - Assert.That(task.In.PropertyParseTree["Other"], Is.Not.Null); - Assert.That(task.In.PropertyParseTree["Other"]["TheLastManStanding"], Is.Not.Null); + Assert.That(task.In.RootNode["Other"], Is.Not.Null); + Assert.That(task.In.RootNode["Other"]["TheLastManStanding"], Is.Not.Null); //Check the node types - Assert.That(task.In.PropertyParseTree["Other"], Is.TypeOf(typeof(PropertyParseTreeDynamicNode))); - Assert.That(task.In.PropertyParseTree["Other"]["TheLastManStanding"], Is.TypeOf(typeof(PropertyParseTreeDynamicChildNode))); + Assert.That(task.In.RootNode["Other"], Is.TypeOf(typeof(DynamicNode))); + Assert.That(task.In.RootNode["Other"]["TheLastManStanding"], Is.TypeOf(typeof(DynamicPropertyNode))); } } diff --git a/app/Simpler.Tests/Simpler.Tests.csproj b/app/Simpler.Tests/Simpler.Tests.csproj index 47abbbc..4a7ee05 100644 --- a/app/Simpler.Tests/Simpler.Tests.csproj +++ b/app/Simpler.Tests/Simpler.Tests.csproj @@ -67,12 +67,11 @@ - - - - - - + + + + + @@ -81,7 +80,7 @@ - + diff --git a/app/Simpler/Data/PropertyMappingTree/AbstractNode.cs b/app/Simpler/Data/PropertyMappingTree/AbstractNode.cs new file mode 100644 index 0000000..8632516 --- /dev/null +++ b/app/Simpler/Data/PropertyMappingTree/AbstractNode.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Simpler.Data.PropertyMappingTree +{ + /// + /// + /// + public abstract class AbstractNode + { + /// + /// The property type of the current node + /// + public Type PropertyType { get; set; } + + + /// + /// A List of Node that represents the property parse tree nodes assigned to the current property parse tree node. + /// + public List Children { get; set; } + + /// + /// Creates an instance of the specified type using the constructor that best matches the specified parameters. + /// + /// The default value for the newly created object + /// A reference to the newly created object. + public abstract object CreateObject(object value = null); + + /// + /// Initializes a new instance of the RootNode class. + /// + public AbstractNode() + { + Children = new List(); + } + + public AbstractNode this[string index] + { + get + { + return Children.FirstOrDefault(x => x.Name == index); + } + + set + { + var listIndex = Children.FindIndex(x => x.Name == index); + value.Name = index; + if (listIndex == -1) + { + Children.Add(value); + } + else + { + Children[listIndex] = value; + } + } + } + + /// + /// + /// + public bool IsDynamicProperty { + get + { + return PropertyType.FullName == "System.Object"; + } + } + + + /// + /// the name of the column from the data reader this node references. + /// + public string Name { get; set; } + + /// + /// The index of the column from the data reader this node references. + /// + public int? Index { get; set; } + + /// + /// Sets the property value of a specified object. + /// + /// The object whose property value will be set. + /// The new property value. + public abstract void SetValue(object obj, object value); + + /// + /// Returns the property value of a specified object. + /// + /// The object whose property value will be returned. + /// The property value of the specified object. + public abstract object GetValue(object obj); + } +} diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayChildNode.cs b/app/Simpler/Data/PropertyMappingTree/ArrayElementNode.cs similarity index 91% rename from app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayChildNode.cs rename to app/Simpler/Data/PropertyMappingTree/ArrayElementNode.cs index 9552a9d..f4b51c7 100644 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayChildNode.cs +++ b/app/Simpler/Data/PropertyMappingTree/ArrayElementNode.cs @@ -1,11 +1,11 @@ using System; -namespace Simpler.Data.PropertyParseTree +namespace Simpler.Data.PropertyMappingTree { /// /// /// - public class PropertyParseTreeArrayChildNode : PropertyParseTreeNode + public class ArrayElementNode : AbstractNode { public override object CreateObject(object value = null) { diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayNode.cs b/app/Simpler/Data/PropertyMappingTree/ArrayNode.cs similarity index 62% rename from app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayNode.cs rename to app/Simpler/Data/PropertyMappingTree/ArrayNode.cs index 8d3212f..4862627 100644 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeArrayNode.cs +++ b/app/Simpler/Data/PropertyMappingTree/ArrayNode.cs @@ -1,15 +1,15 @@ using System; -namespace Simpler.Data.PropertyParseTree +namespace Simpler.Data.PropertyMappingTree { /// /// /// - public class PropertyParseTreeArrayNode : PropertyParseTreeObjectNode + public class ArrayNode : ObjectNode { public override object CreateObject(object value = null) { - return Activator.CreateInstance(PropertyType, new object[] { Nodes.Count }); + return Activator.CreateInstance(PropertyType, new object[] { Children.Count }); } } } diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicNode.cs b/app/Simpler/Data/PropertyMappingTree/DynamicNode.cs similarity index 64% rename from app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicNode.cs rename to app/Simpler/Data/PropertyMappingTree/DynamicNode.cs index ee4c2b5..4c3ba9d 100644 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicNode.cs +++ b/app/Simpler/Data/PropertyMappingTree/DynamicNode.cs @@ -1,11 +1,11 @@ using System.Dynamic; -namespace Simpler.Data.PropertyParseTree +namespace Simpler.Data.PropertyMappingTree { /// /// /// - public class PropertyParseTreeDynamicNode : PropertyParseTreeObjectNode + public class DynamicNode : ObjectNode { public override object CreateObject(object value = null) { diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicChildNode.cs b/app/Simpler/Data/PropertyMappingTree/DynamicPropertyNode.cs similarity index 84% rename from app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicChildNode.cs rename to app/Simpler/Data/PropertyMappingTree/DynamicPropertyNode.cs index 69ad861..5dca5bd 100644 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeDynamicChildNode.cs +++ b/app/Simpler/Data/PropertyMappingTree/DynamicPropertyNode.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Dynamic; -namespace Simpler.Data.PropertyParseTree +namespace Simpler.Data.PropertyMappingTree { /// /// /// - public class PropertyParseTreeDynamicChildNode : PropertyParseTreeNode + public class DynamicPropertyNode : AbstractNode { public override object CreateObject(object value = null) { diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeObjectNode.cs b/app/Simpler/Data/PropertyMappingTree/ObjectNode.cs similarity index 93% rename from app/Simpler/Data/PropertyParseTree/PropertyParseTreeObjectNode.cs rename to app/Simpler/Data/PropertyMappingTree/ObjectNode.cs index 925be53..3793589 100644 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeObjectNode.cs +++ b/app/Simpler/Data/PropertyMappingTree/ObjectNode.cs @@ -1,12 +1,12 @@ using System; using System.Reflection; -namespace Simpler.Data.PropertyParseTree +namespace Simpler.Data.PropertyMappingTree { /// /// /// - public class PropertyParseTreeObjectNode : PropertyParseTreeNode + public class ObjectNode : AbstractNode { /// /// diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs deleted file mode 100644 index 6f14cc9..0000000 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTree.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Simpler.Data.PropertyParseTree -{ - /// - /// A hierarchical collection of properties that can reference columns in a data reader, each represented by a PropertyParseTreeNode. - /// - public abstract class PropertyParseTree - { - /// - /// The property type of the current node - /// - public Type PropertyType { get; set; } - - - /// - /// A List of PropertyParseTreeNode that represents the property parse tree nodes assigned to the current property parse tree node. - /// - public List Nodes { get; set; } - - /// - /// Creates an instance of the specified type using the constructor that best matches the specified parameters. - /// - /// The default value for the newly created object - /// A reference to the newly created object. - public abstract object CreateObject(object value = null); - - /// - /// Initializes a new instance of the PropertyParseTree class. - /// - public PropertyParseTree() - { - Nodes = new List(); - } - - public PropertyParseTreeNode this[string index] - { - get - { - return Nodes.FirstOrDefault(x => x.Name == index); - } - - set - { - var listIndex = Nodes.FindIndex(x => x.Name == index); - value.Name = index; - if (listIndex == -1) - { - Nodes.Add(value); - } - else - { - Nodes[listIndex] = value; - } - } - } - - /// - /// - /// - public bool IsDynamicProperty { - get - { - return PropertyType.FullName == "System.Object"; - } - } - } -} diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs deleted file mode 100644 index 47cfb92..0000000 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeNode.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Simpler.Data.PropertyParseTree -{ - /// - /// - /// - public abstract class PropertyParseTreeNode : PropertyParseTree - { - /// - /// the name of the column from the data reader this node references. - /// - public string Name { get; set; } - - /// - /// The index of the column from the data reader this node references. - /// - public int? Index { get; set; } - - /// - /// Sets the property value of a specified object. - /// - /// The object whose property value will be set. - /// The new property value. - public abstract void SetValue(object obj, object value); - - /// - /// Returns the property value of a specified object. - /// - /// The object whose property value will be returned. - /// The property value of the specified object. - public abstract object GetValue(object obj); - } -} diff --git a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs b/app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs deleted file mode 100644 index a479e89..0000000 --- a/app/Simpler/Data/PropertyParseTree/PropertyParseTreeRootNode.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Dynamic; - -namespace Simpler.Data.PropertyParseTree -{ - /// - /// - /// - public class PropertyParseTreeRootNode : PropertyParseTree - { - public override object CreateObject(object value = null) - { - if (IsDynamicProperty) - { - return new ExpandoObject(); - } - - return Activator.CreateInstance(PropertyType); - } - } -} \ No newline at end of file diff --git a/app/Simpler/Data/Tasks/BuildObject.cs b/app/Simpler/Data/Tasks/BuildObject.cs index eba408a..e33e099 100644 --- a/app/Simpler/Data/Tasks/BuildObject.cs +++ b/app/Simpler/Data/Tasks/BuildObject.cs @@ -1,7 +1,7 @@ using System; using System.Data; using System.Dynamic; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; namespace Simpler.Data.Tasks { @@ -10,7 +10,7 @@ public class BuildObject : InOutTask.Input, BuildObject.Out public class Input { public IDataRecord DataRecord { get; set; } - public PropertyParseTree.PropertyParseTree PropertyParseTree { get; set; } + public AbstractNode PropertyParse { get; set; } } public class Output @@ -18,28 +18,28 @@ public class Output public T Object { get; set; } } - public void Parse(PropertyParseTreeNode treeNode, object obj) + public void Parse(AbstractNode node, object obj) { - //if the treeNode does not have a index then just set the value to the instance. - if (treeNode.Index == null) + //if the node does not have a index then just set the value to the instance. + if (node.Index == null) { - treeNode.SetValue(obj, null); + node.SetValue(obj, null); } else { //get the value from the data reader and set the value - var value = In.DataRecord.GetValue((int)treeNode.Index); + var value = In.DataRecord.GetValue((int)node.Index); if (value != null && value.GetType() != typeof(DBNull)) { - treeNode.SetValue(obj, value); + node.SetValue(obj, value); } } //get the newly created object - var childObj = treeNode.GetValue(obj); + var childObj = node.GetValue(obj); //continue down the parse tree - foreach (var childNode in treeNode.Nodes) + foreach (var childNode in node.Children) { Parse(childNode, childObj); } @@ -48,8 +48,8 @@ public void Parse(PropertyParseTreeNode treeNode, object obj) public override void Execute() { //create the root object - var obj = In.PropertyParseTree.CreateObject(); - foreach (var node in In.PropertyParseTree.Nodes) + var obj = In.PropertyParse.CreateObject(); + foreach (var node in In.PropertyParse.Children) { Parse(node, obj); } diff --git a/app/Simpler/Data/Tasks/BuildObjects.cs b/app/Simpler/Data/Tasks/BuildObjects.cs index 97c6515..f6e588b 100644 --- a/app/Simpler/Data/Tasks/BuildObjects.cs +++ b/app/Simpler/Data/Tasks/BuildObjects.cs @@ -18,7 +18,7 @@ public class Output } public FindColumns FindColumns { get; set; } - public BuildPropertyParseTree BuildPropertyParseTree { get; set; } + public BuildPropertyMappingTree BuildPropertyMappingTree { get; set; } public BuildObject BuildObject { get; set; } public override void Execute() @@ -31,13 +31,13 @@ public override void Execute() FindColumns.In.Reader = In.Reader; FindColumns.Execute(); - BuildPropertyParseTree.In.Columns = FindColumns.Out.Columns; - BuildPropertyParseTree.In.InitialType = typeof (T); - BuildPropertyParseTree.Execute(); + BuildPropertyMappingTree.In.Columns = FindColumns.Out.Columns; + BuildPropertyMappingTree.In.InitialType = typeof (T); + BuildPropertyMappingTree.Execute(); do { - BuildObject.In.PropertyParseTree = BuildPropertyParseTree.Out.PropertyParseTree; + BuildObject.In.PropertyParse = BuildPropertyMappingTree.Out.PropertyMappingTree; BuildObject.In.DataRecord = In.Reader; BuildObject.Execute(); objectList.Add(BuildObject.Out.Object); diff --git a/app/Simpler/Data/Tasks/BuildPropertyParseTree.cs b/app/Simpler/Data/Tasks/BuildPropertyMappingTree.cs similarity index 58% rename from app/Simpler/Data/Tasks/BuildPropertyParseTree.cs rename to app/Simpler/Data/Tasks/BuildPropertyMappingTree.cs index ff8d086..d911572 100644 --- a/app/Simpler/Data/Tasks/BuildPropertyParseTree.cs +++ b/app/Simpler/Data/Tasks/BuildPropertyMappingTree.cs @@ -3,11 +3,11 @@ using System.Data; using System.Linq; using System.Reflection; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; namespace Simpler.Data.Tasks { - public class BuildPropertyParseTree : InOutTask + public class BuildPropertyMappingTree : InOutTask { public class Input { @@ -17,14 +17,21 @@ public class Input public class Output { - public PropertyParseTree.PropertyParseTree PropertyParseTree { get; set; } + public AbstractNode PropertyMappingTree { get; set; } } public override void Execute() { var parseColumn = new ParseColumn(); - parseColumn.In.PropertyParseTree = new PropertyParseTreeRootNode { PropertyType = In.InitialType }; + if (In.InitialType.FullName == "System.Object") + { + parseColumn.In.RootNode = new DynamicNode{ PropertyType = In.InitialType }; + } + else + { + parseColumn.In.RootNode = new ObjectNode{ PropertyType = In.InitialType }; + } //order by the longest column names first var columns = In.Columns.OrderByDescending(x => x.Key.Length); @@ -34,7 +41,7 @@ public override void Execute() parseColumn.In.ColumnIndex = column.Value; parseColumn.Execute(); } - Out.PropertyParseTree = parseColumn.In.PropertyParseTree; + Out.PropertyMappingTree = parseColumn.In.RootNode; } } } diff --git a/app/Simpler/Data/Tasks/FindColumns.cs b/app/Simpler/Data/Tasks/FindColumns.cs index 4608180..cd6efbc 100644 --- a/app/Simpler/Data/Tasks/FindColumns.cs +++ b/app/Simpler/Data/Tasks/FindColumns.cs @@ -3,7 +3,7 @@ using System.Data; using System.Linq; using System.Reflection; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; namespace Simpler.Data.Tasks { diff --git a/app/Simpler/Data/Tasks/ParseColumn.cs b/app/Simpler/Data/Tasks/ParseColumn.cs index bd62dbd..de24c34 100644 --- a/app/Simpler/Data/Tasks/ParseColumn.cs +++ b/app/Simpler/Data/Tasks/ParseColumn.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Reflection; -using Simpler.Data.PropertyParseTree; +using Simpler.Data.PropertyMappingTree; namespace Simpler.Data.Tasks { @@ -12,45 +10,45 @@ public class Input { public int ColumnIndex { get; set; } public string ColumnName { get; set; } - public PropertyParseTree.PropertyParseTree PropertyParseTree { get; set; } + public AbstractNode RootNode { get; set; } } - private void ParseColumnName(PropertyParseTree.PropertyParseTree propertyParseTree, string path = "") + private void ParseColumnName(AbstractNode node, string path = "") { var remainingPath = In.ColumnName.Substring(path.Length); //check if we have reached the end of the path if (remainingPath.Length == 0) { - ((PropertyParseTreeNode) propertyParseTree).Index = In.ColumnIndex; + node.Index = In.ColumnIndex; return; } - var propertyType = propertyParseTree.PropertyType; + var propertyType = node.PropertyType; //if the parent is a dyanmic no need to look at properties just set it - if (propertyType == null || propertyType.FullName == "System.Object") + if (node is DynamicNode) { - propertyParseTree[remainingPath] = new PropertyParseTreeDynamicChildNode(); - ParseColumnName(propertyParseTree[remainingPath], path + remainingPath); + node[remainingPath] = new DynamicPropertyNode(); + ParseColumnName(node[remainingPath], path + remainingPath); return; } - //if the parent is an array we need to pull the index off and create a treeNode. - if (propertyType.IsArray) + //if the parent is an array we need to pull the index off and create a TreeNode. + if (node is ArrayNode) { //pull the index off the remainingPath var index = new String(remainingPath.TakeWhile(Char.IsDigit).ToArray()); - //create the treeNode if it doesn't exist - if(propertyParseTree.Nodes.All(x => x.Name != index)) + //create the TreeNode if it doesn't exist + if(node.Children.All(x => x.Name != index)) { - propertyParseTree[index] = new PropertyParseTreeArrayChildNode + node[index] = new ArrayElementNode { PropertyType = propertyType.GetElementType() }; } - ParseColumnName(propertyParseTree[index], path + index); + ParseColumnName(node[index], path + index); return; } @@ -64,13 +62,13 @@ private void ParseColumnName(PropertyParseTree.PropertyParseTree propertyParseTr Check.That(propertyInfo != null, "The DataRecord contains a column '{0}' that does not match a property or nested property.", In.ColumnName); - //check if the treeNode already exisits in the parse tree - if (!propertyParseTree.Nodes.Any(x => x.Name == propertyInfo.Name)) + //check if the node already exisits in the parse tree + if (!node.Children.Any(x => x.Name == propertyInfo.Name)) { - //determine the type of the treeNode and add it to the parse tree + //determine the type of the node and add it to the parse tree if (propertyInfo.PropertyType.FullName == "System.Object") { - propertyParseTree[propertyInfo.Name] = new PropertyParseTreeDynamicNode + node[propertyInfo.Name] = new DynamicNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType @@ -78,7 +76,7 @@ private void ParseColumnName(PropertyParseTree.PropertyParseTree propertyParseTr } else if (propertyInfo.PropertyType.IsArray) { - propertyParseTree[propertyInfo.Name] = new PropertyParseTreeArrayNode + node[propertyInfo.Name] = new ArrayNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType @@ -86,7 +84,7 @@ private void ParseColumnName(PropertyParseTree.PropertyParseTree propertyParseTr } else { - propertyParseTree[propertyInfo.Name] = new PropertyParseTreeObjectNode + node[propertyInfo.Name] = new ObjectNode { PropertyInfo = propertyInfo, PropertyType = propertyInfo.PropertyType @@ -94,12 +92,12 @@ private void ParseColumnName(PropertyParseTree.PropertyParseTree propertyParseTr } } - ParseColumnName(propertyParseTree[propertyInfo.Name], path + propertyInfo.Name); + ParseColumnName(node[propertyInfo.Name], path + propertyInfo.Name); } public override void Execute() { - ParseColumnName(In.PropertyParseTree); + ParseColumnName(In.RootNode); } } } diff --git a/app/Simpler/Simpler.csproj b/app/Simpler/Simpler.csproj index 2566135..ebf795e 100644 --- a/app/Simpler/Simpler.csproj +++ b/app/Simpler/Simpler.csproj @@ -52,19 +52,17 @@ - - - - - - - - + + + + + + - +