Skip to content

Commit 8db8976

Browse files
committed
ForeignKey enhancements for data annotations
1 parent be82103 commit 8db8976

File tree

2 files changed

+78
-43
lines changed

2 files changed

+78
-43
lines changed

EntityFramework.Reverse.POCO.Generator/EF.Reverse.POCO.Core.ttinclude

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<#@ import namespace="System.Data.Entity.Infrastructure.Pluralization" #>
1313
<#@ import namespace="System.Text.RegularExpressions" #>
1414
<#@ import namespace="System.Configuration" #>
15+
<#@ import namespace="System.Runtime.CompilerServices" #>
1516
<#@ import namespace="EnvDTE" #>
1617
<#@ output extension=".cs"#>
1718
<#
@@ -782,14 +783,19 @@
782783
// Calculates the relationship between a child table and it's parent table.
783784
public static Relationship CalcRelationshipSingle(Table parentTable, Table childTable, Column childTableCol, Column parentTableCol)
784785
{
785-
bool childTableSinglePrimaryKey = (childTable.PrimaryKeys.Count() == 1);
786-
bool parentTableSinglePrimaryKey = (parentTable.PrimaryKeys.Count() == 1);
786+
if(!childTableCol.IsPrimaryKey && !childTableCol.IsUniqueConstraint)
787+
return Relationship.ManyToOne;
787788

788-
// 1:1
789-
if(childTableCol.IsPrimaryKey && parentTableCol.IsPrimaryKey && childTableSinglePrimaryKey && parentTableSinglePrimaryKey)
790-
return Relationship.OneToOne;
789+
if(!parentTableCol.IsPrimaryKey && !parentTableCol.IsUniqueConstraint)
790+
return Relationship.ManyToOne;
791791

792-
return Relationship.ManyToOne;
792+
if (childTable.PrimaryKeys.Count() != 1)
793+
return Relationship.ManyToOne;
794+
795+
if (parentTable.PrimaryKeys.Count() != 1)
796+
return Relationship.ManyToOne;
797+
798+
return Relationship.OneToOne;
793799
}
794800

795801
public class EnumDefinition
@@ -832,7 +838,8 @@
832838
public bool IsIdentity;
833839
public bool IsNullable;
834840
public bool IsPrimaryKey;
835-
public bool IsPrimaryKeyViaUniqueIndex;
841+
public bool IsUniqueConstraint;
842+
public bool IsUnique;
836843
public bool IsStoreGenerated;
837844
public bool IsRowVersion;
838845
public bool IsConcurrencyToken; // Manually set via callback
@@ -866,7 +873,7 @@
866873
comments = Name;
867874
if (IsPrimaryKey)
868875
{
869-
if (IsPrimaryKeyViaUniqueIndex)
876+
if (IsUniqueConstraint)
870877
comments += " (Primary key via unique index " + UniqueIndexName + ")";
871878
else
872879
comments += " (Primary key)";
@@ -2591,29 +2598,32 @@ SELECT SERVERPROPERTY('Edition') AS Edition,
25912598
var indexes =
25922599
list.Where(x => x.Schema == indexTable.Schema && x.TableName == indexTable.TableName)
25932600
.OrderBy(o => o.ColumnCount)
2594-
.ThenBy(o => o.KeyOrdinal);
2601+
.ThenBy(o => o.KeyOrdinal)
2602+
.ToList();
25952603

25962604
// Set index on column
25972605
foreach (var index in indexes)
25982606
{
25992607
var col = t.Columns.Find(x => x.Name == index.ColumnName);
2600-
if (col != null)
2601-
{
2602-
col.Indexes.Add(index);
2603-
if (!col.IsPrimaryKey && index.IsPrimaryKey)
2604-
col.IsPrimaryKey = true;
2605-
}
2608+
if (col == null)
2609+
continue;
2610+
2611+
col.Indexes.Add(index);
2612+
2613+
col.IsPrimaryKey = col.IsPrimaryKey || index.IsPrimaryKey;
2614+
col.IsUniqueConstraint = col.IsUniqueConstraint || (index.IsUniqueConstraint && index.ColumnCount == 1);
2615+
col.IsUnique = col.IsUnique || (index.IsUnique && index.ColumnCount == 1);
26062616
}
26072617

26082618
// Check if table has any primary keys
26092619
if (t.PrimaryKeys.Any())
26102620
continue; // Already has a primary key, ignore this unique index / constraint
26112621

26122622
// Find unique indexes for table
2613-
var uniqueIndexKeys =
2614-
indexes.Where(x => x.IsUnique || x.IsPrimaryKey || x.IsUniqueConstraint)
2615-
.OrderBy(o => o.ColumnCount)
2616-
.ThenBy(o => o.KeyOrdinal);
2623+
var uniqueIndexKeys = indexes
2624+
.Where(x => x.IsUnique || x.IsPrimaryKey || x.IsUniqueConstraint)
2625+
.OrderBy(o => o.ColumnCount)
2626+
.ThenBy(o => o.KeyOrdinal);
26172627

26182628
// Process only the first index with the lowest unique column count
26192629
string indexName = null;
@@ -2629,7 +2639,8 @@ SELECT SERVERPROPERTY('Edition') AS Edition,
26292639
if (col != null && !col.IsNullable && !col.Hidden && !col.IsPrimaryKey)
26302640
{
26312641
col.IsPrimaryKey = true;
2632-
col.IsPrimaryKeyViaUniqueIndex = true;
2642+
col.IsUniqueConstraint = true;
2643+
col.IsUnique = true;
26332644
col.UniqueIndexName = indexName;
26342645
}
26352646
}
@@ -2878,31 +2889,50 @@ SELECT SERVERPROPERTY('Edition') AS Edition,
28782889

