Skip to content

Commit

Permalink
Replace DbContext.DesignTimeModel with IDesignTimeModel service
Browse files Browse the repository at this point in the history
  • Loading branch information
AndriySvyryd committed Mar 26, 2021
1 parent 4456798 commit 3be1be4
Show file tree
Hide file tree
Showing 15 changed files with 102 additions and 37 deletions.
13 changes: 6 additions & 7 deletions src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Update;
Expand All @@ -21,7 +20,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal
public class CosmosDatabaseCreator : IDatabaseCreator
{
private readonly ICosmosClientWrapper _cosmosClient;
private readonly IModel _designModel;
private readonly IDesignTimeModel _designTimeModel;
private readonly IUpdateAdapterFactory _updateAdapterFactory;
private readonly IDatabase _database;

Expand All @@ -33,12 +32,12 @@ public class CosmosDatabaseCreator : IDatabaseCreator
/// </summary>
public CosmosDatabaseCreator(
ICosmosClientWrapper cosmosClient,
ICurrentDbContext context,
IDesignTimeModel designTimeModel,
IUpdateAdapterFactory updateAdapterFactory,
IDatabase database)
{
_cosmosClient = cosmosClient;
_designModel = context.Context.DesignTimeModel;
_designTimeModel = designTimeModel;
_updateAdapterFactory = updateAdapterFactory;
_database = database;
}
Expand All @@ -52,7 +51,7 @@ public CosmosDatabaseCreator(
public virtual bool EnsureCreated()
{
var created = _cosmosClient.CreateDatabaseIfNotExists();
foreach (var entityType in _designModel.GetEntityTypes())
foreach (var entityType in _designTimeModel.Model.GetEntityTypes())
{
var containerName = entityType.GetContainer();
if (containerName != null)
Expand Down Expand Up @@ -81,7 +80,7 @@ public virtual async Task<bool> EnsureCreatedAsync(CancellationToken cancellatio
{
var created = await _cosmosClient.CreateDatabaseIfNotExistsAsync(cancellationToken)
.ConfigureAwait(false);
foreach (var entityType in _designModel.GetEntityTypes())
foreach (var entityType in _designTimeModel.Model.GetEntityTypes())
{
var containerName = entityType.GetContainer();
if (containerName != null)
Expand Down Expand Up @@ -131,7 +130,7 @@ public virtual Task SeedAsync(CancellationToken cancellationToken = default)
private IUpdateAdapter AddSeedData()
{
var updateAdapter = _updateAdapterFactory.CreateStandalone();
foreach (var entityType in _designModel.GetEntityTypes())
foreach (var entityType in _designTimeModel.Model.GetEntityTypes())
{
IEntityType? targetEntityType = null;
foreach (var targetSeed in entityType.GetSeedData())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public static IServiceCollection AddDbContextDesignTimeServices(
.AddTransient(_ => context.GetService<IMigrationsModelDiffer>())
.AddTransient(_ => context.GetService<IMigrator>())
.AddTransient(_ => context.GetService<IRelationalTypeMappingSource>())
.AddTransient(_ => context.DesignTimeModel)
.AddTransient(_ => context.GetService<IDesignTimeModel>().Model)
.AddTransient(_ => context.GetService<IModelRuntimeInitializer>());
}
}
8 changes: 4 additions & 4 deletions src/EFCore.InMemory/Storage/Internal/InMemoryDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class InMemoryDatabase : Database, IInMemoryDatabase
private readonly IInMemoryStore _store;
private readonly IUpdateAdapterFactory _updateAdapterFactory;
private readonly IDiagnosticsLogger<DbLoggerCategory.Update> _updateLogger;
private readonly Func<IModel> _getDesignModel;
private readonly IDesignTimeModel _designTimeModel;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -46,7 +46,7 @@ public InMemoryDatabase(
DatabaseDependencies dependencies,
IInMemoryStoreCache storeCache,
IDbContextOptions options,
ICurrentDbContext context,
IDesignTimeModel designTimeModel,
IUpdateAdapterFactory updateAdapterFactory,
IDiagnosticsLogger<DbLoggerCategory.Update> updateLogger)
: base(dependencies)
Expand All @@ -57,7 +57,7 @@ public InMemoryDatabase(
Check.NotNull(updateLogger, nameof(updateLogger));

_store = storeCache.GetStore(options);
_getDesignModel = () => context.Context.DesignTimeModel;
_designTimeModel = designTimeModel;
_updateAdapterFactory = updateAdapterFactory;
_updateLogger = updateLogger;
}
Expand Down Expand Up @@ -98,6 +98,6 @@ public override Task<int> SaveChangesAsync(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual bool EnsureDatabaseCreated()
=> _store.EnsureCreated(_updateAdapterFactory, _getDesignModel(), _updateLogger);
=> _store.EnsureCreated(_updateAdapterFactory, _designTimeModel.Model, _updateLogger);
}
}
11 changes: 8 additions & 3 deletions src/EFCore.Relational/Storage/RelationalDatabaseCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -143,10 +145,13 @@ public virtual Task CreateTablesAsync(CancellationToken cancellationToken = defa
/// <returns> The generated commands. </returns>
protected virtual IReadOnlyList<MigrationCommand> GetCreateTablesCommands(
MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default)
=> Dependencies.MigrationsSqlGenerator.Generate(
Dependencies.ModelDiffer.GetDifferences(null, Dependencies.CurrentContext.Context.DesignTimeModel.GetRelationalModel()),
Dependencies.CurrentContext.Context.DesignTimeModel,
{
var model = Dependencies.CurrentContext.Context.GetService<IDesignTimeModel>().Model;
return Dependencies.MigrationsSqlGenerator.Generate(
Dependencies.ModelDiffer.GetDifferences(null, model.GetRelationalModel()),
model,
options);
}

/// <summary>
/// Determines whether the database contains any tables. No attempt is made to determine if
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
Expand Down Expand Up @@ -178,7 +179,7 @@ private IReadOnlyList<MigrationCommand> CreateCreateOperations()
{
Name = builder.InitialCatalog,
FileName = builder.AttachDBFilename,
Collation = Dependencies.CurrentContext.Context.DesignTimeModel.GetCollation()
Collation = Dependencies.CurrentContext.Context.GetService<IDesignTimeModel>().Model.GetCollation()
}
},
null);
Expand Down
10 changes: 0 additions & 10 deletions src/EFCore/DbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,6 @@ public virtual IModel Model
get => ContextServices.Model;
}

/// <summary>
/// The metadata about the shape of entities, the relationships between them, and how they map to the database.
/// Also includes all the information necessary to initialize the database.
/// </summary>
public virtual IModel DesignTimeModel
{
[DebuggerStepThrough]
get => ContextServices.DesignTimeModel;
}

/// <summary>
/// <para>
/// A unique identifier for the context instance and pool lease, if any.
Expand Down
2 changes: 2 additions & 0 deletions src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public static readonly IDictionary<Type, ServiceCharacteristics> CoreServices
{ typeof(IQueryCompiler), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ICompiledQueryCacheKeyGenerator), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IModel), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDesignTimeModel), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IUpdateAdapterFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ICurrentDbContext), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDbContextDependencies), new ServiceCharacteristics(ServiceLifetime.Scoped) },
Expand Down Expand Up @@ -253,6 +254,7 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<ISingletonOptions, ILoggingOptions>(p => p.GetRequiredService<ILoggingOptions>());
TryAdd<ISingletonOptions, ICoreSingletonOptions>(p => p.GetRequiredService<ICoreSingletonOptions>());
TryAdd(p => GetContextServices(p).Model);
TryAdd<IDesignTimeModel>(p => new DesignTimeModel(GetContextServices(p)));
TryAdd(p => GetContextServices(p).CurrentContext);
TryAdd(p => GetContextServices(p).ContextOptions);
TryAdd<IResettableService, IStateManager>(p => p.GetRequiredService<IStateManager>());
Expand Down
28 changes: 28 additions & 0 deletions src/EFCore/Metadata/IDesignTimeModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Metadata
{
/// <summary>
/// <para>
/// The metadata about the shape of entities, the relationships between them, and how they map to the database.
/// Also includes all the information necessary to initialize the database.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Scoped" />. This means that each
/// <see cref="DbContext" /> instance will use its own instance of this service.
/// The implementation may depend on other services registered with any lifetime.
/// The implementation does not need to be thread-safe.
/// </para>
/// </summary>
public interface IDesignTimeModel
{
/// <summary>
/// Gets the metadata about the shape of entities, the relationships between them, and how they map to the database.
/// Also includes all the information necessary to initialize the database.
/// </summary>
public IModel Model { get; }
}
}
37 changes: 37 additions & 0 deletions src/EFCore/Metadata/Internal/DesignTimeModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 Microsoft.EntityFrameworkCore.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Internal
{
/// <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>
public class DesignTimeModel : IDesignTimeModel
{
private readonly IDbContextServices _contextServices;

/// <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>
public DesignTimeModel(IDbContextServices contextServices)
{
_contextServices = contextServices;
}

/// <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>
public virtual IModel Model => _contextServices.DesignTimeModel;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ public void Can_diff_against_older_ownership_model(Type snapshotType)
var processor = new SnapshotModelProcessor(reporter, modelRuntimeInitializer);
var model = processor.Process(snapshot.Model);

var differences = differ.GetDifferences(model.GetRelationalModel(), context.DesignTimeModel.GetRelationalModel());
var differences = differ.GetDifferences(model.GetRelationalModel(),
context.GetService<IDesignTimeModel>().Model.GetRelationalModel());

Assert.Empty(differences);
}
Expand Down
4 changes: 3 additions & 1 deletion test/EFCore.Proxies.Tests/ChangeDetectionProxyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
Expand Down Expand Up @@ -89,7 +90,8 @@ public void Sets_default_change_tracking_strategy()
{
using var context = new ChangeContext<ChangeValueEntity>();

Assert.Equal(ChangeTrackingStrategy.ChangingAndChangedNotifications, context.DesignTimeModel.GetChangeTrackingStrategy());
Assert.Equal(ChangeTrackingStrategy.ChangingAndChangedNotifications,
context.GetService<IDesignTimeModel>().Model.GetChangeTrackingStrategy());
}

[ConditionalFact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ protected virtual void DiffSnapshot(ModelSnapshot snapshot, DbContext context)
var modelDiffer = context.GetService<IMigrationsModelDiffer>();
var operations = modelDiffer.GetDifferences(
sourceModel.GetRelationalModel(),
context.DesignTimeModel.GetRelationalModel());
context.GetService<IDesignTimeModel>().Model.GetRelationalModel());

Assert.Equal(0, operations.Count);
}
Expand Down
3 changes: 2 additions & 1 deletion test/EFCore.Specification.Tests/FieldMappingTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Threading;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
Expand Down Expand Up @@ -2182,7 +2183,7 @@ protected override void Seed(PoolableDbContext context)
context.Add(CreateBlogAndPosts<BlogFullExplicit, PostFullExplicit>(new List<PostFullExplicit>()));
context.AddRange(CreatePostsAndBlog<BlogFullExplicit, PostFullExplicit>());

if (context.DesignTimeModel.GetPropertyAccessMode() != PropertyAccessMode.Property)
if (context.GetService<IDesignTimeModel>().Model.GetPropertyAccessMode() != PropertyAccessMode.Property)
{
context.Add(CreateBlogAndPosts<BlogReadOnly, PostReadOnly>(new ObservableCollection<PostReadOnly>()));
context.AddRange(CreatePostsAndBlog<BlogReadOnly, PostReadOnly>());
Expand Down
5 changes: 2 additions & 3 deletions test/EFCore.Tests/DbContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ public async Task It_throws_object_disposed_exception(bool async)
(await Assert.ThrowsAsync<ObjectDisposedException>(() => context.FindAsync(typeof(Random), 77).AsTask())).Message);

var methodCount = typeof(DbContext).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Count();
var expectedMethodCount = 51;
var expectedMethodCount = 50;
Assert.True(
methodCount == expectedMethodCount,
userMessage: $"Expected {expectedMethodCount} methods on DbContext but found {methodCount}. "
Expand All @@ -780,14 +780,13 @@ public async Task It_throws_object_disposed_exception(bool async)

Assert.StartsWith(
CoreStrings.ContextDisposed,
Assert.Throws<ObjectDisposedException>(() => context.DesignTimeModel).Message);
Assert.Throws<ObjectDisposedException>(() => context.GetService<IDesignTimeModel>().Model).Message);

var expectedProperties = new List<string>
{
nameof(DbContext.ChangeTracker),
nameof(DbContext.ContextId), // By-design, does not throw for disposed context
nameof(DbContext.Database),
nameof(DbContext.DesignTimeModel),
nameof(DbContext.Model)
};

Expand Down
8 changes: 4 additions & 4 deletions test/EFCore.Tests/ModelSourceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,15 @@ public void Model_from_options_is_preserved()

var context = new ModelContext(model);

Assert.NotSame(context.Model, context.DesignTimeModel);
Assert.NotSame(context.Model, context.GetService<IDesignTimeModel>().Model);
Assert.Same(model, context.Model);
Assert.NotSame(model, context.DesignTimeModel);
Assert.NotSame(model, context.GetService<IDesignTimeModel>().Model);

var designTimeContext = new ModelContext(designTimeModel);

Assert.NotSame(context.Model, designTimeContext.DesignTimeModel);
Assert.NotSame(context.Model, designTimeContext.GetService<IDesignTimeModel>().Model);
Assert.NotSame(model, designTimeContext.Model);
Assert.Same(designTimeModel, designTimeContext.DesignTimeModel);
Assert.Same(designTimeModel, designTimeContext.GetService<IDesignTimeModel>().Model);
}

private class ModelContext : DbContext
Expand Down

0 comments on commit 3be1be4

Please sign in to comment.