Skip to content

Commit

Permalink
Support DbDataSource
Browse files Browse the repository at this point in the history
Part of #2400
  • Loading branch information
roji committed Oct 7, 2022
1 parent 30522b1 commit 5f289d5
Show file tree
Hide file tree
Showing 19 changed files with 398 additions and 120 deletions.
1 change: 0 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<Project>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PackageVersion Include="Npgsql" Version="$(NpgsqlVersion)" />
<PackageVersion Include="Npgsql.NodaTime" Version="$(NpgsqlVersion)" />
<PackageVersion Include="Npgsql.NetTopologySuite" Version="$(NpgsqlVersion)" />
<PackageVersion Include="Npgsql.DependencyInjection" Version="$(NpgsqlVersion)" />

<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
Expand Down
2 changes: 2 additions & 0 deletions src/EFCore.PG.NTS/EFCore.PG.NTS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<PropertyGroup>
<AssemblyName>Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite</AssemblyName>
<RootNamespace>Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks Condition="'$(DeveloperBuild)' == 'True'">net7.0</TargetFrameworks>

<Authors>Shay Rojansky</Authors>
<Description>NetTopologySuite PostGIS spatial support plugin for PostgreSQL/Npgsql Entity Framework Core provider.</Description>
Expand Down
2 changes: 2 additions & 0 deletions src/EFCore.PG.NodaTime/EFCore.PG.NodaTime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<PropertyGroup>
<AssemblyName>Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime</AssemblyName>
<RootNamespace>Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks Condition="'$(DeveloperBuild)' == 'True'">net7.0</TargetFrameworks>

<Authors>Shay Rojansky</Authors>
<Description>NodaTime support plugin for PostgreSQL/Npgsql Entity Framework Core provider.</Description>
Expand Down
2 changes: 2 additions & 0 deletions src/EFCore.PG/EFCore.PG.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<PropertyGroup>
<AssemblyName>Npgsql.EntityFrameworkCore.PostgreSQL</AssemblyName>
<RootNamespace>Npgsql.EntityFrameworkCore.PostgreSQL</RootNamespace>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<TargetFrameworks Condition="'$(DeveloperBuild)' == 'True'">net7.0</TargetFrameworks>

<Authors>Shay Rojansky;Austin Drenski;Yoh Deadfall;</Authors>
<Description>PostgreSQL/Npgsql provider for Entity Framework Core.</Description>
Expand Down
32 changes: 31 additions & 1 deletion src/EFCore.PG/Extensions/NpgsqlDatabaseFacadeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using System.Data.Common;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore;
Expand All @@ -23,4 +25,32 @@ public static class NpgsqlDatabaseFacadeExtensions
/// <returns>True if Npgsql is being used; false otherwise.</returns>
public static bool IsNpgsql(this DatabaseFacade database)
=> database.ProviderName == typeof(NpgsqlOptionsExtension).GetTypeInfo().Assembly.GetName().Name;

/// <summary>
/// Sets the underlying <see cref="DbDataSource" /> configured for this <see cref="DbContext" />.
/// </summary>
/// <remarks>
/// <para>
/// It may not be possible to change the data source if existing connection, if any, is open.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-connections">Connections and connection strings</see> for more information and examples.
/// </para>
/// </remarks>
/// <param name="databaseFacade">The <see cref="DatabaseFacade" /> for the context.</param>
/// <param name="dataSource">The connection string.</param>
public static void SetDbDataSource(this DatabaseFacade databaseFacade, DbDataSource dataSource)
=> ((NpgsqlRelationalConnection)GetFacadeDependencies(databaseFacade).RelationalConnection).DbDataSource = dataSource;