28792890
var fkCols = foreignKeys.Select(x => new
28802891
{
2881-
fkOrdinal = x.Ordinal,
2892+
fk = x,
28822893
col = fkTable.Columns.Find(n => string.Equals(n.Name, x.FkColumn, StringComparison.InvariantCultureIgnoreCase))
28832894
})
28842895
.Where(x => x.col != null)
2885-
.OrderBy(o => o.fkOrdinal)
2896+
.OrderBy(o => o.fk.Ordinal)
28862897
.ToList();
28872898

28882899
if (!fkCols.Any())
28892900
continue;
28902901

2902+
//if(EF6)
2903+
{
2904+
// Check FK has same number of columns as the primary key it points to
2905+
var pks = pkTable.PrimaryKeys.OrderBy(x => x.PropertyType).ThenBy(y => y.Name).ToArray();
2906+
var cols = fkCols.Select(x => x.col).OrderBy(x => x.PropertyType).ThenBy(y => y.Name).ToArray();
2907+
if (pks.Length != cols.Length)
2908+
continue;
2909+
2910+
// EF6 - Cannot have a FK to a non-primary key
2911+
if(pks.Where((pk, ef6Check) => pk.PropertyType != cols[ef6Check].PropertyType).Any())
2912+
continue;
2913+
}
2914+
28912915
var pkCols = foreignKeys.Select(x => pkTable.Columns.Find(n => string.Equals(n.Name, x.PkColumn, StringComparison.InvariantCultureIgnoreCase)))
2892-
.Where(x => x != null && x.IsPrimaryKey)
2916+
.Where(x => x != null)
28932917
.OrderBy(o => o.Ordinal)
28942918
.ToList();
28952919

28962920
if (!pkCols.Any())
28972921
continue;
28982922

2899-
var fkCol = fkCols.First();
2900-
var pkCol = pkCols.First();
2901-
29022923
var relationship = CalcRelationship(pkTable, fkTable, fkCols.Select(c => c.col).ToList(), pkCols);
29032924
if (relationship == Relationship.DoNotUse)
29042925
continue;
29052926

2927+
if(fkCols.All(x => !x.col.IsNullable && !x.col.Hidden) && pkCols.All(x => x.IsPrimaryKey || x.IsUnique))
2928+
{
2929+
foreach (var fk in fkCols)
2930+
fk.fk.IncludeRequiredAttribute = true;
2931+
}
2932+
2933+
var fkCol = fkCols.First();
2934+
var pkCol = pkCols.First();
2935+
29062936
foreignKey = Settings.ForeignKeyProcessing(foreignKeys, fkTable, pkTable, fkCols.Any(x => x.col.IsNullable));
29072937

29082938
string pkTableHumanCaseWithSuffix = foreignKey.PkTableHumanCase(pkTable.Suffix);
@@ -2919,11 +2949,17 @@ SELECT SERVERPROPERTY('Edition') AS Edition,
29192949
foreignKey.IncludeRequiredAttribute ? ", Required" : string.Empty
29202950
);
29212951

2922-
if (!checkForFkNameClashes && relationship == Relationship.OneToOne && foreignKey.IncludeReverseNavigation && fkCol.col.IsPrimaryKey)
2952+
if (!checkForFkNameClashes &&
2953+
relationship == Relationship.OneToOne &&
2954+
foreignKey.IncludeReverseNavigation &&
2955+
fkCols.All(x => x.col.IsPrimaryKey))
29232956
{
29242957
var principalEndAttribute = string.Format("ForeignKey(\"{0}\")", pkPropName);
2925-
if (!fkCol.col.DataAnnotations.Contains(principalEndAttribute))
2926-
fkCol.col.DataAnnotations.Add(principalEndAttribute);
2958+
foreach (var fk in fkCols)
2959+
{
2960+
if (!fk.col.DataAnnotations.Contains(principalEndAttribute))
2961+
fk.col.DataAnnotations.Add(principalEndAttribute);
2962+
}
29272963
}
29282964
}
29292965

