You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
8.1 KiB
C#
210 lines
8.1 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities;
|
|
using EntityFrameworkCore.Jet.Infrastructure;
|
|
using EntityFrameworkCore.Jet.Storage.Internal;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
using Microsoft.EntityFrameworkCore.TestUtilities;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Xunit;
|
|
|
|
namespace EntityFrameworkCore.Jet.FunctionalTests;
|
|
#nullable disable
|
|
public abstract class SaveChangesInterceptionJetTestBase(
|
|
SaveChangesInterceptionJetTestBase.InterceptionJetFixtureBase fixture)
|
|
: SaveChangesInterceptionTestBase(fixture)
|
|
{
|
|
[ConditionalTheory]
|
|
[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 virtual async Task Intercept_concurrency_with_relational_specific_data(bool async, bool inject, bool noAcceptChanges)
|
|
{
|
|
var saveChangesInterceptor = new RelationalConcurrencySaveChangesInterceptor();
|
|
var commandInterceptor = new TestCommandInterceptor();
|
|
|
|
var context = await CreateContextAsync(saveChangesInterceptor, commandInterceptor);
|
|
|
|
using var _ = context;
|
|
|
|
using var transaction = context.Database.BeginTransaction();
|
|
|
|
var entry = context.Entry(new Singularity { Id = 35, Type = "Red Dwarf" });
|
|
entry.State = EntityState.Modified;
|
|
|
|
using var listener = Fixture.SubscribeToDiagnosticListener(context.ContextId);
|
|
|
|
Exception thrown = null;
|
|
|
|
try
|
|
{
|
|
var __ = noAcceptChanges
|
|
? async
|
|
? await context.SaveChangesAsync()
|
|
: context.SaveChanges()
|
|
: async
|
|
? await context.SaveChangesAsync(acceptAllChangesOnSuccess: false)
|
|
: context.SaveChanges(acceptAllChangesOnSuccess: false);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
thrown = e;
|
|
}
|
|
|
|
Assert.Equal(async, saveChangesInterceptor.AsyncCalled);
|
|
Assert.NotEqual(async, saveChangesInterceptor.SyncCalled);
|
|
Assert.NotEqual(saveChangesInterceptor.AsyncCalled, saveChangesInterceptor.SyncCalled);
|
|
Assert.False(saveChangesInterceptor.FailedCalled);
|
|
Assert.Same(context, saveChangesInterceptor.Context);
|
|
Assert.Same(thrown, saveChangesInterceptor.Exception);
|
|
|
|
Assert.True(saveChangesInterceptor.ConcurrencyExceptionCalled);
|
|
Assert.Equal(1, saveChangesInterceptor.Entries.Count);
|
|
Assert.Same(entry.Entity, saveChangesInterceptor.Entries[0].Entity);
|
|
|
|
Assert.Same(commandInterceptor.Connection, saveChangesInterceptor.Connection);
|
|
Assert.Same(commandInterceptor.Command, saveChangesInterceptor.Command);
|
|
Assert.Same(commandInterceptor.DataReader, saveChangesInterceptor.DataReader);
|
|
Assert.Equal(commandInterceptor.ConnectionId, saveChangesInterceptor.ConnectionId);
|
|
Assert.Equal(commandInterceptor.CommandId, saveChangesInterceptor.CommandId);
|
|
}
|
|
|
|
protected class RelationalConcurrencySaveChangesInterceptor : SaveChangesInterceptorBase
|
|
{
|
|
public DbConnection Connection { get; set; }
|
|
public DbCommand Command { get; set; }
|
|
public DbDataReader DataReader { get; set; }
|
|
public Guid CommandId { get; set; }
|
|
public Guid ConnectionId { get; set; }
|
|
|
|
public override InterceptionResult ThrowingConcurrencyException(ConcurrencyExceptionEventData eventData, InterceptionResult result)
|
|
{
|
|
RecordEventData((RelationalConcurrencyExceptionEventData)eventData);
|
|
|
|
return base.ThrowingConcurrencyException(eventData, result);
|
|
}
|
|
|
|
public override ValueTask<InterceptionResult> ThrowingConcurrencyExceptionAsync(
|
|
ConcurrencyExceptionEventData eventData,
|
|
InterceptionResult result,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
RecordEventData((RelationalConcurrencyExceptionEventData)eventData);
|
|
|
|
return base.ThrowingConcurrencyExceptionAsync(eventData, result, cancellationToken);
|
|
}
|
|
|
|
private void RecordEventData(RelationalConcurrencyExceptionEventData eventData)
|
|
{
|
|
Assert.NotNull(eventData.Connection);
|
|
Assert.NotNull(eventData.Command);
|
|
Assert.NotNull(eventData.DataReader);
|
|
|
|
DataReader = eventData.DataReader;
|
|
Command = eventData.Command;
|
|
Connection = eventData.Connection;
|
|
ConnectionId = eventData.ConnectionId;
|
|
CommandId = eventData.CommandId;
|
|
}
|
|
}
|
|
|
|
protected class TestCommandInterceptor : IDbCommandInterceptor
|
|
{
|
|
public DbConnection Connection { get; set; }
|
|
public DbCommand Command { get; set; }
|
|
public DbDataReader DataReader { get; set; }
|
|
public Guid CommandId { get; set; }
|
|
public Guid ConnectionId { get; set; }
|
|
|
|
public virtual DbDataReader ReaderExecuted(
|
|
DbCommand command,
|
|
CommandExecutedEventData eventData,
|
|
DbDataReader result)
|
|
{
|
|
RecordEventData(command, eventData, result);
|
|
|
|
return result;
|
|
}
|
|
|
|
public virtual ValueTask<DbDataReader> ReaderExecutedAsync(
|
|
DbCommand command,
|
|
CommandExecutedEventData eventData,
|
|
DbDataReader result,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
RecordEventData(command, eventData, result);
|
|
|
|
return ValueTask.FromResult(result);
|
|
}
|
|
|
|
private void RecordEventData(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
|
|
{
|
|
DataReader = result;
|
|
Command = command;
|
|
Connection = command.Connection;
|
|
ConnectionId = eventData.ConnectionId;
|
|
CommandId = eventData.CommandId;
|
|
}
|
|
}
|
|
|
|
public abstract class InterceptionJetFixtureBase : InterceptionFixtureBase
|
|
{
|
|
protected override ITestStoreFactory TestStoreFactory
|
|
=> JetTestStoreFactory.Instance;
|
|
|
|
protected override IServiceCollection InjectInterceptors(
|
|
IServiceCollection serviceCollection,
|
|
IEnumerable<IInterceptor> injectedInterceptors)
|
|
=> base.InjectInterceptors(serviceCollection.AddEntityFrameworkJet(), injectedInterceptors);
|
|
|
|
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
|
{
|
|
new JetDbContextOptionsBuilder(base.AddOptions(builder))
|
|
.ExecutionStrategy(d => new JetExecutionStrategy(d));
|
|
return builder;
|
|
}
|
|
}
|
|
|
|
public class SaveChangesInterceptionJetTest(SaveChangesInterceptionJetTest.InterceptionJetFixture fixture)
|
|
: SaveChangesInterceptionJetTestBase(fixture),
|
|
IClassFixture<SaveChangesInterceptionJetTest.InterceptionJetFixture>
|
|
{
|
|
public class InterceptionJetFixture : InterceptionJetFixtureBase
|
|
{
|
|
protected override string StoreName
|
|
=> "SaveChangesInterception";
|
|
|
|
protected override bool ShouldSubscribeToDiagnosticListener
|
|
=> false;
|
|
}
|
|
}
|
|
|
|
public class SaveChangesInterceptionWithDiagnosticsJetTest(
|
|
SaveChangesInterceptionWithDiagnosticsJetTest.InterceptionJetFixture fixture)
|
|
: SaveChangesInterceptionJetTestBase(fixture),
|
|
IClassFixture<SaveChangesInterceptionWithDiagnosticsJetTest.InterceptionJetFixture>
|
|
{
|
|
public class InterceptionJetFixture : InterceptionJetFixtureBase
|
|
{
|
|
protected override string StoreName
|
|
=> "SaveChangesInterceptionWithDiagnostics";
|
|
|
|
protected override bool ShouldSubscribeToDiagnosticListener
|
|
=> true;
|
|
}
|
|
}
|
|
}
|
|
|