diff --git a/src/FilterLists.Api/DependencyInjection/Extensions/IWebHostExtension.cs b/src/FilterLists.Api/DependencyInjection/Extensions/IWebHostExtension.cs index c7a3c8051..fb783bd80 100644 --- a/src/FilterLists.Api/DependencyInjection/Extensions/IWebHostExtension.cs +++ b/src/FilterLists.Api/DependencyInjection/Extensions/IWebHostExtension.cs @@ -1,56 +1,21 @@ using System; -using System.Data.SqlClient; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Polly; namespace FilterLists.Api.DependencyInjection.Extensions { - public static class IWebHostExtension + public static class IWebHostExtension { - public static IWebHost MigrateDbContext(this IWebHost webHost, Action seeder) where TContext : DbContext + public static IWebHost MigrateAndSeedDbContext(this IWebHost webHost, + Action seeder) where TContext : DbContext { using (var scope = webHost.Services.CreateScope()) { var services = scope.ServiceProvider; - - var logger = services.GetRequiredService>(); - var context = services.GetService(); - - try - { - logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}"); - - var retry = Policy.Handle() - .WaitAndRetry(new TimeSpan[] - { - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(8), - }); - - retry.Execute(() => - { - //if the sql server container is not created on run docker compose this - //migration can't fail for network related exception. The retry options for DbContext only - //apply to transient exceptions. - - context.Database - .Migrate(); - - seeder(context, services); - }); - - - logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}"); - } - catch (Exception ex) - { - logger.LogError(ex, $"An error occurred while migrating the database used on context {typeof(TContext).Name}"); - } + context.Database.Migrate(); + seeder(context, services); } return webHost; diff --git a/src/FilterLists.Api/FilterLists.Api.csproj b/src/FilterLists.Api/FilterLists.Api.csproj index 9a055004f..1aec86167 100644 --- a/src/FilterLists.Api/FilterLists.Api.csproj +++ b/src/FilterLists.Api/FilterLists.Api.csproj @@ -41,7 +41,6 @@ - diff --git a/src/FilterLists.Api/Program.cs b/src/FilterLists.Api/Program.cs index 6945694fb..57fa7f2b1 100644 --- a/src/FilterLists.Api/Program.cs +++ b/src/FilterLists.Api/Program.cs @@ -1,7 +1,8 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using FilterLists.Api.DependencyInjection.Extensions; +using FilterLists.Api.DependencyInjection.Extensions; +using FilterLists.Data; using FilterLists.Data.Seed.Extensions; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -11,13 +12,16 @@ public static class Program { public static void Main(string[] args) { - CreateWebHostBuilder(args).Build().MigrateDbContext((context,service)=> - { - - var dataPath = service.GetService()["DataDirectory:Path"].ToString(); - new SeedFilterListsDbContext().SeedOrUpdateAsync(context,dataPath).Wait(); - }).Run(); + CreateWebHostBuilder(args) + .Build() + .MigrateAndSeedDbContext((context, service) => + { + var dataPath = service.GetService()["DataDirectory:Path"].ToString(); + SeedFilterListsDbContext.SeedOrUpdateAsync(context, dataPath).Wait(); + }) + .Run(); } + private static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseUrls("http://localhost:5000") diff --git a/src/FilterLists.Api/Startup.cs b/src/FilterLists.Api/Startup.cs index 3e22f4671..70fa9e78f 100644 --- a/src/FilterLists.Api/Startup.cs +++ b/src/FilterLists.Api/Startup.cs @@ -1,13 +1,10 @@ using System.Linq; using FilterLists.Api.DependencyInjection.Extensions; -using FilterLists.Data; -using FilterLists.Data.Seed.Extensions; using FilterLists.Services.DependencyInjection.Extensions; using JetBrains.Annotations; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Swashbuckle.AspNetCore.Swagger; @@ -26,7 +23,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddFilterListsApiServices(Configuration); services.AddFilterListsApi(); - services.AddSingleton (Configuration); + services.AddSingleton(Configuration); } [UsedImplicitly] @@ -66,7 +63,6 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) opts.DocumentTitle = "FilterLists API v1"; opts.RoutePrefix = "docs"; }); - // MigrateAndSeedDatabase(app); } //TODO: remove hack (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/74#issuecomment-386762178) @@ -77,6 +73,5 @@ private static void UseLowercaseControllerNameInSwaggerHack(SwaggerOptions opts) document.Paths.Clear(); foreach (var pathItem in paths) document.Paths.Add(pathItem.Key, pathItem.Value); }); - } } \ No newline at end of file diff --git a/src/FilterLists.Data/Seed/Extensions/SeedFilterListsDbContext.cs b/src/FilterLists.Data/Seed/Extensions/SeedFilterListsDbContext.cs index eeb694db8..d71c8e95d 100644 --- a/src/FilterLists.Data/Seed/Extensions/SeedFilterListsDbContext.cs +++ b/src/FilterLists.Data/Seed/Extensions/SeedFilterListsDbContext.cs @@ -12,28 +12,28 @@ namespace FilterLists.Data.Seed.Extensions { - public class SeedFilterListsDbContext + public static class SeedFilterListsDbContext { - public async Task SeedOrUpdateAsync(FilterListsDbContext dbContext, string dataPath) + public static async Task SeedOrUpdateAsync(FilterListsDbContext dbContext, string dataPath) { - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext, dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SetDefaultLicenseIdAsync(dbContext); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); - await SeedOrUpdateAsync(dbContext,dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SetDefaultLicenseIdAsync(dbContext); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); + await SeedOrUpdate(dbContext, dataPath); } - private async Task SeedOrUpdateAsync(DbContext dbContext, string dataPath) + private static async Task SeedOrUpdate(DbContext dbContext, string dataPath) where TEntity : class, IBaseEntity { var seed = GetSeed(dataPath); @@ -41,12 +41,12 @@ private async Task SeedOrUpdateAsync(DbContext dbContext, string dataPa await InsertOnDuplicateKeyUpdate(dbContext, seed); } - private List GetSeed(string dataPath) where TEntity : IBaseEntity + private static List GetSeed(string dataPath) where TEntity : IBaseEntity { try { return JsonConvert.DeserializeObject>( - File.ReadAllText(Path.Combine(dataPath,typeof(TEntity).Name + ".json"))); + File.ReadAllText(Path.Combine(dataPath, typeof(TEntity).Name + ".json"))); } catch (FileNotFoundException e) { @@ -55,7 +55,7 @@ private List GetSeed(string dataPath) where TEntity : IBaseEnt } } - private async Task ApplyRemovals(DbContext dbContext, IEnumerable seed) + private static async Task ApplyRemovals(DbContext dbContext, IEnumerable seed) where TEntity : class { var removedEntities = GetRemovedEntities(dbContext, seed); @@ -64,7 +64,7 @@ private async Task ApplyRemovals(DbContext dbContext, IEnumerable GetRemovedEntities(DbContext dbContext, IEnumerable seed) + private static IEnumerable GetRemovedEntities(DbContext dbContext, IEnumerable seed) where TEntity : class { var entityType = dbContext.Model.FindEntityType(typeof(TEntity)); @@ -74,20 +74,18 @@ private IEnumerable GetRemovedEntities(DbContext dbContext, IE return dbContext.Set().Where(notInSeed); } - private Expression GetMatchesAnyPk(IEnumerable seed, IEntityType entityType, - Expression dbEntity) - where TEntity : class => + private static Expression GetMatchesAnyPk(IEnumerable seed, IEntityType entityType, + Expression dbEntity) where TEntity : class => seed.Select(e => entityType.FindPrimaryKey() - .Properties - .Select(p => Expression.Equal( - Expression.Property(dbEntity, p.PropertyInfo), - Expression.Property(Expression.Constant(e), p.PropertyInfo))) + .Properties.Select(p => + Expression.Equal(Expression.Property(dbEntity, p.PropertyInfo), + Expression.Property(Expression.Constant(e), p.PropertyInfo))) .Aggregate(Expression.AndAlso)) .Aggregate(null, (Func)((current, match) => current != null ? Expression.OrElse(current, match) : match)); - private async Task InsertOnDuplicateKeyUpdate(DbContext dbContext, IEnumerable seed) + private static async Task InsertOnDuplicateKeyUpdate(DbContext dbContext, IEnumerable seed) where TEntity : IBaseEntity { var entityType = dbContext.Model.FindEntityType(typeof(TEntity)); @@ -102,24 +100,24 @@ private async Task InsertOnDuplicateKeyUpdate(DbContext dbContext, IEnu await dbContext.Database.ExecuteSqlCommandAsync(sql); } - private List GetPropertiesLessValueGeneratedTimestamps(IEntityType entityType) => + private static List GetPropertiesLessValueGeneratedTimestamps(IEntityType entityType) => entityType.GetProperties() .Where(x => !new List {"CreatedDateUtc", "ModifiedDateUtc"}.Contains(x.Name)) .ToList(); - private string CreateValues(IEnumerable seed, + private static string CreateValues(IEnumerable seed, IReadOnlyCollection properties) where TEntity : IBaseEntity => seed.Select(row => CreateRowValues(properties, row)) .Aggregate("", (current, rowValues) => current == "" ? rowValues : current + ", " + rowValues); - private string CreateRowValues(IEnumerable properties, TEntity row) + private static string CreateRowValues(IEnumerable properties, TEntity row) where TEntity : IBaseEntity => (from property in properties let value = row.GetType().GetProperty(property.Name)?.GetValue(row) select FormatDataForMySql(property, value)).Aggregate("", (rowValues, value) => rowValues == "" ? "(" + value : rowValues + ", " + value) + ")"; - private object FormatDataForMySql(IProperty property, object value) + private static object FormatDataForMySql(IProperty property, object value) { if (value == null) return "NULL"; @@ -132,7 +130,7 @@ private object FormatDataForMySql(IProperty property, object value) return value; } - private string CreateUpdates(IReadOnlyCollection properties) + private static string CreateUpdates(IReadOnlyCollection properties) { var update = (from property in properties @@ -144,13 +142,13 @@ private string CreateUpdates(IReadOnlyCollection properties) return update; } - private string GetUpdateUnchangedColumnHack(IEnumerable properties) + private static string GetUpdateUnchangedColumnHack(IEnumerable properties) { var firstId = properties.First(x => x.IsPrimaryKey()).Name; return firstId + " = VALUES(" + firstId + ")"; } - private async Task SetDefaultLicenseIdAsync(DbContext dbContext) + private static async Task SetDefaultLicenseIdAsync(DbContext dbContext) { var listsWithoutLicense = dbContext.Set() .Where(l => l.LicenseId == null)