Skip to content

Commit 318a98e

Browse files
v2.2.0 (#9)
- Add asynchronous methods (fixes Add asynchronous methods #6) - Add the support to compare the default constraints for the columns (fixes Add the support to retrieve the default constraints. #7). - Fix a bug when comparing primary key with different names (fixes An exception is raised when primary keys are different #8).
1 parent 1a1d9da commit 318a98e

31 files changed

+1028
-153
lines changed

.github/workflows/github-actions-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
type: string
88
description: The version of the library
99
required: true
10-
default: 2.1.0
10+
default: 2.2.0
1111
VersionSuffix:
1212
type: string
1313
description: The version suffix of the library (for example rc.1)

src/Directory.Build.props

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
<PackageReadmeFile>README.md</PackageReadmeFile>
1212
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1313
<PackageReleaseNotes>
14+
2.2.0
15+
- Add SqlServerDatabase.ClearDataAsync() method.
16+
- Add SqlServer.CreateDatabase() method to create database from an Entity Framework DbContext.
17+
- Add SqlServer.CreateEmptyDatabaseAsync() method.
18+
- Add SqlServer.DeleteDatabaseAsync() method.
19+
- Add SqlServerDatabase.ExecuteScriptAsync() method.
20+
- Add SqlServerDatabase.InsertIntoAsync() method.
21+
1422
2.1.0
1523
- PosInformatique.Testing.Databases.SqlServer target the .NET Standard 2.0 platform.
1624
- PosInformatique.Testing.Databases.SqlServer.Dac target the .NET Core 6.0 and .NET Framework 4.6.2

src/Testing.Databases.SqlServer.EntityFramework/EntityFrameworkSqlServerExtensions.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,33 @@ namespace PosInformatique.Testing.Databases.SqlServer
1515
public static class EntityFrameworkSqlServerExtensions
1616
{
1717
/// <summary>
18-
/// Deploy a database using a DACPAC file.
18+
/// Creates a database using the specified Entity Framework <paramref name="context"/>.
1919
/// </summary>
20-
/// <remarks>If a database already exists, it will be deleted.</remarks>
20+
/// <remarks>If the database already exists, it will be deleted.</remarks>
2121
/// <param name="server"><see cref="SqlServer"/> instance where the database will be created.</param>
2222
/// <param name="name">Name of the database to create.</param>
2323
/// <param name="context"><see cref="DbContext"/> which represents the database to create.</param>
2424
/// <returns>An instance of the <see cref="SqlServerDatabase"/> which represents the deployed database.</returns>
25+
public static SqlServerDatabase CreateDatabase(this SqlServer server, string name, DbContext context)
26+
{
27+
var database = server.GetDatabase(name);
28+
29+
context.Database.SetConnectionString(database.ConnectionString);
30+
31+
context.Database.EnsureDeleted();
32+
context.Database.EnsureCreated();
33+
34+
return database;
35+
}
36+
37+
/// <summary>
38+
/// Creates a database using the specified Entity Framework <paramref name="context"/>.
39+
/// </summary>
40+
/// <remarks>If the database already exists, it will be deleted.</remarks>
41+
/// <param name="server"><see cref="SqlServer"/> instance where the database will be created.</param>
42+
/// <param name="name">Name of the database to create.</param>
43+
/// <param name="context"><see cref="DbContext"/> which represents the database to create.</param>
44+
/// <returns>A <see cref="Task"/> which represents the asynchronous operation and contains an instance of the <see cref="SqlServerDatabase"/> that represents the deployed database.</returns>
2545
public static async Task<SqlServerDatabase> CreateDatabaseAsync(this SqlServer server, string name, DbContext context)
2646
{
2747
var database = server.GetDatabase(name);

src/Testing.Databases.SqlServer/Comparer/ISqlObjectDifferencesVisitor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ internal interface ISqlObjectDifferencesVisitor
1111
void Visit<TSqlObject>(SqlObjectDifferences<TSqlObject> differences)
1212
where TSqlObject : SqlObject;
1313

14+
void Visit(SqlColumnDifferences differences);
15+
1416
void Visit(SqlForeignKeyDifferences differences);
1517

1618
void Visit(SqlIndexDifferences differences);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="SqlColumnDifferences.cs" company="P.O.S Informatique">
3+
// Copyright (c) P.O.S Informatique. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace PosInformatique.Testing.Databases
8+
{
9+
/// <summary>
10+
/// Represents the differences of a <see cref="SqlColumn"/> between two databases.
11+
/// </summary>
12+
public class SqlColumnDifferences : SqlObjectDifferences<SqlColumn>
13+
{
14+
internal SqlColumnDifferences(
15+
SqlColumn? source,
16+
SqlColumn? target,
17+
SqlObjectDifferenceType type,
18+
IReadOnlyList<SqlObjectPropertyDifference>? properties,
19+
SqlObjectDifferences<SqlDefaultConstraint>? defaultConstraint)
20+
: base(source, target, type, properties)
21+
{
22+
this.DefaultConstraint = defaultConstraint;
23+
}
24+
25+
internal SqlColumnDifferences(
26+
SqlObjectDifferences<SqlColumn> differences)
27+
: this(differences.Source, differences.Target, differences.Type, differences.Properties, null)
28+
{
29+
}
30+
31+
/// <summary>
32+
/// Gets the difference of the columns in the foreign key compared.
33+
/// </summary>
34+
public SqlObjectDifferences<SqlDefaultConstraint>? DefaultConstraint { get; }
35+
36+
internal override void Accept(ISqlObjectDifferencesVisitor visitor)
37+
{
38+
visitor.Visit(this);
39+
}
40+
}
41+
}

src/Testing.Databases.SqlServer/Comparer/SqlDatabaseComparisonResultsTextGenerator.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ public void Visit<TSqlObject>(SqlObjectDifferences<TSqlObject> differences)
6464
}
6565
}
6666

67+
public void Visit(SqlColumnDifferences differences)
68+
{
69+
this.Visit<SqlColumn>(differences);
70+
71+
this.Generate(differences.DefaultConstraint, "Default constraint");
72+
}
73+
6774
public void Visit(SqlForeignKeyDifferences differences)
6875
{
6976
this.WriteProperties(differences.Properties);
@@ -98,13 +105,7 @@ public void Visit(SqlTableDifferences differences)
98105

99106
this.Generate(differences.Indexes, "Indexes");
100107

101-
if (differences.PrimaryKey is not null)
102-
{
103-
this.Indent();
104-
this.WriteLine($"------ Primary key ------");
105-
differences.PrimaryKey.Accept(this);
106-
this.Unindent();
107-
}
108+
this.Generate(differences.PrimaryKey, "Primary key");
108109

109110
this.Generate(differences.Triggers, "Triggers");
110111

@@ -121,6 +122,17 @@ public void Visit(SqlUniqueConstraintDifferences differences)
121122
this.Generate(differences.Columns, "Columns");
122123
}
123124

125+
private void Generate<TSqlObject>(SqlObjectDifferences<TSqlObject>? difference, string typeName)
126+
where TSqlObject : SqlObject
127+
{
128+
if (difference is null)
129+
{
130+
return;
131+
}
132+
133+
this.Generate([difference], typeName);
134+
}
135+
124136
private void Generate<TSqlObject>(IEnumerable<SqlObjectDifferences<TSqlObject>> differences, string typeName)
125137
where TSqlObject : SqlObject
126138
{

src/Testing.Databases.SqlServer/Comparer/SqlObjectComparer.cs

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,11 @@ public static IList<SqlObjectDifferences<TSqlObject>> Compare<TSqlObject>(IReadO
2626
var keyValue = keySelector(targetObject);
2727
var sourceObject = Find(source, keySelector, keyValue);
2828

29-
if (sourceObject is null)
30-
{
31-
// Missing in the source.
32-
differences.Add(new SqlObjectDifferences<TSqlObject>(null, targetObject, SqlObjectDifferenceType.MissingInSource, null));
33-
}
34-
else
35-
{
36-
// Compare the object using visitor pattern.
37-
var difference = Compare(sourceObject, targetObject);
29+
var difference = Compare(sourceObject, targetObject);
3830

39-
if (difference is not null)
40-
{
41-
differences.Add(difference);
42-
}
31+
if (difference is not null)
32+
{
33+
differences.Add(difference);
4334
}
4435
}
4536

@@ -61,7 +52,7 @@ public static IList<SqlObjectDifferences<TSqlObject>> Compare<TSqlObject>(IReadO
6152

6253
public static IList<SqlTableDifferences> Compare(IReadOnlyList<SqlTable> source, IReadOnlyList<SqlTable> target)
6354
{
64-
return Compare(source, target, t => t.Name, diff => new SqlTableDifferences(diff) { PrimaryKey = null });
55+
return Compare(source, target, t => t.Name, diff => new SqlTableDifferences(diff));
6556
}
6657

6758
public SqlObjectDifferences? Visit(SqlCheckConstraint checkConstraint)
@@ -73,8 +64,10 @@ public static IList<SqlTableDifferences> Compare(IReadOnlyList<SqlTable> source,
7364

7465
public SqlObjectDifferences? Visit(SqlColumn column)
7566
{
76-
return this.CreateDifferences(
77-
column,
67+
var sourceColumn = (SqlColumn)this.source;
68+
69+
// Compare the properties
70+
var differenceProperties = GetPropertyDifferences(
7871
this.CompareProperty(column, t => t.Position, nameof(column.Position)),
7972
this.CompareProperty(column, t => t.MaxLength, nameof(column.MaxLength)),
8073
this.CompareProperty(column, t => t.Precision, nameof(column.Precision)),
@@ -84,6 +77,24 @@ public static IList<SqlTableDifferences> Compare(IReadOnlyList<SqlTable> source,
8477
this.CompareProperty(column, t => t.CollationName, nameof(column.CollationName)),
8578
this.CompareProperty(column, t => t.IsComputed, nameof(column.IsComputed)),
8679
this.CompareProperty(column, t => TsqlCodeHelper.RemoveNotUsefulCharacters(t.ComputedExpression), nameof(column.ComputedExpression), t => t.ComputedExpression));
80+
81+
// Compare the default constraint
82+
var defaultConstraintDifference = Compare(sourceColumn.DefaultConstraint, column.DefaultConstraint);
83+
84+
if (differenceProperties.Count > 0 || defaultConstraintDifference != null)
85+
{
86+
return new SqlColumnDifferences((SqlColumn)this.source, column, SqlObjectDifferenceType.Different, differenceProperties, defaultConstraintDifference);
87+
}
88+
89+
return null;
90+
}
91+
92+
public SqlObjectDifferences? Visit(SqlDefaultConstraint defaultConstraint)
93+
{
94+
return this.CreateDifferences(
95+
defaultConstraint,
96+
this.CompareProperty(defaultConstraint, df => df.Name, nameof(defaultConstraint.Name)),
97+
this.CompareProperty(defaultConstraint, df => TsqlCodeHelper.RemoveNotUsefulCharacters(df.Expression), nameof(defaultConstraint.Expression), df => df.Expression));
8798
}
8899

89100
public SqlObjectDifferences? Visit(SqlForeignKey foreignKey)
@@ -191,7 +202,7 @@ public static IList<SqlTableDifferences> Compare(IReadOnlyList<SqlTable> source,
191202
var checkConstraintDifferences = Compare(sourceTable.CheckConstraints, table.CheckConstraints, tr => tr.Name);
192203

193204
// Compare the columns
194-
var columnsDifferences = Compare(sourceTable.Columns, table.Columns, c => c.Name);
205+
var columnsDifferences = Compare(sourceTable.Columns, table.Columns, c => c.Name, diff => new SqlColumnDifferences(diff));
195206

196207
// Compare the foreign keys
197208
var foreignKeysDifferences = Compare(sourceTable.ForeignKeys, table.ForeignKeys, fk => fk.Name, diff => new SqlForeignKeyDifferences(diff));
@@ -200,7 +211,7 @@ public static IList<SqlTableDifferences> Compare(IReadOnlyList<SqlTable> source,
200211
var indexesDifferences = Compare(sourceTable.Indexes, table.Indexes, i => i.Name, diff => new SqlIndexDifferences(diff));
201212

202213
// Compare the primary key
203-
var primaryKeyDifferences = (SqlPrimaryKeyDifferences?)Compare(CreateArray(sourceTable.PrimaryKey), CreateArray(table.PrimaryKey), pk => pk.Name).SingleOrDefault();
214+
var primaryKeyDifferences = (SqlPrimaryKeyDifferences?)Compare(sourceTable.PrimaryKey, table.PrimaryKey);
204215

205216
// Compare the triggers
206217
var triggersDifferences = Compare(sourceTable.Triggers, table.Triggers, tr => tr.Name);
@@ -262,9 +273,26 @@ public static IList<SqlTableDifferences> Compare(IReadOnlyList<SqlTable> source,
262273
this.CompareProperty(view, v => TsqlCodeHelper.RemoveNotUsefulCharacters(v.Code), nameof(view.Code), v => v.Code));
263274
}
264275

265-
private static SqlObjectDifferences<TSqlObject>? Compare<TSqlObject>(TSqlObject source, TSqlObject target)
276+
private static SqlObjectDifferences<TSqlObject>? Compare<TSqlObject>(TSqlObject? source, TSqlObject? target)
266277
where TSqlObject : SqlObject
267278
{
279+
if (source is null)
280+
{
281+
if (target is null)
282+
{
283+
return null;
284+
}
285+
286+
return new SqlObjectDifferences<TSqlObject>(null, target, SqlObjectDifferenceType.MissingInSource, null);
287+
}
288+
else
289+
{
290+
if (target is null)
291+
{
292+
return new SqlObjectDifferences<TSqlObject>(source, null, SqlObjectDifferenceType.MissingInTarget, null);
293+
}
294+
}
295+
268296
var visitor = new SqlObjectComparer(source);
269297

270298
return (SqlObjectDifferences<TSqlObject>?)target.Accept(visitor);
@@ -303,17 +331,6 @@ private static IReadOnlyList<SqlObjectPropertyDifference> GetPropertyDifferences
303331
return objects.SingleOrDefault(o => Equals(keySelector(o), value));
304332
}
305333

306-
private static T[] CreateArray<T>(T? value)
307-
where T : class
308-
{
309-
if (value is null)
310-
{
311-
return [];
312-
}
313-
314-
return [value];
315-
}
316-
317334
private SqlObjectPropertyDifference? CompareProperty<TSqlObject>(TSqlObject target, Func<TSqlObject, object?> propertyValueForComparison, string name, Func<TSqlObject, object?>? propertyValueToDisplay = null)
318335
where TSqlObject : SqlObject
319336
{

src/Testing.Databases.SqlServer/Comparer/SqlPrimaryKeyDifferences.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ internal SqlPrimaryKeyDifferences(
2424
this.Columns = new ReadOnlyCollection<SqlObjectDifferences<SqlPrimaryKeyColumn>>(columns);
2525
}
2626

27+
internal SqlPrimaryKeyDifferences(
28+
SqlObjectDifferences<SqlPrimaryKey> differences)
29+
: this(differences.Source, differences.Target, differences.Type, differences.Properties, [])
30+
{
31+
}
32+
2733
/// <summary>
2834
/// Gets the difference of the columns in the primary key compared.
2935
/// </summary>

src/Testing.Databases.SqlServer/Comparer/SqlTableDifferences.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ internal SqlTableDifferences(
1818
SqlTable? target,
1919
SqlObjectDifferenceType type,
2020
IReadOnlyList<SqlObjectPropertyDifference>? properties,
21-
IList<SqlObjectDifferences<SqlColumn>> columns,
21+
IList<SqlColumnDifferences> columns,
2222
IList<SqlObjectDifferences<SqlTrigger>> triggers,
2323
IList<SqlObjectDifferences<SqlCheckConstraint>> checkConstraints,
2424
IList<SqlIndexDifferences> indexes,
2525
IList<SqlForeignKeyDifferences> foreignKeys,
2626
IList<SqlUniqueConstraintDifferences> uniqueConstraints)
2727
: base(source, target, type, properties)
2828
{
29-
this.Columns = new ReadOnlyCollection<SqlObjectDifferences<SqlColumn>>(columns);
29+
this.Columns = new ReadOnlyCollection<SqlColumnDifferences>(columns);
3030
this.Triggers = new ReadOnlyCollection<SqlObjectDifferences<SqlTrigger>>(triggers);
3131
this.CheckConstraints = new ReadOnlyCollection<SqlObjectDifferences<SqlCheckConstraint>>(checkConstraints);
3232
this.Indexes = new ReadOnlyCollection<SqlIndexDifferences>(indexes);
@@ -48,7 +48,7 @@ internal SqlTableDifferences(
4848
/// <summary>
4949
/// Gets the columns differences between the two SQL tables.
5050
/// </summary>
51-
public ReadOnlyCollection<SqlObjectDifferences<SqlColumn>> Columns { get; }
51+
public ReadOnlyCollection<SqlColumnDifferences> Columns { get; }
5252

5353
/// <summary>
5454
/// Gets the indexes differences between the two SQL tables.

src/Testing.Databases.SqlServer/ObjectModel/ISqlObjectVisitor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ public interface ISqlObjectVisitor<TResult>
2626
/// <returns>The result of the visit.</returns>
2727
TResult Visit(SqlColumn column);
2828

29+
/// <summary>
30+
/// Visits the specified <paramref name="defaultConstraint"/>.
31+
/// </summary>
32+
/// <param name="defaultConstraint"><see cref="SqlDefaultConstraint"/> to visit.</param>
33+
/// <returns>The result of the visit.</returns>
34+
TResult Visit(SqlDefaultConstraint defaultConstraint);
35+
2936
/// <summary>
3037
/// Visits the specified <paramref name="foreignKey"/>.
3138
/// </summary>

0 commit comments

Comments
 (0)