Skip to content

Commit cd13018

Browse files
committed
Merge branch 'cremor-NH-3119'
2 parents ea68170 + 746bcef commit cd13018

File tree

9 files changed

+208
-24
lines changed

9 files changed

+208
-24
lines changed

releasenotes.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Build vNext
44
** Known BREAKING CHANGES from NH4.0.0.Alpha1 to 4.0.0.Alpha1
55

66
Fixed mapping by code behaviour when map child subclasses (see NH-3135 and NH-3269)
7+
8+
The constructor of AbstractComponentTuplizer now behaves like AbstractEntityTuplizer in the way
9+
that it doesn't create the instantiator any more. Custom component tuplizers that derive
10+
directly from AbstractComponentTuplizer need to add this line of code in their constructor:
11+
instantiator = BuildInstantiator(component);
712

813

914
Build 4.0.0.Alpha1
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Diagnostics;
3+
4+
namespace NHibernate.Test.NHSpecificTest.NH3119
5+
{
6+
public class Entity
7+
{
8+
public virtual Guid Id { get; set; }
9+
public virtual string Name { get; set; }
10+
public virtual Component Component { get; set; }
11+
}
12+
13+
public class Component
14+
{
15+
public Component()
16+
{
17+
LastCtorStackTrace = new StackTrace().ToString();
18+
}
19+
20+
public string Value { get; set; }
21+
public string LastCtorStackTrace { get; private set; }
22+
}
23+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System.IO;
2+
using System.Linq;
3+
using System.Runtime.Serialization.Formatters.Binary;
4+
using NHibernate.Cfg;
5+
using NHibernate.Cfg.MappingSchema;
6+
using NHibernate.Linq;
7+
using NHibernate.Mapping.ByCode;
8+
using NUnit.Framework;
9+
10+
namespace NHibernate.Test.NHSpecificTest.NH3119
11+
{
12+
/// <summary>
13+
/// Fixture using 'by code' mappings
14+
/// </summary>
15+
/// <remarks>
16+
/// This fixture is identical to <see cref="Fixture" /> except the <see cref="Entity" /> mapping is performed
17+
/// by code in the GetMappings method, and does not require the <c>Mappings.hbm.xml</c> file. Use this approach
18+
/// if you prefer.
19+
/// </remarks>
20+
public class ByCodeFixture : TestCaseMappingByCode
21+
{
22+
protected override HbmMapping GetMappings()
23+
{
24+
var mapper = new ModelMapper();
25+
mapper.Class<Entity>(rc =>
26+
{
27+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
28+
rc.Property(x => x.Name);
29+
rc.Component(x => x.Component, c =>
30+
{
31+
c.Property(x => x.Value);
32+
});
33+
});
34+
35+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
36+
}
37+
38+
protected override void OnSetUp()
39+
{
40+
if (!Cfg.Environment.UseReflectionOptimizer)
41+
{
42+
Assert.Ignore("Test only works with reflection optimization enabled");
43+
}
44+
45+
using (ISession session = OpenSession())
46+
using (ITransaction transaction = session.BeginTransaction())
47+
{
48+
var e1 = new Entity { Name = "Name", Component = new Component { Value = "Value" } };
49+
session.Save(e1);
50+
51+
session.Flush();
52+
transaction.Commit();
53+
}
54+
}
55+
56+
protected override void OnTearDown()
57+
{
58+
using (ISession session = OpenSession())
59+
using (ITransaction transaction = session.BeginTransaction())
60+
{
61+
session.Delete("from System.Object");
62+
63+
session.Flush();
64+
transaction.Commit();
65+
}
66+
}
67+
68+
[Test]
69+
public void PocoComponentTuplizer_Instantiate_UsesReflectonOptimizer()
70+
{
71+
using (ISession freshSession = OpenSession())
72+
using (freshSession.BeginTransaction())
73+
{
74+
Entity entity = freshSession.Query<Entity>().Single();
75+
76+
string stackTrace = entity.Component.LastCtorStackTrace;
77+
78+
StringAssert.Contains("NHibernate.Bytecode.Lightweight.ReflectionOptimizer.CreateInstance", stackTrace);
79+
}
80+
}
81+
82+
[Test]
83+
public void PocoComponentTuplizerOfDeserializedConfiguration_Instantiate_UsesReflectonOptimizer()
84+
{
85+
MemoryStream configMemoryStream = new MemoryStream();
86+
BinaryFormatter writer = new BinaryFormatter();
87+
writer.Serialize(configMemoryStream, cfg);
88+
89+
configMemoryStream.Seek(0, SeekOrigin.Begin);
90+
BinaryFormatter reader = new BinaryFormatter();
91+
Configuration deserializedConfig = (Configuration)reader.Deserialize(configMemoryStream);
92+
ISessionFactory factoryFromDeserializedConfig = deserializedConfig.BuildSessionFactory();
93+
94+
using (ISession deserializedSession = factoryFromDeserializedConfig.OpenSession())
95+
using (deserializedSession.BeginTransaction())
96+
{
97+
Entity entity = deserializedSession.Query<Entity>().Single();
98+
99+
string stackTrace = entity.Component.LastCtorStackTrace;
100+
101+
StringAssert.Contains("NHibernate.Bytecode.Lightweight.ReflectionOptimizer.CreateInstance", stackTrace);
102+
}
103+
}
104+
}
105+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,8 @@
11281128
<Compile Include="NHSpecificTest\NH3126\Item.cs" />
11291129
<Compile Include="NHSpecificTest\NH3126\Property.cs" />
11301130
<Compile Include="NHSpecificTest\NH3126\PropertyValue.cs" />
1131+
<Compile Include="NHSpecificTest\NH3119\Entity.cs" />
1132+
<Compile Include="NHSpecificTest\NH3119\FixtureByCode.cs" />
11311133
<Compile Include="NHSpecificTest\NH3124\FixtureByCode.cs" />
11321134
<Compile Include="NHSpecificTest\NH3142\ChildrenTest.cs" />
11331135
<Compile Include="NHSpecificTest\NH3142\DomainClass.cs" />

src/NHibernate/Tuple/Component/AbstractComponentTuplizer.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ protected internal AbstractComponentTuplizer(Mapping.Component component)
3333
{
3434
foundCustomAccessor = true;
3535
}
36-
i++;
36+
i++;
3737
}
3838
if (log.IsDebugEnabled)
3939
{
4040
log.DebugFormat("{0} accessors found for component: {1}", foundCustomAccessor ? "Custom" : "No custom",
41-
component.ComponentClassName);
41+
component.ComponentClassName);
4242
}
4343
hasCustomAccessors = foundCustomAccessor;
4444