@@ -2954,9 +2990,11 @@ SELECT SERVERPROPERTY('Edition') AS Edition,
29542990
mapKey = string.Format("\"{0}\"", fkCol.col.Name);
29552991
}
29562992

2957-
// Always include fluent FK mapping even if only using data annotations
2958-
fkCol.col.ConfigFk.Add(string.Format("{0};{1}", GetRelationship(relationship, fkCol.col, pkCol, pkPropName, fkPropName, manyToManyMapping, mapKey, foreignKey.CascadeOnDelete, foreignKey.IncludeReverseNavigation, foreignKey.IsNotEnforced),
2959-
Settings.IncludeComments != CommentsStyle.None ? " // " + foreignKey.ConstraintName : string.Empty));
2993+
if (!Settings.UseDataAnnotations)
2994+
{
2995+
fkCol.col.ConfigFk.Add(string.Format("{0};{1}", GetRelationship(relationship, fkCol.col, pkCol, pkPropName, fkPropName, manyToManyMapping, mapKey, foreignKey.CascadeOnDelete, foreignKey.IncludeReverseNavigation, foreignKey.IsNotEnforced),
2996+
Settings.IncludeComments != CommentsStyle.None ? " // " + foreignKey.ConstraintName : string.Empty));
2997+
}
29602998

29612999
if(foreignKey.IncludeReverseNavigation)
29623000
pkTable.AddReverseNavigation(relationship, pkTableHumanCase, fkTable, fkPropName, string.Format("{0}.{1}", fkTable.Name, foreignKey.ConstraintName), foreignKeys);
@@ -3010,7 +3048,7 @@ SELECT SERVERPROPERTY('Edition') AS Edition,
30103048
break;
30113049
}
30123050

3013-
if (withMany || pkCol.IsPrimaryKey)
3051+
if (withMany || pkCol.IsPrimaryKey || pkCol.IsUniqueConstraint || pkCol.IsUnique)
30143052
return fkCol.IsNullable || isNotEnforced ? "Optional" : "Required";
30153053

30163054
return "Many";
@@ -3467,10 +3505,11 @@ SELECT SERVERPROPERTY('Edition') AS Edition,
34673505
{
34683506
get
34693507
{
3470-
return Columns.Where(x => x.IsPrimaryKey)
3471-
.OrderBy(x => x.PrimaryKeyOrdinal)
3472-
.ThenBy(x => x.Ordinal)
3473-
.ToList();
3508+
return Columns
3509+
.Where(x => x.IsPrimaryKey)
3510+
.OrderBy(x => x.PrimaryKeyOrdinal)
3511+
.ThenBy(x => x.Ordinal)
3512+
.ToList();
34743513
}
34753514
}
34763515

Tester/TestDatabase_DataAnnotation.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ public class Stafford_Foo
793793
/// <summary>
794794
/// Parent Stafford_Boo pointed by [Foo].([Id]) (FK_Foo_Boo)
795795
/// </summary>
796-
[ForeignKey("Id")] public virtual Stafford_Boo Stafford_Boo { get; set; } // FK_Foo_Boo
796+
[ForeignKey("Id"), Required] public virtual Stafford_Boo Stafford_Boo { get; set; } // FK_Foo_Boo
797797
}
798798

799799
// Child
@@ -825,7 +825,7 @@ public class Synonyms_Child
825825
/// <summary>
826826
/// Parent Synonyms_Parent pointed by [Child].([ParentId]) (FK_Child_Parent)
827827
/// </summary>
828-
[ForeignKey("ParentId")] public virtual Synonyms_Parent Synonyms_Parent { get; set; } // FK_Child_Parent
828+
[ForeignKey("ParentId"), Required] public virtual Synonyms_Parent Synonyms_Parent { get; set; } // FK_Child_Parent
829829
}
830830

831831
// Parent
@@ -939,8 +939,6 @@ public Stafford_FooConfiguration(string schema)
939939
{
940940
Property(x => x.Name).IsFixedLength();
941941

942-
// Foreign keys
943-
HasRequired(a => a.Stafford_Boo).WithOptional(b => b.Stafford_Foo).WillCascadeOnDelete(false); // FK_Foo_Boo
944942
}
945943
}
946944

@@ -957,8 +955,6 @@ public Synonyms_ChildConfiguration(string schema)
957955
{
958956
Property(x => x.ChildName).IsOptional().IsUnicode(false);
959957

960-
// Foreign keys
961-
HasRequired(a => a.Synonyms_Parent).WithMany(b => b.Synonyms_Children).HasForeignKey(c => c.ParentId).WillCascadeOnDelete(false); // FK_Child_Parent
962958
}
963959
}
964960

0 commit comments

Comments
 (0)