Skip to content

Commit 993fe10

Browse files
committed
Merge pull request #64 from msallin/Issue-63
#63: Support self ref associations.
2 parents 0b66b5f + ba6f3fd commit 993fe10

9 files changed

+89
-46
lines changed

SQLite.CodeFirst.Console/Entity/Player.cs

+2
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ public class Player : Person
1515

1616
// Its not possible to set an index on this property. Use the FK property (teamId).
1717
public virtual Team Team { get; set; }
18+
19+
public virtual Player Mentor { get; set; }
1820
}
1921
}

SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs

+3-22
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Collections.Generic;
22
using System.Data.Entity.Core.Metadata.Edm;
33
using System.Linq;
4-
using SQLite.CodeFirst.Builder.NameCreators;
54
using SQLite.CodeFirst.Statement;
65
using SQLite.CodeFirst.Utility;
76

@@ -27,29 +26,11 @@ public CreateDatabaseStatement BuildStatement()
2726

2827
private IEnumerable<CreateTableStatement> GetCreateTableStatements()
2928
{
29+
var associationTypeContainer = new AssociationTypeContainer(edmModel.AssociationTypes, edmModel.Container);
30+
3031
foreach (var entitySet in edmModel.Container.EntitySets)
3132
{
32-
ICollection<AssociationType> associationTypes =
33-
edmModel.AssociationTypes.Where(a => a.Constraint.ToRole.Name == entitySet.Name).ToList();
34-
35-
IList<AssociationTypeWrapper> associationTypeWrappers = new List<AssociationTypeWrapper>();
36-
foreach (var associationType in associationTypes)
37-
{
38-
string fromTable = edmModel.Container.GetEntitySetByName(associationType.Constraint.FromRole.Name, true).Table;
39-
string toTable = edmModel.Container.GetEntitySetByName(associationType.Constraint.ToRole.Name, true).Table;
40-
41-
string fromTableName = TableNameCreator.CreateTableName(fromTable);
42-
string toTableName = TableNameCreator.CreateTableName(toTable);
43-
44-
associationTypeWrappers.Add(new AssociationTypeWrapper
45-
{
46-
AssociationType = associationType,
47-
FromTableName = fromTableName,
48-
ToTableName = toTableName
49-
});
50-
}
51-
52-
var tableStatementBuilder = new CreateTableStatementBuilder(entitySet, associationTypeWrappers);
33+
var tableStatementBuilder = new CreateTableStatementBuilder(entitySet, associationTypeContainer);
5334
yield return tableStatementBuilder.BuildStatement();
5435
}
5536
}

SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Data.Entity.Core.Metadata.Edm;
43
using SQLite.CodeFirst.Builder.NameCreators;
54
using SQLite.CodeFirst.Statement;
@@ -10,19 +9,19 @@ namespace SQLite.CodeFirst.Builder
109
internal class CreateTableStatementBuilder : IStatementBuilder<CreateTableStatement>
1110
{
1211
private readonly EntitySet entitySet;
13-
private readonly IEnumerable<AssociationTypeWrapper> associationTypes;
12+
private readonly AssociationTypeContainer associationTypeContainer;
1413

15-
public CreateTableStatementBuilder(EntitySet entitySet, IEnumerable<AssociationTypeWrapper> associationTypes)
14+
public CreateTableStatementBuilder(EntitySet entitySet, AssociationTypeContainer associationTypeContainer)
1615
{
1716
this.entitySet = entitySet;
18-
this.associationTypes = associationTypes;
17+
this.associationTypeContainer = associationTypeContainer;
1918
}
2019

2120
public CreateTableStatement BuildStatement()
2221
{
2322
var simpleColumnCollection = new ColumnStatementCollectionBuilder(entitySet.ElementType.Properties).BuildStatement();
2423
var primaryKeyStatement = new PrimaryKeyStatementBuilder(entitySet.ElementType.KeyMembers).BuildStatement();
25-
var foreignKeyCollection = new ForeignKeyStatementBuilder(associationTypes).BuildStatement();
24+
var foreignKeyCollection = new ForeignKeyStatementBuilder(associationTypeContainer.GetAssociationTypes(entitySet.Name)).BuildStatement();
2625

2726
var columnStatements = new List<IStatement>();
2827
columnStatements.AddRange(simpleColumnCollection);

SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Collections.Generic;
2-
using System.Data.Entity.Core.Metadata.Edm;
32
using System.Linq;
43
using SQLite.CodeFirst.Statement;
54
using SQLite.CodeFirst.Utility;
@@ -8,9 +7,9 @@ namespace SQLite.CodeFirst.Builder
87
{
98
internal class ForeignKeyStatementBuilder : IStatementBuilder<ColumnStatementCollection>
109
{
11-
private readonly IEnumerable<AssociationTypeWrapper> associationTypes;
10+
private readonly IEnumerable<SqliteAssociationType> associationTypes;
1211

13-
public ForeignKeyStatementBuilder(IEnumerable<AssociationTypeWrapper> associationTypes)
12+
public ForeignKeyStatementBuilder(IEnumerable<SqliteAssociationType> associationTypes)
1413
{
1514
this.associationTypes = associationTypes;
1615
}
@@ -27,10 +26,10 @@ private IEnumerable<ForeignKeyStatement> GetForeignKeyStatements()
2726
{
2827
yield return new ForeignKeyStatement
2928
{
30-
ForeignKey = associationType.AssociationType.Constraint.ToProperties.Select(x => x.Name),
29+
ForeignKey = associationType.ForeignKey,
3130
ForeignTable = associationType.FromTableName,
32-
ForeignPrimaryKey = associationType.AssociationType.Constraint.FromProperties.Select(x => x.Name),
33-
CascadeDelete = associationType.AssociationType.Constraint.FromRole.DeleteBehavior == OperationAction.Cascade
31+
ForeignPrimaryKey = associationType.ForeignPrimaryKey,
32+
CascadeDelete = associationType.CascadeDelete
3433
};
3534
}
3635
}

SQLite.CodeFirst/Internal/Builder/NameCreators/TableNameCreator.cs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ internal static class TableNameCreator
77
{
88
public static string CreateTableName(string tableFromEntitySet)
99
{
10+
tableFromEntitySet = tableFromEntitySet.Trim(SpecialChars.EscapeCharOpen, SpecialChars.EscapeCharClose);
1011
return String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", SpecialChars.EscapeCharOpen, tableFromEntitySet, SpecialChars.EscapeCharClose);
1112
}
1213
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Collections.Generic;
2+
using System.Data.Entity.Core.Metadata.Edm;
3+
using System.Linq;
4+
5+
namespace SQLite.CodeFirst.Utility
6+
{
7+
internal class AssociationTypeContainer
8+
{
9+
private readonly IEnumerable<SqliteAssociationType> sqliteAssociationTypes;
10+
11+
public AssociationTypeContainer(IEnumerable<AssociationType> associationTypes, EntityContainer container)
12+
{
13+
sqliteAssociationTypes = associationTypes.Select(associationType => new SqliteAssociationType(associationType, container));
14+
}
15+
16+
public IEnumerable<SqliteAssociationType> GetAssociationTypes(string entitySetName)
17+
{
18+
return sqliteAssociationTypes.Where(associationType => associationType.ToRoleEntitySetName == entitySetName);
19+
}
20+
}
21+
}

SQLite.CodeFirst/Internal/Utility/AssociationTypeWrapper.cs

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Collections.Generic;
2+
using System.Data.Entity.Core.Metadata.Edm;
3+
using System.Linq;
4+
using SQLite.CodeFirst.Builder.NameCreators;
5+
6+
namespace SQLite.CodeFirst.Utility
7+
{
8+
internal class SqliteAssociationType
9+
{
10+
private const string SelfReferencingPostfix = "Self";
11+
12+
public SqliteAssociationType(AssociationType associationType, EntityContainer container)
13+
{
14+
FromRoleEntitySetName = associationType.Constraint.FromRole.Name;
15+
ToRoleEntitySetName = associationType.Constraint.ToRole.Name;
16+
17+
string fromTable = container.GetEntitySetByName(FromRoleEntitySetName, true).Table;
18+
string toTable;
19+
20+
if (IsSelfReferencing(associationType))
21+
{
22+
toTable = fromTable;
23+
ToRoleEntitySetName = FromRoleEntitySetName;
24+
}
25+
else
26+
{
27+
toTable = container.GetEntitySetByName(ToRoleEntitySetName, true).Table;
28+
}
29+
30+
FromTableName = TableNameCreator.CreateTableName(fromTable);
31+
ToTableName = TableNameCreator.CreateTableName(toTable);
32+
ForeignKey = associationType.Constraint.ToProperties.Select(x => x.Name);
33+
ForeignPrimaryKey = associationType.Constraint.FromProperties.Select(x => x.Name);
34+
CascadeDelete = associationType.Constraint.FromRole.DeleteBehavior == OperationAction.Cascade;
35+
}
36+
37+
private static bool IsSelfReferencing(AssociationType associationType)
38+
{
39+
return associationType.Constraint.ToRole.Name.Remove(associationType.Constraint.ToRole.Name.Length - SelfReferencingPostfix.Length, SelfReferencingPostfix.Length) == associationType.Constraint.FromRole.Name;
40+
}
41+
42+
public string ToRoleEntitySetName { get; set; }
43+
public string FromRoleEntitySetName { get; set; }
44+
public IEnumerable<string> ForeignKey { get; }
45+
public string FromTableName { get; }
46+
public string ToTableName { get; }
47+
public IEnumerable<string> ForeignPrimaryKey { get; }
48+
public bool CascadeDelete { get; }
49+
}
50+
}

SQLite.CodeFirst/SQLite.CodeFirst.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,14 @@
101101
<Compile Include="Internal\Statement\ColumnConstraint\UniqueConstraint.cs" />
102102
<Compile Include="Internal\Utility\HashCreator.cs" />
103103
<Compile Include="Internal\Utility\HistoryEntityTypeValidator.cs" />
104+
<Compile Include="Internal\Utility\SqliteAssociationType.cs" />
104105
<Compile Include="ISqliteSqlGenerator.cs" />
105106
<Compile Include="DbInitializers\SqliteDropCreateDatabaseWhenModelChanges.cs" />
106107
<Compile Include="Entities\History.cs" />
107108
<Compile Include="SqliteSqlGenerator.cs" />
108109
<Compile Include="Internal\Statement\ColumnConstraint\IColumnConstraintCollection.cs" />
109110
<Compile Include="Internal\Statement\IStatementCollection.cs" />
110-
<Compile Include="Internal\Utility\AssociationTypeWrapper.cs" />
111+
<Compile Include="Internal\Utility\AssociationTypeContainer.cs" />
111112
<Compile Include="Internal\Builder\ColumnStatementCollectionBuilder.cs" />
112113
<Compile Include="Internal\Builder\CreateIndexStatementBuilder.cs" />
113114
<Compile Include="Internal\Builder\ForeignKeyStatementBuilder.cs" />

0 commit comments

Comments
 (0)