@@ -53,7 +53,8 @@ protected internal AbstractComponentTuplizer(Mapping.Component component)
5353
propTypes[j] = getters[j].ReturnType;
5454
}
5555

56-
instantiator = BuildInstantiator(component);
56+
// Fix for NH-3119
57+
//instantiator = BuildInstantiator(component);
5758
}
5859

5960
#region IComponentTuplizer Members
@@ -68,7 +69,7 @@ public virtual void SetParent(object component, object parent, ISessionFactoryIm
6869
throw new NotSupportedException();
6970
}
7071

71-
public virtual bool HasParentProperty
72+
public virtual bool HasParentProperty
7273
{
7374
get { return false; }
7475
}

src/NHibernate/Tuple/Component/DynamicMapComponentTuplizer.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ namespace NHibernate.Tuple.Component
99
[Serializable]
1010
public class DynamicMapComponentTuplizer : AbstractComponentTuplizer
1111
{
12-
public DynamicMapComponentTuplizer(Mapping.Component component) : base(component) { }
12+
public DynamicMapComponentTuplizer(Mapping.Component component)
13+
: base(component)
14+
{
15+
// Fix for NH-3119
16+
instantiator = BuildInstantiator(component);
17+
}
1318

1419
public override System.Type MappedClass
1520
{

src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,20 @@ public class PocoComponentTuplizer : AbstractComponentTuplizer
2222
[OnDeserialized]
2323
internal void OnDeserialized(StreamingContext context)
2424
{
25-
this.optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(componentClass, getters, setters);
25+
SetReflectionOptimizer();
26+
27+
if (optimizer != null)
28+
{
29+
// Fix for NH-3119:
30+
// Also set the InstantiationOptimizer on the deserialized PocoInstantiator.
31+
((PocoInstantiator)instantiator).SetOptimizer(optimizer.InstantiationOptimizer);
32+
}
33+
34+
ClearOptimizerWhenUsingCustomAccessors();
2635
}
27-
public PocoComponentTuplizer(Mapping.Component component) : base(component)
36+
37+
public PocoComponentTuplizer(Mapping.Component component)
38+
: base(component)
2839
{
2940
componentClass = component.ComponentClass;
3041

@@ -40,14 +51,12 @@ public PocoComponentTuplizer(Mapping.Component component) : base(component)
4051
parentGetter = parentProperty.GetGetter(componentClass);
4152
}
4253

43-
if (hasCustomAccessors || !Cfg.Environment.UseReflectionOptimizer)
44-
{
45-
optimizer = null;
46-
}
47-
else
48-
{
49-
optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(componentClass, getters, setters);
50-
}
54+
SetReflectionOptimizer();
55+
56+
// Fix for NH-3119
57+
instantiator = BuildInstantiator(component);
58+
59+
ClearOptimizerWhenUsingCustomAccessors();
5160
}
5261

5362
public override System.Type MappedClass
@@ -127,5 +136,21 @@ protected internal override ISetter BuildSetter(Mapping.Component component, Map
127136
{
128137
return prop.GetSetter(component.ComponentClass);
129138
}
139+
140+
protected void SetReflectionOptimizer()
141+
{
142+
if (Cfg.Environment.UseReflectionOptimizer)
143+
{
144+
optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(componentClass, getters, setters);
145+
}
146+
}
147+
148+
protected void ClearOptimizerWhenUsingCustomAccessors()
149+
{
150+
if (hasCustomAccessors)
151+
{
152+
optimizer = null;
153+
}
154+
}
130155
}
131156
}

