Skip to content

Commit 3a651d0

Browse files
authored
Fix support for nullable struct components (#3554)
1 parent f9fcdcd commit 3a651d0

File tree

6 files changed

+109
-76
lines changed

6 files changed

+109
-76
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using NUnit.Framework;
12+
13+
namespace NHibernate.Test.NHSpecificTest.NH1284
14+
{
15+
using System.Threading.Tasks;
16+
[TestFixture]
17+
public class FixtureAsync : BugTestCase
18+
{
19+
protected override void OnTearDown()
20+
{
21+
using var s = OpenSession();
22+
using var tx = s.BeginTransaction();
23+
s.Delete("from Person");
24+
tx.Commit();
25+
}
26+
27+
[Test]
28+
public async Task EmptyValueTypeComponentAsync()
29+
{
30+
Person jimmy;
31+
using (var s = OpenSession())
32+
using (var tx = s.BeginTransaction())
33+
{
34+
var p = new Person("Jimmy Hendrix");
35+
await (s.SaveAsync(p));
36+
await (tx.CommitAsync());
37+
}
38+
39+
using (var s = OpenSession())
40+
using (var tx = s.BeginTransaction())
41+
{
42+
jimmy = await (s.GetAsync<Person>("Jimmy Hendrix"));
43+
await (tx.CommitAsync());
44+
}
45+
46+
Assert.That(jimmy.Address, Is.Null);
47+
}
48+
}
49+
}

src/NHibernate.Test/NHSpecificTest/NH1284/Fixture.cs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,37 @@
22

33
namespace NHibernate.Test.NHSpecificTest.NH1284
44
{
5-
[TestFixture, Ignore("Not supported yet.")]
5+
[TestFixture]
66
public class Fixture : BugTestCase
77
{
8+
protected override void OnTearDown()
9+
{
10+
using var s = OpenSession();
11+
using var tx = s.BeginTransaction();
12+
s.Delete("from Person");
13+
tx.Commit();
14+
}
15+
816
[Test]
917
public void EmptyValueTypeComponent()
1018
{
1119
Person jimmy;
12-
using (ISession s = OpenSession())
13-
using (ITransaction tx = s.BeginTransaction())
20+
using (var s = OpenSession())
21+
using (var tx = s.BeginTransaction())
1422
{
15-
Person p = new Person("Jimmy Hendrix");
23+
var p = new Person("Jimmy Hendrix");
1624
s.Save(p);
1725
tx.Commit();
1826
}
1927

20-
using (ISession s = OpenSession())
21-
using (ITransaction tx = s.BeginTransaction())
28+
using (var s = OpenSession())
29+
using (var tx = s.BeginTransaction())
2230
{
23-
jimmy = (Person)s.Get(typeof(Person), "Jimmy Hendrix");
31+
jimmy = s.Get<Person>("Jimmy Hendrix");
2432
tx.Commit();
2533
}
26-
Assert.IsFalse(jimmy.Address.HasValue);
2734

28-
using (ISession s = OpenSession())
29-
using (ITransaction tx = s.BeginTransaction())
30-
{
31-
s.Delete("from Person");
32-
tx.Commit();
33-
}
35+
Assert.That(jimmy.Address, Is.Null);
3436
}
3537
}
36-
}
38+
}

src/NHibernate.Test/NHSpecificTest/NH1284/Mappings.hbm.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
<property name="GmtOffset"/>
1616
</component>
1717
</class>
18-
</hibernate-mapping>
18+
</hibernate-mapping>

src/NHibernate/Cfg/XmlHbmBinding/ClassIdBinder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private void CreateIdentifierProperty(HbmId idSchema, PersistentClass rootClass,
4545
if (idSchema.name != null)
4646
{
4747
string access = idSchema.access ?? mappings.DefaultAccess;
48-
id.SetTypeUsingReflection(rootClass.MappedClass == null ? null : rootClass.MappedClass.AssemblyQualifiedName, idSchema.name, access);
48+
id.SetTypeUsingReflection(rootClass.MappedClass?.AssemblyQualifiedName, idSchema.name, access);
4949

5050
var property = new Property(id) { Name = idSchema.name };
5151

@@ -85,4 +85,4 @@ private static void BindUnsavedValue(HbmId idSchema, SimpleValue id)
8585
id.NullValue = idSchema.unsavedvalue ?? (id.IdentifierGeneratorStrategy == "assigned" ? "undefined" : null);
8686
}
8787
}
88-
}
88+
}

