From b0d53d30ce9676f8485ef14045f8b8b4592e2bce Mon Sep 17 00:00:00 2001 From: xoniuqe Date: Tue, 1 Dec 2020 23:50:37 +0100 Subject: [PATCH] Fix DEFAULT clause for HasDefaultValueSql() (#81) * Removed parenthesis from column default values (addresses issue #18) * Fixed issue where the file handles were not being released after the database connection was closed. * reverted accidentally changed file to upstream version * Fixed issue where the file handles were not being released after the database connection was closed. * reverted accidentally changed file to upstream version * Add accompanying tests for PR #81. Co-authored-by: Collin Kostichuk Co-authored-by: Collin Kostichuk Co-authored-by: Tobias Arens Co-authored-by: Laurents Meyer --- azure-pipelines.yml | 7 +- .../Migrations/JetMigrationsSqlGenerator.cs | 5 +- .../JetMigrationSqlGeneratorTest.cs | 8 +- test/EFCore.Jet.Tests/JetMigrationTest.cs | 154 ++++++++++++++++++ 4 files changed, 165 insertions(+), 9 deletions(-) create mode 100644 test/EFCore.Jet.Tests/JetMigrationTest.cs diff --git a/azure-pipelines.yml b/azure-pipelines.yml index aeba455..5fe5479 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -100,13 +100,16 @@ jobs: dotnet test .\test\EFCore.Jet.Data.Tests -c $(buildConfiguration) --no-build --logger trx --verbosity detailed exit 0 displayName: 'Run Tests: EFCore.Jet.Data.Tests' - continueOnError: true - pwsh: | $env:EFCoreJet_DefaultConnection = '$(defaultConnection)' dotnet test .\test\EFCore.Jet.FunctionalTests -c $(buildConfiguration) --no-build --logger trx --verbosity detailed exit 0 displayName: 'Run Tests: EFCore.Jet.FunctionalTests' - continueOnError: true + - pwsh: | + $env:EFCoreJet_DefaultConnection = '$(defaultConnection)' + dotnet test .\test\EFCore.Jet.Tests -c $(buildConfiguration) --no-build --logger trx --verbosity detailed + displayName: 'Run Tests: EFCore.Jet.FunctionalTests' + continueOnError: false - task: PublishTestResults@2 displayName: Publish Test Results condition: succeededOrFailed() diff --git a/src/EFCore.Jet/Migrations/JetMigrationsSqlGenerator.cs b/src/EFCore.Jet/Migrations/JetMigrationsSqlGenerator.cs index cc11d0f..a543f38 100644 --- a/src/EFCore.Jet/Migrations/JetMigrationsSqlGenerator.cs +++ b/src/EFCore.Jet/Migrations/JetMigrationsSqlGenerator.cs @@ -759,9 +759,8 @@ namespace Microsoft.EntityFrameworkCore.Migrations if (defaultValueSql != null) { builder - .Append(" DEFAULT (") - .Append(defaultValueSql) - .Append(")"); + .Append(" DEFAULT ") + .Append(defaultValueSql); } else if (defaultValue != null) { diff --git a/test/EFCore.Jet.FunctionalTests/JetMigrationSqlGeneratorTest.cs b/test/EFCore.Jet.FunctionalTests/JetMigrationSqlGeneratorTest.cs index 503a63f..1b11bd0 100644 --- a/test/EFCore.Jet.FunctionalTests/JetMigrationSqlGeneratorTest.cs +++ b/test/EFCore.Jet.FunctionalTests/JetMigrationSqlGeneratorTest.cs @@ -359,7 +359,7 @@ EXEC sp_addextendedproperty 'MS_Description', 'My Comment 2', 'SCHEMA', 'dbo', ' } [ConditionalFact] - public virtual void AddColumnOperation_smalldatetime_with_defaultValue() + public virtual void AddColumnOperation_datetime_with_defaultValue_sql() { Generate( new AddColumnOperation @@ -368,13 +368,13 @@ EXEC sp_addextendedproperty 'MS_Description', 'My Comment 2', 'SCHEMA', 'dbo', ' Schema = null, Name = "Birthday", ClrType = typeof(DateTime), - ColumnType = "smalldatetime", + ColumnType = "datetime", IsNullable = false, - DefaultValue = new DateTime(2019, 1, 1) + DefaultValueSql = "#2019-01-01#" }); AssertSql( - $@"ALTER TABLE `People` ADD `Birthday` smalldatetime NOT NULL DEFAULT '2019-01-01T00:00:00'; + $@"ALTER TABLE `People` ADD `Birthday` datetime NOT NULL DEFAULT #2019-01-01#; "); } diff --git a/test/EFCore.Jet.Tests/JetMigrationTest.cs b/test/EFCore.Jet.Tests/JetMigrationTest.cs new file mode 100644 index 0000000..3f3e1c8 --- /dev/null +++ b/test/EFCore.Jet.Tests/JetMigrationTest.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using EntityFrameworkCore.Jet.Data; +using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Metadata.Conventions; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace EntityFrameworkCore.Jet +{ + public class JetMigrationTest : IDisposable + { + [ConditionalFact] + public virtual void Create_table_with_HasDefaultValueSql() + { + using var context = CreateContext( + builder => + { + builder.Entity( + entity => + { + entity.Property(e => e.BestServedBefore) + .HasDefaultValueSql("#2021-12-31#"); + + entity.HasData( + new Cookie + { + CookieId = 1, + Name = "Basic", + }); + }); + }); + + var cookies = context.Set() + .ToList(); + + Assert.Single(cookies); + Assert.Equal(new DateTime(2021, 12, 31), cookies[0].BestServedBefore); + } + + public class Cookie + { + public int CookieId { get; set; } + public string Name { get; set; } + public DateTime BestServedBefore { get; set; } + } + + private class Context : DbContext + { + private readonly IServiceProvider _serviceProvider; + private readonly string _databaseName; + private readonly Action _modelBuilder; + private readonly Action _options; + + public List SqlCommands { get; } = new List(); + + public Context(string databaseName, Action modelBuilder = null, Action options = null, IServiceCollection serviceCollection = null) + { + _serviceProvider = (serviceCollection ?? new ServiceCollection().AddEntityFrameworkJet()) + .BuildServiceProvider(); + _databaseName = databaseName; + _modelBuilder = modelBuilder; + _options = options; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder + .UseJet(_databaseName, DataAccessProviderType.OleDb /*TestEnvironment.DataAccessProviderFactory*/, b => b.ApplyConfiguration()) + .AddInterceptors(new CommandInterceptor()) + .UseInternalServiceProvider(_serviceProvider); + + if (_modelBuilder != null) + { + var conventionSet = JetConventionSetBuilder.Build(); + var modelBuilder = new ModelBuilder(conventionSet); + + _modelBuilder.Invoke(modelBuilder); + + var model = modelBuilder.FinalizeModel(); + optionsBuilder.UseModel(model); + } + + _options?.Invoke(_serviceProvider, optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } + + private class CommandInterceptor : DbCommandInterceptor + { + public override InterceptionResult ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result) + { + ((Context)eventData.Context).SqlCommands.Add(command.CommandText); + return base.ReaderExecuting(command, eventData, result); + } + + public override InterceptionResult ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result) + { + ((Context)eventData.Context).SqlCommands.Add(command.CommandText); + return base.ScalarExecuting(command, eventData, result); + } + + public override InterceptionResult NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result) + { + ((Context)eventData.Context).SqlCommands.Add(command.CommandText); + return base.NonQueryExecuting(command, eventData, result); + } + + public override Task> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, CancellationToken cancellationToken = new CancellationToken()) + { + ((Context)eventData.Context).SqlCommands.Add(command.CommandText); + return base.ReaderExecutingAsync(command, eventData, result, cancellationToken); + } + + public override Task> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, CancellationToken cancellationToken = new CancellationToken()) + { + ((Context)eventData.Context).SqlCommands.Add(command.CommandText); + return base.ScalarExecutingAsync(command, eventData, result, cancellationToken); + } + + public override Task> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, CancellationToken cancellationToken = new CancellationToken()) + { + ((Context)eventData.Context).SqlCommands.Add(command.CommandText); + return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken); + } + } + } + + public JetMigrationTest() + { + TestStore = JetTestStore.CreateInitialized(nameof(JetMigrationTest)); + } + + protected JetTestStore TestStore { get; } + public virtual void Dispose() => TestStore.Dispose(); + + private Context CreateContext(Action modelBuilder) + { + var context = new Context(TestStore.Name, modelBuilder); + + TestStore.Clean(context); + + return context; + } + } +} \ No newline at end of file