Skip to content

Commit

Permalink
[PostgreSQL] No longer require restart after creating the database (#467
Browse files Browse the repository at this point in the history
)

Npgsql, the PostgreSQL driver, caches the database's type information on the initial connection. This causes issues when BaGet creates the database as it adds the citext extension to support case insensitive columns. BaGet will now forcefully reload the database's types after migrations are run.

See: #442
See: npgsql/efcore.pg#170 (comment)
  • Loading branch information
loic-sharma authored Feb 9, 2020
1 parent e2c1d18 commit fbd5b06
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 4 deletions.
5 changes: 1 addition & 4 deletions src/BaGet.Core.Server/Extensions/IHostExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ public static async Task RunMigrationsAsync(this IHost host, CancellationToken c
{
var ctx = scope.ServiceProvider.GetRequiredService<IContext>();

// TODO: An "InvalidOperationException" is thrown and caught due to a bug
// in EF Core 3.0. This is fixed in 3.1.
// See: https://github.com/dotnet/efcore/issues/18307
await ctx.Database.MigrateAsync(cancellationToken);
await ctx.RunMigrationsAsync(cancellationToken);
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/BaGet.Core/Entities/AbstractContext.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
Expand Down Expand Up @@ -31,6 +32,9 @@ public AbstractContext(DbContextOptions<TContext> options)

public Task<int> SaveChangesAsync() => SaveChangesAsync(default);

public virtual async Task RunMigrationsAsync(CancellationToken cancellationToken)
=> await Database.MigrateAsync(cancellationToken);

public abstract bool IsUniqueConstraintViolationException(DbUpdateException exception);

public virtual bool SupportsLimitInSubqueries => true;
Expand Down
8 changes: 8 additions & 0 deletions src/BaGet.Core/Entities/IContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,13 @@ public interface IContext
bool SupportsLimitInSubqueries { get; }

Task<int> SaveChangesAsync(CancellationToken cancellationToken);

/// <summary>
/// Applies any pending migrations for the context to the database.
/// Creates the database if it does not already exist.
/// </summary>
/// <param name="cancellationToken">A token to cancel the task.</param>
/// <returns>A task that completes once migrations are applied.</returns>
Task RunMigrationsAsync(CancellationToken cancellationToken);
}
}
18 changes: 18 additions & 0 deletions src/BaGet.Database.PostgreSql/PostgreSqlContext.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Threading;
using System.Threading.Tasks;
using BaGet.Core;
using Microsoft.EntityFrameworkCore;
using Npgsql;
Expand All @@ -24,6 +26,22 @@ public override bool IsUniqueConstraintViolationException(DbUpdateException exce
code == UniqueConstraintViolationErrorCode;
}

public override async Task RunMigrationsAsync(CancellationToken cancellationToken)
{
await base.RunMigrationsAsync(cancellationToken);

// Npgsql caches the database's type information on the initial connection.
// This causes issues when BaGet creates the database as it may add the citext
// extension to support case insensitive columns.
// See: https://github.com/loic-sharma/BaGet/issues/442
// See: https://github.com/npgsql/efcore.pg/issues/170#issuecomment-303417225
if (Database.GetDbConnection() is NpgsqlConnection connection)
{
await connection.OpenAsync(cancellationToken);
connection.ReloadTypes();
}
}

protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
Expand Down

0 comments on commit fbd5b06

Please sign in to comment.