private static IRelationalDatabaseFacadeDependencies GetFacadeDependencies(DatabaseFacade databaseFacade)
{
var dependencies = ((IDatabaseFacadeDependenciesAccessor)databaseFacade).Dependencies;

if (dependencies is IRelationalDatabaseFacadeDependencies relationalDependencies)
{
return relationalDependencies;
}

throw new InvalidOperationException(RelationalStrings.RelationalNotInUse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,43 @@ public static DbContextOptionsBuilder UseNpgsql(
return optionsBuilder;
}

/// <summary>
/// Configures the context to connect to a PostgreSQL database with Npgsql.
/// </summary>
/// <param name="optionsBuilder">A builder for setting options on the context.</param>
/// <param name="dataSource">A <see cref="DbDataSource" /> which will be used to get database connections.</param>
/// <param name="npgsqlOptionsAction">An optional action to allow additional Npgsql-specific configuration.</param>
/// <returns>
/// The options builder so that further configuration can be chained.
/// </returns>
public static DbContextOptionsBuilder UseNpgsql(
this DbContextOptionsBuilder optionsBuilder,
DbDataSource dataSource,
Action<NpgsqlDbContextOptionsBuilder>? npgsqlOptionsAction = null)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
Check.NotNull(dataSource, nameof(dataSource));

var extension = (NpgsqlOptionsExtension)GetOrCreateExtension(optionsBuilder).WithDataSource(dataSource);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

ConfigureWarnings(optionsBuilder);

npgsqlOptionsAction?.Invoke(new NpgsqlDbContextOptionsBuilder(optionsBuilder));

return optionsBuilder;
}

/// <summary>
/// <para>
/// Configures the context to connect to a PostgreSQL server with Npgsql, but without initially setting any
/// <see cref="DbConnection" /> or connection string.
/// <see cref="DbConnection" />, <see cref="DbDataSource" /> or connection string.
/// </para>
/// <para>
/// The connection or connection string must be set before the <see cref="DbContext" /> is used to connect
/// to a database. Set a connection using <see cref="RelationalDatabaseFacadeExtensions.SetDbConnection" />.
/// Set a connection string using <see cref="RelationalDatabaseFacadeExtensions.SetConnectionString" />.
/// The connection, data source or connection string must be set before the <see cref="DbContext" /> is used to connect
/// to a database. Set a connection using <see cref="RelationalDatabaseFacadeExtensions.SetDbConnection" />, a data source using
/// <see cref="NpgsqlDatabaseFacadeExtensions.SetDbDataSource" />, or a connection string using
/// <see cref="RelationalDatabaseFacadeExtensions.SetConnectionString" />.
/// </para>
/// </summary>
/// <param name="optionsBuilder">The builder being used to configure the context.</param>
Expand Down Expand Up @@ -156,6 +184,23 @@ public static DbContextOptionsBuilder<TContext> UseNpgsql<TContext>(
=> (DbContextOptionsBuilder<TContext>)UseNpgsql(
(DbContextOptionsBuilder)optionsBuilder, connection, npgsqlOptionsAction);

/// <summary>
/// Configures the context to connect to a PostgreSQL database with Npgsql.
/// </summary>
/// <param name="optionsBuilder">A builder for setting options on the context.</param>
/// <param name="dataSource">A <see cref="DbDataSource" /> which will be used to get database connections.</param>
/// <param name="npgsqlOptionsAction">An optional action to allow additional Npgsql-specific configuration.</param>
/// <returns>
/// The options builder so that further configuration can be chained.
/// </returns>
public static DbContextOptionsBuilder<TContext> UseNpgsql<TContext>(
this DbContextOptionsBuilder<TContext> optionsBuilder,
DbDataSource dataSource,
Action<NpgsqlDbContextOptionsBuilder>? npgsqlOptionsAction = null)
where TContext : DbContext
=> (DbContextOptionsBuilder<TContext>)UseNpgsql(
(DbContextOptionsBuilder)optionsBuilder, dataSource, npgsqlOptionsAction);

/// <summary>
/// Returns an existing instance of <see cref="NpgsqlOptionsExtension"/>, or a new instance if one does not exist.
/// </summary>
Expand Down
43 changes: 42 additions & 1 deletion src/EFCore.PG/Infrastructure/Internal/NpgsqlOptionsExtension.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Data.Common;
using System.Globalization;
using System.Net.Security;
using System.Text;
Expand All @@ -9,9 +10,16 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
/// </summary>
public class NpgsqlOptionsExtension : RelationalOptionsExtension
{
private DbDataSource? _dataSource, _calculatedDataSource;
private DbContextOptionsExtensionInfo? _info;
private readonly List<UserRangeDefinition> _userRangeDefinitions;

/// <summary>
/// The <see cref="DbDataSource" />, or <see langword="null" /> if a connection string or <see cref="DbConnection" /> was used
/// instead of a <see cref="DbDataSource" />.
/// </summary>
public virtual DbDataSource? DataSource => _calculatedDataSource;

/// <summary>
/// The name of the database for administrative operations.
/// </summary>
Expand Down Expand Up @@ -65,6 +73,7 @@ public NpgsqlOptionsExtension()
/// <param name="copyFrom">The instance to copy.</param>
public NpgsqlOptionsExtension(NpgsqlOptionsExtension copyFrom) : base(copyFrom)
{
_dataSource = copyFrom._dataSource;
AdminDatabase = copyFrom.AdminDatabase;
PostgresVersion = copyFrom.PostgresVersion;
UseRedshift = copyFrom.UseRedshift;
Expand All @@ -85,6 +94,21 @@ public NpgsqlOptionsExtension(NpgsqlOptionsExtension copyFrom) : base(copyFrom)
/// </summary>
public override int? MinBatchSize => base.MinBatchSize ?? 2;

/// <summary>
/// Creates a new instance with all options the same as for this instance, but with the given option changed.
/// It is unusual to call this method directly. Instead use <see cref="DbContextOptionsBuilder" />.
/// </summary>
/// <param name="dataSource">The option to change.</param>
/// <returns>A new instance with the option changed.</returns>
public virtual RelationalOptionsExtension WithDataSource(DbDataSource? dataSource)
{
var clone = (NpgsqlOptionsExtension)Clone();

clone._dataSource = dataSource;

return clone;
}

/// <summary>
/// Returns a copy of the current instance configured with the specified range mapping.
/// </summary>
Expand Down Expand Up @@ -176,6 +200,18 @@ public override void Validate(IDbContextOptions options)
{
base.Validate(options);

// If we don't have an explicitly-configured data source, try to get one from the application service provider.
_calculatedDataSource = _dataSource
?? options.FindExtension<CoreOptionsExtension>()?.ApplicationServiceProvider?.GetService<NpgsqlDataSource>();

if (_calculatedDataSource is not null
&& (ProvideClientCertificatesCallback is not null
|| RemoteCertificateValidationCallback is not null
|| ProvidePasswordCallback is not null))
{
throw new InvalidOperationException();
}

if (UseRedshift && PostgresVersion is not null)
{
throw new InvalidOperationException($"{nameof(UseRedshift)} and {nameof(PostgresVersion)} cannot both be set");
Expand Down Expand Up @@ -320,7 +356,7 @@ public override string LogFragment
builder.Append(";");
}

builder.Length = builder.Length -1;
builder.Length -= 1;
builder.Append("] ");
}

Expand All @@ -339,6 +375,11 @@ public override int GetServiceProviderHashCode()
hashCode.Add(userRangeDefinition);
}

if (Extension._dataSource is not null)
{
hashCode.Add(Extension._dataSource.ConnectionString);
}

hashCode.Add(Extension.AdminDatabase);
hashCode.Add(Extension.PostgresVersion);
hashCode.Add(Extension.UseRedshift);
Expand Down
7 changes: 1 addition & 6 deletions src/EFCore.PG/Internal/NpgsqlSingletonOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ public class NpgsqlSingletonOptions : INpgsqlSingletonOptions
/// </summary>
public static readonly Version DefaultPostgresVersion = new(12, 0);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
/// <inheritdoc />
public virtual Version PostgresVersion { get; private set; } = null!;

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions src/EFCore.PG/Properties/NpgsqlStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore.PG/Properties/NpgsqlStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,7 @@
<data name="RowValueComparisonRequiresTuplesOfSameLength" xml:space="preserve">
<value>Row values comparisons require two tuple arguments of the same length.</value>
</data>
<data name="CannotUseDataSourceWithAuthCallbacks" xml:space="preserve">
<value>Cannot set ProvideClientCertificatesCallback, RemoteCertificateValidationCallback or ProvidePasswordCallback when a data source is provided.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface INpgsqlRelationalConnection : IRelationalConnection
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
INpgsqlRelationalConnection CreateMasterConnection();
INpgsqlRelationalConnection CreateAdminConnection();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
8 changes: 4 additions & 4 deletions src/EFCore.PG/Storage/Internal/NpgsqlDatabaseCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public NpgsqlDatabaseCreator(
/// </summary>
public override void Create()
{
using (var masterConnection = _connection.CreateMasterConnection())
using (var masterConnection = _connection.CreateAdminConnection())
{
try
{
Expand All @@ -82,7 +82,7 @@ public override void Create()
/// </summary>
public override async Task CreateAsync(CancellationToken cancellationToken = default)
{
using (var masterConnection = _connection.CreateMasterConnection())
using (var masterConnection = _connection.CreateAdminConnection())
{
try
{
Expand Down Expand Up @@ -278,7 +278,7 @@ public override void Delete()
{
ClearAllPools();

using (var masterConnection = _connection.CreateMasterConnection())
using (var masterConnection = _connection.CreateAdminConnection())
{
Dependencies.MigrationCommandExecutor
.ExecuteNonQuery(CreateDropCommands(), masterConnection);
Expand All @@ -295,7 +295,7 @@ public override async Task DeleteAsync(CancellationToken cancellationToken = def
{
ClearAllPools();

using (var masterConnection = _connection.CreateMasterConnection())
using (var masterConnection = _connection.CreateAdminConnection())
{
await Dependencies.MigrationCommandExecutor
.ExecuteNonQueryAsync(CreateDropCommands(), masterConnection, cancellationToken)
Expand Down
Loading

0 comments on commit 5f289d5

Please sign in to comment.