src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs

Lines changed: 37 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using NHibernate.Mapping;
55
using System;
66
using NHibernate.Util;
7-
using Array = System.Array;
87

98
namespace NHibernate.Cfg.XmlHbmBinding
109
{
@@ -14,7 +13,6 @@ public class PropertiesBinder : ClassBinder
1413
private readonly Component component;
1514
private readonly string entityName;
1615
private readonly System.Type mappedClass;
17-
private readonly string className;
1816
private readonly bool componetDefaultNullable;
1917
private readonly string propertyBasePath;
2018

@@ -38,7 +36,6 @@ public PropertiesBinder(Mappings mappings, PersistentClass persistentClass)
3836
this.persistentClass = persistentClass;
3937
entityName = persistentClass.EntityName;
4038
propertyBasePath = entityName;
41-
className = persistentClass.ClassName;
4239
mappedClass = persistentClass.MappedClass;
4340
componetDefaultNullable = true;
4441
component = null;
@@ -50,7 +47,6 @@ public PropertiesBinder(Mappings mappings, Component component, string className
5047
persistentClass = component.Owner;
5148
this.component = component;
5249
entityName = className;
53-
this.className = component.ComponentClassName;
5450
mappedClass = component.ComponentClass;
5551
propertyBasePath = path;
5652
componetDefaultNullable = isNullable;
@@ -87,26 +83,14 @@ public void Bind(IEnumerable<IEntityPropertyMapping> properties, Table table, ID
8783

8884
string propertyName = entityPropertyMapping.Name;
8985

90-
ICollectionPropertiesMapping collectionMapping;
91-
HbmManyToOne manyToOneMapping;
92-
HbmAny anyMapping;
93-
HbmOneToOne oneToOneMapping;
94-
HbmProperty propertyMapping;
95-
HbmComponent componentMapping;
96-
HbmDynamicComponent dynamicComponentMapping;
97-
HbmNestedCompositeElement nestedCompositeElementMapping;
98-
HbmKeyProperty keyPropertyMapping;
99-
HbmKeyManyToOne keyManyToOneMapping;
100-
HbmProperties propertiesMapping;
101-
102-
if ((propertyMapping = entityPropertyMapping as HbmProperty) != null)
86+
if (entityPropertyMapping is HbmProperty propertyMapping)
10387
{
10488
var value = new SimpleValue(table);
10589
new ValuePropertyBinder(value, Mappings).BindSimpleValue(propertyMapping, propertyName, true);
106-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
90+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
10791
BindValueProperty(propertyMapping, property);
10892
}
109-
else if ((collectionMapping = entityPropertyMapping as ICollectionPropertiesMapping) != null)
93+
else if (entityPropertyMapping is ICollectionPropertiesMapping collectionMapping)
11094
{
11195
var collectionBinder = new CollectionBinder(Mappings);
11296
string propertyPath = propertyName == null ? null : StringHelper.Qualify(propertyBasePath, propertyName);
@@ -116,59 +100,59 @@ public void Bind(IEnumerable<IEntityPropertyMapping> properties, Table table, ID
116100

117101
mappings.AddCollection(collection);
118102

119-
property = CreateProperty(collectionMapping, className, collection, inheritedMetas);
103+
property = CreateProperty(collectionMapping, mappedClass, collection, inheritedMetas);
120104
BindCollectionProperty(collectionMapping, property);
121105
}
122-
else if ((propertiesMapping = entityPropertyMapping as HbmProperties) != null)
106+
else if (entityPropertyMapping is HbmProperties propertiesMapping)
123107
{
124108
var subpath = propertyName == null ? null : StringHelper.Qualify(propertyBasePath, propertyName);
125109
var value = CreateNewComponent(table);
126110
BindComponent(propertiesMapping, value, null, entityName, subpath, componetDefaultNullable, inheritedMetas);
127-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
111+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
128112
BindComponentProperty(propertiesMapping, property, value);
129113
}
130-
else if ((manyToOneMapping = entityPropertyMapping as HbmManyToOne) != null)
114+
else if (entityPropertyMapping is HbmManyToOne manyToOneMapping)
131115
{
132116
var value = new ManyToOne(table);
133117
BindManyToOne(manyToOneMapping, value, propertyName, true);
134-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
118+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
135119
BindManyToOneProperty(manyToOneMapping, property);
136120
}
137-
else if ((componentMapping = entityPropertyMapping as HbmComponent) != null)
121+
else if (entityPropertyMapping is HbmComponent componentMapping)
138122
{
139123
string subpath = propertyName == null ? null : StringHelper.Qualify(propertyBasePath, propertyName);
140124
var value = CreateNewComponent(table);
141125
// NH: Modified from H2.1 to allow specifying the type explicitly using class attribute
142126
System.Type reflectedClass = mappedClass == null ? null : GetPropertyType(componentMapping.Class, mappedClass, propertyName, componentMapping.Access);
143127
BindComponent(componentMapping, value, reflectedClass, entityName, subpath, componetDefaultNullable, inheritedMetas);
144-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
128+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
145129
BindComponentProperty(componentMapping, property, value);
146130
}
147-
else if ((oneToOneMapping = entityPropertyMapping as HbmOneToOne) != null)
131+
else if (entityPropertyMapping is HbmOneToOne oneToOneMapping)
148132
{
149133
var value = new OneToOne(table, persistentClass);
150134
BindOneToOne(oneToOneMapping, value);
151-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
135+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
152136
BindOneToOneProperty(oneToOneMapping, property);
153137
}
154-
else if ((dynamicComponentMapping = entityPropertyMapping as HbmDynamicComponent) != null)
138+
else if (entityPropertyMapping is HbmDynamicComponent dynamicComponentMapping)
155139
{
156140
string subpath = propertyName == null ? null : StringHelper.Qualify(propertyBasePath, propertyName);
157141
var value = CreateNewComponent(table);
158142
// NH: Modified from H2.1 to allow specifying the type explicitly using class attribute
159143
System.Type reflectedClass = mappedClass == null ? null : GetPropertyType(dynamicComponentMapping.Class, mappedClass, propertyName, dynamicComponentMapping.Access);
160144
BindComponent(dynamicComponentMapping, value, reflectedClass, entityName, subpath, componetDefaultNullable, inheritedMetas);
161-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
145+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
162146
BindComponentProperty(dynamicComponentMapping, property, value);
163147
}
164-
else if ((anyMapping = entityPropertyMapping as HbmAny) != null)
148+
else if (entityPropertyMapping is HbmAny anyMapping)
165149
{
166150
var value = new Any(table);
167151
BindAny(anyMapping, value, true);
168-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
152+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
169153
BindAnyProperty(anyMapping, property);
170154
}
171-
else if ((nestedCompositeElementMapping = entityPropertyMapping as HbmNestedCompositeElement) != null)
155+
else if (entityPropertyMapping is HbmNestedCompositeElement nestedCompositeElementMapping)
172156
{
173157
if (component == null)
174158
{
@@ -179,19 +163,19 @@ public void Bind(IEnumerable<IEntityPropertyMapping> properties, Table table, ID
179163
// NH: Modified from H2.1 to allow specifying the type explicitly using class attribute
180164
System.Type reflectedClass = mappedClass == null ? null : GetPropertyType(nestedCompositeElementMapping.Class, mappedClass, propertyName, nestedCompositeElementMapping.access);
181165
BindComponent(nestedCompositeElementMapping, value, reflectedClass, entityName, subpath, componetDefaultNullable, inheritedMetas);
182-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
166+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
183167
}
184-
else if ((keyPropertyMapping = entityPropertyMapping as HbmKeyProperty) != null)
168+
else if (entityPropertyMapping is HbmKeyProperty keyPropertyMapping)
185169
{
186170
var value = new SimpleValue(table);
187171
new ValuePropertyBinder(value, Mappings).BindSimpleValue(keyPropertyMapping, propertyName, componetDefaultNullable);
188-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
172+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
189173
}
190-
else if ((keyManyToOneMapping = entityPropertyMapping as HbmKeyManyToOne) != null)
174+
else if (entityPropertyMapping is HbmKeyManyToOne keyManyToOneMapping)
191175
{
192176
var value = new ManyToOne(table);
193177
BindKeyManyToOne(keyManyToOneMapping, value, propertyName, componetDefaultNullable);
194-
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
178+
property = CreateProperty(entityPropertyMapping, mappedClass, value, inheritedMetas);
195179
}
196180

197181
if (property != null)
@@ -402,30 +386,27 @@ private void BindCollectionProperty(ICollectionPropertiesMapping collectionMappi
402386
property.Cascade = collectionMapping.Cascade ?? mappings.DefaultCascade;
403387
}
404388

405-
private Property CreateProperty(IEntityPropertyMapping propertyMapping, string propertyOwnerClassName, IValue value, IDictionary<string, MetaAttribute> inheritedMetas)
389+
private Property CreateProperty(IEntityPropertyMapping propertyMapping, System.Type propertyOwnerType, IValue value, IDictionary<string, MetaAttribute> inheritedMetas)
406390
{
391+
var type = propertyOwnerType?.UnwrapIfNullable();
407392
if (string.IsNullOrEmpty(propertyMapping.Name))
408-
{
409-
throw new MappingException("A property mapping must define the name attribute [" + propertyOwnerClassName + "]");
410-
}
393+
throw new MappingException("A property mapping must define the name attribute [" + type + "]");
411394

412395
var propertyAccessorName = GetPropertyAccessorName(propertyMapping.Access);
413396

414-
if (!string.IsNullOrEmpty(propertyOwnerClassName) && value.IsSimpleValue)
415-
value.SetTypeUsingReflection(propertyOwnerClassName, propertyMapping.Name, propertyAccessorName);
397+
if (type != null && value.IsSimpleValue)
398+
value.SetTypeUsingReflection(type.AssemblyQualifiedName, propertyMapping.Name, propertyAccessorName);
416399

417-
var property = new Property
418-
{
419-
Name = propertyMapping.Name,
420-
PropertyAccessorName = propertyAccessorName,
421-
Value = value,
422-
IsLazy = propertyMapping.IsLazyProperty,
423-
LazyGroup = propertyMapping.GetLazyGroup(),
424-
IsOptimisticLocked = propertyMapping.OptimisticLock,
425-
MetaAttributes = GetMetas(propertyMapping, inheritedMetas)
426-
};
427-
428-
return property;
400+
return new Property
401+
{
402+
Name = propertyMapping.Name,
403+
PropertyAccessorName = propertyAccessorName,
404+
Value = value,
405+
IsLazy = propertyMapping.IsLazyProperty,
406+
LazyGroup = propertyMapping.GetLazyGroup(),
407+
IsOptimisticLocked = propertyMapping.OptimisticLock,
408+
MetaAttributes = GetMetas(propertyMapping, inheritedMetas)
409+
};
429410
}
430411

431412
private string GetPropertyAccessorName(string propertyMappedAccessor)

src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using NHibernate.Bytecode.Lightweight;
44
using NHibernate.Intercept;
55
using NHibernate.Properties;
6+
using NHibernate.Util;
67

78
namespace NHibernate.Tuple.Component
89
{
@@ -155,12 +156,12 @@ protected internal override IInstantiator BuildInstantiator(Mapping.Component co
155156

156157
protected internal override IGetter BuildGetter(Mapping.Component component, Mapping.Property prop)
157158
{
158-
return prop.GetGetter(component.ComponentClass);
159+
return prop.GetGetter(component.ComponentClass.UnwrapIfNullable());
159160
}
160161

161162
protected internal override ISetter BuildSetter(Mapping.Component component, Mapping.Property prop)
162163
{
163-
return prop.GetSetter(component.ComponentClass);
164+
return prop.GetSetter(component.ComponentClass.UnwrapIfNullable());
164165
}
165166

166167
protected void SetReflectionOptimizer()

0 commit comments

Comments
 (0)