Skip to content

Commit 8140cfb

Browse files
committed
Added caching mechanism for DatabaseFacade dependencies
1 parent 968eff6 commit 8140cfb

13 files changed

+405
-65
lines changed

src/EFCore.Relational/Extensions/RelationalDatabaseFacadeExtensions.cs

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -208,22 +208,21 @@ public static int ExecuteSqlCommand(
208208
Check.NotNull(sql, nameof(sql));
209209
Check.NotNull(parameters, nameof(parameters));
210210

211-
var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();
212-
var logger = databaseFacade.GetService<IDiagnosticsLogger<DbLoggerCategory.Database.Command>>();
211+
var concurrencyDetector = GetFacadeDependencies(databaseFacade).ConcurrencyDetector;
212+
var logger = GetFacadeDependencies(databaseFacade).CommandLogger;
213213

214214
using (concurrencyDetector.EnterCriticalSection())
215215
{
216-
var rawSqlCommand = databaseFacade
217-
.GetRelationalService<IRawSqlCommandBuilder>()
216+
var rawSqlCommand = GetFacadeDependencies(databaseFacade).RawSqlCommandBuilder
218217
.Build(sql.Format, parameters);
219218

220219
return rawSqlCommand
221220
.RelationalCommand
222221
.ExecuteNonQuery(
223222
new RelationalCommandParameterObject(
224-
databaseFacade.GetRelationalService<IRelationalConnection>(),
223+
GetFacadeDependencies(databaseFacade).RelationalConnection,
225224
rawSqlCommand.ParameterValues,
226-
databaseFacade.GetService<ICurrentDbContext>().Context,
225+
((IDatabaseFacadeDependenciesAccessor)databaseFacade).Context,
227226
logger));
228227
}
229228
}
@@ -374,22 +373,22 @@ public static async Task<int> ExecuteSqlCommandAsync(
374373
Check.NotNull(sql, nameof(sql));
375374
Check.NotNull(parameters, nameof(parameters));
376375

377-
var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();
378-
var logger = databaseFacade.GetService<IDiagnosticsLogger<DbLoggerCategory.Database.Command>>();
376+
var facadeDependencies = GetFacadeDependencies(databaseFacade);
377+
var concurrencyDetector = facadeDependencies.ConcurrencyDetector;
378+
var logger = facadeDependencies.CommandLogger;
379379

380380
using (await concurrencyDetector.EnterCriticalSectionAsync(cancellationToken))
381381
{
382-
var rawSqlCommand = databaseFacade
383-
.GetRelationalService<IRawSqlCommandBuilder>()
382+
var rawSqlCommand = GetFacadeDependencies(databaseFacade).RawSqlCommandBuilder
384383
.Build(sql.Format, parameters);
385384

386385
return await rawSqlCommand
387386
.RelationalCommand
388387
.ExecuteNonQueryAsync(
389388
new RelationalCommandParameterObject(
390-
databaseFacade.GetRelationalService<IRelationalConnection>(),
389+
facadeDependencies.RelationalConnection,
391390
rawSqlCommand.ParameterValues,
392-
databaseFacade.GetService<ICurrentDbContext>().Context,
391+
((IDatabaseFacadeDependenciesAccessor)databaseFacade).Context,
393392
logger),
394393
cancellationToken);
395394
}
@@ -490,22 +489,22 @@ public static int ExecuteSqlRaw(
490489
Check.NotNull(sql, nameof(sql));
491490
Check.NotNull(parameters, nameof(parameters));
492491

493-
var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();
494-
var logger = databaseFacade.GetService<IDiagnosticsLogger<DbLoggerCategory.Database.Command>>();
492+
var facadeDependencies = GetFacadeDependencies(databaseFacade);
493+
var concurrencyDetector = facadeDependencies.ConcurrencyDetector;
494+
var logger = facadeDependencies.CommandLogger;
495495

496496
using (concurrencyDetector.EnterCriticalSection())
497497
{
498-
var rawSqlCommand = databaseFacade
499-
.GetRelationalService<IRawSqlCommandBuilder>()
498+
var rawSqlCommand = GetFacadeDependencies(databaseFacade).RawSqlCommandBuilder
500499
.Build(sql, parameters);
501500

502501
return rawSqlCommand
503502
.RelationalCommand
504503
.ExecuteNonQuery(
505504
new RelationalCommandParameterObject(
506-
databaseFacade.GetRelationalService<IRelationalConnection>(),
505+
facadeDependencies.RelationalConnection,
507506
rawSqlCommand.ParameterValues,
508-
databaseFacade.GetService<ICurrentDbContext>().Context,
507+
((IDatabaseFacadeDependenciesAccessor)databaseFacade).Context,
509508
logger));
510509
}
511510
}
@@ -642,22 +641,22 @@ public static async Task<int> ExecuteSqlRawAsync(
642641
Check.NotNull(sql, nameof(sql));
643642
Check.NotNull(parameters, nameof(parameters));
644643

645-
var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>();
646-
var logger = databaseFacade.GetService<IDiagnosticsLogger<DbLoggerCategory.Database.Command>>();
644+
var facadeDependencies = GetFacadeDependencies(databaseFacade);
645+
var concurrencyDetector = facadeDependencies.ConcurrencyDetector;
646+
var logger = facadeDependencies.CommandLogger;
647647

648648
using (await concurrencyDetector.EnterCriticalSectionAsync(cancellationToken))
649649
{
650-
var rawSqlCommand = databaseFacade
651-
.GetRelationalService<IRawSqlCommandBuilder>()
650+
var rawSqlCommand = GetFacadeDependencies(databaseFacade).RawSqlCommandBuilder
652651
.Build(sql, parameters);
653652

654653
return await rawSqlCommand
655654
.RelationalCommand
656655
.ExecuteNonQueryAsync(
657656
new RelationalCommandParameterObject(
658-
databaseFacade.GetRelationalService<IRelationalConnection>(),
657+
facadeDependencies.RelationalConnection,
659658
rawSqlCommand.ParameterValues,
660-
databaseFacade.GetService<ICurrentDbContext>().Context,
659+
((IDatabaseFacadeDependenciesAccessor)databaseFacade).Context,
661660
logger),
662661
cancellationToken);
663662
}
@@ -669,7 +668,7 @@ public static async Task<int> ExecuteSqlRawAsync(
669668
/// <param name="databaseFacade"> The <see cref="DatabaseFacade" /> for the context. </param>
670669
/// <returns> The <see cref="DbConnection" /> </returns>
671670
public static DbConnection GetDbConnection([NotNull] this DatabaseFacade databaseFacade)
672-
=> databaseFacade.GetRelationalService<IRelationalConnection>().DbConnection;
671+
=> GetFacadeDependencies(databaseFacade).RelationalConnection.DbConnection;
673672

674673
/// <summary>
675674
/// Opens the underlying <see cref="DbConnection" />.
@@ -678,7 +677,7 @@ public static DbConnection GetDbConnection([NotNull] this DatabaseFacade databas
678677
public static void OpenConnection([NotNull] this DatabaseFacade databaseFacade)
679678
=> databaseFacade.CreateExecutionStrategy().Execute(
680679
databaseFacade, database
681-
=> database.GetRelationalService<IRelationalConnection>().Open());
680+
=> GetFacadeDependencies(database).RelationalConnection.Open());
682681

683682
/// <summary>
684683
/// Opens the underlying <see cref="DbConnection" />.
@@ -693,14 +692,14 @@ public static Task OpenConnectionAsync(
693692
CancellationToken cancellationToken = default)
694693
=> databaseFacade.CreateExecutionStrategy().ExecuteAsync(
695694
databaseFacade, (database, ct) =>
696-
database.GetRelationalService<IRelationalConnection>().OpenAsync(cancellationToken), cancellationToken);
695+
GetFacadeDependencies(database).RelationalConnection.OpenAsync(cancellationToken), cancellationToken);
697696

698697
/// <summary>
699698
/// Closes the underlying <see cref="DbConnection" />.
700699
/// </summary>
701700
/// <param name="databaseFacade"> The <see cref="DatabaseFacade" /> for the context. </param>
702701
public static void CloseConnection([NotNull] this DatabaseFacade databaseFacade)
703-
=> databaseFacade.GetRelationalService<IRelationalConnection>().Close();
702+
=> GetFacadeDependencies(databaseFacade).RelationalConnection.Close();
704703

705704
/// <summary>
706705
/// Starts a new transaction with a given <see cref="IsolationLevel" />.
@@ -774,7 +773,7 @@ public static IDbContextTransaction UseTransaction(
774773
/// <param name="databaseFacade"> The <see cref="DatabaseFacade" /> for the context. </param>
775774
/// <param name="timeout"> The timeout to use, in seconds. </param>
776775
public static void SetCommandTimeout([NotNull] this DatabaseFacade databaseFacade, int? timeout)
777-
=> databaseFacade.GetRelationalService<IRelationalConnection>().CommandTimeout = timeout;
776+
=> GetFacadeDependencies(databaseFacade).RelationalConnection.CommandTimeout = timeout;
778777

779778
/// <summary>
780779
/// <para>
@@ -814,7 +813,7 @@ public static void SetCommandTimeout([NotNull] this DatabaseFacade databaseFacad
814813
/// <param name="databaseFacade"> The <see cref="DatabaseFacade" /> for the context. </param>
815814
/// <returns> The timeout, in seconds, or null if no timeout has been set. </returns>
816815
public static int? GetCommandTimeout([NotNull] this DatabaseFacade databaseFacade)
817-
=> databaseFacade.GetRelationalService<IRelationalConnection>().CommandTimeout;
816+
=> GetFacadeDependencies(databaseFacade).RelationalConnection.CommandTimeout;
818817

819818
/// <summary>
820819
/// Generates a script to create all tables for the current model.
@@ -825,6 +824,18 @@ public static void SetCommandTimeout([NotNull] this DatabaseFacade databaseFacad
825824
public static string GenerateCreateScript([NotNull] this DatabaseFacade databaseFacade)
826825
=> databaseFacade.GetRelationalService<IRelationalDatabaseCreator>().GenerateCreateScript();
827826

827+
private static IRelationalDatabaseFacadeDependencies GetFacadeDependencies(DatabaseFacade databaseFacade)
828+
{
829+
var dependencies = ((IDatabaseFacadeDependenciesAccessor)databaseFacade).Dependencies;
830+
831+
if (dependencies is IRelationalDatabaseFacadeDependencies relationalDependencies)
832+
{
833+
return relationalDependencies;
834+
}
835+
836+
throw new InvalidOperationException(RelationalStrings.RelationalNotInUse);
837+
}
838+
828839
private static TService GetRelationalService<TService>(this IInfrastructure<IServiceProvider> databaseFacade)
829840
{
830841
Check.NotNull(databaseFacade, nameof(databaseFacade));
@@ -839,6 +850,6 @@ private static TService GetRelationalService<TService>(this IInfrastructure<ISer
839850
}
840851

841852
private static IDbContextTransactionManager GetTransactionManager([NotNull] this DatabaseFacade databaseFacade)
842-
=> Check.NotNull(databaseFacade, nameof(databaseFacade)).GetService<IDbContextTransactionManager>();
853+
=> ((IDatabaseFacadeDependenciesAccessor)Check.NotNull(databaseFacade, nameof(databaseFacade))).Dependencies.TransactionManager;
843854
}
844855
}

src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using JetBrains.Annotations;
77
using Microsoft.EntityFrameworkCore.Diagnostics;
8+
using Microsoft.EntityFrameworkCore.Internal;
89
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
910
using Microsoft.EntityFrameworkCore.Migrations;
1011
using Microsoft.EntityFrameworkCore.Migrations.Internal;
@@ -76,6 +77,7 @@ public static readonly IDictionary<Type, ServiceCharacteristics> RelationalServi
7677
{ typeof(IMigrationsAssembly), new ServiceCharacteristics(ServiceLifetime.Scoped) },
7778
{ typeof(IBatchExecutor), new ServiceCharacteristics(ServiceLifetime.Scoped) },
7879
{ typeof(IRelationalConnection), new ServiceCharacteristics(ServiceLifetime.Scoped) },
80+
{ typeof(IRelationalDatabaseFacadeDependencies), new ServiceCharacteristics(ServiceLifetime.Scoped) },
7981
{ typeof(IRelationalDatabaseCreator), new ServiceCharacteristics(ServiceLifetime.Scoped) },
8082
{ typeof(IHistoryRepository), new ServiceCharacteristics(ServiceLifetime.Scoped) },
8183
{ typeof(INamedConnectionStringResolver), new ServiceCharacteristics(ServiceLifetime.Scoped) },
@@ -153,6 +155,8 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
153155
TryAdd<INamedConnectionStringResolver, NamedConnectionStringResolver>();
154156
TryAdd<IEvaluatableExpressionFilter, RelationalEvaluatableExpressionFilter>();
155157
TryAdd<IRelationalTransactionFactory, RelationalTransactionFactory>();
158+
TryAdd<IDatabaseFacadeDependencies>(p => p.GetService<IRelationalDatabaseFacadeDependencies>());
159+
TryAdd<IRelationalDatabaseFacadeDependencies, RelationalDatabaseFacadeDependencies>();
156160
TryAdd<IRelationalInterceptors, RelationalInterceptors>();
157161
TryAdd<IInterceptors>(p => p.GetService<IRelationalInterceptors>());
158162

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.EntityFrameworkCore.Storage;
5+
using Microsoft.Extensions.DependencyInjection;
6+
7+
namespace Microsoft.EntityFrameworkCore.Internal
8+
{
9+
/// <summary>
10+
/// <para>
11+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
12+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
13+
/// any release. You should only use it directly in your code with extreme caution and knowing that
14+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
15+
/// </para>
16+
/// <para>
17+
/// The service lifetime is <see cref="ServiceLifetime.Scoped"/>. This means that each
18+
/// <see cref="DbContext"/> instance will use its own instance of this service.
19+
/// The implementation may depend on other services registered with any lifetime.
20+
/// The implementation does not need to be thread-safe.
21+
/// </para>
22+
/// </summary>
23+
public interface IRelationalDatabaseFacadeDependencies : IDatabaseFacadeDependencies
24+
{
25+
/// <summary>
26+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
27+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
28+
/// any release. You should only use it directly in your code with extreme caution and knowing that
29+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
30+
/// </summary>
31+
IRelationalConnection RelationalConnection { get; }
32+
33+
/// <summary>
34+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
35+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
36+
/// any release. You should only use it directly in your code with extreme caution and knowing that
37+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
38+
/// </summary>
39+
IRawSqlCommandBuilder RawSqlCommandBuilder { get; }
40+
}
41+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using JetBrains.Annotations;
6+
using Microsoft.EntityFrameworkCore.Diagnostics;
7+
using Microsoft.EntityFrameworkCore.Storage;
8+
9+
namespace Microsoft.EntityFrameworkCore.Internal
10+
{
11+
public class RelationalDatabaseFacadeDependencies : DatabaseFacadeDependencies, IRelationalDatabaseFacadeDependencies
12+
{
13+
/// <summary>
14+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
15+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
16+
/// any release. You should only use it directly in your code with extreme caution and knowing that
17+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
18+
/// </summary>
19+
public RelationalDatabaseFacadeDependencies(
20+
[NotNull] IDbContextTransactionManager transactionManager,
21+
[NotNull] IDatabaseCreator databaseCreator,
22+
[NotNull] IExecutionStrategyFactory executionStrategyFactory,
23+
[NotNull] IEnumerable<IDatabaseProvider> databaseProviders,
24+
[NotNull] IDiagnosticsLogger<DbLoggerCategory.Database.Command> commandLogger,
25+
[NotNull] IConcurrencyDetector concurrencyDetector,
26+
[NotNull] IRelationalConnection relationalConnection,
27+
[NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder)
28+
: base(
29+
transactionManager,
30+
databaseCreator,
31+
executionStrategyFactory,
32+
databaseProviders,
33+
commandLogger,
34+
concurrencyDetector)
35+
{
36+
RelationalConnection = relationalConnection;
37+
RawSqlCommandBuilder = rawSqlCommandBuilder;
38+
}
39+
40+
/// <summary>
41+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
42+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
43+
/// any release. You should only use it directly in your code with extreme caution and knowing that
44+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
45+
/// </summary>
46+
public virtual IRelationalConnection RelationalConnection { get; }
47+
48+
/// <summary>
49+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
50+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
51+
/// any release. You should only use it directly in your code with extreme caution and knowing that
52+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
53+
/// </summary>
54+
public virtual IRawSqlCommandBuilder RawSqlCommandBuilder { get; }
55+
}
56+
}

src/EFCore/DbContext.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,15 @@ public virtual DatabaseFacade Database
118118
{
119119
CheckDisposed();
120120

121-
return _database ?? (_database = new DatabaseFacade(this));
121+
return _database ??= new DatabaseFacade(this);
122122
}
123123
}
124124

125125
/// <summary>
126126
/// Provides access to information and operations for entity instances this context is tracking.
127127
/// </summary>
128128
public virtual ChangeTracker ChangeTracker
129-
=> _changeTracker
130-
?? (_changeTracker = InternalServiceProvider.GetRequiredService<IChangeTrackerFactory>().Create());
129+
=> _changeTracker ??= InternalServiceProvider.GetRequiredService<IChangeTrackerFactory>().Create();
131130

132131
/// <summary>
133132
/// The metadata about the shape of entities, the relationships between them, and how they map to the database.
@@ -336,7 +335,7 @@ private IDbContextDependencies DbContextDependencies
336335
{
337336
CheckDisposed();
338337

339-
return _dbContextDependencies ?? (_dbContextDependencies = InternalServiceProvider.GetRequiredService<IDbContextDependencies>());
338+
return _dbContextDependencies ??= InternalServiceProvider.GetRequiredService<IDbContextDependencies>();
340339
}
341340
}
342341

0 commit comments

Comments
 (0)