Skip to content

Commit

Permalink
Service dependencies parameter class for RelationalConnection
Browse files Browse the repository at this point in the history
This is a proof-of-concept implementation of parameter objects for service dependencies. Issue #7465.

Notes:
- Parameter class is sealed.
- Ended up going with Clone on the parameters object--cleaner than other things I tried, and general purpose. We can add overloads of Clone as needed when doing point releases without breaking existing code. (May want to consider whether parameter should be options.)
- Registration is not try-add and is of concrete class
  • Loading branch information
ajcvickers committed Jan 26, 2017
1 parent 532195c commit 7b62116
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ public static void TryAddDefaultRelationalServices([NotNull] IServiceCollection
.AddScoped<IExpressionFragmentTranslator, RelationalCompositeExpressionFragmentTranslator>()
.AddScoped<ISqlTranslatingExpressionVisitorFactory, SqlTranslatingExpressionVisitorFactory>());

// Add service dependencies parameter classes.
// These are not try-added and are added as concrete types because these registrations should
// not be changed by provider or application code.
serviceCollection
.AddScoped<RelationalConnectionDependencies>();

ServiceCollectionProviderInfrastructure.TryAddDefaultEntityFrameworkServices(serviceCollection);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@
<Compile Include="Storage\RawSqlCommand.cs" />
<Compile Include="Storage\RelationalCommandBuilderExtensions.cs" />
<Compile Include="Storage\RelationalConnection.cs" />
<Compile Include="Storage\RelationalConnectionDependencies.cs" />
<Compile Include="Storage\RelationalDatabase.cs" />
<Compile Include="Storage\RelationalDatabaseCreator.cs" />
<Compile Include="Storage\RelationalDataReader.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,18 @@ public abstract class RelationalConnection : IRelationalConnection
private int _openedCount;
private bool _openedInternally;
private int? _commandTimeout;
private readonly ILogger _logger;

/// <summary>
/// Initializes a new instance of the <see cref="IRelationalConnection" /> class.
/// Initializes a new instance of the <see cref="RelationalConnection" /> class.
/// </summary>
/// <param name="options"> The options for the context that this connection will be used with. </param>
/// <param name="logger"> The logger to write to. </param>
protected RelationalConnection([NotNull] IDbContextOptions options, [NotNull] ILogger logger)
/// <param name="dependencies">Parameter object containing dependencies for this service. </param>
protected RelationalConnection([NotNull] RelationalConnectionDependencies dependencies)
{
Check.NotNull(options, nameof(options));
Check.NotNull(logger, nameof(logger));
Check.NotNull(dependencies, nameof(dependencies));

_logger = logger;
Dependencies = dependencies;

var relationalOptions = RelationalOptionsExtension.Extract(options);
var relationalOptions = RelationalOptionsExtension.Extract(dependencies.ContextOptions);

_commandTimeout = relationalOptions.CommandTimeout;

Expand All @@ -77,6 +74,11 @@ protected RelationalConnection([NotNull] IDbContextOptions options, [NotNull] IL
}
}

/// <summary>
/// Parameter object containing service dependencies.
/// </summary>
protected virtual RelationalConnectionDependencies Dependencies { get; }

