Skip to content

Commit 7028f72

Browse files
authored
Merge pull request #300 from Atypical-Consulting/fix/fk-constraints
fix: generate FOREIGN KEY constraints in DatabaseInitializer
2 parents 52687cf + d709efa commit 7028f72

3 files changed

Lines changed: 113 additions & 6 deletions

File tree

src/templates/Ninjadog.Templates.CrudWebApi/Template/Database/DatabaseInitializerTemplate.cs

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,17 @@ private static string GenerateCreateTableSqlQueries(List<NinjadogEntityWithKey>
4949
{
5050
stringBuilder
5151
.AppendLine()
52-
.AppendLine($"await connection.ExecuteAsync(@\"{GenerateSqlCreateTableQuery(entity, enumNames, softDelete, auditing, provider)}\");");
52+
.AppendLine($"await connection.ExecuteAsync(@\"{GenerateSqlCreateTableQuery(entity, entities, enumNames, softDelete, auditing, provider)}\");");
5353
}
5454

5555
return stringBuilder.ToString();
5656
}
5757

58-
private static string GenerateSqlCreateTableQuery(NinjadogEntityWithKey entity, HashSet<string>? enumNames, bool softDelete, bool auditing, string provider)
58+
private static string GenerateSqlCreateTableQuery(NinjadogEntityWithKey entity, List<NinjadogEntityWithKey> allEntities, HashSet<string>? enumNames, bool softDelete, bool auditing, string provider)
5959
{
6060
var st = entity.StringTokens;
6161
var entityKey = entity.Properties.GetEntityKey();
62+
var fkConstraints = GetForeignKeyConstraints(entity, allEntities);
6263
IndentedStringBuilder stringBuilder = new(0);
6364

6465
stringBuilder
@@ -74,7 +75,7 @@ private static string GenerateSqlCreateTableQuery(NinjadogEntityWithKey entity,
7475
{
7576
var p = nonKeyProperties[i];
7677
var isLast = i == nonKeyProperties.Count - 1;
77-
var needsComma = !isLast || softDelete || auditing;
78+
var needsComma = !isLast || softDelete || auditing || fkConstraints.Count > 0;
7879

7980
if (needsComma)
8081
{
@@ -88,7 +89,7 @@ private static string GenerateSqlCreateTableQuery(NinjadogEntityWithKey entity,
8889

8990
if (softDelete)
9091
{
91-
var needsComma = auditing;
92+
var needsComma = auditing || fkConstraints.Count > 0;
9293
stringBuilder
9394
.AppendLine("IsDeleted INTEGER NOT NULL DEFAULT 0,")
9495
.Append(needsComma ? "DeletedAt TEXT," : "DeletedAt TEXT)");
@@ -100,14 +101,77 @@ private static string GenerateSqlCreateTableQuery(NinjadogEntityWithKey entity,
100101

101102
if (auditing)
102103
{
104+
var needsComma = fkConstraints.Count > 0;
103105
stringBuilder
104-
.AppendLine("CreatedAt TEXT NOT NULL,")
105-
.Append("UpdatedAt TEXT)");
106+
.AppendLine("CreatedAt TEXT NOT NULL,");
107+
if (needsComma)
108+
{
109+
stringBuilder.AppendLine("UpdatedAt TEXT,");
110+
}
111+
else
112+
{
113+
stringBuilder.Append("UpdatedAt TEXT)");
114+
}
115+
}
116+
117+
for (var i = 0; i < fkConstraints.Count; i++)
118+
{
119+
var (fkColumn, parentTable, parentPk) = fkConstraints[i];
120+
var isLast = i == fkConstraints.Count - 1;
121+
var constraint = $"FOREIGN KEY ({fkColumn}) REFERENCES {parentTable}({parentPk})";
122+
123+
if (isLast)
124+
{
125+
stringBuilder.Append($"{constraint})");
126+
}
127+
else
128+
{
129+
stringBuilder.AppendLine($"{constraint},");
130+
}
106131
}
107132

108133
return stringBuilder.ToString();
109134
}
110135

136+
private static List<(string FkColumn, string ParentTable, string ParentPk)> GetForeignKeyConstraints(
137+
NinjadogEntityWithKey entity, List<NinjadogEntityWithKey> allEntities)
138+
{
139+
var constraints = new List<(string FkColumn, string ParentTable, string ParentPk)>();
140+
141+
foreach (var potentialParent in allEntities)
142+
{
143+
if (potentialParent.Relationships == null)
144+
{
145+
continue;
146+
}
147+
148+
foreach (var (_, relationship) in potentialParent.Relationships)
149+
{
150+
if (relationship.RelatedEntity != entity.Key)
151+
{
152+
continue;
153+
}
154+
155+
if (relationship.RelationshipType is not (NinjadogEntityRelationshipType.OneToMany or NinjadogEntityRelationshipType.OneToOne))
156+
{
157+
continue;
158+
}
159+
160+
var parentPk = potentialParent.Properties.GetEntityKey();
161+
var fkColumnName = parentPk.Key == "Id"
162+
? $"{potentialParent.Key}Id"
163+
: parentPk.Key;
164+
165+
if (entity.Properties.ContainsKey(fkColumnName))
166+
{
167+
constraints.Add((fkColumnName, potentialParent.StringTokens.Models, parentPk.Key));
168+
}
169+
}
170+
}
171+
172+
return constraints;
173+
}
174+
111175
private static string MapToDbType(string typeName, string provider, HashSet<string>? enumNames = null)
112176
{
113177
return enumNames?.Contains(typeName) == true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//------------------------------------------------------------------------------
2+
// This code was powered by the Ninjadog Engine
3+
//
4+
// Developed by: Atypical Consulting SRL - Philippe Matray
5+
// Generated on: [SCRUBBED]
6+
// Version : [SCRUBBED]
7+
//
8+
// This file is part of a custom software solution crafted to meet your
9+
// specific needs. For optimal performance and compatibility, modifications
10+
// should be coordinated with Ninjadog support services.
11+
//------------------------------------------------------------------------------
12+
13+
using Dapper;
14+
15+
namespace TestApp.Api.Database;
16+
17+
public partial class DatabaseInitializer(IDbConnectionFactory connectionFactory)
18+
{
19+
public async Task InitializeAsync()
20+
{
21+
using var connection = await connectionFactory.CreateConnectionAsync();
22+
23+
await connection.ExecuteAsync(@"CREATE TABLE IF NOT EXISTS Authors (
24+
Id CHAR(36) PRIMARY KEY,
25+
Name TEXT NOT NULL)");
26+
27+
await connection.ExecuteAsync(@"CREATE TABLE IF NOT EXISTS Posts (
28+
Id CHAR(36) PRIMARY KEY,
29+
Title TEXT NOT NULL,
30+
Content TEXT NOT NULL,
31+
AuthorId CHAR(36) NOT NULL,
32+
FOREIGN KEY (AuthorId) REFERENCES Authors(Id))");
33+
34+
}
35+
}

src/tests/Ninjadog.Tests/Templates/DatabaseInitializerTemplateTests.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,12 @@ public Task GenerateOne_WithAuditing_AddsAuditColumns()
3030
var result = _template.GenerateOne(settings);
3131
return Verify(result.Content);
3232
}
33+
34+
[Fact]
35+
public Task GenerateOne_WithRelationships_AddsForeignKeyConstraints()
36+
{
37+
var settings = new RelationshipSettings();
38+
var result = _template.GenerateOne(settings);
39+
return Verify(result.Content);
40+
}
3341
}

0 commit comments

Comments
 (0)