Skip to content

Commit 92a0a51

Browse files
AndrewShepherdhazzik
authored andcommitted
NH-3913 - Correctly apply Component Property Customizers (#534)
The ComponentRelationMapper must be given the full path to the component.
1 parent 307fd8d commit 92a0a51

File tree

3 files changed

+252
-6
lines changed

3 files changed

+252
-6
lines changed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
using NHibernate.Cfg.MappingSchema;
2+
using NHibernate.Mapping.ByCode;
3+
using System;
4+
using System.Collections;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using NUnit.Framework;
8+
9+
namespace NHibernate.Test.MappingByCode.MappersTests
10+
{
11+
public class ComponentWithBagOfNestedComponentsTests : TestCase
12+
{
13+
public class OwnedItem
14+
{
15+
public virtual string NameOnClient { get; set; }
16+
}
17+
class Owner
18+
{
19+
private Lazy<IList<OwnedItem>> _ownedItems = new Lazy<IList<OwnedItem>>(() => new List<OwnedItem>());
20+
21+
public virtual long Id { get; set; }
22+
23+
public virtual IList<OwnedItem> OwnedItems
24+
{
25+
get { return _ownedItems.Value; }
26+
set { _ownedItems = new Lazy<IList<OwnedItem>>(() => value); }
27+
}
28+
}
29+
30+
class OwnerChildOne : Owner
31+
{
32+
}
33+
34+
class OwnerChildTwo: Owner
35+
{
36+
}
37+
38+
public class Intermediate
39+
{
40+
private Lazy<IList<OwnedItem>> _children = new Lazy<IList<OwnedItem>>(() => new List<OwnedItem>());
41+
public virtual IList<OwnedItem> Children
42+
{
43+
get { return _children.Value; }
44+
set { _children = new Lazy<IList<OwnedItem>>(() => value); }
45+
}
46+
}
47+
48+
public class OwnerWithIntermediate
49+
{
50+
public virtual long Id { get; set; }
51+
public virtual Intermediate Intermediate { get; set; }
52+
}
53+
54+
protected override IList Mappings
55+
{
56+
get
57+
{
58+
// We can perform these tests without
59+
// creating a data schema
60+
return new string[0];
61+
}
62+
}
63+
64+
[Test]
65+
public void BagIsPropertyOfEntity()
66+
{
67+
var mapper = new ModelMapper();
68+
mapper.Class<Owner>(classMapper =>
69+
{
70+
classMapper.Id(p => p.Id);
71+
classMapper.Bag<OwnedItem>
72+
(
73+
parent => parent.OwnedItems,
74+
bagPropertyMapper =>
75+
{
76+
bagPropertyMapper.Table("Child");
77+
bagPropertyMapper.Key(k => k.Column("Parent_Id"));
78+
},
79+
r => r.Component(
80+
child =>
81+
{
82+
child.Property
83+
(
84+
c => c.NameOnClient,
85+
pm =>
86+
{
87+
pm.Column("NameInDatabase");
88+
}
89+
);
90+
}
91+
)
92+
);
93+
});
94+
var mappings = mapper.CompileMappingForAllExplicitlyAddedEntities();
95+
96+
HbmBag hbmBag = mappings
97+
.Items.Cast<HbmClass>()
98+
.Single()
99+
.Properties.Cast<HbmBag>()
100+
.Single();
101+
102+
Assert.That(hbmBag.Item, Is.InstanceOf<HbmCompositeElement>());
103+
HbmCompositeElement childElement = (HbmCompositeElement)hbmBag.Item;
104+
Assert.That(childElement.Properties.Count(), Is.EqualTo(1));
105+
HbmProperty propertyMapping = childElement.Properties.Cast<HbmProperty>().Single();
106+
Assert.That(propertyMapping.Name, Is.EqualTo("NameOnClient"));
107+
Assert.That(propertyMapping.Columns.Count(), Is.EqualTo(1));
108+
Assert.That(propertyMapping.Columns.Single().name, Is.EqualTo("NameInDatabase"));
109+
}
110+
111+
[Test]
112+
public void BagIsPropertyOfComponent()
113+
{
114+
var mapper = new ModelMapper();
115+
mapper.Class<OwnerWithIntermediate>(classMapper =>
116+
{
117+
classMapper.Id(p => p.Id);
118+
classMapper.Component(
119+
p => p.Intermediate,
120+
componentMapper =>
121+
componentMapper.Bag<OwnedItem>
122+
(
123+
intermediate => intermediate.Children,
124+
bagPropertyMapper =>
125+
{
126+
bagPropertyMapper.Table("Child");
127+
bagPropertyMapper.Key(k => k.Column("Parent_Id"));
128+
},
129+
r => r.Component(child =>
130+
child.Property
131+
(
132+
c => c.NameOnClient,
133+
pm =>
134+
{
135+
pm.Column("NameInDatabase");
136+
}
137+
)
138+
)
139+
)
140+
);
141+
});
142+
143+
var mappings = mapper.CompileMappingForAllExplicitlyAddedEntities();
144+
145+
HbmBag hbmBag = mappings
146+
.Items.Cast<HbmClass>()
147+
.Single()
148+
.Properties.Cast<HbmComponent>()
149+
.Single()
150+
.Properties.Cast<HbmBag>()
151+
.Single();
152+
153+
Assert.That(hbmBag.Item, Is.InstanceOf<HbmCompositeElement>());
154+
HbmCompositeElement childElement = (HbmCompositeElement)hbmBag.Item;
155+
Assert.That(childElement.Properties.Count(), Is.EqualTo(1));
156+
HbmProperty propertyMapping = childElement.Properties.Cast<HbmProperty>().Single();
157+
Assert.That(propertyMapping.Name, Is.EqualTo("NameOnClient"));
158+
Assert.That(propertyMapping.Columns.Count(), Is.EqualTo(1));
159+
Assert.That(propertyMapping.Columns.Single().name, Is.EqualTo("NameInDatabase"));
160+
}
161+
162+
[Test]
163+
public void PropertyCustomizerDifferentiatesBetweenChildClasses()
164+
{
165+
var mapper = new ModelMapper();
166+
mapper.Class<OwnerChildOne>(classMapper =>
167+
{
168+
classMapper.Id(p => p.Id);
169+
classMapper.Bag<OwnedItem>
170+
(
171+
parent => parent.OwnedItems,
172+
bagPropertyMapper =>
173+
{
174+
bagPropertyMapper.Table("ChildOne");
175+
bagPropertyMapper.Key(k => k.Column("Parent_Id"));
176+
},
177+
r => r.Component(
178+
child =>
179+
{
180+
child.Property
181+
(
182+
c => c.NameOnClient,
183+
pm =>
184+
{
185+
pm.Column("OwnerChildOne_CustomColumnName");
186+
}
187+
);
188+
}
189+
)
190+
);
191+
});
192+
mapper.Class<OwnerChildTwo>(classMapper =>
193+
{
194+
classMapper.Id(p => p.Id);
195+
classMapper.Bag<OwnedItem>
196+
(
197+
parent => parent.OwnedItems,
198+
bagPropertyMapper =>
199+
{
200+
bagPropertyMapper.Table("ChildTwo");
201+
bagPropertyMapper.Key(k => k.Column("Parent_Id"));
202+
},
203+
r => r.Component(
204+
child =>
205+
{
206+
child.Property
207+
(
208+
c => c.NameOnClient,
209+
pm =>
210+
{
211+
pm.Column("OwnerChildTwo_CustomColumnName");
212+
}
213+
);
214+
}
215+
)
216+
);
217+
});
218+
219+
var mappings = mapper.CompileMappingForAllExplicitlyAddedEntities();
220+
221+
222+
HbmBag bag1 = mappings
223+
.Items.Cast<HbmClass>()
224+
.Where(c => c.Name == typeof(OwnerChildOne).FullName)
225+
.Single()
226+
.Properties.Cast<HbmBag>()
227+
.Single();
228+
229+
HbmCompositeElement childElement1 = (HbmCompositeElement)bag1.Item;
230+
HbmProperty propertyMapping1 = childElement1.Properties.Cast<HbmProperty>().Single();
231+
Assert.That(propertyMapping1.Columns.Single().name, Is.EqualTo("OwnerChildOne_CustomColumnName"));
232+
233+
HbmBag bag2 = mappings
234+
.Items.Cast<HbmClass>()
235+
.Where(c => c.Name == typeof(OwnerChildTwo).FullName)
236+
.Single()
237+
.Properties.Cast<HbmBag>()
238+
.Single();
239+
240+
HbmCompositeElement childElement2 = (HbmCompositeElement)bag2.Item;
241+
HbmProperty propertyMapping2 = childElement2.Properties.Cast<HbmProperty>().Single();
242+
Assert.That(propertyMapping2.Columns.Single().name, Is.EqualTo("OwnerChildTwo_CustomColumnName"));
243+
}
244+
245+
246+
}
247+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@
580580
<Compile Include="Linq\ByMethod\SumTests.cs" />
581581
<Compile Include="Logging\Log4NetLoggerTest.cs" />
582582
<Compile Include="Logging\LoggerProviderTest.cs" />
583+
<Compile Include="MappingByCode\MappersTests\ComponentWithBagOfNestedComponentsTests.cs" />
583584
<Compile Include="MappingByCode\ConventionModelMapperTests\ComponetsAccessorTests.cs" />
584585
<Compile Include="MappingByCode\ConventionModelMapperTests\ComponetsParentAccessorTests.cs" />
585586
<Compile Include="MappingByCode\ConventionModelMapperTests\PropertyToFieldAccessorTest.cs" />

src/NHibernate/Mapping/ByCode/ModelMapper.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,7 +1300,7 @@ protected virtual ICollectionElementRelationMapper DetermineCollectionElementRel
13001300
}
13011301
if (modelInspector.IsComponent(collectionElementType))
13021302
{
1303-
return new ComponentRelationMapper(property, ownerType, collectionElementType, membersProvider, modelInspector, customizerHolder, this);
1303+
return new ComponentRelationMapper(propertyPath, ownerType, collectionElementType, membersProvider, modelInspector, customizerHolder, this);
13041304
}
13051305
if (modelInspector.IsManyToAny(property))
13061306
{
@@ -1327,18 +1327,18 @@ private IMapKeyRelationMapper DetermineMapKeyRelationType(MemberInfo member, Pro
13271327

13281328
private class ComponentRelationMapper : ICollectionElementRelationMapper
13291329
{
1330-
private readonly MemberInfo collectionMember;
1330+
private readonly PropertyPath propertyPath;
13311331
private readonly System.Type componentType;
13321332
private readonly ICustomizersHolder customizersHolder;
13331333
private readonly IModelInspector domainInspector;
13341334
private readonly ICandidatePersistentMembersProvider membersProvider;
13351335
private readonly ModelMapper modelMapper;
13361336
private readonly System.Type ownerType;
13371337

1338-
public ComponentRelationMapper(MemberInfo collectionMember, System.Type ownerType, System.Type componentType, ICandidatePersistentMembersProvider membersProvider,
1338+
public ComponentRelationMapper(PropertyPath propertyPath, System.Type ownerType, System.Type componentType, ICandidatePersistentMembersProvider membersProvider,
13391339
IModelInspector domainInspector, ICustomizersHolder customizersHolder, ModelMapper modelMapper)
13401340
{
1341-
this.collectionMember = collectionMember;
1341+
this.propertyPath = propertyPath;
13421342
this.ownerType = ownerType;
13431343
this.componentType = componentType;
13441344
this.membersProvider = membersProvider;
@@ -1362,7 +1362,6 @@ public void Map(ICollectionElementRelation relation)
13621362
}
13631363
customizersHolder.InvokeCustomizers(componentType, x);
13641364

1365-
var propertyPath = new PropertyPath(null, collectionMember);
13661365
MapProperties(componentType, propertyPath, x, persistentProperties.Where(pi => pi != parentReferenceProperty));
13671366
});
13681367
}
@@ -1379,7 +1378,6 @@ private IEnumerable<MemberInfo> GetPersistentProperties(System.Type type)
13791378

13801379
private void MapProperties(System.Type type, PropertyPath memberPath, IComponentElementMapper propertiesContainer, IEnumerable<MemberInfo> persistentProperties)
13811380
{
1382-
// TODO check PropertyPath behaviour when the component is in a collection
13831381
foreach (MemberInfo property in persistentProperties)
13841382
{
13851383
MemberInfo member = property;

0 commit comments

Comments
 (0)