Skip to content

Commit 46fe1db

Browse files
James Freiwirthjagregory
authored andcommitted
Mapping composite user types with multiple columns now working. Resolves issues 129 and 365.
1 parent 35ecfa9 commit 46fe1db

File tree

3 files changed

+84
-119
lines changed

3 files changed

+84
-119
lines changed

src/FluentNHibernate.Testing/AutoMapping/Apm/AutoPersistenceModelTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,20 @@ public void TypeConventionShouldForcePropertyToBeMapped()
508508
.Element("class/property").HasAttribute("name", "Custom");
509509
}
510510

511+
[Test]
512+
public void TypeConventionShouldForceCompositePropertyToBeMappedWithCorrectNumberOfColumns()
513+
{
514+
var autoMapper = AutoMap.AssemblyOf<ClassWithCompositeUserType>()
515+
.Conventions.Add<CustomCompositeTypeConvention>()
516+
.Where(t => t.Namespace == "FluentNHibernate.Automapping.TestFixtures" && t != typeof(DoubleString));
517+
518+
var mappedColumns = new
519+
AutoMappingTester<ClassWithCompositeUserType>(autoMapper)
520+
.Element("class/property");
521+
522+
mappedColumns.HasThisManyChildNodes(2);
523+
}
524+
511525
[Test]
512526
public void ComponentTypesAutoMapped()
513527
{

src/FluentNHibernate.Testing/AutoMapping/TestFixtures.cs

Lines changed: 36 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
using FluentNHibernate.Automapping.TestFixtures.ComponentTypes;
77
using FluentNHibernate.Automapping.TestFixtures.CustomCompositeTypes;
88
using FluentNHibernate.Automapping.TestFixtures.CustomTypes;
9+
using FluentNHibernate.Conventions.AcceptanceCriteria;
10+
using FluentNHibernate.Conventions.Inspections;
11+
using FluentNHibernate.Conventions.Instances;
912
using FluentNHibernate.Mapping;
1013
using FluentNHibernate.Conventions;
1114
using Iesi.Collections.Generic;
@@ -135,7 +138,7 @@ public class ClassWithUserType
135138
public class ClassWithCompositeUserType
136139
{
137140
public int Id { get; set; }
138-
public DoubleStringType DoubleStringType { get; set; }
141+
public DoubleString SomeStringTuple { get; set; }
139142
}
140143

141144
public class Customer
@@ -189,9 +192,28 @@ public class Custom
189192

190193
}
191194

195+
public class DoubleString
196+
{
197+
public string s1 { get; set; }
198+
public string s2 { get; set; }
199+
}
200+
192201
public class CustomTypeConvention : UserTypeConvention<CustomUserType>
193202
{}
194203

204+
public class CustomCompositeTypeConvention : IUserTypeConvention
205+
{
206+
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
207+
{
208+
criteria.Expect(x => x.Type == typeof(DoubleString));
209+
}
210+
211+
public void Apply(IPropertyInstance instance)
212+
{
213+
instance.CustomType<DoubleStringType>();
214+
}
215+
}
216+
195217
public class CustomUserType : IUserType
196218
{
197219
public new bool Equals(object x, object y)
@@ -264,28 +286,29 @@ public System.Type ReturnedClass
264286
{
265287
if (x == y) return true;
266288
if (x == null || y == null) return false;
267-
string[] lhs = (string[])x;
268-
string[] rhs = (string[])y;
269289

270-
return lhs[0].Equals(rhs[0]) && lhs[1].Equals(rhs[1]);
290+
DoubleString lhs = (DoubleString)x;
291+
DoubleString rhs = (DoubleString)y;
292+
293+
return lhs.s1 == rhs.s1 && lhs.s2 == rhs.s2;
271294
}
272295

273296
public int GetHashCode(object x)
274297
{
275298
unchecked
276299
{
277-
string[] a = (string[])x;
278-
return a[0].GetHashCode() + 31 * a[1].GetHashCode();
300+
DoubleString a = (DoubleString)x;
301+
return a.s1.GetHashCode() + 31 * a.s2.GetHashCode();
279302
}
280303
}
281304

282305
public Object DeepCopy(Object x)
283306
{
284307
if (x == null) return null;
285-
string[] result = new string[2];
286-
string[] input = (string[])x;
287-
result[0] = input[0];
288-
result[1] = input[1];
308+
DoubleString result = new DoubleString();
309+
DoubleString input = (DoubleString)x;
310+
result.s1 = input.s1;
311+
result.s2 = input.s2;
289312
return result;
290313
}
291314

@@ -305,10 +328,10 @@ public Object NullSafeGet(IDataReader rs, string[] names, ISessionImplementor se
305328

306329
public void NullSafeSet(IDbCommand st, Object value, int index, ISessionImplementor session)
307330
{
308-
string[] strings = (value == null) ? new string[2] : (string[])value;
331+
DoubleString ds = value as DoubleString ?? new DoubleString();
309332

310-
NHibernateUtil.String.NullSafeSet(st, strings[0], index, session);
311-
NHibernateUtil.String.NullSafeSet(st, strings[1], index + 1, session);
333+
NHibernateUtil.String.NullSafeSet(st, ds.s1, index, session);
334+
NHibernateUtil.String.NullSafeSet(st, ds.s2, index + 1, session);
312335
}
313336

314337
public string[] PropertyNames
@@ -352,103 +375,6 @@ public object Replace(object original, object target, ISessionImplementor sessio
352375
return DeepCopy(original);
353376
}
354377
}
355-
356-
/// <summary>
357-
/// Extracted from the NHibernate.Model
358-
/// </summary>
359-
public class CustomCompositeUserType : ICompositeUserType
360-
{
361-
public System.Type ReturnedClass
362-
{
363-
get { return typeof(string[]); }
364-
}
365-
366-
public new bool Equals(object x, object y)
367-
{
368-
if (x == y) return true;
369-
if (x == null || y == null) return false;
370-
string[] lhs = (string[])x;
371-
string[] rhs = (string[])y;
372-
373-
return lhs[0].Equals(rhs[0]) && lhs[1].Equals(rhs[1]);
374-
}
375-
376-
public int GetHashCode(object x)
377-
{
378-
unchecked
379-
{
380-
string[] a = (string[])x;
381-
return a[0].GetHashCode() + 31 * a[1].GetHashCode();
382-
}
383-
}
384-
385-
public Object DeepCopy(Object x)
386-
{
387-
if (x == null) return null;
388-
string[] result = new string[2];
389-
string[] input = (string[])x;
390-
result[0] = input[0];
391-
result[1] = input[1];
392-
return result;
393-
}
394-
395-
public bool IsMutable
396-
{
397-
get { return true; }
398-
}
399-
400-
public Object NullSafeGet(IDataReader rs, string[] names, ISessionImplementor session, Object owner)
401-
{
402-
string first = (string)NHibernateUtil.String.NullSafeGet(rs, names[0], session, owner);
403-
string second = (string)NHibernateUtil.String.NullSafeGet(rs, names[1], session, owner);
404-
405-
return (first == null && second == null) ? null : new string[] { first, second };
406-
}
407-
408-
public void NullSafeSet(IDbCommand st, Object value, int index, ISessionImplementor session)
409-
{
410-
string[] strings = (value == null) ? new string[2] : (string[])value;
411-
412-
NHibernateUtil.String.NullSafeSet(st, strings[0], index, session);
413-
NHibernateUtil.String.NullSafeSet(st, strings[1], index + 1, session);
414-
}
415-
416-
public string[] PropertyNames
417-
{
418-
get { return new string[] { "s1", "s2" }; }
419-
}
420-
421-
public IType[] PropertyTypes
422-
{
423-
get { return new IType[] { NHibernateUtil.String, NHibernateUtil.String }; }
424-
}
425-
426-
public Object GetPropertyValue(Object component, int property)
427-
{
428-
return ((string[])component)[property];
429-
}
430-
431-
public void SetPropertyValue(Object component, int property, Object value)
432-
{
433-
((string[])component)[property] = (string)value;
434-
}
435-
436-
public object Assemble(object cached, ISessionImplementor session, object owner)
437-
{
438-
return DeepCopy(cached);
439-
}
440-
441-
public object Disassemble(Object value, ISessionImplementor session)
442-
{
443-
return DeepCopy(value);
444-
}
445-
446-
public object Replace(object original, object target, ISessionImplementor session, object owner)
447-
{
448-
return DeepCopy(original);
449-
}
450-
}
451-
452378
}
453379