/// <summary>
/// Creates a <see cref="DbConnection" /> to the database.
/// </summary>
Expand All @@ -86,7 +88,7 @@ protected RelationalConnection([NotNull] IDbContextOptions options, [NotNull] IL
/// <summary>
/// Gets the logger to write to.
/// </summary>
protected virtual ILogger Logger => _logger;
protected virtual ILogger Logger => Dependencies.Logger;

/// <summary>
/// Gets the connection string for the database.
Expand All @@ -112,7 +114,7 @@ public virtual int? CommandTimeout
set
{
if (value.HasValue
&& (value < 0))
&& value < 0)
{
throw new ArgumentException(RelationalStrings.InvalidCommandTimeout);
}
Expand Down Expand Up @@ -182,9 +184,7 @@ public virtual async Task<IDbContextTransaction> BeginTransactionAsync(

private IDbContextTransaction BeginTransactionWithNoPreconditions(IsolationLevel isolationLevel)
{
Check.NotNull(_logger, nameof(_logger));

_logger.LogDebug(
Logger.LogDebug(
RelationalEventId.BeginningTransaction,
isolationLevel,
il => RelationalStrings.RelationalLoggerBeginningTransaction(il.ToString("G")));
Expand All @@ -193,7 +193,7 @@ private IDbContextTransaction BeginTransactionWithNoPreconditions(IsolationLevel
= new RelationalTransaction(
this,
DbConnection.BeginTransaction(isolationLevel),
_logger,
Logger,
transactionOwned: true);

return CurrentTransaction;
Expand Down Expand Up @@ -221,7 +221,7 @@ public virtual IDbContextTransaction UseTransaction(DbTransaction transaction)

Open();

CurrentTransaction = new RelationalTransaction(this, transaction, _logger, transactionOwned: false);
CurrentTransaction = new RelationalTransaction(this, transaction, Logger, transactionOwned: false);
}

return CurrentTransaction;
Expand Down Expand Up @@ -267,7 +267,7 @@ public virtual void Open()

if (_connection.Value.State != ConnectionState.Open)
{
_logger.LogDebug(
Logger.LogDebug(
RelationalEventId.OpeningConnection,
new
{
Expand Down Expand Up @@ -311,7 +311,7 @@ public virtual void Open()

if (_connection.Value.State != ConnectionState.Open)
{
_logger.LogDebug(
Logger.LogDebug(
RelationalEventId.OpeningConnection,
new
{
Expand Down Expand Up @@ -360,7 +360,7 @@ public virtual void Close()
{
if (_connection.Value.State != ConnectionState.Closed)
{
_logger.LogDebug(
Logger.LogDebug(
RelationalEventId.ClosingConnection,
new
{
Expand Down Expand Up @@ -388,7 +388,7 @@ public virtual void Close()
public virtual IValueBufferCursor ActiveCursor { get; set; }

void IResettableService.Reset() => Dispose();

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.Storage
{
/// <summary>
/// <para>
/// Service dependencies parameter class for <see cref="RelationalConnection" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
public sealed class RelationalConnectionDependencies
{
/// <summary>
/// <para>
/// Creates the service dependencies parameter object for a <see cref="RelationalConnection" />.
/// </para>
/// <para>
/// Do not call this constructor directly from provider or application code as it may change
/// as new dependencies are added. Use the <see cref="Clone" /> method instead.
/// </para>
/// </summary>
/// <param name="contextOptions"> The options for the current context instance. </param>
/// <param name="logger"> The logger to write to. </param>
public RelationalConnectionDependencies(
[NotNull] IDbContextOptions contextOptions,
[NotNull] ILogger<IRelationalConnection> logger)
{
Check.NotNull(contextOptions, nameof(contextOptions));
Check.NotNull(logger, nameof(logger));

ContextOptions = contextOptions;
Logger = logger;
}

/// <summary>
/// The options for the current context instance.
/// </summary>
public IDbContextOptions ContextOptions { get; }

/// <summary>
/// The logger to write to.
/// </summary>
public ILogger<IRelationalConnection> Logger { get; }

/// <summary>
/// Clones this dependency parameter object, optionally replacing existing dependencies.
/// </summary>
/// <param name="contextOptions">
/// A replacement for the current dependency of this type, or null to continue to use the current dependency.
/// </param>
/// <param name="logger">
/// A replacement for the current dependency of this type, or null to continue to use the current dependency.
/// </param>
/// <returns></returns>
public RelationalConnectionDependencies Clone(
[CanBeNull] IDbContextOptions contextOptions = null,
[CanBeNull] ILogger<IRelationalConnection> logger = null)
=> new RelationalConnectionDependencies(
contextOptions ?? ContextOptions,
logger ?? Logger);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
using System.Data.Common;
using System.Data.SqlClient;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.Storage.Internal
{
Expand All @@ -24,17 +22,8 @@ public class SqlServerConnection : RelationalConnection, ISqlServerConnection
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public SqlServerConnection(
[NotNull] IDbContextOptions options,
// ReSharper disable once SuggestBaseTypeForParameter
[NotNull] ILogger<SqlServerConnection> logger)
: base(options, logger)
{
}

private SqlServerConnection(
[NotNull] IDbContextOptions options, [NotNull] ILogger logger)
: base(options, logger)
public SqlServerConnection([NotNull] RelationalConnectionDependencies dependencies)
: base(dependencies)
{
}

Expand All @@ -51,11 +40,16 @@ private SqlServerConnection(
/// </summary>
public virtual ISqlServerConnection CreateMasterConnection()
{
var builder = new SqlConnectionStringBuilder(ConnectionString) { InitialCatalog = "master" };
builder.Remove("AttachDBFilename");
var connectionStringBuilder = new SqlConnectionStringBuilder(ConnectionString) { InitialCatalog = "master" };
connectionStringBuilder.Remove("AttachDBFilename");

var contextOptions = new DbContextOptionsBuilder()
.UseSqlServer(
connectionStringBuilder.ConnectionString,
b => b.CommandTimeout(CommandTimeout ?? DefaultMasterConnectionCommandTimeout))
.Options;

return new SqlServerConnection(new DbContextOptionsBuilder()
.UseSqlServer(builder.ConnectionString, b => b.CommandTimeout(CommandTimeout ?? DefaultMasterConnectionCommandTimeout)).Options, Logger);
return new SqlServerConnection(Dependencies.Clone(contextOptions));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.Storage.Internal
{
Expand All @@ -29,17 +27,15 @@ public class SqliteRelationalConnection : RelationalConnection, ISqliteRelationa
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public SqliteRelationalConnection(
[NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder,
[NotNull] IDbContextOptions options,
// ReSharper disable once SuggestBaseTypeForParameter
[NotNull] ILogger<SqliteRelationalConnection> logger)
: base(options, logger)
[NotNull] RelationalConnectionDependencies dependencies,
[NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder)
: base(dependencies)
{
Check.NotNull(rawSqlCommandBuilder, nameof(rawSqlCommandBuilder));

_rawSqlCommandBuilder = rawSqlCommandBuilder;

var optionsExtension = options.Extensions.OfType<SqliteOptionsExtension>().FirstOrDefault();
var optionsExtension = dependencies.ContextOptions.Extensions.OfType<SqliteOptionsExtension>().FirstOrDefault();
if (optionsExtension != null)
{
_enforceForeignKeys = optionsExtension.EnforceForeignKeys;
Expand Down Expand Up @@ -119,18 +115,14 @@ private void EnableForeignKeys()
/// </summary>
public virtual ISqliteRelationalConnection CreateReadOnlyConnection()
{
var builder = new SqliteConnectionStringBuilder(ConnectionString)
var connectionStringBuilder = new SqliteConnectionStringBuilder(ConnectionString)
{
Mode = SqliteOpenMode.ReadOnly
};

var options = new DbContextOptionsBuilder();
options.UseSqlite(builder.ToString());
var contextOptions = new DbContextOptionsBuilder().UseSqlite(connectionStringBuilder.ToString()).Options;

return new SqliteRelationalConnection(
_rawSqlCommandBuilder,
options.Options,
(ILogger<SqliteRelationalConnection>)Logger);
return new SqliteRelationalConnection(Dependencies.Clone(contextOptions), _rawSqlCommandBuilder);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ public class FakeRelationalConnection : RelationalConnection
private readonly List<FakeDbConnection> _dbConnections = new List<FakeDbConnection>();

public FakeRelationalConnection(IDbContextOptions options)
: base(options, new Logger<FakeRelationalConnection>(new LoggerFactory()))
: base(new RelationalConnectionDependencies(
options,
new Logger<FakeRelationalConnection>(new LoggerFactory())))
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,18 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests.Utilities
{
public class TestSqlServerConnection : ISqlServerConnection
{
private readonly ISqlServerConnection _realConnection;

public TestSqlServerConnection(IDbContextOptions options, ILogger<SqlServerConnection> logger)
: this(new SqlServerConnection(options, logger))
public TestSqlServerConnection(RelationalConnectionDependencies dependencies)
: this(new SqlServerConnection(dependencies))
{
}

Expand Down
Loading

0 comments on commit 7b62116

Please sign in to comment.