diff --git a/doc/reference/modules/basic_mapping.xml b/doc/reference/modules/basic_mapping.xml
index c6d349b29fd..923714dec99 100644
--- a/doc/reference/modules/basic_mapping.xml
+++ b/doc/reference/modules/basic_mapping.xml
@@ -2081,7 +2081,8 @@
The <dynamic-component> element allows an IDictionary
- to be mapped as a component, where the property names refer to keys of the dictionary.
+ or IDictionary to be mapped as a component, where the property
+ names refer to keys of the dictionary.
diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH1039Generic/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH1039Generic/Fixture.cs
new file mode 100644
index 00000000000..dc9c9831243
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/NH1039Generic/Fixture.cs
@@ -0,0 +1,68 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+
+namespace NHibernate.Test.NHSpecificTest.NH1039Generic
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FixtureAsync : 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 async Task testAsync()
+ {
+ 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);
+
+ await (s.SaveAsync(person));
+ await (tx.CommitAsync());
+ }
+ using (ISession s = OpenSession())
+ using (ITransaction tx = s.BeginTransaction())
+ {
+ Person person = (Person)await (s.CreateCriteria(typeof(Person)).UniqueResultAsync());
+
+ 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/Async/NHSpecificTest/NH1796Generic/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH1796Generic/Fixture.cs
new file mode 100644
index 00000000000..7d31066907f
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/NH1796Generic/Fixture.cs
@@ -0,0 +1,76 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Collections.Generic;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.NH1796Generic
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FixtureAsync: BugTestCase
+ {
+ [Test]
+ public async Task MergeAsync()
+ {
+ var entity = new Entity { Name = "Vinnie Luther" };
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.SaveAsync(entity));
+ await (t.CommitAsync());
+ }
+
+ entity.DynProps = new Dictionary();
+ entity.DynProps["StrProp"] = "Modified";
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.MergeAsync(entity));
+ await (t.CommitAsync());
+ }
+
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.CreateQuery("delete from Entity").ExecuteUpdateAsync());
+ await (t.CommitAsync());
+ }
+ }
+
+ [Test]
+ public async Task SaveOrUpdateAsync()
+ {
+ var entity = new Entity { Name = "Vinnie Luther" };
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.SaveOrUpdateAsync(entity));
+ await (t.CommitAsync());
+ }
+
+ entity.DynProps = new Dictionary();
+ entity.DynProps["StrProp"] = "Modified";
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.SaveOrUpdateAsync(entity));
+ await (t.CommitAsync());
+ }
+
+ using (ISession s = OpenSession())
+ using (ITransaction t = s.BeginTransaction())
+ {
+ await (s.CreateQuery("delete from Entity").ExecuteUpdateAsync());
+ await (t.CommitAsync());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH2664Generic/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH2664Generic/Fixture.cs
new file mode 100644
index 00000000000..8080952e1f9
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/NH2664Generic/Fixture.cs
@@ -0,0 +1,124 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Collections;
+using System.Linq;
+using NHibernate.Linq;
+using NUnit.Framework;
+using System.Linq.Expressions;
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.NH2664Generic
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FixtureAsync : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override IList Mappings => new[] {"NHSpecificTest.NH2664Generic.Mappings.hbm.xml"};
+
+ ///
+ /// push some data into the database
+ /// Really functions as a save test also
+ ///
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var tran = session.BeginTransaction())
+ {
+ session.Save(
+ new Product
+ {
+ ProductId = "1",
+ Properties =
+ {
+ ["Name"] = "First Product",
+ ["Description"] = "First Description"
+ }
+ });
+
+ session.Save(
+ new Product
+ {
+ ProductId = "2",
+ Properties =
+ {
+ ["Name"] = "Second Product",
+ ["Description"] = "Second Description"
+ }
+ });
+
+ session.Save(
+ new Product
+ {
+ ProductId = "3",
+ Properties =
+ {
+ ["Name"] = "val",
+ ["Description"] = "val"
+ }
+ });
+
+ tran.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var tran = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Product").ExecuteUpdate();
+ tran.Commit();
+ }
+ }
+
+ [Test]
+ public async Task Query_DynamicComponentAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var product = await ((
+ from p in session.Query()
+ where (string) p.Properties["Name"] == "First Product"
+ select p
+ ).SingleAsync());
+
+ Assert.That(product, Is.Not.Null);
+ Assert.That(product.Properties["Name"], Is.EqualTo("First Product"));
+ }
+ }
+
+ [Test]
+ public async Task Multiple_Query_Does_Not_CacheAsync()
+ {
+ using (var session = OpenSession())
+ {
+ // Query by name
+ var product1 = await ((
+ from p in session.Query()
+ where (string) p.Properties["Name"] == "First Product"
+ select p
+ ).SingleAsync());
+ 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 = await ((
+ from p in session.Query()
+ where (string) p.Properties["Description"] == "Second Description"
+ select p
+ ).SingleAsync());
+ Assert.That(product2.ProductId, Is.EqualTo("2"));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3571Generic/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3571Generic/Fixture.cs
new file mode 100644
index 00000000000..3af2d88003a
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3571Generic/Fixture.cs
@@ -0,0 +1,109 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Collections;
+using System.Linq;
+using NHibernate.Linq;
+using NUnit.Framework;
+using System.Linq.Expressions;
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.NH3571Generic
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FixtureAsync : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override IList Mappings => new[] {"NHSpecificTest.NH3571Generic.Mappings.hbm.xml"};
+
+ ///
+ /// push some data into the database
+ /// Really functions as a save test also
+ ///
+ protected override void 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()
+ {
+ using (var session = OpenSession())
+ using (var tran = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Product").ExecuteUpdate();
+ tran.Commit();
+ }
+ }
+
+ [Test]
+ public async Task CanQueryDynamicComponentInComponentAsync()
+ {
+ using (var session = OpenSession())
+ {
+ var product = await ((
+ from p in session.Query()
+ where (string) p.Details.Properties["Name"] == "First Product"
+ select p
+ ).SingleAsync());
+
+ Assert.That(product, Is.Not.Null);
+ Assert.That(product.Details.Properties["Name"], Is.EqualTo("First Product"));
+ }
+ }
+
+ [Test]
+ public async Task MultipleQueriesShouldNotCacheAsync()
+ {
+ using (var session = OpenSession())
+ {
+ // Query by name
+ var product1 = await ((
+ from p in session.Query()
+ where (string) p.Details.Properties["Name"] == "First Product"
+ select p
+ ).SingleAsync());
+ 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 = await ((
+ from p in session.Query()
+ where (string) p.Details.Properties["Description"] == "Second Description"
+ select p
+ ).SingleAsync());
+ Assert.That(product2.ProductId, Is.EqualTo("2"));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs
index c5db3e91f91..457107d5a4e 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()
Assert.That(hbmDynamicComponent.Properties.Select(x=> x.Name), Is.EquivalentTo(new [] {"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();
+ Assert.That(hbmDynamicComponent, Is.Not.Null);
+ Assert.That(hbmDynamicComponent.Properties.Select(x=> x.Name), Is.EquivalentTo(new [] {"MyInt", "MyDate"}));
+ }
+
[Test]
public void WhenMapDynCompoPropertiesThenShouldAssignPropertyType()
{
@@ -54,6 +83,22 @@ public void WhenMapDynCompoPropertiesThenShouldAssignPropertyType()
Assert.That(hbmDynamicComponent.Properties.OfType().All(x => !string.IsNullOrEmpty(x.type1)), Is.True);
}
+ [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();
+ Assert.That(hbmDynamicComponent.Properties.OfType().All(x => !string.IsNullOrEmpty(x.type1)), Is.True);
+ }
+
[Test]
public void WhenMapDynCompoAttributesThenMapAttributes()
{
@@ -61,14 +106,17 @@ public void WhenMapDynCompoAttributesThenMapAttributes()
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);
- });
+ 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(Person) });
@@ -80,5 +128,32 @@ public void WhenMapDynCompoAttributesThenMapAttributes()
Assert.That(hbmDynamicComponent.optimisticlock, Is.False);
Assert.That(hbmDynamicComponent.unique, Is.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();
+ Assert.That(hbmDynamicComponent.access, Does.Contain("field"));
+ Assert.That(hbmDynamicComponent.insert, Is.False);
+ Assert.That(hbmDynamicComponent.update, Is.False);
+ Assert.That(hbmDynamicComponent.optimisticlock, Is.False);
+ Assert.That(hbmDynamicComponent.unique, Is.True);
+ }
}
}
diff --git a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/JoinGenericDynamicComponentTests.cs b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/JoinGenericDynamicComponentTests.cs
new file mode 100644
index 00000000000..d21147c6acd
--- /dev/null
+++ b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/JoinGenericDynamicComponentTests.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NHibernate.Mapping.ByCode.Conformist;
+using NUnit.Framework;
+
+namespace NHibernate.Test.MappingByCode.ExplicitMappingTests
+{
+ [TestFixture]
+ public class JoinGenericDynamicComponentTests
+ {
+ public class MyClass
+ {
+ public virtual int Code { get; set; }
+ public virtual string JoinedName { get; set; }
+ public virtual MyOther Relation { get; set; }
+ public virtual MyOther JoinedRelation { get; set; }
+ public virtual IDictionary Attributes { get; set; }
+ public virtual IDictionary JoinedAttributes { get; set; }
+ }
+
+ public class MyOther
+ {
+ public int Id { get; set; }
+ }
+
+ public class MyClassMap : ClassMapping
+ {
+ public MyClassMap()
+ {
+ // basic table related properties
+ ManyToOne(x => x.Relation);
+ Component(p => p.Attributes,
+ new
+ {
+ IsVisible = false,
+ Hash = default(Guid),
+ Reference = default(MyOther)
+ },
+ m =>
+ {
+ m.Property(p => p.IsVisible);
+ m.Property(p => p.Hash);
+ m.ManyToOne(p => p.Reference);
+ });
+ Property(x => x.Code);
+
+ // joined table stuff
+ Join("JoinedAttributes", x =>
+ {
+ x.Property(p => p.JoinedName);
+ x.Component(p => p.JoinedAttributes,
+ new
+ {
+ OtherReference = default(MyOther),
+ Description = string.Empty,
+ Age = 0,
+ },
+ m =>
+ {
+ m.ManyToOne(p => p.OtherReference);
+ m.Property(p => p.Description);
+ m.Property(p => p.Age);
+ });
+ x.ManyToOne(p => p.JoinedRelation);
+ });
+ }
+ }
+
+ private static HbmClass CompileClassMapping()
+ {
+ var mapper = new ModelMapper();
+ mapper.AddMapping(typeof(MyClassMap));
+
+ var hbmMapping = mapper.CompileMappingFor(new[] { typeof(MyClass) });
+ var hbmClass = hbmMapping.RootClasses[0];
+
+ return hbmClass;
+ }
+
+ [Test]
+ public void WhenPropertyIsMappedOnRootThenItBelongsToRootTable()
+ {
+ //
+ var hbmClass = CompileClassMapping();
+ Assert.That(hbmClass, Is.Not.Null);
+
+ var rootProperties = hbmClass.Properties;
+ // p.Name == "Code");
+
+ Assert.That(hbmPropCode, Is.Not.Null);
+ Assert.That(hbmPropCode, Is.TypeOf());
+ }
+
+ [Test]
+ public void WhenDynamicComponentIsMappedOnRootThenItBelongsToRootTable()
+ {
+ //
+ var hbmClass = CompileClassMapping();
+ Assert.That(hbmClass, Is.Not.Null);
+
+ var rootProperties = hbmClass.Properties;
+ // p.Name == "Attributes")
+ ;
+
+ Assert.That(hbmPropAttributes, Is.Not.Null);
+ Assert.That(hbmPropAttributes, Is.TypeOf());
+ }
+
+ [Test]
+ public void WhenRelationIsMappedOnRootThenItBelongsToRootTable()
+ {
+ //
+ var hbmClass = CompileClassMapping();
+ Assert.That(hbmClass, Is.Not.Null);
+
+ var rootProperties = hbmClass.Properties;
+ // p.Name == "Relation");
+
+ Assert.That(hbmPropRelation, Is.Not.Null);
+ Assert.That(hbmPropRelation, Is.TypeOf());
+ }
+
+ [Test]
+ public void WhenJoinedPropertyIsMappedOnJoinThenItBelongsToJoinTable()
+ {
+ //
+ var hbmClass = CompileClassMapping();
+ Assert.That(hbmClass, Is.Not.Null);
+
+ //
+ var hbmJoined = hbmClass.Joins.FirstOrDefault();
+ Assert.That(hbmJoined, Is.Not.Null);
+
+ var rootProperties = hbmJoined.Properties;
+ //
+ // p.Name == "JoinedName");
+
+ Assert.That(hbmPropJoinedName, Is.Not.Null);
+ Assert.That(hbmPropJoinedName, Is.TypeOf());
+ }
+
+ [Test]
+ public void WhenJoinedRelationIsMappedOnJoinThenItBelongsToJoinTable()
+ {
+ //
+ var hbmClass = CompileClassMapping();
+ Assert.That(hbmClass, Is.Not.Null);
+
+ //
+ var hbmJoined = hbmClass.Joins.FirstOrDefault();
+ Assert.That(hbmJoined, Is.Not.Null);
+
+ var rootProperties = hbmJoined.Properties;
+ //
+ // p.Name == "JoinedRelation");
+
+ Assert.That(hbmPropJoinedRelation, Is.Not.Null);
+ Assert.That(hbmPropJoinedRelation, Is.TypeOf());
+ }
+
+ [Test]
+ public void WhenJoinedDynamicComponentIsMappedOnJoinThenItBelongsToJoinTable()
+ {
+ //
+ var hbmClass = CompileClassMapping();
+ Assert.That(hbmClass, Is.Not.Null);
+
+ //
+ var hbmJoined = hbmClass.Joins.FirstOrDefault();
+ Assert.That(hbmJoined, Is.Not.Null);
+
+ var rootProperties = hbmJoined.Properties;
+ //
+ //
+ var hbmPropJoinedAttributes = rootProperties
+ .FirstOrDefault(p => p.Name == "JoinedAttributes");
+
+ Assert.That(hbmPropJoinedAttributes, Is.Not.Null);
+ Assert.That(hbmPropJoinedAttributes, Is.TypeOf());
+ }
+ }
+}
\ 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 2e2201019a1..90288d083f2 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()
Assert.That(properties.Single(), Is.TypeOf().And.Property("Name").EqualTo("DynCompo"));
}
+ [Test]
+ public void AddDynamicComponentPropertyGeneric()
+ {
+ var properties = new List();
+ var map = new StubPropertyContainerMapper(properties);
+ map.Component(For.Property(x => x.DynCompo), (IDynamicComponentMapper cp) => { });
+ Assert.That(properties.Single(), Is.TypeOf().And.Property("Name").EqualTo("DynCompo"));
+ }
+
[Test]
public void CallDynamicComponentMapper()
{
@@ -136,6 +149,16 @@ public void CallDynamicComponentMapper()
Assert.That(called, Is.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);
+ Assert.That(called, Is.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 3eb10a3163b..29962a22737 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;
@@ -23,6 +24,18 @@ public IDictionary Info
}
}
+ private class PersonWithGenericInfo
+ {
+ public int Id { get; set; }
+#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
+ private IDictionary info;
+#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
+ public IDictionary Info
+ {
+ get { return info; }
+ }
+ }
+
private class MyClass
{
public int Something { get; set; }
@@ -40,6 +53,19 @@ public void WhenAddThenHas()
Assert.That(component.Properties.Select(x => x.Name), Is.EquivalentTo(new[] { "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) => { });
+
+ Assert.That(component.Properties.Select(x => x.Name), Is.EquivalentTo(new[] { "A" }));
+ }
+
[Test]
public void WhenCustomizeThenCallCustomizer()
{
@@ -54,6 +80,20 @@ public void WhenCustomizeThenCallCustomizer()
Assert.That(called, Is.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);
+
+ Assert.That(called, Is.True);
+ }
+
[Test]
public void WhenCustomizeAccessorThenIgnore()
{
@@ -66,5 +106,18 @@ public void WhenCustomizeAccessorThenIgnore()
Assert.That(component.Properties.OfType().Single().Access, Is.Null.Or.Empty);
}
+
+ [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));
+
+ Assert.That(component.Properties.OfType().Single().Access, Is.Null.Or.Empty);
+ }
}
}
diff --git a/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/DynComponentPropertyOnDynamicCompoTests.cs b/src/NHibernate.Test/MappingByCode/MappersTests/DynamicComponentMapperTests/DynComponentPropertyOnDynamicCompoTests.cs
index cb4f8452b28..8d8fc4fcd50 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;
@@ -23,6 +24,19 @@ public IDictionary Info
}
}
+ private class PersonWithGenericInfo
+ {
+ public int Id { get; set; }
+#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
+ private IDictionary info;
+#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
+ public IDictionary Info
+ {
+ get { return info; }
+ }
+ }
+
+
[Test]
public void WhenAddThenHas()
{
@@ -36,6 +50,19 @@ public void WhenAddThenHas()
Assert.That(component.Properties.Select(x => x.Name), Is.EquivalentTo(new[] { "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) => { });
+
+ Assert.That(component.Properties.Select(x => x.Name), Is.EquivalentTo(new[] { "Info" }));
+ }
+
[Test]
public void WhenCustomizeThenCallCustomizer()
{
@@ -50,6 +77,20 @@ public void WhenCustomizeThenCallCustomizer()
Assert.That(called, Is.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);
+
+ Assert.That(called, Is.True);
+ }
+
[Test]
public void WhenCustomizeAccessorThenIgnore()
{
@@ -62,5 +103,18 @@ public void WhenCustomizeAccessorThenIgnore()
Assert.That(component.Properties.OfType().Single().Access, Is.Null.Or.Empty);
}
+
+ [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));
+
+ Assert.That(component.Properties.OfType().Single().Access, Is.Null.Or.Empty);
+ }
}
}
diff --git a/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH1039Generic/Fixture.cs
new file mode 100644
index 00000000000..5322334e3ec
--- /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..44f1d986396
--- /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..36b8bf31c54
--- /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..99d927d9cc3
--- /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..0a51915da76
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH2664Generic/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.NH2664Generic
+{
+ [TestFixture]
+ public class Fixture : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override IList Mappings => new[] {"NHSpecificTest.NH2664Generic.Mappings.hbm.xml"};
+
+ ///
+ /// push some data into the database
+ /// Really functions as a save test also
+ ///
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var tran = session.BeginTransaction())
+ {
+ session.Save(
+ new Product
+ {
+ ProductId = "1",
+ Properties =
+ {
+ ["Name"] = "First Product",
+ ["Description"] = "First Description"
+ }
+ });
+
+ session.Save(
+ new Product
+ {
+ ProductId = "2",
+ Properties =
+ {
+ ["Name"] = "Second Product",
+ ["Description"] = "Second Description"
+ }
+ });
+
+ session.Save(
+ new Product
+ {
+ ProductId = "3",
+ Properties =
+ {
+ ["Name"] = "val",
+ ["Description"] = "val"
+ }
+ });
+
+ tran.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var tran = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Product").ExecuteUpdate();
+ tran.Commit();
+ }
+ }
+
+ [Test]
+ public void Query_DynamicComponent()
+ {
+ using (var session = OpenSession())
+ {
+ var product = (
+ from p in session.Query()
+ where (string) p.Properties["Name"] == "First Product"
+ select p
+ ).Single();
+
+ Assert.That(product, Is.Not.Null);
+ Assert.That(product.Properties["Name"], Is.EqualTo("First Product"));
+ }
+ }
+
+ [Test]
+ public void Multiple_Query_Does_Not_Cache()
+ {
+ using (var session = OpenSession())
+ {
+ // Query by name
+ var product1 = (
+ from p in session.Query()
+ where (string) 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 (string) 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 (string) a.Properties["Name"] == "val"
+ select a;
+ Expression> key2 = () =>
+ from a in session.Query()
+ where (string) a.Properties["Description"] == "val"
+ select a;
+
+ var nhKey1 = new NhLinqExpression(key1.Body, Sfi);
+ var nhKey2 = new NhLinqExpression(key2.Body, Sfi);
+
+ Assert.That(nhKey2.Key, Is.Not.EqualTo(nhKey1.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..aa972b932ab
--- /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..1a3a396287d
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH3571Generic/Fixture.cs
@@ -0,0 +1,122 @@
+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 => "NHibernate.Test";
+
+ protected override IList Mappings => new[] {"NHSpecificTest.NH3571Generic.Mappings.hbm.xml"};
+
+ ///
+ /// push some data into the database
+ /// Really functions as a save test also
+ ///
+ protected override void 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()
+ {
+ using (var session = OpenSession())
+ using (var tran = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Product").ExecuteUpdate();
+ tran.Commit();
+ }
+ }
+
+ [Test]
+ public void CanQueryDynamicComponentInComponent()
+ {
+ using (var session = OpenSession())
+ {
+ var product = (
+ from p in session.Query()
+ where (string) p.Details.Properties["Name"] == "First Product"
+ select p
+ ).Single();
+
+ Assert.That(product, Is.Not.Null);
+ Assert.That(product.Details.Properties["Name"], Is.EqualTo("First Product"));
+ }
+ }
+
+ [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, Sfi);
+ var nhKey2 = new NhLinqExpression(key2.Body, Sfi);
+
+ Assert.That(nhKey2.Key, Is.Not.EqualTo(nhKey1.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..c70990576a6
--- /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/Linq/Visitors/VisitorUtil.cs b/src/NHibernate/Linq/Visitors/VisitorUtil.cs
index 6175dcf374b..c4fa337d572 100644
--- a/src/NHibernate/Linq/Visitors/VisitorUtil.cs
+++ b/src/NHibernate/Linq/Visitors/VisitorUtil.cs
@@ -17,7 +17,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 115eae3ef56..582f77ab79a 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;
@@ -40,6 +41,7 @@ public interface IBasePlainPropertyContainerMapper : IMinimalPlainPr
void Component(Expression> property, Action> mapping);
void Component(Expression> property);
void Component(Expression> property, TComponent dynamicComponentTemplate, Action> mapping);
+ void Component(Expression>> property, TComponent dynamicComponentTemplate, Action> mapping) where TComponent : class;
void Component(string notVisiblePropertyOrFieldName, Action> mapping);
void Component(string notVisiblePropertyOrFieldName);
diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/JoinCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/JoinCustomizer.cs
index f7dbf87d8a1..530890e04f0 100644
--- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/JoinCustomizer.cs
+++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/JoinCustomizer.cs
@@ -132,8 +132,14 @@ protected override void RegisterComponentMapping(Expression(Expression> property
- , Action> mapping)
+ protected override void RegisterDynamicComponentMapping(Expression> property, Action> mapping)
+ {
+ MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property);
+ ExplicitDeclarationsHolder.AddAsPropertySplit(new SplitDefinition(typeof(TEntity), splitGroupId, member));
+ base.RegisterDynamicComponentMapping(property, mapping);
+ }
+
+ protected override void RegisterDynamicComponentMapping(Expression>> property, Action> mapping)
{
MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property);
ExplicitDeclarationsHolder.AddAsPropertySplit(new SplitDefinition(typeof(TEntity), splitGroupId, member));
@@ -161,4 +167,4 @@ protected override void RegisterIdBagMapping(Expression(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)
{
MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property);
@@ -105,6 +111,13 @@ protected virtual void RegisterDynamicComponentMapping(Expression(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)
{
foreach (var member in members)
diff --git a/src/NHibernate/Tuple/Component/ComponentMetamodel.cs b/src/NHibernate/Tuple/Component/ComponentMetamodel.cs
index b42673c4cb9..274b9e90cb9 100644
--- a/src/NHibernate/Tuple/Component/ComponentMetamodel.cs
+++ b/src/NHibernate/Tuple/Component/ComponentMetamodel.cs
@@ -26,6 +26,7 @@ public ComponentMetamodel(Mapping.Component component)
propertyIndexes[property.Name] = i;
i++;
}
+
EntityMode = component.HasPocoRepresentation ? EntityMode.Poco : EntityMode.Map;
var componentTuplizerFactory = new ComponentTuplizerFactory();
diff --git a/src/NHibernate/Tuple/Component/DynamicComponentInstantiator.cs b/src/NHibernate/Tuple/Component/DynamicComponentInstantiator.cs
new file mode 100644
index 00000000000..719a1afd8be
--- /dev/null
+++ b/src/NHibernate/Tuple/Component/DynamicComponentInstantiator.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+
+namespace NHibernate.Tuple.Component
+{
+ [Serializable]
+ internal class DynamicComponentInstantiator : IInstantiator
+ {
+ public object Instantiate(object id) => Instantiate();
+
+ public object Instantiate() => new Dictionary();
+
+ public bool IsInstance(object obj) => obj is Dictionary;
+ }
+}
\ No newline at end of file
diff --git a/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs b/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs
index 681b0128cf3..7332a7f861d 100644
--- a/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs
+++ b/src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs
@@ -1,5 +1,7 @@
using System;
using NHibernate.Properties;
+using System.Collections;
+using System.Collections.Generic;
namespace NHibernate.Tuple.Component
{
@@ -16,30 +18,16 @@ public DynamicMapComponentTuplizer(Mapping.Component component)
instantiator = BuildInstantiator(component);
}
- public override System.Type MappedClass
- {
- get { return typeof(System.Collections.IDictionary); }
- }
-
- protected internal override IInstantiator BuildInstantiator(Mapping.Component component)
- {
- return new DynamicMapInstantiator();
- }
-
- protected internal override IGetter BuildGetter(Mapping.Component component, Mapping.Property prop)
- {
- return BuildPropertyAccessor(prop).GetGetter(null, prop.Name);
- }
+ public override System.Type MappedClass =>
+ typeof(Dictionary);
- protected internal override ISetter BuildSetter(Mapping.Component component, Mapping.Property prop)
- {
- return BuildPropertyAccessor(prop).GetSetter(null, prop.Name);
- }
+ protected internal override IInstantiator BuildInstantiator(Mapping.Component component) =>
+ new DynamicComponentInstantiator();
- private IPropertyAccessor BuildPropertyAccessor(Mapping.Property property)
- {
- return PropertyAccessorFactory.DynamicMapPropertyAccessor;
- }
+ protected internal override IGetter BuildGetter(Mapping.Component component, Mapping.Property prop) =>
+ PropertyAccessorFactory.DynamicMapPropertyAccessor.GetGetter(null, prop.Name);
+ protected internal override ISetter BuildSetter(Mapping.Component component, Mapping.Property prop) =>
+ PropertyAccessorFactory.DynamicMapPropertyAccessor.GetSetter(null, prop.Name);
}
}
diff --git a/src/NHibernate/Tuple/DynamicMapInstantiator.cs b/src/NHibernate/Tuple/DynamicMapInstantiator.cs
index 0511ea3c7e6..0a8cce0e4a9 100644
--- a/src/NHibernate/Tuple/DynamicMapInstantiator.cs
+++ b/src/NHibernate/Tuple/DynamicMapInstantiator.cs
@@ -13,6 +13,8 @@ public class DynamicMapInstantiator : IInstantiator
private readonly string entityName;
private readonly HashSet isInstanceEntityNames = new HashSet();
+ //Since v5.2
+ [Obsolete("This constructor is not used and will be removed in a future version.")]
public DynamicMapInstantiator()
{
entityName = null;