Skip to content

Commit a71f81f

Browse files
committed
Fix for weird key column issue
1.1 introduced a regression where entities that are the target of multiple Reference mappings from the same source would end up with multiple key columns being defined based on the property names. The source of this was the RelationshipKeyPairingVisitor, which frankly I have no idea what it was trying to do. I think it might have something to do with composite keys and relationships, but the usage isn't very clear. For the time being I've removed this functionality.
1 parent e7efe21 commit a71f81f

File tree

7 files changed

+124
-22
lines changed

7 files changed

+124
-22
lines changed

src/FluentNHibernate.Specs/Conventions/BiDirectionalKeysSpecs.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@ public class when_mapping_a_many_to_one_one_to_many_bi_directional_relationship
2121
child = mappings.SelectMany(x => x.Classes).FirstOrDefault(x => x.Type == typeof(Child));
2222
};
2323

24+
// these are ignored due to a regression this code caused - as this feature was added only as an
25+
// experiment, I doubt anyone will miss it for the time being.
26+
27+
[Ignore]
2428
It should_use_the_many_to_one_columns_for_the_one_to_many_key = () =>
2529
parent.Collections.Single().Key.Columns.Select(x => x.Name).ShouldContainOnly("one", "two");
2630

31+
[Ignore]
2732
It shouldnt_alter_the_many_to_one_columns = () =>
2833
child.References.Single().Columns.Select(x => x.Name).ShouldContainOnly("one", "two");
2934

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Linq;
2+
using FluentNHibernate.MappingModel.ClassBased;
3+
using FluentNHibernate.Specs.FluentInterface.Fixtures.BiDirectionalKeyIssue;
4+
using Machine.Specifications;
5+
6+
namespace FluentNHibernate.Specs.FluentInterface
7+
{
8+
public class when_there_are_two_references_to_the_same_entity
9+
{
10+
Establish context = () =>
11+
{
12+
model = new FluentNHibernate.PersistenceModel();
13+
model.Add(new ContactMap());
14+
model.Add(new ContactEmailMap());
15+
model.Add(new ContactPhoneMap());
16+
model.Add(new CaseMap());
17+
};
18+
19+
Because of = () =>
20+
contact_mapping = model.BuildMappingFor<Contact>();
21+
22+
It should_work_like_1_0_did_aka_not_create_multiple_columns_to_the_same_entity = () =>
23+
contact_mapping.Collections
24+
.Single(x => x.Name == "EmailAddresses")
25+
.Key.Columns.Select(x => x.Name).ShouldContainOnly("Contact_id");
26+
27+
static FluentNHibernate.PersistenceModel model;
28+
static ClassMapping contact_mapping;
29+
}
30+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using FluentNHibernate.Mapping;
4+
5+
namespace FluentNHibernate.Specs.FluentInterface.Fixtures.BiDirectionalKeyIssue
6+
{
7+
public abstract class Entity
8+
{
9+
public Guid Id { get; set; }
10+
}
11+
12+
public class ContactMap : DomainMap<Contact>
13+
{
14+
public ContactMap()
15+
{
16+
HasMany(x => x.EmailAddresses);
17+
HasMany(x => x.PhoneNumbers);
18+
}
19+
}
20+
21+
public class Contact : Entity
22+
{
23+
public virtual IEnumerable<ContactEmail> EmailAddresses { get; set; }
24+
public virtual IEnumerable<ContactPhone> PhoneNumbers { get; set; }
25+
}
26+
27+
public class ContactEmail : Entity
28+
{
29+
public virtual string EmailAddress { get; set; }
30+
public virtual Contact Contact { get; set; }
31+
}
32+
33+
public class ContactPhone : Entity
34+
{
35+
public virtual string PhoneNumber { get; set; }
36+
}
37+
38+
public class ContactEmailMap : DomainMap<ContactEmail>
39+
{
40+
public ContactEmailMap()
41+
{
42+
Map(c => c.EmailAddress);
43+
References(x => x.Contact);
44+
}
45+
}
46+
47+
public class ContactPhoneMap : DomainMap<ContactPhone>
48+
{
49+
public ContactPhoneMap()
50+
{
51+
Map(c => c.PhoneNumber);
52+
}
53+
}
54+
55+
public class DomainMap<T> : ClassMap<T> where T : Entity
56+
{
57+
public DomainMap()
58+
{
59+
Id(x => x.Id).Column("id").GeneratedBy.GuidComb();
60+
Table(typeof(T).Name);
61+
}
62+
}
63+
64+
public class CaseMap : DomainMap<Case>
65+
{
66+
public CaseMap()
67+
{
68+
References(c => c.AlternateContact);
69+
References(c => c.Contact);
70+
Table("Cases");
71+
}
72+
}
73+
74+
public class Case : Entity
75+
{
76+
public virtual Contact Contact { get; set; }
77+
public virtual Contact AlternateContact { get; set; }
78+
}
79+
}

src/FluentNHibernate.Specs/FluentNHibernate.Specs.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@
144144
</ItemGroup>
145145
<ItemGroup>
146146
<Compile Include="Automapping\AutomappingSpecs.NestedClasses.cs" />
147+
<Compile Include="FluentInterface\BiDirectionalKeyIssueSpecs.cs" />
147148
<Compile Include="FluentInterface\ExternalComponentOutputSpecs.cs" />
149+
<Compile Include="FluentInterface\Fixtures\BiDirectionalKeyIssue.cs" />
148150
</ItemGroup>
149151
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
150152
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

src/FluentNHibernate/Automapping/Steps/ReferenceStep.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public bool ShouldMap(Member member)
2626
public void Map(ClassMappingBase classMap, Member member)
2727
{
2828
var manyToOne = CreateMapping(member);
29+
manyToOne.ContainingEntityType = classMap.Type;
2930
classMap.AddReference(manyToOne);
3031
}
3132

src/FluentNHibernate/MappingModel/KeyMapping.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public void AddDefaultColumn(ColumnMapping mapping)
8787

8888
public void ClearColumns()
8989
{
90-
columns.Clear();
90+
columns.ClearAll();
9191
}
9292

9393
public override bool IsSpecified(string property)

src/FluentNHibernate/Visitors/RelationshipKeyPairingVisitor.cs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,15 @@ public override void ProcessManyToOne(ManyToOneMapping thisSide)
1414
// other side is always going to be a collection for a many-to-one mapping
1515
var otherSide = (ICollectionMapping)thisSide.OtherSide;
1616

17-
// both sides have user-defined key columns... leave alone!
18-
if (otherSide.Key.Columns.HasUserDefined() && thisSide.Columns.HasUserDefined())
19-
return;
20-
21-
if (otherSide.Key.Columns.HasUserDefined())
17+
if (thisSide.ContainingEntityType == otherSide.ContainingEntityType)
2218
{
23-
// only other side has user-defined columns, so we'll bring them across to this side
24-
thisSide.ClearColumns();
25-
otherSide.Key.Columns.Each(x => thisSide.AddColumn(x.Clone()));
26-
return;
27-
}
19+
// special case for self-referential relationships
20+
if (thisSide.Columns.HasUserDefined() || otherSide.Key.Columns.HasUserDefined())
21+
return; // leave alone if user defined
2822

29-
if (otherSide.Key.Columns.HasUserDefined())
30-
{
31-
// only other side has user-defined columns, so we'll bring them across to this side
32-
thisSide.ClearColumns();
33-
otherSide.Key.Columns.Each(x => thisSide.AddColumn(x.Clone()));
34-
return;
23+
otherSide.Key.ClearColumns();
24+
thisSide.Columns.Each(x => otherSide.Key.AddDefaultColumn(x.Clone()));
3525
}
36-
37-
// the other side doesn't have any user defined columns, so we'll use the ones from this side
38-
// whether they're user defined or not.
39-
otherSide.Key.ClearColumns();
40-
thisSide.Columns.Each(x => otherSide.Key.AddColumn(x.Clone()));
4126
}
4227
}
4328
}

0 commit comments

Comments
 (0)