|
|
|
|
@ -1,35 +1,35 @@
|
|
|
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using EntityFrameworkCore.Jet.Data;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using EntityFrameworkCore.Jet.Data;
|
|
|
|
|
using EntityFrameworkCore.Jet.FunctionalTests.Query;
|
|
|
|
|
using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Internal;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Query;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.TestUtilities;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Xunit;
|
|
|
|
|
using Xunit.Abstractions;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
|
|
|
|
|
// ReSharper disable MethodHasAsyncOverload
|
|
|
|
|
|
|
|
|
|
// ReSharper disable InconsistentNaming
|
|
|
|
|
// ReSharper disable UnusedAutoPropertyAccessor.Local
|
|
|
|
|
// ReSharper disable UnusedMember.Local
|
|
|
|
|
// ReSharper disable ClassNeverInstantiated.Local
|
|
|
|
|
// ReSharper disable VirtualMemberCallInConstructor
|
|
|
|
|
namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
{
|
|
|
|
|
namespace EntityFrameworkCore.Jet.FunctionalTests;
|
|
|
|
|
|
|
|
|
|
public class DbContextPoolingTest : IClassFixture<NorthwindQueryJetFixture<NoopModelCustomizer>>
|
|
|
|
|
{
|
|
|
|
|
private static DbContextOptionsBuilder<TContext> ConfigureOptions<TContext>(DbContextOptionsBuilder<TContext> optionsBuilder)
|
|
|
|
|
@ -43,19 +43,51 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
.UseJet(JetNorthwindTestStoreFactory.NorthwindConnectionString)
|
|
|
|
|
.EnableServiceProviderCaching(false);
|
|
|
|
|
|
|
|
|
|
private static IServiceProvider BuildServiceProvider<TContextService, TContext>()
|
|
|
|
|
private static IServiceProvider BuildServiceProvider<TContextService, TContext>(Action<DbContextOptionsBuilder> optionsAction = null)
|
|
|
|
|
where TContextService : class
|
|
|
|
|
where TContext : DbContext, TContextService
|
|
|
|
|
=> new ServiceCollection()
|
|
|
|
|
.AddDbContextPool<TContextService, TContext>(ob => ConfigureOptions(ob))
|
|
|
|
|
.AddDbContextPool<ISecondContext, SecondContext>(ob => ConfigureOptions(ob))
|
|
|
|
|
.AddDbContextPool<TContextService, TContext>(
|
|
|
|
|
ob =>
|
|
|
|
|
{
|
|
|
|
|
var builder = ConfigureOptions(ob);
|
|
|
|
|
if (optionsAction != null)
|
|
|
|
|
{
|
|
|
|
|
optionsAction(builder);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.AddDbContextPool<ISecondContext, SecondContext>(
|
|
|
|
|
ob =>
|
|
|
|
|
{
|
|
|
|
|
var builder = ConfigureOptions(ob);
|
|
|
|
|
if (optionsAction != null)
|
|
|
|
|
{
|
|
|
|
|
optionsAction(builder);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.BuildServiceProvider(validateScopes: true);
|
|
|
|
|
|
|
|
|
|
private static IServiceProvider BuildServiceProvider<TContext>()
|
|
|
|
|
private static IServiceProvider BuildServiceProvider<TContext>(Action<DbContextOptionsBuilder> optionsAction = null)
|
|
|
|
|
where TContext : DbContext
|
|
|
|
|
=> new ServiceCollection()
|
|
|
|
|
.AddDbContextPool<TContext>(ob => ConfigureOptions(ob))
|
|
|
|
|
.AddDbContextPool<SecondContext>(ob => ConfigureOptions(ob))
|
|
|
|
|
.AddDbContextPool<TContext>(
|
|
|
|
|
ob =>
|
|
|
|
|
{
|
|
|
|
|
var builder = ConfigureOptions(ob);
|
|
|
|
|
if (optionsAction != null)
|
|
|
|
|
{
|
|
|
|
|
optionsAction(builder);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.AddDbContextPool<SecondContext>(
|
|
|
|
|
ob =>
|
|
|
|
|
{
|
|
|
|
|
var builder = ConfigureOptions(ob);
|
|
|
|
|
if (optionsAction != null)
|
|
|
|
|
{
|
|
|
|
|
optionsAction(builder);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.BuildServiceProvider(validateScopes: true);
|
|
|
|
|
|
|
|
|
|
private static IServiceProvider BuildServiceProviderWithFactory<TContext>()
|
|
|
|
|
@ -153,7 +185,10 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
public DbSet<Customer> Customers { get; set; }
|
|
|
|
|
|
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
|
|
|
=> modelBuilder.Entity<Customer>().ToTable("Customers");
|
|
|
|
|
{
|
|
|
|
|
modelBuilder.Entity<Customer>().ToTable("Customers");
|
|
|
|
|
modelBuilder.Entity<Order>().ToTable("Orders");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Dispose()
|
|
|
|
|
{
|
|
|
|
|
@ -173,19 +208,26 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
public DbSet<Customer> Customers { get; set; }
|
|
|
|
|
|
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
|
|
|
=> modelBuilder.Entity<Customer>().ToTable("Customers");
|
|
|
|
|
{
|
|
|
|
|
modelBuilder.Entity<Customer>().ToTable("Customers");
|
|
|
|
|
modelBuilder.Entity<Order>().ToTable("Orders");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class Customer
|
|
|
|
|
{
|
|
|
|
|
public string CustomerId { get; set; }
|
|
|
|
|
public string CompanyName { get; set; }
|
|
|
|
|
public ILazyLoader LazyLoader { get; set; }
|
|
|
|
|
public ObservableCollection<Order> Orders { get; } = new();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class Order
|
|
|
|
|
{
|
|
|
|
|
public string OrderId { get; set; }
|
|
|
|
|
public int OrderId { get; set; }
|
|
|
|
|
public ILazyLoader LazyLoader { get; set; }
|
|
|
|
|
public string CustomerId { get; set; }
|
|
|
|
|
public Customer Customer { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private interface ISecondContext
|
|
|
|
|
@ -424,7 +466,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalFact]
|
|
|
|
|
public void Throws_when_pooled_context_constructor_has_more_than_one_parameter()
|
|
|
|
|
public void Throws_when_pooled_context_constructor_has_second_parameter_that_cannot_be_resolved_from_service_provider()
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider
|
|
|
|
|
= new ServiceCollection().AddDbContextPool<TwoParameterConstructorContext>(_ => { })
|
|
|
|
|
@ -432,21 +474,22 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
|
|
|
|
|
using var scope = serviceProvider.CreateScope();
|
|
|
|
|
|
|
|
|
|
Assert.Equal(
|
|
|
|
|
CoreStrings.PoolingContextCtorError(nameof(TwoParameterConstructorContext)),
|
|
|
|
|
Assert.Throws<InvalidOperationException>(() => scope.ServiceProvider.GetService<TwoParameterConstructorContext>()).Message);
|
|
|
|
|
Assert.Throws<InvalidOperationException>(() => scope.ServiceProvider.GetService<TwoParameterConstructorContext>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class TwoParameterConstructorContext : DbContext
|
|
|
|
|
{
|
|
|
|
|
public string StringParameter { get; }
|
|
|
|
|
|
|
|
|
|
public TwoParameterConstructorContext(DbContextOptions options, string x)
|
|
|
|
|
: base(options)
|
|
|
|
|
{
|
|
|
|
|
StringParameter = x;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalFact]
|
|
|
|
|
public void Throws_when_pooled_context_constructor_wrong_parameter()
|
|
|
|
|
public void Throws_when_pooled_context_constructor_has_single_parameter_that_cannot_be_resolved_from_service_provider()
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider
|
|
|
|
|
= new ServiceCollection().AddDbContextPool<WrongParameterConstructorContext>(_ => { })
|
|
|
|
|
@ -454,10 +497,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
|
|
|
|
|
using var scope = serviceProvider.CreateScope();
|
|
|
|
|
|
|
|
|
|
Assert.Equal(
|
|
|
|
|
CoreStrings.PoolingContextCtorError(nameof(WrongParameterConstructorContext)),
|
|
|
|
|
Assert.Throws<InvalidOperationException>(() => scope.ServiceProvider.GetService<WrongParameterConstructorContext>())
|
|
|
|
|
.Message);
|
|
|
|
|
Assert.Throws<InvalidOperationException>(() => scope.ServiceProvider.GetService<WrongParameterConstructorContext>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class WrongParameterConstructorContext : DbContext
|
|
|
|
|
@ -468,6 +508,35 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalFact]
|
|
|
|
|
public void Throws_when_pooled_context_constructor_has_scoped_service()
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider
|
|
|
|
|
= new ServiceCollection()
|
|
|
|
|
.AddDbContextPool<TwoParameterConstructorContext>(_ => { })
|
|
|
|
|
.AddScoped(sp => "string")
|
|
|
|
|
.BuildServiceProvider(validateScopes: true);
|
|
|
|
|
|
|
|
|
|
using var scope = serviceProvider.CreateScope();
|
|
|
|
|
|
|
|
|
|
Assert.Throws<InvalidOperationException>(() => scope.ServiceProvider.GetService<TwoParameterConstructorContext>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalFact]
|
|
|
|
|
public void Does_not_throw_when_pooled_context_constructor_has_singleton_service()
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider
|
|
|
|
|
= new ServiceCollection()
|
|
|
|
|
.AddDbContextPool<TwoParameterConstructorContext>(_ => { })
|
|
|
|
|
.AddSingleton("string")
|
|
|
|
|
.BuildServiceProvider(validateScopes: true);
|
|
|
|
|
|
|
|
|
|
using var scope = serviceProvider.CreateScope();
|
|
|
|
|
var context = scope.ServiceProvider.GetService<TwoParameterConstructorContext>();
|
|
|
|
|
|
|
|
|
|
Assert.Equal("string", context.StringParameter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalFact]
|
|
|
|
|
public void Does_not_throw_when_parameterless_and_correct_constructor()
|
|
|
|
|
{
|
|
|
|
|
@ -513,16 +582,22 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalFact]
|
|
|
|
|
public void Can_pool_non_derived_context()
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
[InlineData(false, false)]
|
|
|
|
|
[InlineData(true, false)]
|
|
|
|
|
[InlineData(false, true)]
|
|
|
|
|
[InlineData(true, true)]
|
|
|
|
|
public async Task Can_pool_non_derived_context(bool useFactory, bool async)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = BuildServiceProvider<DbContext>();
|
|
|
|
|
var serviceProvider = useFactory
|
|
|
|
|
? BuildServiceProviderWithFactory<DbContext>()
|
|
|
|
|
: BuildServiceProvider<DbContext>();
|
|
|
|
|
|
|
|
|
|
var serviceScope1 = serviceProvider.CreateScope();
|
|
|
|
|
var context1 = serviceScope1.ServiceProvider.GetService<DbContext>();
|
|
|
|
|
var context1 = await GetContextAsync(serviceScope1);
|
|
|
|
|
|
|
|
|
|
var serviceScope2 = serviceProvider.CreateScope();
|
|
|
|
|
var context2 = serviceScope2.ServiceProvider.GetService<DbContext>();
|
|
|
|
|
var context2 = await GetContextAsync(serviceScope2);
|
|
|
|
|
|
|
|
|
|
Assert.NotSame(context1, context2);
|
|
|
|
|
|
|
|
|
|
@ -535,8 +610,18 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
Assert.Equal(1, id1.Lease);
|
|
|
|
|
Assert.Equal(1, id2.Lease);
|
|
|
|
|
|
|
|
|
|
serviceScope1.Dispose();
|
|
|
|
|
serviceScope2.Dispose();
|
|
|
|
|
if (useFactory)
|
|
|
|
|
{
|
|
|
|
|
await Dispose(context1, async);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope1, async);
|
|
|
|
|
await Dispose(serviceScope2, async);
|
|
|
|
|
|
|
|
|
|
if (useFactory)
|
|
|
|
|
{
|
|
|
|
|
await Dispose(context2, async);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var id1d = context1.ContextId;
|
|
|
|
|
var id2d = context2.ContextId;
|
|
|
|
|
@ -547,7 +632,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
Assert.Equal(1, id2d.Lease);
|
|
|
|
|
|
|
|
|
|
var serviceScope3 = serviceProvider.CreateScope();
|
|
|
|
|
var context3 = serviceScope3.ServiceProvider.GetService<DbContext>();
|
|
|
|
|
var context3 = await GetContextAsync(serviceScope3);
|
|
|
|
|
|
|
|
|
|
var id1r = context3.ContextId;
|
|
|
|
|
|
|
|
|
|
@ -558,7 +643,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
Assert.Equal(2, id1r.Lease);
|
|
|
|
|
|
|
|
|
|
var serviceScope4 = serviceProvider.CreateScope();
|
|
|
|
|
var context4 = serviceScope4.ServiceProvider.GetService<DbContext>();
|
|
|
|
|
var context4 = await GetContextAsync(serviceScope4);
|
|
|
|
|
|
|
|
|
|
var id2r = context4.ContextId;
|
|
|
|
|
|
|
|
|
|
@ -567,17 +652,26 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
Assert.NotEqual(default, id2r.InstanceId);
|
|
|
|
|
Assert.NotEqual(id2, id2r);
|
|
|
|
|
Assert.Equal(2, id2r.Lease);
|
|
|
|
|
|
|
|
|
|
async Task<DbContext> GetContextAsync(IServiceScope serviceScope)
|
|
|
|
|
=> useFactory
|
|
|
|
|
? async
|
|
|
|
|
? await serviceScope.ServiceProvider.GetService<IDbContextFactory<DbContext>>()!.CreateDbContextAsync()
|
|
|
|
|
: serviceScope.ServiceProvider.GetService<IDbContextFactory<DbContext>>()!.CreateDbContext()
|
|
|
|
|
: serviceScope.ServiceProvider.GetService<DbContext>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalFact]
|
|
|
|
|
public void ContextIds_make_sense_when_not_pooling()
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
[InlineData(false)]
|
|
|
|
|
[InlineData(true)]
|
|
|
|
|
public async Task ContextIds_make_sense_when_not_pooling(bool async)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = new ServiceCollection()
|
|
|
|
|
.AddDbContext<DbContext>(
|
|
|
|
|
ob
|
|
|
|
|
=> ob.UseJet(JetNorthwindTestStoreFactory.NorthwindConnectionString, TestEnvironment.DataAccessProviderFactory)
|
|
|
|
|
=> ob.UseJet(JetNorthwindTestStoreFactory.NorthwindConnectionString)
|
|
|
|
|
.EnableServiceProviderCaching(false))
|
|
|
|
|
.BuildServiceProvider();
|
|
|
|
|
.BuildServiceProvider(validateScopes: true);
|
|
|
|
|
|
|
|
|
|
var serviceScope1 = serviceProvider.CreateScope();
|
|
|
|
|
var context1 = serviceScope1.ServiceProvider.GetService<DbContext>();
|
|
|
|
|
@ -587,8 +681,8 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
|
|
|
|
|
Assert.NotSame(context1, context2);
|
|
|
|
|
|
|
|
|
|
var id1 = context1.ContextId;
|
|
|
|
|
var id2 = context2.ContextId;
|
|
|
|
|
var id1 = context1!.ContextId;
|
|
|
|
|
var id2 = context2!.ContextId;
|
|
|
|
|
|
|
|
|
|
Assert.NotEqual(default, id1.InstanceId);
|
|
|
|
|
Assert.NotEqual(default, id2.InstanceId);
|
|
|
|
|
@ -597,8 +691,8 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
Assert.Equal(0, id1.Lease);
|
|
|
|
|
Assert.Equal(0, id2.Lease);
|
|
|
|
|
|
|
|
|
|
serviceScope1.Dispose();
|
|
|
|
|
serviceScope2.Dispose();
|
|
|
|
|
await Dispose(serviceScope1, async);
|
|
|
|
|
await Dispose(serviceScope2, async);
|
|
|
|
|
|
|
|
|
|
var id1d = context1.ContextId;
|
|
|
|
|
var id2d = context2.ContextId;
|
|
|
|
|
@ -632,9 +726,11 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
[InlineData(true)]
|
|
|
|
|
[InlineData(false)]
|
|
|
|
|
public void Contexts_are_pooled(bool useInterface)
|
|
|
|
|
[InlineData(false, false)]
|
|
|
|
|
[InlineData(true, false)]
|
|
|
|
|
[InlineData(false, true)]
|
|
|
|
|
[InlineData(true, true)]
|
|
|
|
|
public async Task Contexts_are_pooled(bool useInterface, bool async)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = useInterface
|
|
|
|
|
? BuildServiceProvider<IPooledContext, PooledContext>()
|
|
|
|
|
@ -665,8 +761,9 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
Assert.NotSame(context1, context2);
|
|
|
|
|
Assert.NotSame(secondContext1, secondContext2);
|
|
|
|
|
|
|
|
|
|
serviceScope1.Dispose();
|
|
|
|
|
serviceScope2.Dispose();
|
|
|
|
|
await Dispose(serviceScope1, async);
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope2, async);
|
|
|
|
|
|
|
|
|
|
var serviceScope3 = serviceProvider.CreateScope();
|
|
|
|
|
var scopedProvider3 = serviceScope3.ServiceProvider;
|
|
|
|
|
@ -695,6 +792,10 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
|
|
|
|
|
Assert.Same(context2, context4);
|
|
|
|
|
Assert.Same(secondContext2, secondContext4);
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope3, async);
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope4, async);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
@ -702,11 +803,64 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
[InlineData(true, false)]
|
|
|
|
|
[InlineData(false, true)]
|
|
|
|
|
[InlineData(true, true)]
|
|
|
|
|
public async Task Context_configuration_is_reset(bool useInterface, bool async)
|
|
|
|
|
public async Task Contexts_are_pooled_with_factory(bool async, bool withDependencyInjection)
|
|
|
|
|
{
|
|
|
|
|
var factory = BuildFactory<PooledContext>(withDependencyInjection);
|
|
|
|
|
|
|
|
|
|
var context1 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
var secondContext1 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
|
|
|
|
|
var context2 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
var secondContext2 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
|
|
|
|
|
Assert.NotSame(context1, context2);
|
|
|
|
|
Assert.NotSame(secondContext1, secondContext2);
|
|
|
|
|
|
|
|
|
|
await Dispose(context1, async);
|
|
|
|
|
await Dispose(secondContext1, async);
|
|
|
|
|
await Dispose(context2, async);
|
|
|
|
|
await Dispose(secondContext2, async);
|
|
|
|
|
|
|
|
|
|
var context3 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
var secondContext3 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
|
|
|
|
|
Assert.Same(context1, context3);
|
|
|
|
|
Assert.Same(secondContext1, secondContext3);
|
|
|
|
|
|
|
|
|
|
var context4 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
var secondContext4 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
|
|
|
|
|
Assert.Same(context2, context4);
|
|
|
|
|
Assert.Same(secondContext2, secondContext4);
|
|
|
|
|
|
|
|
|
|
await Dispose(context1, async);
|
|
|
|
|
await Dispose(secondContext1, async);
|
|
|
|
|
await Dispose(context2, async);
|
|
|
|
|
await Dispose(secondContext2, async);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
[InlineData(false, false, null)]
|
|
|
|
|
[InlineData(true, false, null)]
|
|
|
|
|
[InlineData(false, true, null)]
|
|
|
|
|
[InlineData(true, true, null)]
|
|
|
|
|
[InlineData(false, false, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(true, false, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(false, true, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(true, true, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(false, false, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(true, false, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(false, true, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(true, true, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(false, false, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
[InlineData(true, false, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
[InlineData(false, true, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
[InlineData(true, true, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
public async Task Context_configuration_is_reset(bool useInterface, bool async, QueryTrackingBehavior? queryTrackingBehavior)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = useInterface
|
|
|
|
|
? BuildServiceProvider<IPooledContext, PooledContext>()
|
|
|
|
|
: BuildServiceProvider<PooledContext>();
|
|
|
|
|
? BuildServiceProvider<IPooledContext, PooledContext>(b => UseQueryTrackingBehavior(b, queryTrackingBehavior))
|
|
|
|
|
: BuildServiceProvider<PooledContext>(b => UseQueryTrackingBehavior(b, queryTrackingBehavior));
|
|
|
|
|
|
|
|
|
|
var serviceScope = serviceProvider.CreateScope();
|
|
|
|
|
var scopedProvider = serviceScope.ServiceProvider;
|
|
|
|
|
@ -768,7 +922,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
|
|
|
|
|
Assert.False(context2!.ChangeTracker.AutoDetectChangesEnabled);
|
|
|
|
|
Assert.False(context2.ChangeTracker.LazyLoadingEnabled);
|
|
|
|
|
Assert.Equal(QueryTrackingBehavior.TrackAll, context2.ChangeTracker.QueryTrackingBehavior);
|
|
|
|
|
Assert.Equal(queryTrackingBehavior ?? QueryTrackingBehavior.TrackAll, context2.ChangeTracker.QueryTrackingBehavior);
|
|
|
|
|
Assert.Equal(CascadeTiming.Never, context2.ChangeTracker.CascadeDeleteTiming);
|
|
|
|
|
Assert.Equal(CascadeTiming.Never, context2.ChangeTracker.DeleteOrphansTiming);
|
|
|
|
|
Assert.Equal(AutoTransactionBehavior.Never, context2.Database.AutoTransactionBehavior);
|
|
|
|
|
@ -809,11 +963,17 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
[InlineData(false)]
|
|
|
|
|
[InlineData(true)]
|
|
|
|
|
public async Task Uninitialized_context_configuration_is_reset_properly(bool async)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = BuildServiceProvider<SecondContext>();
|
|
|
|
|
[InlineData(false, null)]
|
|
|
|
|
[InlineData(true, null)]
|
|
|
|
|
[InlineData(false, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(true, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(false, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(true, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(false, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
[InlineData(true, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
public async Task Uninitialized_context_configuration_is_reset_properly(bool async, QueryTrackingBehavior? queryTrackingBehavior)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = BuildServiceProvider<SecondContext>(b => UseQueryTrackingBehavior(b, queryTrackingBehavior));
|
|
|
|
|
|
|
|
|
|
var serviceScope = serviceProvider.CreateScope();
|
|
|
|
|
var ctx = serviceScope.ServiceProvider.GetRequiredService<SecondContext>();
|
|
|
|
|
@ -1062,11 +1222,17 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
=> _changeTracker_OnDetectedEntityChanges = true;
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
[InlineData(false)]
|
|
|
|
|
[InlineData(true)]
|
|
|
|
|
public async Task Default_Context_configuration_is_reset(bool async)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = BuildServiceProvider<DefaultOptionsPooledContext>();
|
|
|
|
|
[InlineData(false, null)]
|
|
|
|
|
[InlineData(true, null)]
|
|
|
|
|
[InlineData(false, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(true, QueryTrackingBehavior.TrackAll)]
|
|
|
|
|
[InlineData(false, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(true, QueryTrackingBehavior.NoTracking)]
|
|
|
|
|
[InlineData(false, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
[InlineData(true, QueryTrackingBehavior.NoTrackingWithIdentityResolution)]
|
|
|
|
|
public async Task Default_Context_configuration_is_reset(bool async, QueryTrackingBehavior? queryTrackingBehavior)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = BuildServiceProvider<DefaultOptionsPooledContext>(b => UseQueryTrackingBehavior(b, queryTrackingBehavior));
|
|
|
|
|
|
|
|
|
|
var serviceScope = serviceProvider.CreateScope();
|
|
|
|
|
var scopedProvider = serviceScope.ServiceProvider;
|
|
|
|
|
@ -1092,7 +1258,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
|
|
|
|
|
Assert.True(context2!.ChangeTracker.AutoDetectChangesEnabled);
|
|
|
|
|
Assert.True(context2.ChangeTracker.LazyLoadingEnabled);
|
|
|
|
|
Assert.Equal(QueryTrackingBehavior.TrackAll, context2.ChangeTracker.QueryTrackingBehavior);
|
|
|
|
|
Assert.Equal(queryTrackingBehavior ?? QueryTrackingBehavior.TrackAll, context2.ChangeTracker.QueryTrackingBehavior);
|
|
|
|
|
Assert.Equal(CascadeTiming.Immediate, context2.ChangeTracker.CascadeDeleteTiming);
|
|
|
|
|
Assert.Equal(CascadeTiming.Immediate, context2.ChangeTracker.DeleteOrphansTiming);
|
|
|
|
|
Assert.Equal(AutoTransactionBehavior.WhenNeeded, context2.Database.AutoTransactionBehavior);
|
|
|
|
|
@ -1214,6 +1380,101 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
Assert.False(weakRef.IsAlive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory] // Issue #25486
|
|
|
|
|
[InlineData(false, false, false)]
|
|
|
|
|
[InlineData(true, false, false)]
|
|
|
|
|
[InlineData(false, true, false)]
|
|
|
|
|
[InlineData(true, true, false)]
|
|
|
|
|
[InlineData(false, false, true)]
|
|
|
|
|
[InlineData(true, false, true)]
|
|
|
|
|
[InlineData(false, true, true)]
|
|
|
|
|
[InlineData(true, true, true)]
|
|
|
|
|
public async Task Service_properties_are_disposed(bool useInterface, bool async, bool load)
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = useInterface
|
|
|
|
|
? BuildServiceProvider<IPooledContext, PooledContext>()
|
|
|
|
|
: BuildServiceProvider<PooledContext>();
|
|
|
|
|
|
|
|
|
|
var serviceScope = serviceProvider.CreateScope();
|
|
|
|
|
var scopedProvider = serviceScope.ServiceProvider;
|
|
|
|
|
|
|
|
|
|
var context1 = useInterface
|
|
|
|
|
? (PooledContext)scopedProvider.GetService<IPooledContext>()
|
|
|
|
|
: scopedProvider.GetService<PooledContext>();
|
|
|
|
|
|
|
|
|
|
context1.ChangeTracker.LazyLoadingEnabled = true;
|
|
|
|
|
|
|
|
|
|
var entity = context1.Customers.First(c => c.CustomerId == "ALFKI");
|
|
|
|
|
var orderLoader = entity.LazyLoader;
|
|
|
|
|
if (load)
|
|
|
|
|
{
|
|
|
|
|
orderLoader.Load(entity, nameof(Customer.Orders));
|
|
|
|
|
Assert.True(orderLoader.IsLoaded(entity, nameof(Customer.Orders)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.Equal(load ? 7 : 1, context1.ChangeTracker.Entries().Count());
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope, async);
|
|
|
|
|
|
|
|
|
|
if (load)
|
|
|
|
|
{
|
|
|
|
|
orderLoader.Load(entity, nameof(Customer.Orders));
|
|
|
|
|
Assert.True(orderLoader.IsLoaded(entity, nameof(Customer.Orders)));
|
|
|
|
|
orderLoader.SetLoaded(entity, nameof(Customer.Orders), loaded: false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AssertDisposed(() => orderLoader.Load(entity, nameof(Customer.Orders)), "Customer", "Orders");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory] // Issue #25486
|
|
|
|
|
[InlineData(false, false, false)]
|
|
|
|
|
[InlineData(true, false, false)]
|
|
|
|
|
[InlineData(false, true, false)]
|
|
|
|
|
[InlineData(true, true, false)]
|
|
|
|
|
[InlineData(false, false, true)]
|
|
|
|
|
[InlineData(true, false, true)]
|
|
|
|
|
[InlineData(false, true, true)]
|
|
|
|
|
[InlineData(true, true, true)]
|
|
|
|
|
public async Task Service_properties_are_disposed_with_factory(bool async, bool withDependencyInjection, bool load)
|
|
|
|
|
{
|
|
|
|
|
var factory = BuildFactory<PooledContext>(withDependencyInjection);
|
|
|
|
|
|
|
|
|
|
var context1 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
|
|
|
|
|
context1.ChangeTracker.LazyLoadingEnabled = true;
|
|
|
|
|
|
|
|
|
|
var entity = context1.Customers.First(c => c.CustomerId == "ALFKI");
|
|
|
|
|
var orderLoader = entity.LazyLoader;
|
|
|
|
|
if (load)
|
|
|
|
|
{
|
|
|
|
|
orderLoader.Load(entity, nameof(Customer.Orders));
|
|
|
|
|
Assert.True(orderLoader.IsLoaded(entity, nameof(Customer.Orders)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.Equal(load ? 7 : 1, context1.ChangeTracker.Entries().Count());
|
|
|
|
|
|
|
|
|
|
await Dispose(context1, async);
|
|
|
|
|
|
|
|
|
|
if (load)
|
|
|
|
|
{
|
|
|
|
|
orderLoader.Load(entity, nameof(Customer.Orders));
|
|
|
|
|
Assert.True(orderLoader.IsLoaded(entity, nameof(Customer.Orders)));
|
|
|
|
|
orderLoader.SetLoaded(entity, nameof(Customer.Orders), loaded: false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AssertDisposed(() => orderLoader.Load(entity, nameof(Customer.Orders)), "Customer", "Orders");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void AssertDisposed(Action testCode, string entityTypeName, string navigationName)
|
|
|
|
|
=> Assert.Equal(
|
|
|
|
|
CoreStrings.WarningAsErrorTemplate(
|
|
|
|
|
CoreEventId.LazyLoadOnDisposedContextWarning.ToString(),
|
|
|
|
|
CoreResources.LogLazyLoadOnDisposedContext(new TestLogger<TestLoggingDefinitions>())
|
|
|
|
|
.GenerateMessage(entityTypeName, navigationName),
|
|
|
|
|
"CoreEventId.LazyLoadOnDisposedContextWarning"),
|
|
|
|
|
Assert.Throws<InvalidOperationException>(
|
|
|
|
|
testCode).Message);
|
|
|
|
|
|
|
|
|
|
[ConditionalTheory]
|
|
|
|
|
[InlineData(false, false)]
|
|
|
|
|
[InlineData(true, false)]
|
|
|
|
|
@ -1381,7 +1642,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
{
|
|
|
|
|
var serviceProvider = useInterface
|
|
|
|
|
? BuildServiceProvider<IPooledContext, PooledContext>()
|
|
|
|
|
: BuildServiceProvider<PooledContext>();
|
|
|
|
|
: BuildServiceProvider<PooledContext>(o => o.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
|
|
|
|
|
|
|
|
|
|
var serviceScope = serviceProvider.CreateScope();
|
|
|
|
|
var scopedProvider = serviceScope.ServiceProvider;
|
|
|
|
|
@ -1499,11 +1760,11 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection1.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection1.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope, async);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Closed, connection1.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Closed, connection1.State);
|
|
|
|
|
|
|
|
|
|
serviceScope = serviceProvider.CreateScope();
|
|
|
|
|
scopedProvider = serviceScope.ServiceProvider;
|
|
|
|
|
@ -1514,7 +1775,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
var connection2 = context2.Database.GetDbConnection();
|
|
|
|
|
Assert.Same(connection1, connection2);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Closed, connection1.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Closed, connection1.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope, async);
|
|
|
|
|
}
|
|
|
|
|
@ -1580,11 +1841,11 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope, async);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection.State);
|
|
|
|
|
|
|
|
|
|
serviceScope = serviceProvider.CreateScope();
|
|
|
|
|
scopedProvider = serviceScope.ServiceProvider;
|
|
|
|
|
@ -1594,7 +1855,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
|
|
|
|
|
Assert.Same(connection, context2.Database.GetDbConnection());
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(serviceScope, async);
|
|
|
|
|
}
|
|
|
|
|
@ -1654,11 +1915,11 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection1.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection1.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(context1, async);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Closed, connection1.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Closed, connection1.State);
|
|
|
|
|
|
|
|
|
|
var context2 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
Assert.Same(context1, context2);
|
|
|
|
|
@ -1666,7 +1927,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
var connection2 = context2.Database.GetDbConnection();
|
|
|
|
|
Assert.Same(connection1, connection2);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Closed, connection1.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Closed, connection1.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(context2, async);
|
|
|
|
|
}
|
|
|
|
|
@ -1748,18 +2009,18 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(context1, async);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection.State);
|
|
|
|
|
|
|
|
|
|
var context2 = async ? await factory.CreateDbContextAsync() : factory.CreateDbContext();
|
|
|
|
|
Assert.Same(context1, context2);
|
|
|
|
|
|
|
|
|
|
Assert.Same(connection, context2.Database.GetDbConnection());
|
|
|
|
|
|
|
|
|
|
Assert.Equal(System.Data.ConnectionState.Open, connection.State);
|
|
|
|
|
Assert.Equal(ConnectionState.Open, connection.State);
|
|
|
|
|
|
|
|
|
|
await Dispose(context2, async);
|
|
|
|
|
}
|
|
|
|
|
@ -1908,6 +2169,14 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
})));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UseQueryTrackingBehavior(DbContextOptionsBuilder optionsBuilder, QueryTrackingBehavior? queryTrackingBehavior)
|
|
|
|
|
{
|
|
|
|
|
if (queryTrackingBehavior.HasValue)
|
|
|
|
|
{
|
|
|
|
|
optionsBuilder.UseQueryTrackingBehavior(queryTrackingBehavior.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task Dispose(IDisposable disposable, bool async)
|
|
|
|
|
{
|
|
|
|
|
if (async)
|
|
|
|
|
@ -1920,12 +2189,10 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly ITestOutputHelper _testOutputHelper = null;
|
|
|
|
|
private readonly ITestOutputHelper _testOutputHelper;
|
|
|
|
|
|
|
|
|
|
// ReSharper disable once UnusedParameter.Local
|
|
|
|
|
public DbContextPoolingTest(NorthwindQueryJetFixture<NoopModelCustomizer> fixture, ITestOutputHelper testOutputHelper)
|
|
|
|
|
{
|
|
|
|
|
//_testOutputHelper = testOutputHelper;
|
|
|
|
|
}
|
|
|
|
|
_testOutputHelper = testOutputHelper;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|