-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixed IDatabaseInitializer sorting with InitializeAfterAttribute (was…
… only comparing neighbors) & added support for transitive dependencies
- Loading branch information
1 parent
3cbef32
commit fdf7628
Showing
9 changed files
with
238 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 0 additions & 17 deletions
17
Revo.Infrastructure/DataAccess/DatabaseInitializerDependencyComparer.cs
This file was deleted.
Oops, something went wrong.
44 changes: 44 additions & 0 deletions
44
Revo.Infrastructure/DataAccess/DatabaseInitializerDependencySorter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
|
||
namespace Revo.Infrastructure.DataAccess | ||
{ | ||
public class DatabaseInitializerDependencySorter : IDatabaseInitializerSorter | ||
{ | ||
public List<IDatabaseInitializer> GetSorted(IReadOnlyCollection<IDatabaseInitializer> initializers) | ||
{ | ||
var result = new List<IDatabaseInitializer>(); | ||
var remaining = initializers.ToList(); | ||
|
||
while (remaining.Count > 0) | ||
{ | ||
var next = remaining | ||
.Where(x => GetDependencies(x) | ||
.All(depType => result.Any(depType.IsInstanceOfType))) | ||
.ToArray(); | ||
if (next.Length == 0) | ||
{ | ||
throw new InvalidOperationException( | ||
$"Unable to sort IDatabaseInitializers - either you have cyclic dependencies or some dependencies were not found: ${string.Join(", ", remaining.Select(x => x.GetType().Name))}"); | ||
} | ||
|
||
result.AddRange(next); | ||
foreach (var di in next) | ||
{ | ||
remaining.Remove(di); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private IEnumerable<Type> GetDependencies(IDatabaseInitializer databaseInitializer) | ||
{ | ||
return databaseInitializer.GetType() | ||
.GetCustomAttributes<InitializeAfterAttribute>() | ||
.Select(x => x.InitializerType); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 0 additions & 8 deletions
8
Revo.Infrastructure/DataAccess/IDatabaseInitializerComparer.cs
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Revo.Infrastructure.DataAccess | ||
{ | ||
public interface IDatabaseInitializerSorter | ||
{ | ||
List<IDatabaseInitializer> GetSorted(IReadOnlyCollection<IDatabaseInitializer> initializers); | ||
} | ||
} |
175 changes: 175 additions & 0 deletions
175
Tests/Revo.Infrastructure.Tests/DataAccess/DatabaseInitializerDependencySorterTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
using FluentAssertions; | ||
using MoreLinq; | ||
using Revo.Infrastructure.DataAccess; | ||
using Revo.Infrastructure.Repositories; | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace Revo.Infrastructure.Tests.DataAccess; | ||
|
||
public class DatabaseInitializerDependencySorterTests | ||
{ | ||
private DatabaseInitializerDependencySorter sut = new(); | ||
|
||
[Fact] | ||
public void Sort() | ||
{ | ||
var initializers = new IDatabaseInitializer[] | ||
{ | ||
new TestDiA1(), | ||
new TestDiA(), | ||
}; | ||
|
||
var result = sut.GetSorted(initializers); | ||
result.Should().BeEquivalentTo(new IDatabaseInitializer[] | ||
{ | ||
initializers.OfType<TestDiA>().First(), | ||
initializers.OfType<TestDiA1>().First(), | ||
}); | ||
} | ||
|
||
[Fact] | ||
public void Sort_Transitive() | ||
{ | ||
var initializers = new IDatabaseInitializer[] | ||
{ | ||
new TestDiA1A(), | ||
new TestDiA1(), | ||
new TestDiA(), | ||
}; | ||
|
||
var result = sut.GetSorted(initializers); | ||
result.Should().BeEquivalentTo(new IDatabaseInitializer[] | ||
{ | ||
initializers.OfType<TestDiA>().First(), | ||
initializers.OfType<TestDiA1>().First(), | ||
initializers.OfType<TestDiA1A>().First() | ||
}); | ||
} | ||
|
||
[Fact] | ||
public void Sort_MultipleDependencies() | ||
{ | ||
var initializers = new IDatabaseInitializer[] | ||
{ | ||
new TestDiC(), | ||
new TestDiA1A(), | ||
new TestDiA1(), | ||
new TestDiA(), | ||
new TestDiB(), | ||
new TestDiB1() | ||
}; | ||
|
||
var result = sut.GetSorted(initializers); | ||
result.Should().Contain(initializers); | ||
|
||
result.IndexOf(initializers.OfType<TestDiA>().First()).Should() | ||
.BeLessThan(result.IndexOf(initializers.OfType<TestDiA1>().First())); | ||
result.IndexOf(initializers.OfType<TestDiA1>().First()).Should() | ||
.BeLessThan(result.IndexOf(initializers.OfType<TestDiA1A>().First())); | ||
result.IndexOf(initializers.OfType<TestDiB>().First()).Should() | ||
.BeLessThan(result.IndexOf(initializers.OfType<TestDiB1>().First())); | ||
result.IndexOf(initializers.OfType<TestDiA1A>().First()).Should() | ||
.BeLessThan(result.IndexOf(initializers.OfType<TestDiC>().First())); | ||
result.IndexOf(initializers.OfType<TestDiB1>().First()).Should() | ||
.BeLessThan(result.IndexOf(initializers.OfType<TestDiC>().First())); | ||
} | ||
|
||
[Fact] | ||
public void Sort_ThrowsOnCyclicDependency() | ||
{ | ||
var initializers = new IDatabaseInitializer[] | ||
{ | ||
new TestDiD(), | ||
new TestDiE() | ||
}; | ||
|
||
sut.Invoking(x => x.GetSorted(initializers)) | ||
.Should().Throw<InvalidOperationException>(); | ||
} | ||
|
||
[Fact] | ||
public void Sort_ThrowsOnMissingDependency() | ||
{ | ||
var initializers = new IDatabaseInitializer[] | ||
{ | ||
new TestDiD() | ||
}; | ||
|
||
sut.Invoking(x => x.GetSorted(initializers)) | ||
.Should().Throw<InvalidOperationException>(); | ||
} | ||
|
||
public class TestDiA : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
|
||
public class TestDiB : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
|
||
[InitializeAfter(typeof(TestDiA))] | ||
public class TestDiA1 : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
|
||
[InitializeAfter(typeof(TestDiB))] | ||
public class TestDiB1 : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
|
||
[InitializeAfter(typeof(TestDiA1))] | ||
public class TestDiA1A : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
|
||
[InitializeAfter(typeof(TestDiA1A))] | ||
[InitializeAfter(typeof(TestDiB1))] | ||
public class TestDiC : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
|
||
[InitializeAfter(typeof(TestDiE))] | ||
public class TestDiD : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
|
||
[InitializeAfter(typeof(TestDiD))] | ||
public class TestDiE : IDatabaseInitializer | ||
{ | ||
public Task InitializeAsync(IRepository repository) | ||
{ | ||
throw new System.NotImplementedException(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters