From 05858b6985c9908c96946ee1f35d2db366a85cf6 Mon Sep 17 00:00:00 2001 From: Ricardo Peres Date: Wed, 20 Aug 2014 11:39:06 +0100 Subject: [PATCH 1/4] NH-3670 Created additional tests with generic properties for NH-1796, NH-1039, NH-3571 and NH-2664 --- .../NHSpecificTest/NH1039Generic/Fixture.cs | 57 ++++++++ .../NH1039Generic/Mappings.hbm.xml | 21 +++ .../NHSpecificTest/NH1039Generic/Model.cs | 35 +++++ .../NHSpecificTest/NH1796Generic/Entity.cs | 12 ++ .../NHSpecificTest/NH1796Generic/Fixture.cs | 65 +++++++++ .../NH1796Generic/Mappings.hbm.xml | 16 +++ .../NHSpecificTest/NH2664Generic/Fixture.cs | 130 +++++++++++++++++ .../NH2664Generic/Mappings.hbm.xml | 17 +++ .../NHSpecificTest/NH2664Generic/Product.cs | 24 ++++ .../NHSpecificTest/NH3571Generic/Fixture.cs | 134 ++++++++++++++++++ .../NH3571Generic/Mappings.hbm.xml | 17 +++ .../NHSpecificTest/NH3571Generic/Product.cs | 43 ++++++ src/NHibernate.Test/NHibernate.Test.csproj | 14 +- .../Linq/Functions/DictionaryGenerator.cs | 2 +- src/NHibernate/Linq/NhRelinqQueryParser.cs | 3 +- src/NHibernate/Linq/Visitors/VisitorUtil.cs | 2 +- .../ByCode/IPlainPropertyContainerMapper.cs | 4 + .../PropertyContainerCustomizer.cs | 15 ++ .../Component/DynamicMapComponentTuplizer.cs | 28 +++- .../Tuple/DynamicMapInstantiator.cs | 15 +- 20 files changed, 645 insertions(+), 9 deletions(-) create mode 100644 src/NHibernate.Test/NHSpecificTest/NH1039Generic/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH1039Generic/Mappings.hbm.xml create mode 100644 src/NHibernate.Test/NHSpecificTest/NH1039Generic/Model.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH1796Generic/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH1796Generic/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH1796Generic/Mappings.hbm.xml create mode 100644 src/NHibernate.Test/NHSpecificTest/NH2664Generic/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH2664Generic/Mappings.hbm.xml create mode 100644 src/NHibernate.Test/NHSpecificTest/NH2664Generic/Product.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3571Generic/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3571Generic/Mappings.hbm.xml create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3571Generic/Product.cs diff --git a/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Fixture.cs new file mode 100644 index 00000000000..ea69b48b52f --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Fixture.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + + +namespace NHibernate.Test.NHSpecificTest.NH1039Generic +{ + [TestFixture] + public class Fixture : BugTestCase + { + public override string BugNumber + { + get { return "NH1039Generic"; } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + s.Delete("from Person"); + tx.Commit(); + } + } + + [Test] + public void test() + { + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + Person person = new Person("1"); + person.Name = "John Doe"; + var set = new HashSet(); + set.Add("555-1234"); + set.Add("555-4321"); + person.Properties.Add("Phones", set); + + s.Save(person); + tx.Commit(); + } + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + Person person = (Person)s.CreateCriteria(typeof(Person)).UniqueResult(); + + Assert.AreEqual("1", person.ID); + Assert.AreEqual("John Doe", person.Name); + Assert.AreEqual(1, person.Properties.Count); + Assert.That(person.Properties["Phones"], Is.InstanceOf>()); + Assert.IsTrue(((ISet) person.Properties["Phones"]).Contains("555-1234")); + Assert.IsTrue(((ISet) person.Properties["Phones"]).Contains("555-4321")); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Mappings.hbm.xml new file mode 100644 index 00000000000..5f6c057add1 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Mappings.hbm.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Model.cs b/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Model.cs new file mode 100644 index 00000000000..e8ceb658051 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Model.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace NHibernate.Test.NHSpecificTest.NH1039Generic +{ + public class Person + { + public Person() { } + public Person(string id) { this._ID = id; } + + private string _ID; + public virtual string ID + { + get { return _ID; } + set { _ID = value; } + } + + private string _Name; + public virtual string Name + { + get { return _Name; } + set { _Name = value; } + } + + private IDictionary _Properties = new Dictionary(); + public virtual IDictionary Properties + { + get { return _Properties; } + set { _Properties = value; } + } + } + +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Entity.cs b/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Entity.cs new file mode 100644 index 00000000000..b643a5a7e17 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Entity.cs @@ -0,0 +1,12 @@ +using System.Collections; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.NH1796Generic +{ + public class Entity + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual IDictionary DynProps { get; set; } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Fixture.cs new file mode 100644 index 00000000000..443753c9135 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Fixture.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH1796Generic +{ + [TestFixture] + public class Fixture: BugTestCase + { + [Test] + public void Merge() + { + var entity = new Entity { Name = "Vinnie Luther" }; + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.Save(entity); + t.Commit(); + } + + entity.DynProps = new Dictionary(); + entity.DynProps["StrProp"] = "Modified"; + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.Merge(entity); + t.Commit(); + } + + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.CreateQuery("delete from Entity").ExecuteUpdate(); + t.Commit(); + } + } + + [Test] + public void SaveOrUpdate() + { + var entity = new Entity { Name = "Vinnie Luther" }; + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.SaveOrUpdate(entity); + t.Commit(); + } + + entity.DynProps = new Dictionary(); + entity.DynProps["StrProp"] = "Modified"; + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.SaveOrUpdate(entity); + t.Commit(); + } + + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.CreateQuery("delete from Entity").ExecuteUpdate(); + t.Commit(); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Mappings.hbm.xml new file mode 100644 index 00000000000..dcdbc19a554 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH1796Generic/Mappings.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Fixture.cs new file mode 100644 index 00000000000..33359db9aac --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Fixture.cs @@ -0,0 +1,130 @@ +using System.Collections; +using System.Linq; +using NHibernate.Linq; +using NUnit.Framework; +using System.Linq.Expressions; +using System; + +namespace NHibernate.Test.NHSpecificTest.NH2664Generic +{ + [TestFixture] + public class Fixture : TestCase + { + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + protected override IList Mappings + { + get + { + return new[] + { + "NHSpecificTest.NH2664Generic.Mappings.hbm.xml" + }; + } + } + + /// + /// push some data into the database + /// Really functions as a save test also + /// + protected override void OnSetUp() + { + base.OnSetUp(); + + using (var session = OpenSession()) + { + using (var tran = session.BeginTransaction()) + { + Product product = new Product(); + product.ProductId = "1"; + product.Properties["Name"] = "First Product"; + product.Properties["Description"] = "First Description"; + + session.Save(product); + + product = new Product(); + product.ProductId = "2"; + product.Properties["Name"] = "Second Product"; + product.Properties["Description"] = "Second Description"; + + session.Save(product); + + product = new Product(); + product.ProductId = "3"; + product.Properties["Name"] = "val"; + product.Properties["Description"] = "val"; + + session.Save(product); + + tran.Commit(); + } + } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var session = OpenSession()) + { + using (var tran = session.BeginTransaction()) + { + session.Delete("from Product"); + tran.Commit(); + } + } + + } + + [Test] + public void Query_DynamicComponent() + { + using (var session = OpenSession()) + { + var product = + (from p in session.Query() where p.Properties["Name"] == "First Product" select p).Single(); + + Assert.IsNotNull(product); + Assert.AreEqual("First Product", product.Properties["Name"]); + } + } + + [Test] + public void Multiple_Query_Does_Not_Cache() + { + using (var session = OpenSession()) + { + // Query by name + var product1 = (from p in session.Query() + where p.Properties["Name"] == "First Product" + select p).Single(); + Assert.That(product1.ProductId, Is.EqualTo("1")); + + // Query by description (this test is to verify that the dictionary + // index isn't cached from the query above. + var product2 = (from p in session.Query() + where p.Properties["Description"] == "Second Description" + select p).Single(); + Assert.That(product2.ProductId, Is.EqualTo("2")); + } + } + + [Test] + public void Different_Key_In_DynamicComponentDictionary_Returns_Different_Keys() + { + using (var session = OpenSession()) + { + Expression> key1 = () => (from a in session.Query() where a.Properties["Name"] == "val" select a); + Expression> key2 = () => (from a in session.Query() where a.Properties["Description"] == "val" select a); + + var nhKey1 = new NhLinqExpression(key1.Body, sessions); + var nhKey2 = new NhLinqExpression(key2.Body, sessions); + + Assert.AreNotEqual(nhKey1.Key, nhKey2.Key); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Mappings.hbm.xml new file mode 100644 index 00000000000..e49ef76e13f --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Mappings.hbm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Product.cs b/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Product.cs new file mode 100644 index 00000000000..769dceae5b2 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH2664Generic/Product.cs @@ -0,0 +1,24 @@ +using System.Collections; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.NH2664Generic +{ + public class Product + { + public virtual string ProductId { get; set; } + + private IDictionary _properties; + + public virtual IDictionary Properties + { + get + { + if (_properties == null) + _properties = new Dictionary(); + + return _properties; + } + set { _properties = value; } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Fixture.cs new file mode 100644 index 00000000000..13ef0c9f8bb --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Fixture.cs @@ -0,0 +1,134 @@ +using System.Collections; +using System.Linq; +using NHibernate.Linq; +using NUnit.Framework; +using System.Linq.Expressions; +using System; + +namespace NHibernate.Test.NHSpecificTest.NH3571Generic +{ + [TestFixture] + public class Fixture : TestCase + { + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + protected override IList Mappings + { + get + { + return new[] + { + "NHSpecificTest.NH3571Generic.Mappings.hbm.xml" + }; + } + } + + /// + /// push some data into the database + /// Really functions as a save test also + /// + protected override void OnSetUp() + { + base.OnSetUp(); + + using (var session = OpenSession()) + { + using (var tran = session.BeginTransaction()) + { + var product = new Product {ProductId = "1"}; + product.Details.Properties["Name"] = "First Product"; + product.Details.Properties["Description"] = "First Description"; + + session.Save(product); + + product = new Product {ProductId = "2"}; + product.Details.Properties["Name"] = "Second Product"; + product.Details.Properties["Description"] = "Second Description"; + + session.Save(product); + + product = new Product {ProductId = "3"}; + product.Details.Properties["Name"] = "val"; + product.Details.Properties["Description"] = "val"; + + session.Save(product); + + tran.Commit(); + } + } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (var session = OpenSession()) + { + using (var tran = session.BeginTransaction()) + { + session.Delete("from Product"); + tran.Commit(); + } + } + + } + + [Test] + public void CanQueryDynamicComponentInComponent() + { + using (var session = OpenSession()) + { + var query = session.CreateQuery("from Product p where p.Details.Properties.Name=:name"); + query.SetString("name", "First Product"); + //var product = query.List().FirstOrDefault(); + var product = + (from p in session.Query() where (string) p.Details.Properties["Name"] == "First Product" select p).Single(); + + Assert.IsNotNull(product); + Assert.AreEqual("First Product", product.Details.Properties["Name"]); + } + } + + + [Test] + public void MultipleQueriesShouldNotCache() + { + using (var session = OpenSession()) + { + // Query by name + var product1 = (from p in session.Query() + where (string) p.Details.Properties["Name"] == "First Product" + select p).Single(); + Assert.That(product1.ProductId, Is.EqualTo("1")); + + // Query by description (this test is to verify that the dictionary + // index isn't cached from the query above. + var product2 = (from p in session.Query() + where (string) p.Details.Properties["Description"] == "Second Description" + select p).Single(); + Assert.That(product2.ProductId, Is.EqualTo("2")); + } + } + + + [Test] + public void DifferentKeyInDynamicComponentDictionaryReturnsDifferentExpressionKeys() + { + using (var session = OpenSession()) + { +// ReSharper disable AccessToDisposedClosure Ok since the expressions aren't actually used after the using block. + Expression> key1 = () => (from a in session.Query() where (string) a.Details.Properties["Name"] == "val" select a); + Expression> key2 = () => (from a in session.Query() where (string) a.Details.Properties["Description"] == "val" select a); +// ReSharper restore AccessToDisposedClosure + + var nhKey1 = new NhLinqExpression(key1.Body, sessions); + var nhKey2 = new NhLinqExpression(key2.Body, sessions); + + Assert.AreNotEqual(nhKey1.Key, nhKey2.Key); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Mappings.hbm.xml new file mode 100644 index 00000000000..dcd0da1877e --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Mappings.hbm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Product.cs b/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Product.cs new file mode 100644 index 00000000000..c808bb7c1a7 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Product.cs @@ -0,0 +1,43 @@ +using System.Collections; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.NH3571Generic +{ + public class Product + { + public Product() + { + Details = new ProductDetails(); + } + public virtual string ProductId { get; set; } + + public virtual ProductDetails Details + { + get; + set; + } + + public virtual IList DetailsList + { + get; + set; + } + } + + public class ProductDetails + { + private IDictionary _properties; + + public virtual IDictionary Properties + { + get + { + if (_properties == null) + _properties = new Dictionary(); + + return _properties; + } + set { _properties = value; } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index a8d325494c7..c7d14f881c6 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -687,6 +687,14 @@ + + + + + + + + @@ -3047,6 +3055,10 @@ + + + + @@ -3630,4 +3642,4 @@ if exist hibernate.cfg.xml (del hibernate.cfg.xml) if exist "$(ProjectDir)hibernate.cfg.xml" (copy "$(ProjectDir)hibernate.cfg.xml" "hibernate.cfg.xml") copy /y "..\..\..\NHibernate.DomainModel\ABC.hbm.xml" "ABC.hbm.xml" - + \ No newline at end of file diff --git a/src/NHibernate/Linq/Functions/DictionaryGenerator.cs b/src/NHibernate/Linq/Functions/DictionaryGenerator.cs index d45e594a540..c87a5c686b6 100644 --- a/src/NHibernate/Linq/Functions/DictionaryGenerator.cs +++ b/src/NHibernate/Linq/Functions/DictionaryGenerator.cs @@ -58,7 +58,7 @@ public abstract class DictionaryRuntimeMethodHqlGeneratorBase : IRun public bool SupportsMethod(MethodInfo method) { - return (method != null) && (method.Name == MethodName) && method.IsMethodOf(typeof(IDictionary)); + return (method != null) && (method.Name == MethodName) && (method.IsMethodOf(typeof(IDictionary)) || method.IsMethodOf(typeof(IDictionary))); } public IHqlGeneratorForMethod GetMethodGenerator(MethodInfo method) diff --git a/src/NHibernate/Linq/NhRelinqQueryParser.cs b/src/NHibernate/Linq/NhRelinqQueryParser.cs index 4ef1f85ac29..7d92a6aa74a 100644 --- a/src/NHibernate/Linq/NhRelinqQueryParser.cs +++ b/src/NHibernate/Linq/NhRelinqQueryParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -86,7 +87,7 @@ public NHibernateNodeTypeProvider() public bool IsRegistered(MethodInfo method) { // Avoid Relinq turning IDictionary.Contains into ContainsResultOperator. We do our own processing for that method. - if (method.DeclaringType == typeof(IDictionary) && method.Name == "Contains") + if ((method.DeclaringType == typeof(IDictionary) || method.DeclaringType == typeof(IDictionary)) && method.Name == "Contains") return false; return defaultNodeTypeProvider.IsRegistered(method); diff --git a/src/NHibernate/Linq/Visitors/VisitorUtil.cs b/src/NHibernate/Linq/Visitors/VisitorUtil.cs index 08fcf11a985..ea0847ffe2d 100644 --- a/src/NHibernate/Linq/Visitors/VisitorUtil.cs +++ b/src/NHibernate/Linq/Visitors/VisitorUtil.cs @@ -16,7 +16,7 @@ public static bool IsDynamicComponentDictionaryGetter(MethodInfo method, Express // A dynamic component must be an IDictionary with a string key. - if (method.Name != "get_Item" || !typeof(IDictionary).IsAssignableFrom(targetObject.Type)) + if (method.Name != "get_Item" || (!typeof(IDictionary).IsAssignableFrom(targetObject.Type) && !typeof(IDictionary).IsAssignableFrom(targetObject.Type))) return false; var key = arguments.First() as ConstantExpression; diff --git a/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs b/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs index 32ce29e6ac1..fa28f51ee94 100644 --- a/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs +++ b/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; @@ -43,6 +44,9 @@ void Component(Expression> property, void Component(Expression> property, TComponent dynamicComponentTemplate, Action> mapping) where TComponent : class; + void Component(Expression>> property, + TComponent dynamicComponentTemplate, + Action> mapping) where TComponent : class; void Component(string notVisiblePropertyOrFieldName, Action> mapping) where TComponent : class; diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs index d417d7a62ce..a0f9fb46c89 100644 --- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs +++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -103,6 +104,13 @@ public void Component(Expression> propert RegisterDynamicComponentMapping(property, mapping); } + public void Component(Expression>> property, + TComponent dynamicComponentTemplate, + Action> mapping) where TComponent : class + { + RegisterDynamicComponentMapping(property, mapping); + } + protected virtual void RegisterDynamicComponentMapping(Expression> property, Action> mapping) where TComponent : class { MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property); @@ -110,6 +118,13 @@ protected virtual void RegisterDynamicComponentMapping(Expression(mapping, member, memberOf); } + protected virtual void RegisterDynamicComponentMapping(Expression>> property, Action> mapping) where TComponent : class + { + MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property); + MemberInfo memberOf = TypeExtensions.DecodeMemberAccessExpressionOf(property); + RegisterDynamicComponentMapping(mapping, member, memberOf); + } + protected void RegisterDynamicComponentMapping(Action> mapping, params MemberInfo[] members) where TComponent : class { diff --git a/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs b/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs index 17bc0ae2271..024e1829603 100644 --- a/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs +++ b/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs @@ -1,5 +1,9 @@ using System; +using System.Linq; +using System.Collections.Generic; using NHibernate.Properties; +using System.Collections; +using System.Reflection; namespace NHibernate.Tuple.Component { @@ -9,21 +13,41 @@ namespace NHibernate.Tuple.Component [Serializable] public class DynamicMapComponentTuplizer : AbstractComponentTuplizer { + private readonly Mapping.Component component; + public DynamicMapComponentTuplizer(Mapping.Component component) : base(component) { + this.component = component; // Fix for NH-3119 instantiator = BuildInstantiator(component); } public override System.Type MappedClass { - get { return typeof(System.Collections.IDictionary); } + get + { + var ownerFullName = this.component.Owner.MappedClass.FullName; + var roleName = this.component.RoleName; + var spare = roleName.Substring(ownerFullName.Length + 1); + var parts = spare.Split('.'); + var prop = null as PropertyInfo; + var owner = this.component.Owner.MappedClass; + + for (var i = 0; i < parts.Length; ++i) + { + prop = owner.GetProperty(parts[i], BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + + owner = prop.PropertyType; + } + + return typeof(IDictionary).IsAssignableFrom(prop.PropertyType) ? typeof(IDictionary) : typeof(IDictionary); + } } protected internal override IInstantiator BuildInstantiator(Mapping.Component component) { - return new DynamicMapInstantiator(); + return new DynamicMapInstantiator(component); } protected internal override IGetter BuildGetter(Mapping.Component component, Mapping.Property prop) diff --git a/src/NHibernate/Tuple/DynamicMapInstantiator.cs b/src/NHibernate/Tuple/DynamicMapInstantiator.cs index d2054c743f9..86f2022898f 100644 --- a/src/NHibernate/Tuple/DynamicMapInstantiator.cs +++ b/src/NHibernate/Tuple/DynamicMapInstantiator.cs @@ -12,10 +12,12 @@ public class DynamicMapInstantiator : IInstantiator private readonly string entityName; private readonly HashSet isInstanceEntityNames = new HashSet(); + private readonly Mapping.Component component; - public DynamicMapInstantiator() + public DynamicMapInstantiator(Mapping.Component component) { entityName = null; + this.component = component; } public DynamicMapInstantiator(PersistentClass mappingInfo) @@ -48,7 +50,14 @@ public object Instantiate() protected virtual IDictionary GenerateMap() { - return new Hashtable(); + if ((this.component != null) && (typeof(IDictionary).IsAssignableFrom(this.component.Type.ReturnedClass) == true)) + { + return new Dictionary(); + } + else + { + return new Hashtable(); + } } public bool IsInstance(object obj) @@ -65,7 +74,7 @@ public bool IsInstance(object obj) } else { - return false; + return obj is IDictionary; } } From 7fa84e5fa577dc17b203e1e957777f76b9c892e1 Mon Sep 17 00:00:00 2001 From: Ricardo Peres Date: Sun, 31 Aug 2014 22:28:18 +0100 Subject: [PATCH 2/4] NH-3670 --- .../Component/DynamicMapComponentTuplizer.cs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs b/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs index 024e1829603..e763f5ea69f 100644 --- a/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs +++ b/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs @@ -27,21 +27,28 @@ public override System.Type MappedClass { get { - var ownerFullName = this.component.Owner.MappedClass.FullName; - var roleName = this.component.RoleName; - var spare = roleName.Substring(ownerFullName.Length + 1); - var parts = spare.Split('.'); - var prop = null as PropertyInfo; - var owner = this.component.Owner.MappedClass; - - for (var i = 0; i < parts.Length; ++i) + if ((this.component != null) && (this.component.Owner.MappedClass != null)) { - prop = owner.GetProperty(parts[i], BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + var ownerFullName = this.component.Owner.MappedClass.FullName; + var roleName = this.component.RoleName; + var spare = roleName.Substring(ownerFullName.Length + 1); + var parts = spare.Split('.'); + var prop = null as PropertyInfo; + var owner = this.component.Owner.MappedClass; - owner = prop.PropertyType; - } + for (var i = 0; i < parts.Length; ++i) + { + prop = owner.GetProperty(parts[i], BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + + owner = prop.PropertyType; + } - return typeof(IDictionary).IsAssignableFrom(prop.PropertyType) ? typeof(IDictionary) : typeof(IDictionary); + return typeof(IDictionary).IsAssignableFrom(prop.PropertyType) ? typeof(IDictionary) : typeof(IDictionary); + } + else + { + return typeof(IDictionary); + } } } From cd7fe59d16ab39712d2293411a208f31731b6f8a Mon Sep 17 00:00:00 2001 From: Ricardo Peres Date: Tue, 2 Sep 2014 22:37:28 +0100 Subject: [PATCH 3/4] Added more unit tests --- .../DynamicComponentMappingTests.cs | 72 +++++++++++++++++++ .../AbstractPropertyContainerMapperTest.cs | 23 ++++++ .../ComponentPropertyOnDynamicCompoTests.cs | 51 +++++++++++++ ...DynComponentPropertyOnDynamicCompoTests.cs | 52 ++++++++++++++ 4 files changed, 198 insertions(+) diff --git a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs index 9d620418727..0920dd2f569 100644 --- a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs +++ b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; @@ -21,6 +22,17 @@ public IDictionary Info } } + private class PersonWithGenericInfo + { + public int Id { get; set; } + private IDictionary info; + public IDictionary Info + { + get { return info; } + set { info = value; } + } + } + [Test] public void WhenMapDynCompoThenMapItAndItsProperties() { @@ -38,6 +50,23 @@ public void WhenMapDynCompoThenMapItAndItsProperties() hbmDynamicComponent.Properties.Select(x=> x.Name).Should().Have.SameValuesAs("MyInt", "MyDate"); } + [Test] + public void WhenMapDynCompoThenMapItAndItsPropertiesGeneric() + { + var mapper = new ModelMapper(); + mapper.Class(map => + { + map.Id(x => x.Id, idmap => { }); + map.Component(x => x.Info, new { MyInt = 5, MyDate = DateTime.Now }, z => { }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(PersonWithGenericInfo) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().SingleOrDefault(); + hbmDynamicComponent.Should().Not.Be.Null(); + hbmDynamicComponent.Properties.Select(x => x.Name).Should().Have.SameValuesAs("MyInt", "MyDate"); + } + [Test] public void WhenMapDynCompoPropertiesThenShouldAssignPropertyType() { @@ -54,6 +83,22 @@ public void WhenMapDynCompoPropertiesThenShouldAssignPropertyType() hbmDynamicComponent.Properties.OfType().Select(x => x.type1).All(x=> x.Satisfy(value=> !string.IsNullOrEmpty(value))); } + [Test] + public void WhenMapDynCompoPropertiesThenShouldAssignPropertyTypeGeneric() + { + var mapper = new ModelMapper(); + mapper.Class(map => + { + map.Id(x => x.Id, idmap => { }); + map.Component(x => x.Info, new { MyInt = 5, MyDate = DateTime.Now }, z => { }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(PersonWithGenericInfo) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().Single(); + hbmDynamicComponent.Properties.OfType().Select(x => x.type1).All(x => x.Satisfy(value => !string.IsNullOrEmpty(value))); + } + [Test] public void WhenMapDynCompoAttributesThenMapAttributes() { @@ -80,5 +125,32 @@ public void WhenMapDynCompoAttributesThenMapAttributes() hbmDynamicComponent.optimisticlock.Should().Be.False(); hbmDynamicComponent.unique.Should().Be.True(); } + + [Test] + public void WhenMapDynCompoAttributesThenMapAttributesGeneric() + { + var mapper = new ModelMapper(); + mapper.Class(map => + { + map.Id(x => x.Id, idmap => { }); + map.Component(x => x.Info, new { MyInt = 5 }, z => + { + z.Access(Accessor.Field); + z.Insert(false); + z.Update(false); + z.Unique(true); + z.OptimisticLock(false); + }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(PersonWithGenericInfo) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().SingleOrDefault(); + hbmDynamicComponent.access.Should().Contain("field"); + hbmDynamicComponent.insert.Should().Be.False(); + hbmDynamicComponent.update.Should().Be.False(); + hbmDynamicComponent.optimisticlock.Should().Be.False(); + hbmDynamicComponent.unique.Should().Be.True(); + } } } \ No newline at end of file diff --git a/src/NHibernate.Test/MappingByCode/MappersTests/AbstractPropertyContainerMapperTest.cs b/src/NHibernate.Test/MappingByCode/MappersTests/AbstractPropertyContainerMapperTest.cs index 0b33f95696c..6566c794caf 100644 --- a/src/NHibernate.Test/MappingByCode/MappersTests/AbstractPropertyContainerMapperTest.cs +++ b/src/NHibernate.Test/MappingByCode/MappersTests/AbstractPropertyContainerMapperTest.cs @@ -37,6 +37,10 @@ private class MyClassWithDynamic { public IDictionary DynCompo { get; set; } } + private class MyClassWithDynamicGeneric + { + public IDictionary DynCompo { get; set; } + } [Test] public void CantCreateWithoutHbmMapping() @@ -126,6 +130,15 @@ public void AddDynamicComponentProperty() properties.Single().Should().Be.OfType().And.ValueOf.Name.Should().Be.EqualTo("DynCompo"); } + [Test] + public void AddDynamicComponentPropertyGeneric() + { + var properties = new List(); + var map = new StubPropertyContainerMapper(properties); + map.Component(For.Property(x => x.DynCompo), (IDynamicComponentMapper cp) => { }); + properties.Single().Should().Be.OfType().And.ValueOf.Name.Should().Be.EqualTo("DynCompo"); + } + [Test] public void CallDynamicComponentMapper() { @@ -136,6 +149,16 @@ public void CallDynamicComponentMapper() called.Should().Be.True(); } + [Test] + public void CallDynamicComponentMapperGeneric() + { + var properties = new List(); + var map = new StubPropertyContainerMapper(properties); + var called = false; + map.Component(For.Property(x => x.DynCompo), (IDynamicComponentMapper cp) => called = true); + called.Should().Be.True(); + } + private class HackPropertyContainerMapper : AbstractPropertyContainerMapper { public HackPropertyContainerMapper(System.Type container, HbmMapping mapDoc) : base(container, mapDoc) {} diff --git a/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/ComponentPropertyOnDynamicCompoTests.cs b/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/ComponentPropertyOnDynamicCompoTests.cs index 0b584410fbd..a716019030c 100644 --- a/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/ComponentPropertyOnDynamicCompoTests.cs +++ b/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/ComponentPropertyOnDynamicCompoTests.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Collections.Generic; using System.Linq; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; @@ -20,6 +21,16 @@ public IDictionary Info } } + private class PersonWithGenericInfo + { + public int Id { get; set; } + private IDictionary info; + public IDictionary Info + { + get { return info; } + } + } + private class MyClass { public int Something { get; set; } @@ -37,6 +48,19 @@ public void WhenAddThenHas() component.Properties.Select(x => x.Name).Should().Have.SameSequenceAs("A"); } + [Test] + public void WhenAddThenHasGeneric() + { + var mapdoc = new HbmMapping(); + var component = new HbmDynamicComponent(); + var mapper = new DynamicComponentMapper(component, For.Property(p => p.Info), mapdoc); + var propertyInfo = (new { A = (MyClass)null }).GetType().GetProperty("A"); + + mapper.Component(propertyInfo, (IComponentMapper x) => { }); + + component.Properties.Select(x => x.Name).Should().Have.SameSequenceAs("A"); + } + [Test] public void WhenCustomizeThenCallCustomizer() { @@ -51,6 +75,20 @@ public void WhenCustomizeThenCallCustomizer() called.Should().Be.True(); } + [Test] + public void WhenCustomizeThenCallCustomizerGeneric() + { + var mapdoc = new HbmMapping(); + var component = new HbmDynamicComponent(); + var mapper = new DynamicComponentMapper(component, For.Property(p => p.Info), mapdoc); + var propertyInfo = (new { A = (MyClass)null }).GetType().GetProperty("A"); + + var called = false; + mapper.Component(propertyInfo, (IComponentMapper x) => called = true); + + called.Should().Be.True(); + } + [Test] public void WhenCustomizeAccessorThenIgnore() { @@ -63,5 +101,18 @@ public void WhenCustomizeAccessorThenIgnore() component.Properties.OfType().Single().Access.Should().Be.NullOrEmpty(); } + + [Test] + public void WhenCustomizeAccessorThenIgnoreGeneric() + { + var mapdoc = new HbmMapping(); + var component = new HbmDynamicComponent(); + var mapper = new DynamicComponentMapper(component, For.Property(p => p.Info), mapdoc); + var propertyInfo = (new { A = (MyClass)null }).GetType().GetProperty("A"); + + mapper.Component(propertyInfo, (IComponentMapper x) => x.Access(Accessor.Field)); + + component.Properties.OfType().Single().Access.Should().Be.NullOrEmpty(); + } } } \ No newline at end of file diff --git a/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/DynComponentPropertyOnDynamicCompoTests.cs b/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/DynComponentPropertyOnDynamicCompoTests.cs index 0c43769697f..48b1bf39b3f 100644 --- a/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/DynComponentPropertyOnDynamicCompoTests.cs +++ b/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/DynComponentPropertyOnDynamicCompoTests.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Collections.Generic; using System.Linq; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; @@ -20,6 +21,17 @@ public IDictionary Info } } + private class PersonWithGenericInfo + { + public int Id { get; set; } + private IDictionary info; + public IDictionary Info + { + get { return info; } + } + } + + [Test] public void WhenAddThenHas() { @@ -33,6 +45,19 @@ public void WhenAddThenHas() component.Properties.Select(x => x.Name).Should().Have.SameSequenceAs("Info"); } + [Test] + public void WhenAddThenHasGeneric() + { + var mapdoc = new HbmMapping(); + var component = new HbmDynamicComponent(); + var mapper = new DynamicComponentMapper(component, For.Property(p => p.Info), mapdoc); + var propertyInfo = For.Property(p => p.Info);//just as another dyn-compo + + mapper.Component(propertyInfo, (IDynamicComponentMapper x) => { }); + + component.Properties.Select(x => x.Name).Should().Have.SameSequenceAs("Info"); + } + [Test] public void WhenCustomizeThenCallCustomizer() { @@ -47,6 +72,20 @@ public void WhenCustomizeThenCallCustomizer() called.Should().Be.True(); } + [Test] + public void WhenCustomizeThenCallCustomizerGeneric() + { + var mapdoc = new HbmMapping(); + var component = new HbmDynamicComponent(); + var mapper = new DynamicComponentMapper(component, For.Property(p => p.Info), mapdoc); + var propertyInfo = For.Property(p => p.Info);//just as another dyn-compo + + var called = false; + mapper.Component(propertyInfo, (IDynamicComponentMapper x) => called = true); + + called.Should().Be.True(); + } + [Test] public void WhenCustomizeAccessorThenIgnore() { @@ -59,5 +98,18 @@ public void WhenCustomizeAccessorThenIgnore() component.Properties.OfType().Single().Access.Should().Be.NullOrEmpty(); } + + [Test] + public void WhenCustomizeAccessorThenIgnoreGeneric() + { + var mapdoc = new HbmMapping(); + var component = new HbmDynamicComponent(); + var mapper = new DynamicComponentMapper(component, For.Property(p => p.Info), mapdoc); + var propertyInfo = For.Property(p => p.Info);//just as another dyn-compo + + mapper.Component(propertyInfo, (IDynamicComponentMapper x) => x.Access(Accessor.Field)); + + component.Properties.OfType().Single().Access.Should().Be.NullOrEmpty(); + } } } \ No newline at end of file From ff8767b3618d6e092e08ff7f2edb801a5794a2ec Mon Sep 17 00:00:00 2001 From: Ricardo Peres Date: Tue, 2 Sep 2014 22:59:51 +0100 Subject: [PATCH 4/4] Added unit test --- src/NHibernate.Test/Linq/LinqQuerySamples.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/NHibernate.Test/Linq/LinqQuerySamples.cs b/src/NHibernate.Test/Linq/LinqQuerySamples.cs index 26b00200b9a..a0f4b431e33 100755 --- a/src/NHibernate.Test/Linq/LinqQuerySamples.cs +++ b/src/NHibernate.Test/Linq/LinqQuerySamples.cs @@ -897,6 +897,22 @@ into g ObjectDumper.Write(q, 1); } + [Test] + public void GroupTwoQueriesAndSum() + { + //NH-3534 + var queryWithAggregation = from o1 in db.Orders + from o2 in db.Orders + where o1.Customer.CustomerId == o2.Customer.CustomerId && o1.OrderDate == o2.OrderDate + group o1 by new { o1.Customer.CustomerId, o1.OrderDate } into g + select new { CustomerId = g.Key.CustomerId, LastOrderDate = g.Max(x => x.OrderDate) }; + + var result = queryWithAggregation.ToList(); + + Assert.IsNotNull(result); + Assert.IsNotEmpty(result); + } + [Category("GROUP BY/HAVING")] [Test(Description = "This sample uses a where clause after a group by clause " + "to find all categories that have at least 10 products.")]