src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ public class PocoEntityTuplizer : AbstractEntityTuplizer
3434
internal void OnDeserialized(StreamingContext context)
3535
{
3636
SetReflectionOptimizer();
37+
38+
if (optimizer != null)
39+
{
40+
// Also set the InstantiationOptimizer on the deserialized PocoInstantiator.
41+
((PocoInstantiator)Instantiator).SetOptimizer(optimizer.InstantiationOptimizer);
42+
}
43+
44+
ClearOptimizerWhenUsingCustomAccessors();
3745
}
3846
protected void SetReflectionOptimizer()
3947
{
@@ -62,10 +70,7 @@ public PocoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappe
6270

6371
Instantiator = BuildInstantiator(mappedEntity);
6472

65-
if (hasCustomAccessors)
66-
{
67-
optimizer = null;
68-
}
73+
ClearOptimizerWhenUsingCustomAccessors();
6974

7075
proxyValidator = Cfg.Environment.BytecodeProvider.ProxyFactoryFactory.ProxyValidator;
7176
}
@@ -316,5 +321,13 @@ public override EntityMode EntityMode
316321
{
317322
get { return EntityMode.Poco; }
318323
}
324+
325+
protected void ClearOptimizerWhenUsingCustomAccessors()
326+
{
327+
if (hasCustomAccessors)
328+
{
329+
optimizer = null;
330+
}
331+
}
319332
}
320333
}

src/NHibernate/Tuple/PocoInstantiator.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ namespace NHibernate.Tuple
1414
public class PocoInstantiator : IInstantiator, IDeserializationCallback
1515
{
1616
private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(PocoInstantiator));
17-
17+
1818
private readonly System.Type mappedClass;
19-
19+
2020
[NonSerialized]
21-
private readonly IInstantiationOptimizer optimizer;
21+
private IInstantiationOptimizer optimizer;
2222

2323
private readonly IProxyFactory proxyFactory;
2424

2525
private readonly bool generateFieldInterceptionProxy;
2626

2727
private readonly bool embeddedIdentifier;
28-
28+
2929
[NonSerialized]
3030
private ConstructorInfo constructor;
31-
31+
3232
private readonly System.Type proxyInterface;
3333

3434
public PocoInstantiator()
@@ -134,5 +134,10 @@ public void OnDeserialization(object sender)
134134
}
135135

136136
#endregion
137+
138+
public void SetOptimizer(IInstantiationOptimizer optimizer)
139+
{
140+
this.optimizer = optimizer;
141+
}
137142
}
138143
}

0 commit comments

Comments
 (0)