From 741c26fa33505516ca66b364d91c523861525828 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Fri, 13 Aug 2021 21:36:26 +0100 Subject: [PATCH] Log provider version in addition to EF Core version Fixes #22906 --- .../Diagnostics/CoreLoggerExtensions.cs | 8 ++- src/EFCore/Properties/CoreStrings.Designer.cs | 10 ++-- src/EFCore/Properties/CoreStrings.resx | 4 +- src/EFCore/Storage/DatabaseProvider.cs | 9 +++ src/EFCore/Storage/IDatabaseProvider.cs | 8 +++ .../LoggingInMemoryTest.cs | 6 ++ .../LoggingTestBase.cs | 3 + .../LoggingSqlServerTest.cs | 5 ++ .../LoggingSqliteTest.cs | 5 ++ test/EFCore.Tests/DbContextLoggerTests.cs | 58 +++++++++---------- 10 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs index efb61cf359a..69f513b49be 100644 --- a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs +++ b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs @@ -17,6 +17,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Update; using Microsoft.Extensions.Logging; @@ -895,6 +896,7 @@ public static void ContextInitialized( ProductInfo.GetVersion(), context.GetType().ShortDisplayName(), context.Database.ProviderName, + GetProviderVersion(context), contextOptions.BuildOptionsFragment()); } @@ -912,14 +914,18 @@ public static void ContextInitialized( private static string ContextInitialized(EventDefinitionBase definition, EventData payload) { - var d = (EventDefinition)definition; + var d = (EventDefinition)definition; var p = (ContextInitializedEventData)payload; return d.GenerateMessage( ProductInfo.GetVersion(), p.Context.GetType().ShortDisplayName(), p.Context.Database.ProviderName, + GetProviderVersion(p.Context), p.ContextOptions.BuildOptionsFragment()); } + + private static string? GetProviderVersion(DbContext context) + => context.GetService>().FirstOrDefault()?.Version; /// /// Logs for the event. diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs index bcbddc1f02d..fa8df9fb1b3 100644 --- a/src/EFCore/Properties/CoreStrings.Designer.cs +++ b/src/EFCore/Properties/CoreStrings.Designer.cs @@ -3217,9 +3217,9 @@ public static EventDefinition LogContextDisposed(IDiagnosticsLogger logg } /// - /// Entity Framework Core {version} initialized '{contextType}' using provider '{provider}' with options: {options} + /// Entity Framework Core {version} initialized '{contextType}' using provider '{provider}:{providerVersion}' with options: {options} /// - public static EventDefinition LogContextInitialized(IDiagnosticsLogger logger) + public static EventDefinition LogContextInitialized(IDiagnosticsLogger logger) { var definition = ((LoggingDefinitions)logger.Definitions).LogContextInitialized; if (definition == null) @@ -3227,18 +3227,18 @@ public static EventDefinition LogContextDisposed(IDiagnosticsLogger logg definition = NonCapturingLazyInitializer.EnsureInitialized( ref ((LoggingDefinitions)logger.Definitions).LogContextInitialized, logger, - static logger => new EventDefinition( + static logger => new EventDefinition( logger.Options, CoreEventId.ContextInitialized, LogLevel.Information, "CoreEventId.ContextInitialized", - level => LoggerMessage.Define( + level => LoggerMessage.Define( level, CoreEventId.ContextInitialized, _resourceManager.GetString("LogContextInitialized")!))); } - return (EventDefinition)definition; + return (EventDefinition)definition; } /// diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx index 8943dee6744..4a6e7123e17 100644 --- a/src/EFCore/Properties/CoreStrings.resx +++ b/src/EFCore/Properties/CoreStrings.resx @@ -755,8 +755,8 @@ Debug CoreEventId.ContextDisposed string - Entity Framework Core {version} initialized '{contextType}' using provider '{provider}' with options: {options} - Information CoreEventId.ContextInitialized string string string? string + Entity Framework Core {version} initialized '{contextType}' using provider '{provider}:{providerVersion}' with options: {options} + Information CoreEventId.ContextInitialized string string string? string? string An attempt was made to lazy-load navigation '{navigation}' on a detached entity of type '{entityType}'. Lazy-loading is not supported for detached entities or entities that are loaded with 'AsNoTracking'. diff --git a/src/EFCore/Storage/DatabaseProvider.cs b/src/EFCore/Storage/DatabaseProvider.cs index 2b72934f84d..5509f6f6095 100644 --- a/src/EFCore/Storage/DatabaseProvider.cs +++ b/src/EFCore/Storage/DatabaseProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; +using System.Reflection; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; @@ -52,6 +53,14 @@ public DatabaseProvider(DatabaseProviderDependencies dependencies) public virtual string Name => typeof(TOptionsExtension).Assembly.GetName().Name!; + /// + /// The unique name used to identify the database provider. This should be the same as the NuGet package name + /// for the providers runtime. + /// + public virtual string? Version + => typeof(TOptionsExtension).Assembly + .GetCustomAttribute()?.InformationalVersion; + /// /// Gets a value indicating whether this database provider has been selected for a given context. /// diff --git a/src/EFCore/Storage/IDatabaseProvider.cs b/src/EFCore/Storage/IDatabaseProvider.cs index 0a7f29ce550..25fb8fe3504 100644 --- a/src/EFCore/Storage/IDatabaseProvider.cs +++ b/src/EFCore/Storage/IDatabaseProvider.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Reflection; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection; @@ -29,6 +30,13 @@ public interface IDatabaseProvider /// string Name { get; } + /// + /// The value of the + /// for the database provider assembly. + /// + string? Version + => null; + /// /// Gets a value indicating whether this database provider has been configured for a given context. /// diff --git a/test/EFCore.InMemory.FunctionalTests/LoggingInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/LoggingInMemoryTest.cs index 174415aea86..ac0ace229a7 100644 --- a/test/EFCore.InMemory.FunctionalTests/LoggingInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/LoggingInMemoryTest.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Reflection; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.EntityFrameworkCore @@ -15,6 +17,10 @@ protected override DbContextOptionsBuilder CreateOptionsBuilder(IServiceCollecti protected override string ProviderName => "Microsoft.EntityFrameworkCore.InMemory"; + protected override string ProviderVersion + => typeof(InMemoryOptionsExtension).Assembly + .GetCustomAttribute()?.InformationalVersion; + protected override string DefaultOptions => "StoreName=LoggingInMemoryTest "; } diff --git a/test/EFCore.Specification.Tests/LoggingTestBase.cs b/test/EFCore.Specification.Tests/LoggingTestBase.cs index c04286b4893..11915e94154 100644 --- a/test/EFCore.Specification.Tests/LoggingTestBase.cs +++ b/test/EFCore.Specification.Tests/LoggingTestBase.cs @@ -43,12 +43,15 @@ protected virtual string ExpectedMessage(string optionsFragment) ProductInfo.GetVersion(), nameof(LoggingContext), ProviderName, + ProviderVersion, optionsFragment ?? "None").Trim(); protected abstract DbContextOptionsBuilder CreateOptionsBuilder(IServiceCollection services); protected abstract string ProviderName { get; } + protected abstract string ProviderVersion { get; } + protected virtual string DefaultOptions => null; diff --git a/test/EFCore.SqlServer.FunctionalTests/LoggingSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/LoggingSqlServerTest.cs index 75d976b18ba..84d2c8973a9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/LoggingSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/LoggingSqlServerTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Reflection; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; using Microsoft.Extensions.DependencyInjection; @@ -20,5 +21,9 @@ protected override DbContextOptionsBuilder CreateOptionsBuilder( protected override string ProviderName => "Microsoft.EntityFrameworkCore.SqlServer"; + + protected override string ProviderVersion + => typeof(SqlServerOptionsExtension).Assembly + .GetCustomAttribute()?.InformationalVersion; } } diff --git a/test/EFCore.Sqlite.FunctionalTests/LoggingSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/LoggingSqliteTest.cs index c788c604997..d975d248985 100644 --- a/test/EFCore.Sqlite.FunctionalTests/LoggingSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/LoggingSqliteTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Reflection; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; using Microsoft.Extensions.DependencyInjection; @@ -20,5 +21,9 @@ protected override DbContextOptionsBuilder CreateOptionsBuilder( protected override string ProviderName => "Microsoft.EntityFrameworkCore.Sqlite"; + + protected override string ProviderVersion + => typeof(SqliteOptionsExtension).Assembly + .GetCustomAttribute()?.InformationalVersion; } } diff --git a/test/EFCore.Tests/DbContextLoggerTests.cs b/test/EFCore.Tests/DbContextLoggerTests.cs index be903ad0408..79db722afa7 100644 --- a/test/EFCore.Tests/DbContextLoggerTests.cs +++ b/test/EFCore.Tests/DbContextLoggerTests.cs @@ -14,20 +14,20 @@ namespace Microsoft.EntityFrameworkCore public class DbContextLoggerTests { private const string ContextInitialized = - @"info: HH:mm:ss.fff CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure) - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "; + @"info: HH:mm:ss.fff CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure) " + + @" Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "; private const string SaveChangesStarting = - @"dbug: HH:mm:ss.fff CoreEventId.SaveChangesStarting[10004] (Microsoft.EntityFrameworkCore.Update) - SaveChanges starting for 'LoggingContext'."; + @"dbug: HH:mm:ss.fff CoreEventId.SaveChangesStarting[10004] (Microsoft.EntityFrameworkCore.Update) " + + @" SaveChanges starting for 'LoggingContext'."; private const string SaveChangesCompleted = - @"dbug: HH:mm:ss.fff CoreEventId.SaveChangesCompleted[10005] (Microsoft.EntityFrameworkCore.Update) - SaveChanges completed for 'LoggingContext' with 0 entities written to the database."; + @"dbug: HH:mm:ss.fff CoreEventId.SaveChangesCompleted[10005] (Microsoft.EntityFrameworkCore.Update) " + + @" SaveChanges completed for 'LoggingContext' with 0 entities written to the database."; private const string ContextDisposed = - @"dbug: HH:mm:ss.fff CoreEventId.ContextDisposed[10407] (Microsoft.EntityFrameworkCore.Infrastructure) - 'LoggingContext' disposed."; + @"dbug: HH:mm:ss.fff CoreEventId.ContextDisposed[10407] (Microsoft.EntityFrameworkCore.Infrastructure) " + + @" 'LoggingContext' disposed."; [ConditionalTheory] [InlineData(true)] @@ -234,7 +234,7 @@ public async Task Log_with_raw_message(bool async) AssertLog( actual, - @"Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -248,7 +248,7 @@ public async Task Log_raw_single_line(bool async) AssertLog( actual, - @"Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -265,7 +265,7 @@ public async Task Log_default_single_line(bool async) AssertLog( actual, - @"info: HH:mm:ss.fff CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure) -> Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"info: HH:mm:ss.fff CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure) -> Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -279,8 +279,8 @@ public async Task Log_only_level(bool async) AssertLog( actual, - @"info: - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"info: + Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -294,8 +294,8 @@ public async Task Log_only_local_time(bool async) AssertLog( actual, - @" HH:mm:ss.fff - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @" HH:mm:ss.fff + Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -309,8 +309,8 @@ public async Task Log_only_UTC_time(bool async) AssertLog( actual, - @"YYYY-MM-DDTHH:MM:SS.MMMMMMTZ - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"YYYY-MM-DDTHH:MM:SS.MMMMMMTZ + Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -324,8 +324,8 @@ public async Task Log_only_ID(bool async) AssertLog( actual, - @"CoreEventId.ContextInitialized[10403] - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"CoreEventId.ContextInitialized[10403] + Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -339,8 +339,8 @@ public async Task Log_only_category(bool async) AssertLog( actual, - @"(Microsoft.EntityFrameworkCore.Infrastructure) - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"(Microsoft.EntityFrameworkCore.Infrastructure) " + + @" Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -355,8 +355,8 @@ public async Task Log_level_and_ID(bool async) AssertLog( actual, - @"info: CoreEventId.ContextInitialized[10403] - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"info: CoreEventId.ContextInitialized[10403] " + + @" Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -374,8 +374,8 @@ public async Task Log_level_and_UTC(bool async) AssertLog( actual, - @"info: YYYY-MM-DDTHH:MM:SS.MMMMMMTZ - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"info: YYYY-MM-DDTHH:MM:SS.MMMMMMTZ " + + @" Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } [ConditionalTheory] @@ -390,14 +390,14 @@ public async Task Log_default_UTC(bool async) AssertLog( actual, - @"info: YYYY-MM-DDTHH:MM:SS.MMMMMMTZ CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure) - Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory' with options: StoreName=DbContextLoggerTests "); + @"info: YYYY-MM-DDTHH:MM:SS.MMMMMMTZ CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure) " + + @" Entity Framework Core X.X.X-any initialized 'LoggingContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:X.X.X-any' with options: StoreName=DbContextLoggerTests "); } private static void AssertLog(string actual, params string[] lines) => Assert.Equal( - string.Join(Environment.NewLine, lines) + Environment.NewLine, - actual, + string.Concat(lines).ReplaceLineEndings(""), + actual.ReplaceLineEndings(""), ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true);