From 014932911af9295d3056912641023718ae4d56ea Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Mon, 22 May 2017 10:43:07 -0700 Subject: [PATCH] Add sugar for determining the database provider in use Part of #7166 Main scenario here is conditional behavior in OnModelCreating now that we build a different model per provider. --- .../InMemoryDatabaseFacadeExtensions.cs | 36 ++++ .../InMemoryDbContextOptionsExtensions.cs | 1 - .../SqlServerDatabaseFacadeExtensions.cs | 35 ++++ .../SqlServerDbContextOptionsExtensions.cs | 1 - .../SqliteDatabaseFacadeExtensions.cs | 35 ++++ ...SqliteDbContextOptionsBuilderExtensions.cs | 1 - src/EFCore/Infrastructure/DatabaseFacade.cs | 21 ++ .../InMemoryDatabaseFacadeTest.cs | 25 +++ .../SqlServerDatabaseFacadeTest.cs | 183 ++++++++++++++++++ .../SqliteDatabaseFacadeTest.cs | 38 ++++ 10 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 src/EFCore.InMemory/Extensions/InMemoryDatabaseFacadeExtensions.cs create mode 100644 src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs create mode 100644 src/EFCore.Sqlite.Core/SqliteDatabaseFacadeExtensions.cs create mode 100644 test/EFCore.InMemory.Tests/InMemoryDatabaseFacadeTest.cs create mode 100644 test/EFCore.SqlServer.Tests/SqlServerDatabaseFacadeTest.cs create mode 100644 test/EFCore.Sqlite.Tests/SqliteDatabaseFacadeTest.cs diff --git a/src/EFCore.InMemory/Extensions/InMemoryDatabaseFacadeExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryDatabaseFacadeExtensions.cs new file mode 100644 index 00000000000..996980e8ffe --- /dev/null +++ b/src/EFCore.InMemory/Extensions/InMemoryDatabaseFacadeExtensions.cs @@ -0,0 +1,36 @@ +// 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 System; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Infrastructure.Internal; + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore +{ + /// + /// In-memory specific extension methods for . + /// + public static class InMemoryDatabaseFacadeExtensions + { + /// + /// + /// Returns true if the database provider currently in use is the in-memory provider. + /// + /// + /// This method can only be used after the has been configured because + /// it is only then that the provider is known. This means that this method cannot be used + /// in because this is where application code sets the + /// provider to use as part of configuring the context. + /// + /// + /// The facade from . + /// True if the in-memory database is being used; false otherwise. + public static bool IsInMemory([NotNull] this DatabaseFacade database) + => database.ProviderName.Equals( + typeof(InMemoryOptionsExtension).GetTypeInfo().Assembly.GetName().Name, + StringComparison.Ordinal); + } +} diff --git a/src/EFCore.InMemory/Extensions/InMemoryDbContextOptionsExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryDbContextOptionsExtensions.cs index c808cfd03a7..c5df248593f 100644 --- a/src/EFCore.InMemory/Extensions/InMemoryDbContextOptionsExtensions.cs +++ b/src/EFCore.InMemory/Extensions/InMemoryDbContextOptionsExtensions.cs @@ -5,7 +5,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Utilities; // ReSharper disable once CheckNamespace diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs new file mode 100644 index 00000000000..0108751b905 --- /dev/null +++ b/src/EFCore.SqlServer/Extensions/SqlServerDatabaseFacadeExtensions.cs @@ -0,0 +1,35 @@ +// 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 System; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Infrastructure.Internal; + +namespace Microsoft.EntityFrameworkCore +{ + /// + /// SQL Server specific extension methods for . + /// + public static class SqlServerDatabaseFacadeExtensions + { + /// + /// + /// Returns true if the database provider currently in use is the SQL Server provider. + /// + /// + /// This method can only be used after the has been configured because + /// it is only then that the provider is known. This means that this method cannot be used + /// in because this is where application code sets the + /// provider to use as part of configuring the context. + /// + /// + /// The facade from . + /// True if SQL Server is being used; false otherwise. + public static bool IsSqlServer([NotNull] this DatabaseFacade database) + => database.ProviderName.Equals( + typeof(SqlServerOptionsExtension).GetTypeInfo().Assembly.GetName().Name, + StringComparison.Ordinal); + } +} diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsExtensions.cs index 0990a996458..13fcb05c0f1 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsExtensions.cs @@ -6,7 +6,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Utilities; // ReSharper disable once CheckNamespace diff --git a/src/EFCore.Sqlite.Core/SqliteDatabaseFacadeExtensions.cs b/src/EFCore.Sqlite.Core/SqliteDatabaseFacadeExtensions.cs new file mode 100644 index 00000000000..a1db3177414 --- /dev/null +++ b/src/EFCore.Sqlite.Core/SqliteDatabaseFacadeExtensions.cs @@ -0,0 +1,35 @@ +// 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 System; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Infrastructure.Internal; + +namespace Microsoft.EntityFrameworkCore +{ + /// + /// SQLite specific extension methods for . + /// + public static class SqliteDatabaseFacadeExtensions + { + /// + /// + /// Returns true if the database provider currently in use is the SQLite provider. + /// + /// + /// This method can only be used after the has been configured because + /// it is only then that the provider is known. This means that this method cannot be used + /// in because this is where application code sets the + /// provider to use as part of configuring the context. + /// + /// + /// The facade from . + /// True if SQLite is being used; false otherwise. + public static bool IsSqlite([NotNull] this DatabaseFacade database) + => database.ProviderName.Equals( + typeof(SqliteOptionsExtension).GetTypeInfo().Assembly.GetName().Name, + StringComparison.Ordinal); + } +} diff --git a/src/EFCore.Sqlite.Core/SqliteDbContextOptionsBuilderExtensions.cs b/src/EFCore.Sqlite.Core/SqliteDbContextOptionsBuilderExtensions.cs index 8b61f487c28..49fe9b36802 100644 --- a/src/EFCore.Sqlite.Core/SqliteDbContextOptionsBuilderExtensions.cs +++ b/src/EFCore.Sqlite.Core/SqliteDbContextOptionsBuilderExtensions.cs @@ -6,7 +6,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore diff --git a/src/EFCore/Infrastructure/DatabaseFacade.cs b/src/EFCore/Infrastructure/DatabaseFacade.cs index 2af85fe7fb4..b251caa330b 100644 --- a/src/EFCore/Infrastructure/DatabaseFacade.cs +++ b/src/EFCore/Infrastructure/DatabaseFacade.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -182,6 +184,25 @@ public virtual IDbContextTransaction CurrentTransaction /// public virtual bool AutoTransactionsEnabled { get; set; } = true; + /// + /// + /// Returns the invariant name of the database provider currently in use. + /// The invariant name is typically the name of the provider assembly. + /// It is usually easier to use a sugar method auch as 'IsSqlServer()' instead of + /// calling this method directly. + /// + /// + /// This method can only be used after the has been configured because + /// it is only then that the provider is known. This means that this method cannot be used + /// in because this is where application code sets the + /// provider to use as part of configuring the context. + /// + /// + public virtual string ProviderName + => _context.GetService>() + ?.Select(p => p.InvariantName) + .FirstOrDefault(); + /// /// /// Gets the scoped being used to resolve services. diff --git a/test/EFCore.InMemory.Tests/InMemoryDatabaseFacadeTest.cs b/test/EFCore.InMemory.Tests/InMemoryDatabaseFacadeTest.cs new file mode 100644 index 00000000000..1f94380faae --- /dev/null +++ b/test/EFCore.InMemory.Tests/InMemoryDatabaseFacadeTest.cs @@ -0,0 +1,25 @@ +// 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 Xunit; + +namespace Microsoft.EntityFrameworkCore.InMemory.Tests +{ + public class InMemoryDatabaseFacadeTest + { + [Fact] + public void IsInMemory_when_using_in_memory() + { + using (var context = new ProviderContext()) + { + Assert.True(InMemoryDatabaseFacadeExtensions.IsInMemory(context.Database)); + } + } + + private class ProviderContext : DbContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseInMemoryDatabase("Maltesers"); + } + } +} diff --git a/test/EFCore.SqlServer.Tests/SqlServerDatabaseFacadeTest.cs b/test/EFCore.SqlServer.Tests/SqlServerDatabaseFacadeTest.cs new file mode 100644 index 00000000000..aaaba40a45e --- /dev/null +++ b/test/EFCore.SqlServer.Tests/SqlServerDatabaseFacadeTest.cs @@ -0,0 +1,183 @@ +// 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 System; +using Microsoft.EntityFrameworkCore.Internal; +using Xunit; + +namespace Microsoft.EntityFrameworkCore.SqlServer.Tests +{ + public class SqlServerDatabaseFacadeTest + { + [Fact] + public void IsSqlServer_when_using_OnConfguring() + { + using (var context = new SqlServerOnConfiguringContext()) + { + Assert.True(context.Database.IsSqlServer()); + } + } + + [Fact] + public void IsSqlServer_in_OnModelCreating_when_using_OnConfguring() + { + using (new SqlServerOnModelContext()) + { + } + } + + [Fact] + public void IsSqlServer_in_constructor_when_using_OnConfguring() + { + using (new SqlServerConstructorContext()) + { + } + } + + [Fact] + public void Cannot_use_IsSqlServer_in_OnConfguring() + { + using (var context = new SqlServerUseInOnConfiguringContext()) + { + Assert.Equal( + CoreStrings.RecursiveOnConfiguring, + Assert.Throws( + () => + { + var _ = context.Model; // Trigger context initialization + }).Message); + } + } + + [Fact] + public void IsSqlServer_when_using_constructor() + { + using (var context = new ProviderContext( + new DbContextOptionsBuilder().UseSqlServer("Database=Maltesers").Options)) + { + Assert.True(context.Database.IsSqlServer()); + } + } + + [Fact] + public void IsSqlServer_in_OnModelCreating_when_using_constructor() + { + using (var context = new ProviderOnModelContext( + new DbContextOptionsBuilder().UseSqlServer("Database=Maltesers").Options)) + { + var _ = context.Model; // Trigger context initialization + Assert.True(context.IsSqlServerSet); + } + } + + [Fact] + public void IsSqlServer_in_constructor_when_using_constructor() + { + using (var context = new ProviderConstructorContext( + new DbContextOptionsBuilder().UseSqlServer("Database=Maltesers").Options)) + { + var _ = context.Model; // Trigger context initialization + Assert.True(context.IsSqlServerSet); + } + } + + [Fact] + public void Cannot_use_IsSqlServer_in_OnConfguring_with_constructor() + { + using (var context = new ProviderUseInOnConfiguringContext( + new DbContextOptionsBuilder().UseSqlServer("Database=Maltesers").Options)) + { + Assert.Equal( + CoreStrings.RecursiveOnConfiguring, + Assert.Throws( + () => + { + var _ = context.Model; // Trigger context initialization + }).Message); + } + } + + [Fact] + public void Not_IsSqlServer_when_using_different_provider() + { + using (var context = new ProviderContext( + new DbContextOptionsBuilder().UseInMemoryDatabase("Maltesers").Options)) + { + Assert.False(context.Database.IsSqlServer()); + } + } + + private class ProviderContext : DbContext + { + protected ProviderContext() + { + } + + public ProviderContext(DbContextOptions options) + : base(options) + { + } + } + + private class SqlServerOnConfiguringContext : ProviderContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseSqlServer("Database=Maltesers"); + } + + private class SqlServerOnModelContext : SqlServerOnConfiguringContext + { + protected override void OnModelCreating(ModelBuilder modelBuilder) + => Assert.True(Database.IsSqlServer()); + } + + private class SqlServerConstructorContext : SqlServerOnConfiguringContext + { + public SqlServerConstructorContext() + => Assert.True(Database.IsSqlServer()); + } + + private class SqlServerUseInOnConfiguringContext : SqlServerOnConfiguringContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + + Database.IsSqlServer(); + } + } + + private class ProviderOnModelContext : ProviderContext + { + public ProviderOnModelContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => IsSqlServerSet = Database.IsSqlServer(); + + public bool? IsSqlServerSet { get; private set; } + } + + private class ProviderConstructorContext : ProviderContext + { + public ProviderConstructorContext(DbContextOptions options) + : base(options) + => IsSqlServerSet = Database.IsSqlServer(); + + public bool? IsSqlServerSet { get; } + } + + private class ProviderUseInOnConfiguringContext : ProviderContext + { + public ProviderUseInOnConfiguringContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => Database.IsSqlServer(); + } + } +} diff --git a/test/EFCore.Sqlite.Tests/SqliteDatabaseFacadeTest.cs b/test/EFCore.Sqlite.Tests/SqliteDatabaseFacadeTest.cs new file mode 100644 index 00000000000..08b3de1991f --- /dev/null +++ b/test/EFCore.Sqlite.Tests/SqliteDatabaseFacadeTest.cs @@ -0,0 +1,38 @@ +// 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 Xunit; + +namespace Microsoft.EntityFrameworkCore.Sqlite.Tests +{ + public class SqliteDatabaseFacadeTest + { + [Fact] + public void IsSqlite_when_using_SQLite() + { + using (var context = new ProviderContext( + new DbContextOptionsBuilder().UseSqlite("Database=Maltesers").Options)) + { + Assert.True(SqliteDatabaseFacadeExtensions.IsSqlite(context.Database)); + } + } + + [Fact] + public void Not_IsSqlite_when_using_different_provider() + { + using (var context = new ProviderContext( + new DbContextOptionsBuilder().UseInMemoryDatabase("Maltesers").Options)) + { + Assert.False(SqliteDatabaseFacadeExtensions.IsSqlite(context.Database)); + } + } + + private class ProviderContext : DbContext + { + public ProviderContext(DbContextOptions options) + : base(options) + { + } + } + } +}