Skip to content

Commit 70ce00b

Browse files
committed
Fix property replacement if needed inside component
1 parent a3ac45e commit 70ce00b

File tree

4 files changed

+136
-7
lines changed

4 files changed

+136
-7
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using NUnit.Framework;
4+
5+
namespace NHibernate.Test.NHSpecificTest.NH1039Dynamic
6+
{
7+
[TestFixture]
8+
public class Fixture : BugTestCase
9+
{
10+
public override string BugNumber => "NH1039Dynamic";
11+
12+
protected override void OnTearDown()
13+
{
14+
base.OnTearDown();
15+
using (var s = OpenSession())
16+
using (var tx = s.BeginTransaction())
17+
{
18+
s.Delete("from Person");
19+
tx.Commit();
20+
}
21+
}
22+
23+
[Test]
24+
public void Test()
25+
{
26+
using (var s = OpenSession())
27+
using (var tx = s.BeginTransaction())
28+
{
29+
var person = new Person("1")
30+
{
31+
Name = "John Doe",
32+
Properties =
33+
{
34+
Phones = new HashSet<object> {"555-1234", "555-4321"}
35+
}
36+
};
37+
38+
s.Save(person);
39+
tx.Commit();
40+
}
41+
using (var s = OpenSession())
42+
using (s.BeginTransaction())
43+
{
44+
var person = (Person) s.CreateCriteria(typeof(Person)).UniqueResult();
45+
46+
Assert.That(person.ID, Is.EqualTo("1"));
47+
Assert.That(person.Name, Is.EqualTo("John Doe"));
48+
var phones = person.Properties.Phones as ISet<object>;
49+
Assert.That(phones, Is.Not.Null);
50+
Assert.That(phones.Contains("555-1234"), Is.True);
51+
Assert.That(phones.Contains("555-4321"), Is.True);
52+
}
53+
}
54+
}
55+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3+
namespace="NHibernate.Test.NHSpecificTest.NH1039Dynamic"
4+
assembly="NHibernate.Test">
5+
6+
<class name="Person" table="NH1039_Person">
7+
<id name="ID" type="string" length="32">
8+
<generator class="assigned"/>
9+
</id>
10+
11+
<property name="Name"/>
12+
13+
<dynamic-component name="Properties">
14+
<set name="Phones" table="NH1039_Phone">
15+
<key column="PersonId"/>
16+
<element column="`Number`" type="string"/>
17+
</set>
18+
</dynamic-component>
19+
</class>
20+
21+
</hibernate-mapping>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Dynamic;
5+
using System.Text;
6+
7+
namespace NHibernate.Test.NHSpecificTest.NH1039Dynamic
8+
{
9+
public class Person
10+
{
11+
public Person()
12+
{
13+
}
14+
15+
public Person(string id)
16+
{
17+
ID = id;
18+
}
19+
20+
public virtual string ID { get; set; }
21+
22+
public virtual string Name { get; set; }
23+
24+
public virtual dynamic Properties { get; set; } = new ExpandoObject();
25+
}
26+
}

src/NHibernate/Properties/MapAccessor.cs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public class MapAccessor : IPropertyAccessor
2222
[Serializable]
2323
public sealed class MapSetter : ISetter
2424
{
25+
private static readonly ConcurrentDictionary<Tuple<System.Type, string>, CallSite<Func<CallSite, object, object, object>>> SetMemberSites =
26+
new ConcurrentDictionary<Tuple<System.Type, string>, CallSite<Func<CallSite, object, object, object>>>();
27+
2528
private readonly string _name;
2629

2730
internal MapSetter(string name) => _name = name;
@@ -32,15 +35,39 @@ public sealed class MapSetter : ISetter
3235

3336
public void Set(object target, object value)
3437
{
35-
((IDictionary) target)[_name] = value;
38+
switch (target)
39+
{
40+
case IDictionary d:
41+
d[_name] = value;
42+
break;
43+
case IDictionary<string, object> d:
44+
d[_name] = value;
45+
break;
46+
default:
47+
var site = SetMemberSites.GetOrAdd(
48+
System.Tuple.Create(target.GetType(), _name),
49+
t => CallSite<Func<CallSite, object, object, object>>.Create(
50+
Binder.GetMember(
51+
CSharpBinderFlags.None,
52+
t.Item2,
53+
t.Item1,
54+
new[]
55+
{
56+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
57+
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
58+
})));
59+
60+
site.Target(site, target, value);
61+
break;
62+
}
3663
}
3764
}
3865

3966
[Serializable]
4067
public sealed class MapGetter : IGetter
4168
{
42-
private static readonly ConcurrentDictionary<string, CallSite<Func<CallSite, object, object>>> GetMemberSites =
43-
new ConcurrentDictionary<string, CallSite<Func<CallSite, object, object>>>();
69+
private static readonly ConcurrentDictionary<Tuple<System.Type, string>, CallSite<Func<CallSite, object, object>>> GetMemberSites =
70+
new ConcurrentDictionary<Tuple<System.Type, string>, CallSite<Func<CallSite, object, object>>>();
4471

4572
private readonly string _name;
4673

@@ -65,12 +92,12 @@ public object Get(object target)
6592
return result;
6693
default:
6794
var site = GetMemberSites.GetOrAdd(
68-
_name,
69-
name => CallSite<Func<CallSite, object, object>>.Create(
95+
System.Tuple.Create(target.GetType(), _name),
96+
t => CallSite<Func<CallSite, object, object>>.Create(
7097
Binder.GetMember(
7198
CSharpBinderFlags.None,
72-
name,
73-
target.GetType(),
99+
t.Item2,
100+
t.Item1,
74101
new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)})));
75102

76103
return site.Target(site, target);

0 commit comments

Comments
 (0)