Skip to content

Commit

Permalink
Sync to an RC2 build (#265)
Browse files Browse the repository at this point in the history
* RC2 History Repo

* return the right ValueGenerated if it is mapped to json

* Since Jet doesn't do anything for GO we don't need to do anything like Sql server does

* Update the HistoryRepository

* Remove unused tests

* [GitHub Actions] Update green tests.

---------

Co-authored-by: github-actions <[email protected]>
  • Loading branch information
ChrisJollyAU and github-actions authored Oct 3, 2024
1 parent b64c1d5 commit 8033475
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 167 deletions.
6 changes: 3 additions & 3 deletions Dependencies.targets
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<DotNetVersion>[9.0.100-rc.1.24452.12,9.0.999]</DotNetVersion>
<EFCoreVersion>[9.0.0-rc.1.24451.1,9.0.999]</EFCoreVersion>
<MSLibVersion>[9.0.0-rc.1.24431.7,9.0.999]</MSLibVersion>
<EFCoreVersion>[9.0.0-rc.2.24460.3,9.0.999]</EFCoreVersion>
<MSLibVersion>[9.0.0-rc.2.24456.9,9.0.999]</MSLibVersion>
</PropertyGroup>

<ItemGroup>
Expand All @@ -26,7 +26,7 @@
<PackageReference Update="System.Data.Odbc" Version="$(MSLibVersion)" />
<PackageReference Update="System.Data.OleDb" Version="$(MSLibVersion)" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Design" Version="$(EFCoreVersion)" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational.Specification.Tests" Version="$(EFCoreVersion)" /> <!-- Should be same as efcoreversion. preview6 was broken and published separately -->
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational.Specification.Tests" Version="$(EFCoreVersion)" />
<PackageReference Update="Microsoft.Extensions.Logging.Console" Version="$(MSLibVersion)" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Update="MSTest.TestAdapter" Version="3.6.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace EntityFrameworkCore.Jet.Data.JetStoreSchemaDefinition
{
internal static class JetSchemaOperationsHandling
public static class JetSchemaOperationsHandling
{
private static readonly Regex _renameTableRegex = new Regex(
$@"^\s*alter\s+table\s+{GetIdentifierPattern("OldTableName")}\s+rename\s+to\s+{GetIdentifierPattern("NewTableName")}\s*$",
Expand All @@ -12,6 +12,9 @@ internal static class JetSchemaOperationsHandling
$@"^\s*alter\s+table\s+{GetIdentifierPattern("TableName")}\s+rename\s+column\s+{GetIdentifierPattern("OldColumnName")}\s+to\s+{GetIdentifierPattern("NewColumnName")}\s*$",
RegexOptions.IgnoreCase);

public static bool IsDatabaseOperation(string commandText)
=> _renameTableRegex.IsMatch(commandText) || _renameTableColumnRegex.IsMatch(commandText);

public static bool TryDatabaseOperation(JetConnection connection, string commandText)
{
var match = _renameTableRegex.Match(commandText);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public static IServiceCollection AddEntityFrameworkJet([NotNull] this IServiceCo
.TryAdd<IRelationalConnection>(p => p.GetRequiredService<IJetRelationalConnection>())
.TryAdd<IMigrationsSqlGenerator, JetMigrationsSqlGenerator>()
.TryAdd<IRelationalDatabaseCreator, JetDatabaseCreator>()
.TryAdd<IMigrationCommandExecutor, JetMigrationCommandExecutor>()
.TryAdd<IHistoryRepository, JetHistoryRepository>()
.TryAdd<ICompiledQueryCacheKeyGenerator, JetCompiledQueryCacheKeyGenerator>()
.TryAdd<IExecutionStrategyFactory, JetExecutionStrategyFactory>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
Expand Down Expand Up @@ -61,6 +62,14 @@ public override void ProcessPropertyAnnotationChanged(
/// <returns> The store value generation strategy to set for the given property. </returns>
protected override ValueGenerated? GetValueGenerated(IConventionProperty property)
{
if (property.DeclaringType.IsMappedToJson()
#pragma warning disable EF1001 // Internal EF Core API usage.
&& property.IsOrdinalKeyProperty()
#pragma warning restore EF1001 // Internal EF Core API usage.
&& (property.DeclaringType as IReadOnlyEntityType)?.FindOwnership()!.IsUnique == false)
{
return ValueGenerated.OnAdd;
}
var declaringTable = property.GetMappedStoreObjects(StoreObjectType.Table).FirstOrDefault();
if (declaringTable.Name == null)
{
Expand Down
61 changes: 23 additions & 38 deletions src/EFCore.Jet/Migrations/Internal/JetHistoryRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace EntityFrameworkCore.Jet.Migrations.Internal
public class JetHistoryRepository : HistoryRepository
{
private static readonly TimeSpan _retryDelay = TimeSpan.FromSeconds(1);

public override LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Explicit;
/// <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
Expand Down Expand Up @@ -128,18 +128,21 @@ public override string GetDeleteScript(string migrationId)
.ToString();
}

public override IDisposable GetDatabaseLock()
public override IMigrationsDatabaseLock AcquireDatabaseLock()
{
if (!InterpretExistsResult(Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
.ExecuteScalar(CreateRelationalCommandParameters())))
Dependencies.MigrationsLogger.AcquiringMigrationLock();

if (!InterpretExistsResult(
Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
.ExecuteScalar(CreateRelationalCommandParameters())))
{
try
{
CreateLockTableCommand().ExecuteNonQuery(CreateRelationalCommandParameters());
}
catch (DbException)
catch (DbException e)
{
//if (!e.Message.Contains("already exists")) throw;
if (!e.Message.Contains("already exists")) throw;
}
}

Expand All @@ -152,11 +155,11 @@ public override IDisposable GetDatabaseLock()
try
{
insertCount = (int?)CreateInsertLockCommand(DateTimeOffset.UtcNow)
.ExecuteScalar(CreateRelationalCommandParameters());
.ExecuteScalar(CreateRelationalCommandParameters());
}
catch (DbException)
catch (DbException e)
{
//if (!e.Message.Contains("duplicate")) throw;
if (!e.Message.Contains("duplicate")) throw;
}
if ((int)insertCount! == 1)
{
Expand All @@ -169,44 +172,28 @@ public override IDisposable GetDatabaseLock()
retryDelay = retryDelay.Add(retryDelay);
}
}

throw new TimeoutException();
}

public override async Task<IAsyncDisposable> GetDatabaseLockAsync(CancellationToken cancellationToken = new CancellationToken())
public override async Task<IMigrationsDatabaseLock> AcquireDatabaseLockAsync(
CancellationToken cancellationToken = default)
{
if (!InterpretExistsResult(await Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
.ExecuteScalarAsync(CreateRelationalCommandParameters(), cancellationToken).ConfigureAwait(false)))
Dependencies.MigrationsLogger.AcquiringMigrationLock();

if (!InterpretExistsResult(
await Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
.ExecuteScalarAsync(CreateRelationalCommandParameters(), cancellationToken).ConfigureAwait(false)))
{
//No CREATE TABLE IF EXISTS in Jet. We try a normal CREATE TABLE and catch the exception if it already exists
try
{
await CreateLockTableCommand()
.ExecuteNonQueryAsync(CreateRelationalCommandParameters(), cancellationToken)
.ConfigureAwait(false);
}
catch (DbException)
{
//if (!e.Message.Contains("already exists")) throw;
}
await CreateLockTableCommand().ExecuteNonQueryAsync(CreateRelationalCommandParameters(), cancellationToken)
.ConfigureAwait(false);
}

var retryDelay = _retryDelay;
while (true)
{
var dbLock = CreateMigrationDatabaseLock();
int? insertCount = 0;
try
{
insertCount = (int?)await CreateInsertLockCommand(DateTimeOffset.UtcNow)
var insertCount = await CreateInsertLockCommand(DateTimeOffset.UtcNow)
.ExecuteScalarAsync(CreateRelationalCommandParameters(), cancellationToken)
.ConfigureAwait(false);

}
catch (DbException)
{
//if (!e.Message.Contains("duplicate")) throw;
}
if ((int)insertCount! == 1)
{
return dbLock;
Expand All @@ -218,8 +205,6 @@ await CreateLockTableCommand()
retryDelay = retryDelay.Add(retryDelay);
}
}

throw new TimeoutException();
}

private IRelationalCommand CreateLockTableCommand()
Expand Down Expand Up @@ -254,7 +239,7 @@ DELETE FROM `{LockTableName}`
}

private JetMigrationDatabaseLock CreateMigrationDatabaseLock()
=> new(CreateDeleteLockCommand(), CreateRelationalCommandParameters());
=> new(CreateDeleteLockCommand(), CreateRelationalCommandParameters(), this);

private RelationalCommandParameterObject CreateRelationalCommandParameters()
=> new(
Expand Down
85 changes: 85 additions & 0 deletions src/EFCore.Jet/Migrations/Internal/JetMigrationCommandExecutor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Migrations;
using System.Transactions;
using EntityFrameworkCore.Jet.Data.JetStoreSchemaDefinition;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Storage;

namespace EntityFrameworkCore.Jet.Migrations.Internal
{
public class JetMigrationCommandExecutor(IExecutionStrategy executionStrategy) : MigrationCommandExecutor(executionStrategy)
{
/// <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 override int ExecuteNonQuery(
IReadOnlyList<MigrationCommand> migrationCommands,
IRelationalConnection connection,
MigrationExecutionState executionState,
bool commitTransaction,
System.Data.IsolationLevel? isolationLevel = null)
{
var batches = CreateMigrationBatches(migrationCommands);
foreach (var batch in batches)
{
base.ExecuteNonQuery(batch, connection, executionState, true, isolationLevel);
}

return -1;
}

public override async Task<int> ExecuteNonQueryAsync(
IReadOnlyList<MigrationCommand> migrationCommands,
IRelationalConnection connection,
MigrationExecutionState executionState,
bool commitTransaction,
System.Data.IsolationLevel? isolationLevel = null,
CancellationToken cancellationToken = default)
{
var batches = CreateMigrationBatches(migrationCommands);
foreach (var batch in batches)
{
await base.ExecuteNonQueryAsync(batch, connection, executionState, true, isolationLevel, cancellationToken);
}

return -1;
}

List<IReadOnlyList<MigrationCommand>> CreateMigrationBatches(IReadOnlyList<MigrationCommand> migrationCommands)
{
//create new batch if JetSchemaOperationsHandling.IsDatabaseOperation is true otherwise had to current batch
var migrationBatches = new List<IReadOnlyList<MigrationCommand>>();
var currentBatch = new List<MigrationCommand>();
foreach (var migrationCommand in migrationCommands)
{
if (JetSchemaOperationsHandling.IsDatabaseOperation(migrationCommand.CommandText))
{
if (currentBatch.Any())
{
migrationBatches.Add(currentBatch);
currentBatch = new List<MigrationCommand>();
}
migrationBatches.Add(new List<MigrationCommand> { migrationCommand });
}
else
{
currentBatch.Add(migrationCommand);
}
}
if (currentBatch.Any())
{
migrationBatches.Add(currentBatch);
}
return migrationBatches;
}
}
}
37 changes: 33 additions & 4 deletions src/EFCore.Jet/Migrations/Internal/JetMigrationDatabaseLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;

namespace EntityFrameworkCore.Jet.Migrations.Internal;
Expand All @@ -15,19 +17,37 @@ namespace EntityFrameworkCore.Jet.Migrations.Internal;
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class JetMigrationDatabaseLock(
IRelationalCommand relationalCommand,
IRelationalCommand releaseLockCommand,
RelationalCommandParameterObject relationalCommandParameters,
IHistoryRepository historyRepository,
CancellationToken cancellationToken = default)
: IDisposable, IAsyncDisposable
: IMigrationsDatabaseLock
{
/// <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 IHistoryRepository HistoryRepository => historyRepository;

/// <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 void Dispose()
=> relationalCommand.ExecuteScalar(relationalCommandParameters);
{
try
{
releaseLockCommand.ExecuteScalar(relationalCommandParameters);
}
catch (DbException e)
{
if (!e.Message.Contains("cannot find the input table")) throw;
}
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -36,5 +56,14 @@ public void Dispose()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public async ValueTask DisposeAsync()
=> await relationalCommand.ExecuteScalarAsync(relationalCommandParameters, cancellationToken).ConfigureAwait(false);
{
try
{
await releaseLockCommand.ExecuteScalarAsync(relationalCommandParameters, cancellationToken);
}
catch (DbException e)
{
if (!e.Message.Contains("cannot find the input table")) throw;
}
}
}
Loading

0 comments on commit 8033475

Please sign in to comment.