454380
namespace FluentNHibernate.Automapping.TestFixtures.SuperTypes

src/FluentNHibernate/Conventions/Instances/PropertyInstance.cs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.Linq;
44
using FluentNHibernate.Conventions.Inspections;
55
using FluentNHibernate.MappingModel;
6+
using NHibernate.Type;
7+
using NHibernate.UserTypes;
68

79
namespace FluentNHibernate.Conventions.Instances
810
{
@@ -59,28 +61,30 @@ public PropertyInstance(PropertyMapping mapping)
5961
}
6062
}
6163

62-
public void CustomType<T>()
64+
public void CustomType(TypeReference type)
6365
{
6466
if (!mapping.IsSpecified("Type"))
65-
mapping.Type = new TypeReference(typeof(T));
67+
{
68+
mapping.Type = type;
69+
70+
if (typeof(ICompositeUserType).IsAssignableFrom(mapping.Type.GetUnderlyingSystemType()))
71+
AddColumnsForCompositeUserType();
72+
}
6673
}
6774

68-
public void CustomType(TypeReference type)
75+
public void CustomType<T>()
6976
{
70-
if (!mapping.IsSpecified("Type"))
71-
mapping.Type = type;
77+
CustomType(typeof(T));
7278
}
7379

7480
public void CustomType(Type type)
7581
{
76-
if (!mapping.IsSpecified("Type"))
77-
mapping.Type = new TypeReference(type);
82+
CustomType(new TypeReference(type));
7883
}
7984

8085
public void CustomType(string type)
8186
{
82-
if (!mapping.IsSpecified("Type"))
83-
mapping.Type = new TypeReference(type);
87+
CustomType(new TypeReference(type));
8488
}
8589

8690
public void CustomSqlType(string sqlType)
@@ -210,5 +214,26 @@ public void Column(string columnName)
210214
foreach (var column in mapping.Columns)
211215
column.Index = value;
212216
}
217+
218+
private void AddColumnsForCompositeUserType()
219+
{
220+
var inst = (ICompositeUserType)Activator.CreateInstance(mapping.Type.GetUnderlyingSystemType());
221+
222+
if (inst.PropertyNames.Length > 1)
223+
{
224+
var existingColumn = mapping.Columns.Single();
225+
mapping.ClearColumns();
226+
var propertyPrefix = existingColumn.Name;
227+
for (int i = 0; i < inst.PropertyNames.Length; i++)
228+
{
229+
var propertyName = inst.PropertyNames[i];
230+
var propertyType = inst.PropertyTypes[i];
231+
232+
var column = ColumnMapping.BaseOn(existingColumn);
233+
column.Name = propertyPrefix + "_" + propertyName;
234+
mapping.AddColumn(column);
235+
}
236+
}
237+
}
213238
}
214239
}

0 commit comments

Comments
 (0)