├── .editorconfig
├── .gitignore
├── Directory.Build.Override.props.sample
├── Directory.Build.props
├── EFCore.BulkExtensions.sln
├── LICENSE
├── README.md
├── azure-pipelines.yml
├── efcore-ext.snk
├── src
├── Abstraction
│ ├── Bulk
│ │ ├── BatchOperationExtensions.cs
│ │ ├── BatchOperationMethods.cs
│ │ └── BatchOptionsExtension.cs
│ ├── EFCore.Bulk-3.1.csproj
│ ├── EFCore.Bulk-5.0.csproj
│ ├── EFCore.Bulk-6.0.csproj
│ ├── Metadata
│ │ └── BulkEntityTypeExtensions.cs
│ ├── Query
│ │ ├── BulkQueryExecutor.cs
│ │ ├── NavigationExpandingExpressionVisitor.cs
│ │ ├── ParameterExtractingExpressionVisitor.cs
│ │ ├── QueryCompilationContextFactory.cs
│ │ ├── QueryCompiler.cs
│ │ └── QueryRelatedFactoryAndBypass.cs
│ └── Utilities
│ │ ├── Check.cs
│ │ ├── ExpressionBuilder.cs
│ │ ├── GenericUtility.cs
│ │ ├── MergeJoin.cs
│ │ ├── ReflectiveUtility.cs
│ │ └── ServiceAnnotation.cs
├── InMemory
│ ├── Bulk
│ │ ├── BulkOperationBase.cs
│ │ ├── DeleteOperation.cs
│ │ ├── MergeOperation.cs
│ │ ├── SelectIntoOperation.cs
│ │ ├── UpdateOperation.cs
│ │ └── UpsertOperation.cs
│ ├── EFCore.Bulk.InMemory-3.1.csproj
│ ├── EFCore.Bulk.InMemory-5.0.csproj
│ ├── EFCore.Bulk.InMemory-6.0.csproj
│ ├── InMemoryBatchExtensions.cs
│ ├── Query
│ │ ├── QueryCompiler.cs
│ │ ├── QueryContextParameterVisitor.cs
│ │ ├── QueryTranslationPreprocessor.cs
│ │ └── QueryableMethodTranslatingExpressionVisitor.cs
│ └── Storage
│ │ ├── MergeRemapper.cs
│ │ └── UpdateRemapper.cs
├── MySql
│ ├── EFCore.Bulk.MySql-3.1.csproj
│ ├── EFCore.Bulk.MySql-5.0.csproj
│ ├── EFCore.Bulk.MySql-6.0.csproj
│ ├── MySqlBatchExtensions.cs
│ ├── QueryCompilationContextFactory.cs
│ ├── QueryCompiler.cs
│ └── QuerySqlGenerator.cs
├── PostgreSql
│ ├── DateTimeOffsetTranslationPlugin.cs
│ ├── EFCore.Bulk.PostgreSql-3.1.csproj
│ ├── EFCore.Bulk.PostgreSql-5.0.csproj
│ ├── EFCore.Bulk.PostgreSql-6.0.csproj
│ ├── PostgreSqlBatchExtensions.cs
│ ├── QueryCompilationContextFactory.cs
│ ├── QueryCompiler.cs
│ └── QuerySqlGenerator.cs
├── Relational
│ ├── EFCore.Bulk.Relational-3.1.csproj
│ ├── EFCore.Bulk.Relational-5.0.csproj
│ ├── EFCore.Bulk.Relational-6.0.csproj
│ ├── Metadata
│ │ ├── EntityTypeExtensions.cs
│ │ └── StoreObjectIdentifier.cs
│ ├── Provider
│ │ └── ParameterBasedSqlProcessor.cs
│ ├── Query
│ │ ├── ExcludedTableColumnRewritingVisitor.cs
│ │ ├── ParameterValueBasedSelectExpressionOptimizer.cs
│ │ ├── QuerySqlGenerator.cs
│ │ ├── QueryTranslationPreprocessor.cs
│ │ ├── QueryableMethodTranslatingExpressionVisitor.cs
│ │ ├── RelationalBulkQueryExecutor.cs
│ │ ├── RelationalParameterBasedSqlProcessor.cs
│ │ ├── SearchConditionBooleanGuard.cs
│ │ ├── ShapedQueryCompilingExpressionVisitor.cs
│ │ ├── SqlExpressionFactoryExtensions.31.cs
│ │ ├── SqlExpressionFactoryExtensions.50.cs
│ │ ├── SqlExpressionFactoryExtensions.60.cs
│ │ ├── SqlExpressionFactoryExtensions.cs
│ │ ├── SqlExpressionVisitorV2.cs
│ │ ├── SqlExpressions
│ │ │ ├── AffectedRowsExpression.cs
│ │ │ ├── DeleteExpression.cs
│ │ │ ├── ExcludedTableColumnExpression.cs
│ │ │ ├── MergeExpression.cs
│ │ │ ├── SelectIntoExpression.cs
│ │ │ ├── TransientExpandValuesExpression.cs
│ │ │ ├── UpdateExpression.cs
│ │ │ ├── UpsertExpression.cs
│ │ │ ├── ValuesExpression.cs
│ │ │ └── WrappedExpression.cs
│ │ └── ValuesExpressionParameterExpandingVisitor.cs
│ ├── Reduce
│ │ ├── ColumnRewritingVisitor.cs
│ │ ├── OptionsBuilderExtensions.cs
│ │ ├── OptionsExtension.cs
│ │ ├── ProjectionBindingPruningExpressionVisitor.cs
│ │ ├── QueryTranslationPostprocessor.cs
│ │ ├── SelfJoinsPredicateComparer.cs
│ │ ├── SelfJoinsPruningExpressionVisitor.cs
│ │ └── ShaperQueryExpressionReplacingVisitor.cs
│ ├── RelationalExtensions.cs
│ └── Storage
│ │ ├── AnonymousExpressionFactory.cs
│ │ ├── BulkRelationalCommandBuilderExtensions.cs
│ │ └── ValuesRelationalParameter.cs
├── SqlServer
│ ├── EFCore.Bulk.SqlServer-3.1.csproj
│ ├── EFCore.Bulk.SqlServer-5.0.csproj
│ ├── EFCore.Bulk.SqlServer-6.0.csproj
│ ├── MathTranslationPlugin.cs
│ ├── QueryCompilationContextFactory.cs
│ ├── QueryCompiler.cs
│ ├── QuerySqlGenerator.cs
│ ├── QueryableMethodTranslatingExpressionVisitor.cs
│ ├── SqlServerBatchExtensions.cs
│ └── UpsertToMergeRewriter.cs
├── Sqlite
│ ├── EFCore.Bulk.Sqlite-3.1.csproj
│ ├── EFCore.Bulk.Sqlite-5.0.csproj
│ ├── EFCore.Bulk.Sqlite-6.0.csproj
│ ├── QueryCompilationContextFactory.cs
│ ├── QueryCompiler.cs
│ ├── QuerySqlGenerator.cs
│ ├── QueryableMethodTranslatingExpressionVisitor.cs
│ └── SqliteBatchExtensions.cs
└── TableInfo.cs
└── test
├── InMemory
├── Context.Factory.cs
├── Context.PrepareDatabase.cs
├── EFCore.Bulk.InMemory.Tests-3.1.csproj
├── EFCore.Bulk.InMemory.Tests-5.0.csproj
├── EFCore.Bulk.InMemory.Tests-6.0.csproj
├── Functional.Delete.cs
├── Functional.InsertInto.cs
├── Functional.MergeInto.cs
├── Functional.Update.cs
├── Functional.UpdateJoin.cs
└── Functional.Upsert.cs
├── MySql
├── Context.Factory.cs
├── Context.PrepareDatabase.cs
├── EFCore.Bulk.MySql.Tests-3.1.csproj
├── EFCore.Bulk.MySql.Tests-5.0.csproj
├── EFCore.Bulk.MySql.Tests-6.0.csproj
├── Functional.Delete.cs
├── Functional.InsertInto.cs
├── Functional.SelfJoinsRemoval.cs
├── Functional.Update.cs
├── Functional.UpdateJoin.cs
└── Functional.Upsert.cs
├── PostgreSql
├── Context.Factory.cs
├── Context.PrepareDatabase.cs
├── EFCore.Bulk.PostgreSql.Tests-3.1.csproj
├── EFCore.Bulk.PostgreSql.Tests-5.0.csproj
├── EFCore.Bulk.PostgreSql.Tests-6.0.csproj
├── Functional.Delete.cs
├── Functional.InsertInto.cs
├── Functional.SelfJoinsRemoval.cs
├── Functional.Update.cs
├── Functional.UpdateJoin.cs
└── Functional.Upsert.cs
├── Relational
├── Context.CommandInterceptor.cs
├── Context.Factory.cs
├── EFCore.Bulk.Relational.Tests-3.1.csproj
├── EFCore.Bulk.Relational.Tests-5.0.csproj
├── EFCore.Bulk.Relational.Tests-6.0.csproj
└── Functional.SelfJoinsRemoval.cs
├── Specifications
├── Compatibility.cs
├── Context.CommandNotifier.cs
├── Context.CommandTracer.cs
├── Context.Factory.cs
├── Context.LoggerFactory.cs
├── Context.PrepareDatabase.cs
├── Context.QueryTestBase.cs
├── EFCore.Bulk.Tests-3.1.csproj
├── EFCore.Bulk.Tests-5.0.csproj
├── EFCore.Bulk.Tests-6.0.csproj
├── Functional.Delete.cs
├── Functional.InsertInto.cs
├── Functional.MergeInto.cs
├── Functional.Update.cs
├── Functional.UpdateJoin.cs
└── Functional.Upsert.cs
├── SqlServer
├── Context.Factory.cs
├── Context.PrepareDatabase.cs
├── EFCore.Bulk.SqlServer.Tests-3.1.csproj
├── EFCore.Bulk.SqlServer.Tests-5.0.csproj
├── EFCore.Bulk.SqlServer.Tests-6.0.csproj
├── Functional.Delete.cs
├── Functional.InsertInto.cs
├── Functional.MergeInto.cs
├── Functional.SelfJoinsRemoval.cs
├── Functional.Update.cs
├── Functional.UpdateJoin.cs
└── Functional.Upsert.cs
├── Sqlite
├── Context.Factory.cs
├── Context.PrepareDatabase.cs
├── EFCore.Bulk.Sqlite.Tests-3.1.csproj
├── EFCore.Bulk.Sqlite.Tests-5.0.csproj
├── EFCore.Bulk.Sqlite.Tests-6.0.csproj
├── Functional.Delete.cs
├── Functional.InsertInto.cs
├── Functional.SelfJoinsRemoval.cs
├── Functional.Update.cs
├── Functional.UpdateJoin.cs
└── Functional.Upsert.cs
└── TestUtilities
├── DatabaseProvider.cs
├── DatabaseProviderSkipConditionAttribute.cs
├── EFCore.TestUtilities.csproj
├── TestPriorityAttribute.cs
├── TestPriorityOrderer.cs
└── Xunit
├── ConditionalFactAttribute.cs
├── ConditionalFactDiscoverer.cs
├── ConditionalFactTestCase.cs
├── ConditionalTheoryAttribute.cs
├── ConditionalTheoryDiscoverer.cs
├── ConditionalTheoryTestCase.cs
├── ContextualTestCaseRunner.cs
├── ITestCondition.cs
├── Output.cs
├── PlatformSkipConditionAttribute.cs
├── TestPlatform.cs
├── UseCultureAttribute.cs
└── XunitTestCaseExtensions.cs
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | dotnet_diagnostic.EF1001.severity = none
3 | csharp_style_prefer_range_operator = false:suggestion
4 |
--------------------------------------------------------------------------------
/Directory.Build.Override.props.sample:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildThisFileDirectory)..\microsoft\efcore-5
5 | $(MSBuildThisFileDirectory)..\microsoft\efcore.pg-5
6 | $(MSBuildThisFileDirectory)..\microsoft\efcore.my-5
7 | $(MSBuildThisFileDirectory)..\microsoft\efcore-6
8 | $(MSBuildThisFileDirectory)..\microsoft\efcore.pg-6
9 | $(MSBuildThisFileDirectory)..\microsoft\efcore.my-6
10 | $(MSBuildThisFileDirectory)..\microsoft\efcore
11 | $(MSBuildThisFileDirectory)..\microsoft\efcore.pg
12 | $(MSBuildThisFileDirectory)..\microsoft\efcore.my
13 |
14 |
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 borisdj
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | trigger:
2 | branches:
3 | include:
4 | - '*'
5 | tags:
6 | include:
7 | - '*'
8 |
9 | resources:
10 | repositories:
11 | - repository: "Azure Repos Mirror"
12 | type: git
13 | name: efcore/efcore-ext
14 |
15 | variables:
16 | - group: azure-repos-sync
17 |
18 | jobs:
19 | - job: Build
20 | displayName: "Build, Test and Publish"
21 |
22 | pool:
23 | vmImage: windows-2022
24 |
25 | steps:
26 | - task: PowerShell@2
27 | inputs:
28 | targetType: 'inline'
29 | script: |
30 | Write-Host "> Setting up PostgreSQL"
31 | $postgresService = Get-Service postgresql* | select -First 1
32 | Set-Service -InputObject $postgresService -StartupType Manual
33 | net start $postgresService.Name
34 | Write-Host ""
35 | Write-Host "> Setting up MySQL"
36 | mysqld --initialize-insecure
37 | mysqld install
38 | $mysqlService = Get-Service mysql* | select -First 1
39 | net start $mysqlService.Name
40 | mysql -u root --skip-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'Password12!'; FLUSH PRIVILEGES;"
41 | Write-Host ""
42 | Write-Host "> Listing .NET SDKs"
43 | dotnet --list-sdks
44 | displayName: 'Setup Environment'
45 |
46 | - task: DotNetCoreCLI@2
47 | inputs:
48 | command: 'build'
49 | displayName: 'Restore and Build Plugin'
50 |
51 | - task: DotNetCoreCLI@2
52 | inputs:
53 | command: 'test'
54 | arguments: '--no-restore --collect "Code coverage"'
55 | displayName: 'Run Unit Tests'
56 |
57 | - task: DotNetCoreCLI@2
58 | inputs:
59 | command: 'custom'
60 | custom: 'pack'
61 | arguments: '--no-restore -c Release -o $(Build.ArtifactStagingDirectory)'
62 | displayName: 'Package for NuGet'
63 |
64 | - task: PublishBuildArtifacts@1
65 | inputs:
66 | PathtoPublish: '$(Build.ArtifactStagingDirectory)'
67 | ArtifactName: 'drop'
68 | publishLocation: 'Container'
69 | displayName: 'Publish Artifacts'
70 |
71 | - job: Sync
72 | displayName: "Sync with Azure Repos"
73 |
74 | pool:
75 | vmImage: windows-latest
76 |
77 | steps:
78 | - task: gitmirror@0
79 | inputs:
80 | GitRepoUrl: 'https://tlylz:$(SYNC_PAT)@dev.azure.com/tlylz/efcore/_git/efcore-ext'
81 | displayName: 'Sync via Git Tools'
82 |
--------------------------------------------------------------------------------
/efcore-ext.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang-er/efcore-ext/958a175010dc8039e491a45beb82b91cfed76af0/efcore-ext.snk
--------------------------------------------------------------------------------
/src/Abstraction/EFCore.Bulk-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | $(DefineConstants);EFCORE31
6 | true
7 | Microsoft.EntityFrameworkCore.Bulk
8 | Microsoft.EntityFrameworkCore
9 | $(ExtensionPackagePrefix)
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Abstraction/EFCore.Bulk-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | $(DefineConstants);EFCORE50
6 | true
7 | Microsoft.EntityFrameworkCore.Bulk
8 | Microsoft.EntityFrameworkCore
9 | $(ExtensionPackagePrefix)
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Abstraction/EFCore.Bulk-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | true
7 | Microsoft.EntityFrameworkCore.Bulk
8 | Microsoft.EntityFrameworkCore
9 | $(ExtensionPackagePrefix)
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Abstraction/Metadata/BulkEntityTypeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Linq.Expressions;
4 | using System.Reflection;
5 |
6 | namespace Microsoft.EntityFrameworkCore.Metadata
7 | {
8 | internal static class BulkEntityTypeExtensions
9 | {
10 | public static bool TryGuessKey(this IEntityType entityType, IReadOnlyList bindings, out IKey key)
11 | {
12 | var assignments = new HashSet(bindings.OfType().Select(a => a.Member));
13 |
14 | foreach (var ikey in entityType.GetKeys())
15 | {
16 | bool fulfill = true;
17 | foreach (var prop in ikey.Properties)
18 | {
19 | var member = (MemberInfo)prop.PropertyInfo ?? prop.FieldInfo;
20 | if (!assignments.Contains(member)) fulfill = false;
21 | if (prop.ValueGenerated != ValueGenerated.Never) fulfill = false;
22 | }
23 |
24 | if (fulfill)
25 | {
26 | key = ikey;
27 | return true;
28 | }
29 | }
30 |
31 | key = null;
32 | return false;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Abstraction/Query/BulkQueryExecutor.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Query
4 | {
5 | ///
6 | /// The query executor for non-query commands.
7 | ///
8 | public interface IBulkQueryExecutor
9 | {
10 | ///
11 | /// Executes the non-query command.
12 | ///
13 | /// The count of affected rows.
14 | int Execute();
15 |
16 | ///
17 | /// Asynchronously executes the non-query command.
18 | ///
19 | /// The task for executing this command, returning count of affected rows.
20 | Task ExecuteAsync();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Abstraction/Query/QueryRelatedFactoryAndBypass.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Query
2 | {
3 | ///
4 | public interface IBulkQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory
5 | {
6 | }
7 |
8 | ///
9 | public interface IBulkQueryTranslationPostprocessorFactory : IQueryTranslationPostprocessorFactory
10 | {
11 | }
12 |
13 | ///
14 | public interface IBulkQueryTranslationPreprocessorFactory : IQueryTranslationPreprocessorFactory
15 | {
16 | }
17 |
18 | ///
19 | public interface IBulkShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory
20 | {
21 | }
22 |
23 | ///
24 | public class BypassBulkQueryTranslationPostprocessorFactory : IBulkQueryTranslationPostprocessorFactory
25 | {
26 | private readonly IQueryTranslationPostprocessorFactory _factory;
27 |
28 | /// Bypass the factory.
29 | public BypassBulkQueryTranslationPostprocessorFactory(IQueryTranslationPostprocessorFactory factory)
30 | {
31 | _factory = factory;
32 | }
33 |
34 | ///
35 | public QueryTranslationPostprocessor Create(QueryCompilationContext queryCompilationContext)
36 | {
37 | return _factory.Create(queryCompilationContext);
38 | }
39 | }
40 |
41 | ///
42 | public class BypassBulkShapedQueryCompilingExpressionVisitorFactory : IBulkShapedQueryCompilingExpressionVisitorFactory
43 | {
44 | private readonly IShapedQueryCompilingExpressionVisitorFactory _factory;
45 |
46 | /// Bypass the factory.
47 | public BypassBulkShapedQueryCompilingExpressionVisitorFactory(IShapedQueryCompilingExpressionVisitorFactory factory)
48 | {
49 | _factory = factory;
50 | }
51 |
52 | ///
53 | public ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext)
54 | {
55 | return _factory.Create(queryCompilationContext);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Abstraction/Utilities/ExpressionBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using System.Reflection;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Utilities
6 | {
7 | internal class ExpressionBuilder
8 | {
9 | public const BindingFlags InstanceLevel
10 | = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
11 |
12 | public ParameterExpression Parameter { get; }
13 |
14 | public Expression Current { get; private set; }
15 |
16 | private ExpressionBuilder(ParameterExpression parameterExpression)
17 | {
18 | Parameter = parameterExpression;
19 | Current = parameterExpression;
20 | }
21 |
22 | public ExpressionBuilder AccessField(string fieldName)
23 | {
24 | Current = Expression.Field(
25 | Current,
26 | Current.Type.GetField(fieldName, InstanceLevel));
27 |
28 | return this;
29 | }
30 |
31 | public ExpressionBuilder AccessProperty(string propertyName)
32 | {
33 | Current = Expression.Property(
34 | Current,
35 | Current.Type.GetProperty(propertyName, InstanceLevel));
36 |
37 | return this;
38 | }
39 |
40 | public ExpressionBuilder As()
41 | {
42 | Current = Expression.Convert(Current, typeof(T));
43 | return this;
44 | }
45 |
46 | public ExpressionBuilder As(Type type)
47 | {
48 | Current = Expression.Convert(Current, type);
49 | return this;
50 | }
51 |
52 | public static ExpressionBuilder Begin()
53 | {
54 | return new ExpressionBuilder(Expression.Parameter(typeof(T), "args0"));
55 | }
56 |
57 | public TDelegate Compile()
58 | {
59 | return Expression.Lambda(Current, Parameter).Compile();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Abstraction/Utilities/ServiceAnnotation.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Bulk
2 | {
3 | ///
4 | /// An interface for annotate the original service and implementation.
5 | ///
6 | /// The service type.
7 | /// The implementation type.
8 | public interface IServiceAnnotation
9 | where TImplementation : TService
10 | where TService : class
11 | {
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/InMemory/Bulk/BulkOperationBase.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Bulk
6 | {
7 | public abstract class BulkOperationBase : IBulkQueryExecutor
8 | {
9 | protected readonly object _queryEnumerable;
10 | protected readonly DbContext _dbContext;
11 | protected readonly QueryContext _queryContext;
12 |
13 | protected BulkOperationBase(QueryContext queryContext, object queryExecutor)
14 | {
15 | _queryEnumerable = queryExecutor;
16 | _queryContext = queryContext;
17 | _dbContext = queryContext.Context;
18 | }
19 |
20 | protected abstract void Process(TEntity entity);
21 |
22 | public virtual int Execute()
23 | {
24 | foreach (var entry in (IEnumerable)_queryEnumerable)
25 | {
26 | Process(entry);
27 | }
28 |
29 | return _dbContext.SaveChanges();
30 | }
31 |
32 | public virtual async Task ExecuteAsync()
33 | {
34 | await foreach (var entry in (IAsyncEnumerable)_queryEnumerable)
35 | {
36 | Process(entry);
37 | }
38 |
39 | return await _dbContext.SaveChangesAsync(_queryContext.CancellationToken);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/InMemory/Bulk/DeleteOperation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Bulk
4 | {
5 | public class DeleteOperation : BulkOperationBase
6 | {
7 | public DeleteOperation(QueryContext queryContext, object queryExecutor)
8 | : base(queryContext, queryExecutor)
9 | {
10 | }
11 |
12 | protected override void Process(TEntity entity)
13 | {
14 | _dbContext.Remove(entity);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/InMemory/Bulk/MergeOperation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using Microsoft.EntityFrameworkCore.Storage.Internal;
3 | using System;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Bulk
6 | {
7 | public class MergeOperation : BulkOperationBase>
8 | {
9 | private readonly Func _insertShaper;
10 | private readonly Func _updateExtractor;
11 | private readonly bool _deleteDo;
12 |
13 | public MergeOperation(
14 | QueryContext queryContext,
15 | object queryExecutor,
16 | Func insertShaper,
17 | Func updateExtractor,
18 | bool deleteDo)
19 | : base(queryContext, queryExecutor)
20 | {
21 | _insertShaper = insertShaper;
22 | _updateExtractor = updateExtractor;
23 | _deleteDo = deleteDo;
24 | }
25 |
26 | protected override void Process(MergeRemapper entity)
27 | {
28 | if (entity.Outer == null && entity.Inner == null)
29 | {
30 | throw new InvalidProgramException("There's something wrong with System.Linq.");
31 | }
32 | else if (entity.Outer != null && entity.Inner != null && _updateExtractor != null)
33 | {
34 | _dbContext.Update(_updateExtractor(_queryContext, entity.Outer, entity.Inner));
35 | }
36 | else if (entity.Outer != null && entity.Inner == null && _deleteDo)
37 | {
38 | _dbContext.Remove(entity.Outer);
39 | }
40 | else if (entity.Outer == null && entity.Inner != null && _insertShaper != null)
41 | {
42 | _dbContext.Add(_insertShaper(_queryContext, entity.Inner));
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/InMemory/Bulk/SelectIntoOperation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Bulk
4 | {
5 | public class SelectIntoOperation : BulkOperationBase
6 | {
7 | public SelectIntoOperation(QueryContext queryContext, object queryExecutor)
8 | : base(queryContext, queryExecutor)
9 | {
10 | }
11 |
12 | protected override void Process(TEntity entity)
13 | {
14 | _dbContext.Add(entity);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/InMemory/Bulk/UpdateOperation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using Microsoft.EntityFrameworkCore.Storage.Internal;
3 | using System;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Bulk
6 | {
7 | public class UpdateOperation : BulkOperationBase>
8 | {
9 | private readonly Func, TEntity> _updaterAndExtractor;
10 |
11 | public UpdateOperation(
12 | QueryContext queryContext,
13 | object queryExecutor,
14 | Func, TEntity> updaterAndExtractor)
15 | : base(queryContext, queryExecutor)
16 | {
17 | _updaterAndExtractor = updaterAndExtractor;
18 | }
19 |
20 | protected override void Process(UpdateRemapper source)
21 | {
22 | _dbContext.Update(_updaterAndExtractor(_queryContext, source));
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/InMemory/Bulk/UpsertOperation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using Microsoft.EntityFrameworkCore.Storage.Internal;
3 | using System;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Bulk
6 | {
7 | public class UpsertOperation : BulkOperationBase>
8 | {
9 | private readonly Func _insertShaper;
10 | private readonly Func _updateExtractor;
11 |
12 | public UpsertOperation(
13 | QueryContext queryContext,
14 | object queryExecutor,
15 | Func insertShaper,
16 | Func updateExtractor)
17 | : base(queryContext, queryExecutor)
18 | {
19 | _insertShaper = insertShaper;
20 | _updateExtractor = updateExtractor;
21 | }
22 |
23 | protected override void Process(MergeRemapper entity)
24 | {
25 | var excluded = _insertShaper(_queryContext, entity.Inner);
26 |
27 | if (entity.Outer == null)
28 | {
29 | _dbContext.Add(excluded);
30 | }
31 | else if (_updateExtractor != null)
32 | {
33 | _dbContext.Update(_updateExtractor(_queryContext, entity.Outer, excluded));
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/InMemory/EFCore.Bulk.InMemory-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | $(DefineConstants);EFCORE31;IN_MEMORY
6 | Microsoft.EntityFrameworkCore.Bulk.InMemory
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).InMemory
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/InMemory/EFCore.Bulk.InMemory-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | $(DefineConstants);EFCORE50;IN_MEMORY
6 | Microsoft.EntityFrameworkCore.Bulk.InMemory
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).InMemory
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/InMemory/EFCore.Bulk.InMemory-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60;IN_MEMORY
6 | Microsoft.EntityFrameworkCore.Bulk.InMemory
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).InMemory
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/InMemory/InMemoryBatchExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Bulk;
2 | using Microsoft.EntityFrameworkCore.Infrastructure;
3 | using Microsoft.EntityFrameworkCore.InMemory.Query.Internal;
4 | using Microsoft.EntityFrameworkCore.Query;
5 | using Microsoft.EntityFrameworkCore.Query.Internal;
6 | using System.Collections.Generic;
7 | using System.Linq.Expressions;
8 | using System.Reflection;
9 |
10 | namespace Microsoft.EntityFrameworkCore
11 | {
12 | public static class InMemoryBatchExtensions
13 | {
14 | public static InMemoryDbContextOptionsBuilder UseBulk(this InMemoryDbContextOptionsBuilder builder)
15 | {
16 | var builder1 = typeof(InMemoryDbContextOptionsBuilder)
17 | .GetProperty("OptionsBuilder", BindingFlags.Instance | BindingFlags.NonPublic)
18 | .GetValue(builder) as IDbContextOptionsBuilderInfrastructure;
19 | builder1.AddOrUpdateExtension(new InMemoryBatchOptionsExtension());
20 | return builder;
21 | }
22 |
23 | #if EFCORE60
24 | internal static void ReplaceProjectionMapping(
25 | this InMemoryQueryExpression expression,
26 | IReadOnlyDictionary projectionMapping)
27 | {
28 | expression.ReplaceProjection(projectionMapping);
29 | }
30 | #endif
31 | }
32 |
33 | internal class InMemoryBatchOptionsExtension : BatchOptionsExtension
34 | {
35 | public override string Name => "InMemoryBatchExtension";
36 |
37 | protected override void ApplyServices(ExtensionServicesBuilder services)
38 | {
39 | services.TryAdd();
40 | services.TryAdd();
41 | services.TryAdd();
42 | services.TryAdd();
43 | services.TryAdd();
44 |
45 | services.TryAdd();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/InMemory/Query/QueryContextParameterVisitor.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Reflection;
7 |
8 | namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal
9 | {
10 | public class QueryContextParameterVisitor : ExpressionVisitor
11 | {
12 | private const string CompiledQueryParameterPrefix = "__";
13 |
14 | private static readonly MethodInfo translatingGetParameterValue
15 | = typeof(InMemoryExpressionTranslatingExpressionVisitor)
16 | .GetTypeInfo().GetDeclaredMethod("GetParameterValue");
17 |
18 | private readonly ParameterExpression[] _excluded;
19 |
20 | protected override Expression VisitParameter(ParameterExpression node)
21 | {
22 | if (_excluded.Contains(node)) return node;
23 |
24 | if (node.Name?.StartsWith(CompiledQueryParameterPrefix, StringComparison.Ordinal) == true)
25 | {
26 | return Expression.Call(
27 | translatingGetParameterValue.MakeGenericMethod(node.Type),
28 | QueryCompilationContext.QueryContextParameter,
29 | Expression.Constant(node.Name));
30 | }
31 |
32 | return node;
33 | }
34 |
35 | private QueryContextParameterVisitor(ParameterExpression[] excluded)
36 | {
37 | _excluded = excluded;
38 | }
39 |
40 | public static Expression Process(Expression origin, IEnumerable excluded)
41 | {
42 | return new QueryContextParameterVisitor(excluded?.ToArray() ?? Array.Empty()).Visit(origin);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/InMemory/Storage/MergeRemapper.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Storage.Internal
2 | {
3 | public class MergeRemapper
4 | {
5 | public MergeRemapper(TOuter outer, TInner inner)
6 | {
7 | Outer = outer;
8 | Inner = inner;
9 | }
10 |
11 | public TOuter Outer { get; }
12 |
13 | public TInner Inner { get; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/InMemory/Storage/UpdateRemapper.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Storage.Internal
2 | {
3 | public class UpdateRemapper
4 | {
5 | public UpdateRemapper(TEntity origin, TEntity update)
6 | {
7 | Origin = origin;
8 | Update = update;
9 | }
10 |
11 | public TEntity Origin { get; }
12 |
13 | public TEntity Update { get; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/MySql/EFCore.Bulk.MySql-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | $(DefineConstants);EFCORE31;RELATIONAL;MYSQL
6 | Microsoft.EntityFrameworkCore.Bulk.MySql
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).MySql
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/MySql/EFCore.Bulk.MySql-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | $(DefineConstants);EFCORE50;RELATIONAL;MYSQL
6 | Microsoft.EntityFrameworkCore.Bulk.MySql
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).MySql
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/MySql/EFCore.Bulk.MySql-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60;RELATIONAL;MYSQL
6 | Microsoft.EntityFrameworkCore.Bulk.MySql
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).MySql
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/MySql/MySqlBatchExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Bulk;
2 | using Microsoft.EntityFrameworkCore.Infrastructure;
3 | using Microsoft.EntityFrameworkCore.Query;
4 | using Microsoft.EntityFrameworkCore.Query.Internal;
5 | using Microsoft.EntityFrameworkCore.Storage.Internal;
6 | using Pomelo.EntityFrameworkCore.MySql.Query;
7 |
8 | namespace Microsoft.EntityFrameworkCore
9 | {
10 | public static class MySqlBatchExtensions
11 | {
12 | public static MySqlDbContextOptionsBuilder UseBulk(this MySqlDbContextOptionsBuilder builder)
13 | {
14 | var builder1 = ((IRelationalDbContextOptionsBuilderInfrastructure)builder).OptionsBuilder;
15 | var ext = new MySqlBatchOptionsExtension();
16 | ((IDbContextOptionsBuilderInfrastructure)builder1).AddOrUpdateExtension(ext);
17 | return builder;
18 | }
19 | }
20 |
21 | public class MySqlBatchOptionsExtension : RelationalBatchOptionsExtension
22 | {
23 | public override string Name => "MySqlBatchExtension";
24 |
25 | protected override void ApplyServices(ExtensionServicesBuilder services)
26 | {
27 | services.TryAdd();
28 |
29 | services.TryAdd();
30 | services.TryAdd();
31 | services.TryAdd();
32 | services.TryAdd();
33 |
34 | services.TryAdd();
35 | services.TryAdd();
36 | #if EFCORE50 || EFCORE60
37 | services.TryAdd();
38 | services.TryAdd();
39 | #elif EFCORE31
40 | services.TryAdd();
41 | SearchConditionBooleanGuard.AddTypeField(typeof(NullSemanticsRewritingExpressionVisitor), "_canOptimize");
42 | #endif
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/MySql/QueryCompilationContextFactory.cs:
--------------------------------------------------------------------------------
1 | #if EFCORE50 || EFCORE60
2 |
3 | using Microsoft.EntityFrameworkCore.Bulk;
4 | using Microsoft.EntityFrameworkCore.Query;
5 | using Pomelo.EntityFrameworkCore.MySql.Query.Internal;
6 | using System;
7 | using System.Linq.Expressions;
8 |
9 | namespace Pomelo.EntityFrameworkCore.MySql.Query
10 | {
11 | public class MySqlBulkQueryCompilationContextFactory :
12 | MySqlQueryCompilationContextFactory,
13 | IBulkQueryCompilationContextFactory,
14 | IServiceAnnotation
15 | {
16 | public MySqlBulkQueryCompilationContextFactory(
17 | BulkQueryCompilationContextDependencies dependencies,
18 | RelationalQueryCompilationContextDependencies relationalDependencies)
19 | : base(dependencies.Dependencies, relationalDependencies)
20 | {
21 | }
22 |
23 | public Func CreateQueryExecutor(bool async, Expression query)
24 | {
25 | return Create(async).CreateQueryExecutor(query);
26 | }
27 | }
28 | }
29 |
30 | #endif
--------------------------------------------------------------------------------
/src/MySql/QueryCompiler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Bulk;
3 | using Microsoft.EntityFrameworkCore.Diagnostics;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Metadata;
6 | using Microsoft.EntityFrameworkCore.Query;
7 | using Microsoft.EntityFrameworkCore.Query.Internal;
8 | using Microsoft.EntityFrameworkCore.Storage;
9 | using System;
10 | using System.Linq.Expressions;
11 |
12 | namespace Pomelo.EntityFrameworkCore.MySql.Query
13 | {
14 | public class MySqlBulkQueryCompiler : BulkQueryCompiler
15 | {
16 | public MySqlBulkQueryCompiler(
17 | IQueryContextFactory queryContextFactory,
18 | ICompiledQueryCache compiledQueryCache,
19 | ICompiledQueryCacheKeyGenerator compiledQueryCacheKeyGenerator,
20 | IDatabase database,
21 | IDiagnosticsLogger logger,
22 | ICurrentDbContext currentContext,
23 | IEvaluatableExpressionFilter evaluatableExpressionFilter,
24 | IBulkQueryCompilationContextFactory qccFactory,
25 | IModel model)
26 | : base(queryContextFactory,
27 | compiledQueryCache,
28 | compiledQueryCacheKeyGenerator,
29 | database,
30 | logger,
31 | currentContext,
32 | evaluatableExpressionFilter,
33 | qccFactory,
34 | model)
35 | {
36 | }
37 |
38 | protected override Func CompileBulkCore(IDatabase database, Expression query, IModel model, bool async)
39 | {
40 | if (query is MethodCallExpression methodCallExpression
41 | && methodCallExpression.Method.GetGenericMethodDefinition() == BatchOperationMethods.MergeCollapsed)
42 | {
43 | throw TranslationFailed(query, "MERGE INTO sentences are not supported in MySQL.");
44 | }
45 |
46 | return base.CompileBulkCore(database, query, model, async);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/PostgreSql/EFCore.Bulk.PostgreSql-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | $(DefineConstants);EFCORE31;RELATIONAL;POSTGRE_SQL
6 | Microsoft.EntityFrameworkCore.Bulk.PostgreSql
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).PostgreSql
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/PostgreSql/EFCore.Bulk.PostgreSql-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | $(DefineConstants);EFCORE50;RELATIONAL;POSTGRE_SQL
6 | Microsoft.EntityFrameworkCore.Bulk.PostgreSql
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).PostgreSql
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/PostgreSql/EFCore.Bulk.PostgreSql-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60;RELATIONAL;POSTGRE_SQL
6 | Microsoft.EntityFrameworkCore.Bulk.PostgreSql
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).PostgreSql
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/PostgreSql/QueryCompilationContextFactory.cs:
--------------------------------------------------------------------------------
1 | #if EFCORE50 || EFCORE60
2 |
3 | using Microsoft.EntityFrameworkCore.Bulk;
4 | using Microsoft.EntityFrameworkCore.Query;
5 | using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
6 | using System;
7 | using System.Linq.Expressions;
8 |
9 | namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query
10 | {
11 | public class NpgsqlBulkQueryCompilationContextFactory :
12 | NpgsqlQueryCompilationContextFactory,
13 | IBulkQueryCompilationContextFactory,
14 | IServiceAnnotation
15 | {
16 | public NpgsqlBulkQueryCompilationContextFactory(
17 | BulkQueryCompilationContextDependencies dependencies,
18 | RelationalQueryCompilationContextDependencies relationalDependencies)
19 | : base(dependencies.Dependencies, relationalDependencies)
20 | {
21 | }
22 |
23 | public Func CreateQueryExecutor(bool async, Expression query)
24 | {
25 | return Create(async).CreateQueryExecutor(query);
26 | }
27 | }
28 | }
29 |
30 | #endif
--------------------------------------------------------------------------------
/src/PostgreSql/QueryCompiler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Bulk;
3 | using Microsoft.EntityFrameworkCore.Diagnostics;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Metadata;
6 | using Microsoft.EntityFrameworkCore.Query;
7 | using Microsoft.EntityFrameworkCore.Query.Internal;
8 | using Microsoft.EntityFrameworkCore.Storage;
9 | using System;
10 | using System.Linq.Expressions;
11 |
12 | namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query
13 | {
14 | public class NpgsqlBulkQueryCompiler : BulkQueryCompiler
15 | {
16 | public NpgsqlBulkQueryCompiler(
17 | IQueryContextFactory queryContextFactory,
18 | ICompiledQueryCache compiledQueryCache,
19 | ICompiledQueryCacheKeyGenerator compiledQueryCacheKeyGenerator,
20 | IDatabase database,
21 | IDiagnosticsLogger logger,
22 | ICurrentDbContext currentContext,
23 | IEvaluatableExpressionFilter evaluatableExpressionFilter,
24 | IBulkQueryCompilationContextFactory qccFactory,
25 | IModel model)
26 | : base(queryContextFactory,
27 | compiledQueryCache,
28 | compiledQueryCacheKeyGenerator,
29 | database,
30 | logger,
31 | currentContext,
32 | evaluatableExpressionFilter,
33 | qccFactory,
34 | model)
35 | {
36 | }
37 |
38 | protected override Func CompileBulkCore(IDatabase database, Expression query, IModel model, bool async)
39 | {
40 | if (query is MethodCallExpression methodCallExpression
41 | && methodCallExpression.Method.GetGenericMethodDefinition() == BatchOperationMethods.MergeCollapsed)
42 | {
43 | throw TranslationFailed(query, "MERGE INTO sentences are not supported in PostgreSQL.");
44 | }
45 |
46 | return base.CompileBulkCore(database, query, model, async);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Relational/EFCore.Bulk.Relational-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | $(DefineConstants);EFCORE31;RELATIONAL
6 | Microsoft.EntityFrameworkCore.Bulk.Relational
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).Relational
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/Relational/EFCore.Bulk.Relational-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | $(DefineConstants);EFCORE50;RELATIONAL
6 | Microsoft.EntityFrameworkCore.Bulk.Relational
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).Relational
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/Relational/EFCore.Bulk.Relational-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60;RELATIONAL
6 | Microsoft.EntityFrameworkCore.Bulk.Relational
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).Relational
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/Relational/Metadata/EntityTypeExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Metadata
6 | {
7 | public static class BulkEntityTypeExtensions
8 | {
9 | public static Dictionary GetColumns(this IEntityType entityType)
10 | {
11 | var store = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table).Value;
12 | var result = new Dictionary();
13 |
14 | void DiscoverOwnedEntity(IEntityType currentNavigated, string baseName)
15 | {
16 | foreach (var property in currentNavigated.GetProperties())
17 | {
18 | var name = baseName + "." + property.Name;
19 | result.Add(name.Trim('.'), property.GetColumnName(store));
20 | }
21 |
22 | foreach (var navigation in currentNavigated.GetNavigations())
23 | {
24 | if (!navigation.ForeignKey.IsOwnership) continue;
25 | var name = baseName + "." + navigation.Name;
26 | DiscoverOwnedEntity(navigation.ForeignKey.DeclaringEntityType, name);
27 | }
28 | }
29 |
30 | DiscoverOwnedEntity(entityType, "");
31 | return result;
32 | }
33 |
34 | public static Dictionary GetValueConverters(this IEntityType entityType)
35 | {
36 | var store = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table).Value;
37 | var result = new Dictionary();
38 |
39 | void DiscoverOwnedEntity(IEntityType currentNavigated, string baseName)
40 | {
41 | foreach (var property in currentNavigated.GetProperties())
42 | {
43 | var name = baseName + "." + property.Name;
44 | var converter = property.GetValueConverter();
45 | if (converter != null)
46 | {
47 | result.Add(name.Trim('.'), converter);
48 | }
49 | }
50 |
51 | foreach (var navigation in currentNavigated.GetNavigations())
52 | {
53 | if (!navigation.ForeignKey.IsOwnership) continue;
54 | var name = baseName + "." + navigation.Name;
55 | DiscoverOwnedEntity(navigation.ForeignKey.DeclaringEntityType, name);
56 | }
57 | }
58 |
59 | DiscoverOwnedEntity(entityType, "");
60 | return result;
61 | }
62 |
63 | public static IEntityType FindEntityTypeByTable(this IModel model, string tableName)
64 | {
65 | return model.GetEntityTypes()
66 | .Where(e => e.GetTableName() == tableName && !e.IsOwned())
67 | .FirstOrDefault();
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Relational/Metadata/StoreObjectIdentifier.cs:
--------------------------------------------------------------------------------
1 | #if EFCORE31
2 | #pragma warning disable IDE0060
3 |
4 | namespace Microsoft.EntityFrameworkCore.Metadata
5 | {
6 | internal enum StoreObjectType
7 | {
8 | Table,
9 | }
10 |
11 | internal readonly struct StoreObjectIdentifier
12 | {
13 | public static StoreObjectIdentifier? Create(IEntityType _, StoreObjectType __)
14 | {
15 | return new StoreObjectIdentifier();
16 | }
17 | }
18 |
19 | internal static class StoreObjectIdentifierCompatibility
20 | {
21 | public static string GetColumnName(this IProperty property, in StoreObjectIdentifier _)
22 | {
23 | return property.GetColumnName();
24 | }
25 | }
26 | }
27 |
28 | #pragma warning restore IDE0060
29 | #endif
--------------------------------------------------------------------------------
/src/Relational/Query/ExcludedTableColumnRewritingVisitor.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
2 | using System.Linq.Expressions;
3 |
4 | namespace Microsoft.EntityFrameworkCore.Query
5 | {
6 | public class ExcludedTableColumnRewritingVisitor : SqlExpressionVisitorV2
7 | {
8 | private readonly TableExpression _excludedTable;
9 |
10 | public ExcludedTableColumnRewritingVisitor(TableExpression excludedTable)
11 | {
12 | _excludedTable = excludedTable;
13 | }
14 |
15 | protected override Expression VisitColumn(ColumnExpression columnExpression)
16 | {
17 | if (columnExpression.Table == _excludedTable)
18 | {
19 | return new ExcludedTableColumnExpression(
20 | columnExpression.Name,
21 | columnExpression.Type,
22 | columnExpression.TypeMapping,
23 | columnExpression.IsNullable);
24 | }
25 | else
26 | {
27 | return base.VisitColumn(columnExpression);
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Relational/Query/ParameterValueBasedSelectExpressionOptimizer.cs:
--------------------------------------------------------------------------------
1 | #if EFCORE31
2 | using Microsoft.EntityFrameworkCore.Query.Internal;
3 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
4 | using Microsoft.EntityFrameworkCore.Storage;
5 | using System.Collections.Generic;
6 |
7 | namespace Microsoft.EntityFrameworkCore.Query
8 | {
9 | public class BulkParameterValueBasedSelectExpressionOptimizer : ParameterValueBasedSelectExpressionOptimizer
10 | {
11 | private readonly ISqlExpressionFactory _sqlExpressionFactory;
12 |
13 | public BulkParameterValueBasedSelectExpressionOptimizer(
14 | ISqlExpressionFactory sqlExpressionFactory,
15 | IParameterNameGeneratorFactory parameterNameGeneratorFactory,
16 | bool useRelationalNulls)
17 | : base(sqlExpressionFactory, parameterNameGeneratorFactory, useRelationalNulls)
18 | {
19 | _sqlExpressionFactory = sqlExpressionFactory;
20 | }
21 |
22 | public override (SelectExpression selectExpression, bool canCache) Optimize(
23 | SelectExpression selectExpression,
24 | IReadOnlyDictionary parametersValues)
25 | {
26 | var (optimizedSelectExpression, canCache) = base.Optimize(selectExpression, parametersValues);
27 | var valuesVisitor = new ValuesExpressionParameterExpandingVisitor(_sqlExpressionFactory, parametersValues);
28 |
29 | optimizedSelectExpression = (SelectExpression)valuesVisitor.Visit(optimizedSelectExpression);
30 | canCache = canCache && valuesVisitor.CanCache;
31 |
32 | return (optimizedSelectExpression, canCache);
33 | }
34 | }
35 | }
36 |
37 | #endif
38 |
--------------------------------------------------------------------------------
/src/Relational/Query/QuerySqlGenerator.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Query
2 | {
3 | public interface IBulkQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Relational/Query/RelationalBulkQueryExecutor.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Infrastructure;
2 | using Microsoft.EntityFrameworkCore.Internal;
3 | using Microsoft.EntityFrameworkCore.Query.Internal;
4 | using Microsoft.EntityFrameworkCore.Storage;
5 | using System.Threading.Tasks;
6 |
7 | namespace Microsoft.EntityFrameworkCore.Query
8 | {
9 | public class RelationalBulkQueryExecutor : IBulkQueryExecutor
10 | {
11 | private readonly RelationalCommandCache _relationalCommandCache;
12 | private readonly RelationalQueryContext _queryContext;
13 |
14 | public RelationalBulkQueryExecutor(
15 | RelationalQueryContext relationalQueryContext,
16 | RelationalCommandCache relationalCommandCache)
17 | {
18 | _relationalCommandCache = relationalCommandCache;
19 | _queryContext = relationalQueryContext;
20 | }
21 |
22 | public int Execute()
23 | {
24 | using (_queryContext.ConcurrencyDetector.EnterCriticalSection())
25 | {
26 | #if EFCORE50 || EFCORE60
27 | EntityFrameworkEventSource.Log.QueryExecuting();
28 | #endif
29 |
30 | return _relationalCommandCache
31 | .RentAndPopulateRelationalCommand(_queryContext)
32 | .ExecuteNonQuery(
33 | new RelationalCommandParameterObject(
34 | _queryContext.Connection,
35 | _queryContext.ParameterValues,
36 | null,
37 | _queryContext.Context,
38 | _queryContext.CommandLogger));
39 | }
40 | }
41 |
42 | public async Task ExecuteAsync()
43 | {
44 | using (_queryContext.ConcurrencyDetector.EnterCriticalSection())
45 | {
46 | #if EFCORE50 || EFCORE60
47 | EntityFrameworkEventSource.Log.QueryExecuting();
48 | #endif
49 |
50 | return await _relationalCommandCache
51 | .RentAndPopulateRelationalCommand(_queryContext)
52 | .ExecuteNonQueryAsync(
53 | new RelationalCommandParameterObject(
54 | _queryContext.Connection,
55 | _queryContext.ParameterValues,
56 | null,
57 | _queryContext.Context,
58 | _queryContext.CommandLogger),
59 | _queryContext.CancellationToken)
60 | .ConfigureAwait(false);
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Relational/Query/RelationalParameterBasedSqlProcessor.cs:
--------------------------------------------------------------------------------
1 | #if EFCORE50 || EFCORE60
2 |
3 | namespace Microsoft.EntityFrameworkCore.Query
4 | {
5 | public interface IRelationalBulkParameterBasedSqlProcessorFactory : IRelationalParameterBasedSqlProcessorFactory
6 | {
7 | }
8 | }
9 |
10 | #endif
--------------------------------------------------------------------------------
/src/Relational/Query/SearchConditionBooleanGuard.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Utilities;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq.Expressions;
5 |
6 | namespace Microsoft.EntityFrameworkCore.Query
7 | {
8 | internal class SearchConditionBooleanGuard : IDisposable
9 | {
10 | // bool value: True for predicate sections, false for value sections.
11 | public delegate void Setter(ExpressionVisitor expressionVisitor, bool isSearchCondition);
12 | public delegate bool Getter(ExpressionVisitor expressionVisitor);
13 | public static readonly Dictionary VisitorTypes = new Dictionary();
14 |
15 | private bool _disposed;
16 | private bool _originalValue;
17 | private ExpressionVisitor _visitor;
18 | private Setter _setter;
19 |
20 | public static void AddTypeField(Type type, string memberName)
21 | {
22 | lock (VisitorTypes)
23 | {
24 | if (VisitorTypes.ContainsKey(type)) return;
25 | var param = Expression.Parameter(typeof(ExpressionVisitor));
26 | var value = Expression.Parameter(typeof(bool));
27 | var convert = Expression.Convert(param, type);
28 | var member = type.GetField(memberName, ReflectiveUtility.InstanceLevel);
29 | var field = Expression.Field(convert, member);
30 | var getter = Expression.Lambda(field, param);
31 | var setter = Expression.Lambda(Expression.Assign(field, value), param, value);
32 | VisitorTypes.Add(type, (getter.Compile(), setter.Compile()));
33 | }
34 | }
35 |
36 | private SearchConditionBooleanGuard(ExpressionVisitor visitor, Setter setter, bool origin)
37 | {
38 | _visitor = visitor;
39 | _setter = setter;
40 | _originalValue = origin;
41 | }
42 |
43 | public static IDisposable With(ExpressionVisitor expressionVisitor, bool isSearchCondition, bool? outside = default)
44 | {
45 | var type = expressionVisitor.GetType();
46 | if (!VisitorTypes.ContainsKey(type)) return null;
47 |
48 | var (getter, setter) = VisitorTypes[type];
49 | bool current = getter.Invoke(expressionVisitor);
50 | if (outside.HasValue && outside.Value != current)
51 | {
52 | throw new InvalidOperationException("State corrupt.");
53 | }
54 |
55 | setter.Invoke(expressionVisitor, isSearchCondition);
56 | return new SearchConditionBooleanGuard(expressionVisitor, setter, current);
57 | }
58 |
59 | protected void Dispose(bool disposing)
60 | {
61 | if (!_disposed)
62 | {
63 | if (disposing)
64 | {
65 | _setter?.Invoke(_visitor, _originalValue);
66 | }
67 |
68 | _visitor = null;
69 | _setter = null;
70 | _disposed = true;
71 | }
72 | }
73 |
74 | public void Dispose()
75 | {
76 | Dispose(disposing: true);
77 | GC.SuppressFinalize(this);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Relational/Query/SqlExpressions/AffectedRowsExpression.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Storage;
2 | using System.Linq.Expressions;
3 |
4 | namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
5 | {
6 | public class AffectedRowsExpression : SqlExpression
7 | {
8 | public AffectedRowsExpression()
9 | : base(typeof(int), RelationalTypeMapping.NullMapping)
10 | {
11 | }
12 |
13 | ///
14 | #if EFCORE50 || EFCORE60
15 | protected override void Print(ExpressionPrinter expressionPrinter)
16 | #elif EFCORE31
17 | public override void Print(ExpressionPrinter expressionPrinter)
18 | #endif
19 | {
20 | expressionPrinter.Append("affected rows");
21 | }
22 |
23 | protected override Expression VisitChildren(ExpressionVisitor visitor)
24 | {
25 | return this;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Relational/Query/SqlExpressions/DeleteExpression.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 | using Microsoft.EntityFrameworkCore.Storage;
3 | using Microsoft.EntityFrameworkCore.Utilities;
4 | using System.Collections.Generic;
5 | using System.Linq.Expressions;
6 |
7 | namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
8 | {
9 | ///
10 | /// An expression that represents a DELETE in a SQL tree.
11 | ///
12 | public sealed class DeleteExpression : WrappedExpression
13 | {
14 | public DeleteExpression(
15 | TableExpression table,
16 | SqlExpression? predicate,
17 | IReadOnlyList joinedTables)
18 | {
19 | Check.NotNull(table, nameof(table));
20 | Check.HasNoNulls(joinedTables, nameof(joinedTables));
21 |
22 | Table = table;
23 | JoinedTables = joinedTables;
24 | Predicate = predicate;
25 | }
26 |
27 | ///
28 | /// The primary table to operate on.
29 | ///
30 | public TableExpression Table { get; }
31 |
32 | ///
33 | /// The WHERE predicate for the DELETE.
34 | ///
35 | public SqlExpression? Predicate { get; }
36 |
37 | ///
38 | /// The list of tables sources used to generate the result set.
39 | ///
40 | public IReadOnlyList JoinedTables { get; }
41 |
42 | ///
43 | protected override Expression VisitChildren(ExpressionVisitor visitor)
44 | {
45 | const string CallerName = "VisitDelete";
46 |
47 | using (SearchConditionBooleanGuard.With(visitor, false))
48 | {
49 | var table = visitor.VisitAndConvert(Table, CallerName);
50 | bool changed = table != Table;
51 |
52 | SqlExpression? predicate;
53 | using (SearchConditionBooleanGuard.With(visitor, true, false))
54 | {
55 | predicate = visitor.VisitAndConvert(Predicate, CallerName);
56 | changed = changed || predicate != Predicate;
57 | }
58 |
59 | var joinedTables = visitor.VisitCollection(JoinedTables, CallerName);
60 | changed = changed || joinedTables != JoinedTables;
61 |
62 | return changed
63 | ? new DeleteExpression(table, predicate, joinedTables)
64 | : this;
65 | }
66 | }
67 |
68 | ///
69 | protected override void Prints(ExpressionPrinter expressionPrinter)
70 | {
71 | Check.NotNull(expressionPrinter, nameof(expressionPrinter));
72 |
73 | expressionPrinter.Append("DELETE ");
74 | expressionPrinter.Visit(Table);
75 |
76 | if (JoinedTables.Count > 0)
77 | {
78 | expressionPrinter.AppendLine().Append("FROM ");
79 |
80 | for (int i = 0; i < JoinedTables.Count; i++)
81 | {
82 | expressionPrinter.Visit(JoinedTables[i]);
83 | if (i > 0) expressionPrinter.AppendLine();
84 | }
85 | }
86 |
87 | if (Predicate != null)
88 | {
89 | expressionPrinter.AppendLine().Append("WHERE ");
90 | expressionPrinter.Visit(Predicate);
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Relational/Query/SqlExpressions/ExcludedTableColumnExpression.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Storage;
2 | using Microsoft.EntityFrameworkCore.Utilities;
3 | using System;
4 | using System.Linq.Expressions;
5 |
6 | namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
7 | {
8 | ///
9 | /// An expression that represents an excluded table column in a SQL tree.
10 | ///
11 | public class ExcludedTableColumnExpression : SqlExpression
12 | {
13 | public ExcludedTableColumnExpression(string name, Type type, RelationalTypeMapping typeMapping, bool nullable)
14 | : base(type, typeMapping)
15 | {
16 | Check.NotEmpty(name, nameof(name));
17 |
18 | Name = name;
19 | IsNullable = nullable;
20 | }
21 |
22 | ///
23 | /// The name of corresponding column.
24 | ///
25 | public string Name { get; }
26 |
27 | ///
28 | /// Whether this property is nullable.
29 | ///
30 | public bool IsNullable { get; }
31 |
32 | ///
33 | /// Creates another expression representing this property nullable.
34 | ///
35 | public ExcludedTableColumnExpression MakeNullable()
36 | => new ExcludedTableColumnExpression(Name, Type.MakeNullable(), TypeMapping, true);
37 |
38 | ///
39 | protected override Expression VisitChildren(ExpressionVisitor visitor) => this;
40 |
41 | ///
42 | public override string ToString() => "excluded." + Name;
43 |
44 | ///
45 | #if EFCORE50 || EFCORE60
46 | protected override void Print(ExpressionPrinter expressionPrinter)
47 | #elif EFCORE31
48 | public override void Print(ExpressionPrinter expressionPrinter)
49 | #endif
50 | {
51 | expressionPrinter.Append("excluded.").Append(Name);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Relational/Query/SqlExpressions/SelectIntoExpression.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 | using Microsoft.EntityFrameworkCore.Utilities;
3 | using System.Linq.Expressions;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
6 | {
7 | ///
8 | /// An expression that represents a SELECT INTO in a SQL tree.
9 | ///
10 | public sealed class SelectIntoExpression : WrappedExpression
11 | {
12 | public SelectIntoExpression(
13 | string tableName,
14 | string schema,
15 | SelectExpression selectExpression)
16 | {
17 | Check.NotNull(tableName, nameof(tableName));
18 | Check.NotNull(selectExpression, nameof(selectExpression));
19 |
20 | TableName = tableName;
21 | Schema = schema;
22 | Expression = selectExpression;
23 | }
24 |
25 | ///
26 | /// The table to INSERT INTO.
27 | ///
28 | public string TableName { get; }
29 |
30 | ///
31 | /// The table to INSERT INTO.
32 | ///
33 | public string Schema { get; }
34 |
35 | ///
36 | /// The SELECT expression to insert.
37 | ///
38 | public SelectExpression Expression { get; }
39 |
40 | ///
41 | protected override Expression VisitChildren(ExpressionVisitor visitor)
42 | {
43 | var expression = visitor.VisitAndConvert(Expression, "VisitSelectInto");
44 | if (expression == Expression) return this;
45 | return new SelectIntoExpression(TableName, Schema, expression);
46 | }
47 |
48 | ///
49 | protected override void Prints(ExpressionPrinter expressionPrinter)
50 | {
51 | expressionPrinter.Append("INSERT INTO ").AppendLine(TableName);
52 | expressionPrinter.Visit(Expression);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Relational/Query/SqlExpressions/UpsertExpression.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 | using Microsoft.EntityFrameworkCore.Metadata;
3 | using Microsoft.EntityFrameworkCore.Storage;
4 | using Microsoft.EntityFrameworkCore.Utilities;
5 | using System.Collections.Generic;
6 | using System.Linq.Expressions;
7 |
8 | namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
9 | {
10 | ///
11 | /// An expression that represents an UPSERT in a SQL tree.
12 | ///
13 | public class UpsertExpression : WrappedExpression
14 | {
15 | public UpsertExpression(
16 | TableExpression targetTable,
17 | TableExpressionBase sourceTable,
18 | IReadOnlyList columns,
19 | IReadOnlyList? onConflict,
20 | IKey conflictConstraint)
21 | {
22 | Check.NotNull(targetTable, nameof(targetTable));
23 | Check.HasNoNulls(columns, nameof(columns));
24 | Check.NullOrHasNoNulls(onConflict, nameof(onConflict));
25 | Check.NotNull(conflictConstraint, nameof(conflictConstraint));
26 |
27 | TargetTable = targetTable;
28 | SourceTable = sourceTable;
29 | Columns = columns;
30 | OnConflictUpdate = onConflict;
31 | ConflictConstraint = conflictConstraint;
32 | }
33 |
34 | ///
35 | /// The target table to INSERT INTO.
36 | ///
37 | public TableExpression TargetTable { get; }
38 |
39 | ///
40 | /// The source table to SELECT FROM.
41 | ///
42 | public TableExpressionBase SourceTable { get; }
43 |
44 | ///
45 | /// The columns being inserted, whose alias is the corresponding column name.
46 | ///
47 | public IReadOnlyList Columns { get; }
48 |
49 | ///
50 | /// The expressions being updated when conflict.
51 | ///
52 | public IReadOnlyList? OnConflictUpdate { get; }
53 |
54 | ///
55 | /// The conflict constraint name.
56 | ///
57 | public IKey ConflictConstraint { get; }
58 |
59 | ///
60 | protected override void Prints(ExpressionPrinter expressionPrinter)
61 | {
62 | expressionPrinter.Append("Upsert Entity");
63 | }
64 |
65 | ///
66 | protected override Expression VisitChildren(ExpressionVisitor visitor)
67 | {
68 | const string CallerName = "VisitUpsert";
69 |
70 | var targetTable = visitor.VisitAndConvert(TargetTable, CallerName);
71 | bool changed = targetTable != TargetTable;
72 |
73 | var sourceTable = visitor.VisitAndConvert(SourceTable, CallerName);
74 | changed = changed || sourceTable != SourceTable;
75 |
76 | var onConflictUpdate = visitor.VisitCollection(OnConflictUpdate, CallerName);
77 | changed = changed || onConflictUpdate != OnConflictUpdate;
78 |
79 | var columns = visitor.VisitCollection(Columns, CallerName);
80 | changed = changed || columns != Columns;
81 |
82 | return changed
83 | ? new UpsertExpression(targetTable, sourceTable, columns, onConflictUpdate, ConflictConstraint)
84 | : this;
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Relational/Query/SqlExpressions/WrappedExpression.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions
2 | {
3 | public abstract class WrappedExpression : TableExpressionBase
4 | {
5 | protected WrappedExpression() : base("wrapped") { }
6 |
7 | protected abstract void Prints(ExpressionPrinter expressionPrinter);
8 |
9 | #if EFCORE50 || EFCORE60
10 | protected override void Print(ExpressionPrinter expressionPrinter)
11 | #elif EFCORE31
12 | public override void Print(ExpressionPrinter expressionPrinter)
13 | #endif
14 | {
15 | Prints(expressionPrinter);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Relational/Query/ValuesExpressionParameterExpandingVisitor.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
2 | using System;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 | using System.Linq.Expressions;
6 |
7 | namespace Microsoft.EntityFrameworkCore.Query
8 | {
9 | public class ValuesExpressionParameterExpandingVisitor : SqlExpressionVisitorV2
10 | {
11 | private readonly IReadOnlyDictionary _parameterValues;
12 | private readonly List<(ValuesExpression, ValuesExpression)> _replacement;
13 | private readonly ISqlExpressionFactory _sqlExpressionFactory;
14 |
15 | public bool CanCache => _replacement.Count == 0;
16 |
17 | public ValuesExpressionParameterExpandingVisitor(
18 | ISqlExpressionFactory sqlExpressionFactory,
19 | IReadOnlyDictionary parameterValues)
20 | {
21 | _sqlExpressionFactory = sqlExpressionFactory;
22 | _parameterValues = parameterValues;
23 | _replacement = new List<(ValuesExpression, ValuesExpression)>();
24 | }
25 |
26 | protected override Expression VisitColumn(ColumnExpression columnExpression)
27 | {
28 | #if EFCORE60
29 | if (columnExpression.Table is not JoinExpressionBase joinExpression
30 | || joinExpression.Table is not ValuesExpression valuesExpression)
31 | #else
32 | if (columnExpression.Table is not ValuesExpression valuesExpression)
33 | #endif
34 | {
35 | return columnExpression;
36 | }
37 |
38 | // Only when this is a ValuesExpression should we update the column
39 | var result = (TableExpressionBase)Visit(valuesExpression);
40 | return result != columnExpression.Table
41 | ? _sqlExpressionFactory.Column(columnExpression, result)
42 | : columnExpression;
43 | }
44 |
45 | protected override Expression VisitValues(ValuesExpression valuesExpression)
46 | {
47 | if (valuesExpression.ImmediateValues == null
48 | && valuesExpression.RuntimeParameter != null
49 | && valuesExpression.TupleCount == null)
50 | {
51 | if (_parameterValues.TryGetValue(valuesExpression.RuntimeParameter, out var _lists)
52 | && _lists is IList lists)
53 | {
54 | for (int i = 0; i < _replacement.Count; i++)
55 | {
56 | if (_replacement[i].Item1 == valuesExpression)
57 | {
58 | return _replacement[i].Item2;
59 | }
60 | }
61 |
62 | var newExpr = new ValuesExpression(valuesExpression, lists.Count);
63 | _replacement.Add((valuesExpression, newExpr));
64 | return newExpr;
65 | }
66 | else
67 | {
68 | throw new InvalidOperationException(
69 | "Parameter value corrupted.");
70 | }
71 | }
72 |
73 | return valuesExpression;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Relational/Reduce/OptionsBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Bulk;
2 | using Microsoft.EntityFrameworkCore.Infrastructure;
3 |
4 | namespace Microsoft.EntityFrameworkCore
5 | {
6 | public static class TableSplittingJoinsRemovalExtensions
7 | {
8 | public static DbContextOptionsBuilder UseTableSplittingJoinsRemoval(this DbContextOptionsBuilder builder)
9 | {
10 | ((IDbContextOptionsBuilderInfrastructure)builder).AddOrUpdateExtension(new TableSplittingJoinsRemovalDbContextOptionsExtension());
11 | return builder;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Relational/Reduce/OptionsExtension.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Infrastructure;
2 | using Microsoft.EntityFrameworkCore.Query;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.Extensions.DependencyInjection.Extensions;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 |
9 | namespace Microsoft.EntityFrameworkCore.Bulk
10 | {
11 | public class TableSplittingJoinsRemovalDbContextOptionsExtension : IDbContextOptionsExtension
12 | {
13 | private DbContextOptionsExtensionInfo _info;
14 |
15 | public DbContextOptionsExtensionInfo Info =>
16 | _info ??= new TableSplittingJoinsRemovalDbContextOptionsExtensionInfo(this);
17 |
18 | public void ApplyServices(IServiceCollection services)
19 | {
20 | var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IQueryTranslationPostprocessorFactory));
21 | if (descriptor == null) throw new InvalidOperationException("No IQueryTranslationPostprocessorFactory registered.");
22 |
23 | services.Replace(
24 | ServiceDescriptor.Describe(
25 | typeof(IQueryTranslationPostprocessorFactory),
26 | typeof(TableSplittingJoinsWrappingQueryTranslationPostprocessorFactory<>).MakeGenericType(descriptor.ImplementationType),
27 | descriptor.Lifetime));
28 | }
29 |
30 | public void Validate(IDbContextOptions options)
31 | {
32 | }
33 |
34 | private class TableSplittingJoinsRemovalDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo
35 | {
36 | public TableSplittingJoinsRemovalDbContextOptionsExtensionInfo(
37 | TableSplittingJoinsRemovalDbContextOptionsExtension extension) : base(extension)
38 | {
39 | }
40 |
41 | public override bool IsDatabaseProvider => false;
42 |
43 | public override string LogFragment => "using TableSplittingJoinsRemoval ";
44 |
45 | #if EFCORE31 || EFCORE50
46 | public override long GetServiceProviderHashCode() => 0;
47 | #elif EFCORE60
48 | public override int GetServiceProviderHashCode() => 0;
49 |
50 | public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true;
51 | #endif
52 |
53 | public override void PopulateDebugInfo(IDictionary debugInfo)
54 | => debugInfo["TableSplittingJoinsRemoval"] = "1";
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Relational/Reduce/QueryTranslationPostprocessor.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System;
3 | using System.Linq.Expressions;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Query
6 | {
7 | public class TableSplittingJoinsRemovalWrappingQueryTranslationPostprocessor : QueryTranslationPostprocessor
8 | {
9 | private readonly ISqlExpressionFactory _sqlExpressionFactory;
10 | private readonly QueryCompilationContext _queryCompilationContext;
11 | private readonly QueryTranslationPostprocessor _queryTranslationPostprocessor;
12 |
13 | public TableSplittingJoinsRemovalWrappingQueryTranslationPostprocessor(
14 | QueryTranslationPostprocessorDependencies dependencies,
15 | QueryTranslationPostprocessor queryTranslationPostprocessor,
16 | ISqlExpressionFactory sqlExpressionFactory,
17 | QueryCompilationContext queryCompilationContext)
18 | #if EFCORE50 || EFCORE60
19 | : base(dependencies, queryCompilationContext)
20 | #elif EFCORE31
21 | : base(dependencies)
22 | #endif
23 | {
24 | _queryTranslationPostprocessor = queryTranslationPostprocessor;
25 | _queryCompilationContext = queryCompilationContext;
26 | _sqlExpressionFactory = sqlExpressionFactory;
27 | }
28 |
29 | public override Expression Process(Expression query)
30 | {
31 | query = new SelfJoinsPruningExpressionVisitor(_queryCompilationContext, _sqlExpressionFactory).Reduce(query);
32 | query = _queryTranslationPostprocessor.Process(query);
33 | return query;
34 | }
35 | }
36 |
37 | public class TableSplittingJoinsWrappingQueryTranslationPostprocessorFactory :
38 | IQueryTranslationPostprocessorFactory
39 | where TPostprocessorFactory : IQueryTranslationPostprocessorFactory
40 | {
41 | private readonly QueryTranslationPostprocessorDependencies _dependencies;
42 | private readonly RelationalQueryTranslationPostprocessorDependencies _relationalDependencies;
43 | private readonly IQueryTranslationPostprocessorFactory _factory;
44 |
45 | public TableSplittingJoinsWrappingQueryTranslationPostprocessorFactory(
46 | QueryTranslationPostprocessorDependencies dependencies,
47 | RelationalQueryTranslationPostprocessorDependencies relationalDependencies,
48 | IServiceProvider serviceProvider)
49 | {
50 | _dependencies = dependencies;
51 | _relationalDependencies = relationalDependencies;
52 | _factory = ActivatorUtilities.CreateInstance(serviceProvider);
53 | }
54 |
55 | public virtual QueryTranslationPostprocessor Create(QueryCompilationContext queryCompilationContext)
56 | => new TableSplittingJoinsRemovalWrappingQueryTranslationPostprocessor(
57 | _dependencies,
58 | _factory.Create(queryCompilationContext),
59 | _relationalDependencies.SqlExpressionFactory,
60 | queryCompilationContext);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Relational/Reduce/ShaperQueryExpressionReplacingVisitor.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
2 | using System.Linq.Expressions;
3 |
4 | namespace Microsoft.EntityFrameworkCore.Query
5 | {
6 | public class ShaperQueryExpressionReplacingVisitor : ExpressionVisitor
7 | {
8 | private readonly SelectExpression _origin, _new;
9 |
10 | public ShaperQueryExpressionReplacingVisitor(SelectExpression origin, SelectExpression @new)
11 | {
12 | _origin = origin;
13 | _new = @new;
14 | }
15 |
16 | protected override Expression VisitExtension(Expression node)
17 | {
18 | if (node is ProjectionBindingExpression proj && proj.QueryExpression == _origin)
19 | {
20 | if (proj.Index.HasValue)
21 | {
22 | return new ProjectionBindingExpression(_new, proj.Index.Value, proj.Type);
23 | }
24 | else if (proj.ProjectionMember != null)
25 | {
26 | return new ProjectionBindingExpression(_new, proj.ProjectionMember, proj.Type);
27 | }
28 | #if EFCORE31 || EFCORE50
29 | else if (proj.IndexMap != null)
30 | {
31 | return new ProjectionBindingExpression(_new, proj.IndexMap);
32 | }
33 | #endif
34 | }
35 |
36 | return node == _origin ? _new : base.VisitExtension(node);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Relational/RelationalExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using Microsoft.EntityFrameworkCore.Storage.Internal;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace Microsoft.EntityFrameworkCore.Bulk
8 | {
9 | public abstract class RelationalBatchOptionsExtension : BatchOptionsExtension
10 | {
11 | internal override HashSet GetRequiredServices()
12 | {
13 | var set = base.GetRequiredServices();
14 | set.Add(typeof(IBulkQuerySqlGeneratorFactory));
15 | #if EFCORE50 || EFCORE60
16 | set.Add(typeof(IRelationalBulkParameterBasedSqlProcessorFactory));
17 | #endif
18 | return set;
19 | }
20 |
21 | internal override Dictionary GetServiceLifetimes()
22 | {
23 | var dict = base.GetServiceLifetimes();
24 | #if EFCORE31 || EFCORE50
25 | dict.Add(typeof(IMethodCallTranslatorPlugin), ServiceLifetime.Singleton);
26 | dict.Add(typeof(IMemberTranslatorPlugin), ServiceLifetime.Singleton);
27 | #elif EFCORE60
28 | dict.Add(typeof(IMethodCallTranslatorPlugin), ServiceLifetime.Scoped);
29 | dict.Add(typeof(IMemberTranslatorPlugin), ServiceLifetime.Scoped);
30 | #endif
31 | dict.Add(typeof(IBulkQuerySqlGeneratorFactory), ServiceLifetime.Singleton);
32 | dict.Add(typeof(IAnonymousExpressionFactory), ServiceLifetime.Singleton);
33 | #if EFCORE50
34 | dict.Add(typeof(IRelationalBulkParameterBasedSqlProcessorFactory), ServiceLifetime.Singleton);
35 | #elif EFCORE60
36 | dict.Add(typeof(IRelationalBulkParameterBasedSqlProcessorFactory), ServiceLifetime.Scoped);
37 | #endif
38 | return dict;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Relational/Storage/BulkRelationalCommandBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Linq.Expressions;
7 | using System.Runtime.CompilerServices;
8 |
9 | namespace Microsoft.EntityFrameworkCore.Storage
10 | {
11 | public static class BulkRelationalCommandBuilderExtensions
12 | {
13 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
14 | [DebuggerStepThrough]
15 | public static IRelationalCommandBuilder GenerateList(
16 | this IRelationalCommandBuilder sql,
17 | IReadOnlyList items,
18 | Action generationAction,
19 | Action joinAction = null)
20 | {
21 | joinAction ??= (isb => isb.Append(", "));
22 |
23 | for (var i = 0; i < items.Count; i++)
24 | {
25 | if (i > 0) joinAction(sql);
26 | generationAction(items[i]);
27 | }
28 |
29 | return sql;
30 | }
31 |
32 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
33 | [DebuggerStepThrough]
34 | public static IRelationalCommandBuilder AppendIf(
35 | this IRelationalCommandBuilder sql,
36 | bool condition,
37 | string value)
38 | {
39 | if (condition) sql.Append(value);
40 | return sql;
41 | }
42 |
43 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
44 | [DebuggerStepThrough]
45 | public static ExpressionPrinter VisitCollection(
46 | this ExpressionPrinter expressionPrinter,
47 | IReadOnlyList items,
48 | Action generateExpression,
49 | Action joinAction = null)
50 | {
51 | joinAction ??= (isb => isb.Append(", "));
52 |
53 | for (var i = 0; i < items.Count; i++)
54 | {
55 | if (i > 0) joinAction(expressionPrinter);
56 | generateExpression(expressionPrinter, items[i]);
57 | }
58 |
59 | return expressionPrinter;
60 | }
61 |
62 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
63 | [DebuggerStepThrough]
64 | public static IRelationalCommandBuilder Then(this IRelationalCommandBuilder sql, Action then)
65 | {
66 | then.Invoke();
67 | return sql;
68 | }
69 |
70 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
71 | [DebuggerStepThrough]
72 | public static IReadOnlyList VisitCollection(
73 | this ExpressionVisitor visitor,
74 | IReadOnlyList items,
75 | string callerName)
76 | where TExpression : Expression
77 | {
78 | if (items == null) return null;
79 |
80 | bool changed = false;
81 | var newItems = items.ToList();
82 | for (int i = 0; i < newItems.Count; i++)
83 | {
84 | newItems[i] = visitor.VisitAndConvert(newItems[i], callerName);
85 | changed = changed || newItems[i] != items[i];
86 | }
87 |
88 | return changed ? newItems : items;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Relational/Storage/ValuesRelationalParameter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Utilities;
2 | using System;
3 | using System.Data.Common;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Storage.Internal
6 | {
7 | public class ValuesRelationalParameter : RelationalParameterBase
8 | {
9 | public virtual AnonymousExpressionType Type { get; }
10 |
11 | public virtual string NamePrefix { get; }
12 |
13 | public override string InvariantName { get; }
14 |
15 | public ValuesRelationalParameter(AnonymousExpressionType type, string namePrefix, string invariantName)
16 | #if EFCORE60
17 | : base(invariantName)
18 | #endif
19 | {
20 | Check.NotNull(type, nameof(type));
21 | Check.NotNull(namePrefix, nameof(namePrefix));
22 | Check.NotNull(invariantName, nameof(invariantName));
23 |
24 | Type = type;
25 | NamePrefix = namePrefix;
26 | InvariantName = invariantName;
27 | }
28 |
29 | public override void AddDbParameter(DbCommand command, object value)
30 | {
31 | Check.NotNull(command, nameof(command));
32 | Check.NotNull(value, nameof(value));
33 |
34 | if (value is not System.Collections.IList list)
35 | {
36 | throw new InvalidOperationException("Parameter corrupt.");
37 | }
38 |
39 | for (int i = 0; i < list.Count; i++)
40 | {
41 | Type.AddDbParameter(command, $"{NamePrefix}_{i}", list[i]);
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/SqlServer/EFCore.Bulk.SqlServer-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | $(DefineConstants);EFCORE31;RELATIONAL;SQL_SERVER
6 | Microsoft.EntityFrameworkCore.Bulk.SqlServer
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).SqlServer
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/SqlServer/EFCore.Bulk.SqlServer-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | $(DefineConstants);EFCORE50;RELATIONAL;SQL_SERVER
6 | Microsoft.EntityFrameworkCore.Bulk.SqlServer
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).SqlServer
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/SqlServer/EFCore.Bulk.SqlServer-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60;RELATIONAL;SQL_SERVER
6 | Microsoft.EntityFrameworkCore.Bulk.SqlServer
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).SqlServer
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/SqlServer/MathTranslationPlugin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Diagnostics;
2 | using Microsoft.EntityFrameworkCore.Query;
3 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Reflection;
7 |
8 | namespace Microsoft.EntityFrameworkCore.SqlServer.Query
9 | {
10 | public class MathTranslationPlugin : IMethodCallTranslator, IMethodCallTranslatorPlugin
11 | {
12 | private MathTranslationPlugin[] Translators { get; }
13 |
14 | IEnumerable IMethodCallTranslatorPlugin.Translators => Translators;
15 |
16 | ISqlExpressionFactory Sql { get; }
17 |
18 | public MathTranslationPlugin(ISqlExpressionFactory sqlExpressionFactory)
19 | {
20 | Sql = sqlExpressionFactory;
21 | Translators = new[] { this };
22 | }
23 |
24 | public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList arguments)
25 | {
26 | if (arguments.Count != 2 || method.DeclaringType != typeof(Math)) return null;
27 |
28 | return method.Name switch
29 | {
30 | nameof(Math.Min) => IIF(Sql.LessThan(arguments[0], arguments[1]), arguments[0], arguments[1]),
31 | nameof(Math.Max) => IIF(Sql.GreaterThan(arguments[0], arguments[1]), arguments[0], arguments[1]),
32 | _ => null,
33 | };
34 |
35 | CaseExpression IIF(SqlExpression test, SqlExpression whenTrue, SqlExpression whenFalse) =>
36 | Sql.Case(new[] { new CaseWhenClause(test, whenTrue) }, whenFalse);
37 | }
38 |
39 | public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList arguments, IDiagnosticsLogger logger)
40 | => Translate(instance, method, arguments);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/SqlServer/QueryCompilationContextFactory.cs:
--------------------------------------------------------------------------------
1 | #if EFCORE50 || EFCORE60
2 |
3 | using Microsoft.EntityFrameworkCore.Bulk;
4 | using Microsoft.EntityFrameworkCore.Query;
5 | using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
6 | using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
7 | using System;
8 | using System.Linq.Expressions;
9 |
10 | namespace Microsoft.EntityFrameworkCore.SqlServer.Query
11 | {
12 | public class SqlServerBulkQueryCompilationContextFactory :
13 | SqlServerQueryCompilationContextFactory,
14 | IBulkQueryCompilationContextFactory,
15 | IServiceAnnotation
16 | {
17 | public SqlServerBulkQueryCompilationContextFactory(
18 | BulkQueryCompilationContextDependencies dependencies,
19 | RelationalQueryCompilationContextDependencies relationalDependencies,
20 | ISqlServerConnection sqlServerConnection)
21 | : base(dependencies.Dependencies, relationalDependencies, sqlServerConnection)
22 | {
23 | }
24 |
25 | public Func CreateQueryExecutor(bool async, Expression query)
26 | {
27 | return Create(async).CreateQueryExecutor(query);
28 | }
29 | }
30 | }
31 |
32 | #endif
--------------------------------------------------------------------------------
/src/Sqlite/EFCore.Bulk.Sqlite-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | $(DefineConstants);EFCORE31;RELATIONAL;SQLITE
6 | Microsoft.EntityFrameworkCore.Bulk.Sqlite
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).Sqlite
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/Sqlite/EFCore.Bulk.Sqlite-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | $(DefineConstants);EFCORE50;RELATIONAL;SQLITE
6 | Microsoft.EntityFrameworkCore.Bulk.Sqlite
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).Sqlite
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Sqlite/EFCore.Bulk.Sqlite-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60;RELATIONAL;SQLITE
6 | Microsoft.EntityFrameworkCore.Bulk.Sqlite
7 | Microsoft.EntityFrameworkCore
8 | $(ExtensionPackagePrefix).Sqlite
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Sqlite/QueryCompilationContextFactory.cs:
--------------------------------------------------------------------------------
1 | #if EFCORE50 || EFCORE60
2 |
3 | using Microsoft.EntityFrameworkCore.Bulk;
4 | using Microsoft.EntityFrameworkCore.Query;
5 | using Microsoft.EntityFrameworkCore.Query.Internal;
6 | using System;
7 | using System.Linq.Expressions;
8 |
9 | namespace Microsoft.EntityFrameworkCore.Sqlite.Query
10 | {
11 | public class SqliteBulkQueryCompilationContextFactory :
12 | RelationalQueryCompilationContextFactory,
13 | IBulkQueryCompilationContextFactory,
14 | IServiceAnnotation
15 | {
16 | public SqliteBulkQueryCompilationContextFactory(
17 | BulkQueryCompilationContextDependencies dependencies,
18 | RelationalQueryCompilationContextDependencies relationalDependencies)
19 | : base(dependencies.Dependencies, relationalDependencies)
20 | {
21 | }
22 |
23 | public Func CreateQueryExecutor(bool async, Expression query)
24 | {
25 | return Create(async).CreateQueryExecutor(query);
26 | }
27 | }
28 | }
29 |
30 | #endif
--------------------------------------------------------------------------------
/src/Sqlite/QueryCompiler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Bulk;
2 | using Microsoft.EntityFrameworkCore.Diagnostics;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Query;
6 | using Microsoft.EntityFrameworkCore.Query.Internal;
7 | using Microsoft.EntityFrameworkCore.Storage;
8 | using System;
9 | using System.Linq.Expressions;
10 |
11 | namespace Microsoft.EntityFrameworkCore.Sqlite.Query
12 | {
13 | public class SqliteBulkQueryCompiler : BulkQueryCompiler
14 | {
15 | public SqliteBulkQueryCompiler(
16 | IQueryContextFactory queryContextFactory,
17 | ICompiledQueryCache compiledQueryCache,
18 | ICompiledQueryCacheKeyGenerator compiledQueryCacheKeyGenerator,
19 | IDatabase database,
20 | IDiagnosticsLogger logger,
21 | ICurrentDbContext currentContext,
22 | IEvaluatableExpressionFilter evaluatableExpressionFilter,
23 | IBulkQueryCompilationContextFactory qccFactory,
24 | IModel model)
25 | : base(queryContextFactory,
26 | compiledQueryCache,
27 | compiledQueryCacheKeyGenerator,
28 | database,
29 | logger,
30 | currentContext,
31 | evaluatableExpressionFilter,
32 | qccFactory,
33 | model)
34 | {
35 | }
36 |
37 | protected override Func CompileBulkCore(IDatabase database, Expression query, IModel model, bool async)
38 | {
39 | if (query is MethodCallExpression methodCallExpression
40 | && methodCallExpression.Method.GetGenericMethodDefinition() == BatchOperationMethods.MergeCollapsed)
41 | {
42 | throw TranslationFailed(query, "MERGE INTO sentences are not supported in SQLite.");
43 | }
44 |
45 | return base.CompileBulkCore(database, query, model, async);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Sqlite/SqliteBatchExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Bulk;
2 | using Microsoft.EntityFrameworkCore.Infrastructure;
3 | using Microsoft.EntityFrameworkCore.Query;
4 | using Microsoft.EntityFrameworkCore.Query.Internal;
5 | using Microsoft.EntityFrameworkCore.Sqlite.Query;
6 | using Microsoft.EntityFrameworkCore.Storage.Internal;
7 |
8 | namespace Microsoft.EntityFrameworkCore
9 | {
10 | public static class SqliteBatchExtensions
11 | {
12 | public static SqliteDbContextOptionsBuilder UseBulk(this SqliteDbContextOptionsBuilder builder)
13 | {
14 | var builder1 = ((IRelationalDbContextOptionsBuilderInfrastructure)builder).OptionsBuilder;
15 | var ext = new SqliteBatchOptionsExtension();
16 | ((IDbContextOptionsBuilderInfrastructure)builder1).AddOrUpdateExtension(ext);
17 | return builder;
18 | }
19 | }
20 |
21 | public class SqliteBatchOptionsExtension : RelationalBatchOptionsExtension
22 | {
23 | public override string Name => "SqliteBatchExtension";
24 |
25 | protected override void ApplyServices(ExtensionServicesBuilder services)
26 | {
27 | services.TryAdd();
28 |
29 | services.TryAdd();
30 | services.TryAdd();
31 | services.TryAdd();
32 | services.TryAdd();
33 |
34 | services.TryAdd();
35 | services.TryAdd();
36 | #if EFCORE50 || EFCORE60
37 | services.TryAdd();
38 | services.TryAdd();
39 | #elif EFCORE31
40 | services.TryAdd();
41 | SearchConditionBooleanGuard.AddTypeField(typeof(NullSemanticsRewritingExpressionVisitor), "_canOptimize");
42 | #endif
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/InMemory/Context.Factory.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Tests
2 | {
3 | public class InMemoryContextFactory : ContextFactoryBase
4 | where TContext : DbContext
5 | {
6 | protected override void Configure(DbContextOptionsBuilder optionsBuilder)
7 | {
8 | optionsBuilder.UseInMemoryDatabase(
9 | "Nothing",
10 | s => s.UseBulk());
11 | }
12 |
13 | protected override void EnsureCreated(TContext context)
14 | {
15 | }
16 |
17 | protected override void EnsureDeleted(TContext context)
18 | {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/InMemory/Context.PrepareDatabase.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | [Collection("DatabaseCollection")]
6 | public class DatabaseCollection :
7 | DatabaseCollection>,
8 | ICollectionFixture
9 | {
10 | public DatabaseCollection() : base(new InMemoryContextFactory())
11 | {
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/InMemory/EFCore.Bulk.InMemory.Tests-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | $(DefineConstants);EFCORE31
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.InMemory.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/InMemory/EFCore.Bulk.InMemory.Tests-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE50
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.InMemory.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/InMemory/EFCore.Bulk.InMemory.Tests-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.InMemory.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/InMemory/Functional.Delete.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchDelete;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class InMemoryDeleteTest : DeleteTestBase>
6 | {
7 | public InMemoryDeleteTest(
8 | InMemoryContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/InMemory/Functional.InsertInto.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchInsertInto;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class InMemoryInsertIntoTest : InsertIntoTestBase>
6 | {
7 | public InMemoryInsertIntoTest(
8 | InMemoryContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/InMemory/Functional.MergeInto.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.MergeInto;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class InMemoryMergeIntoTest : MergeIntoTestBase>
6 | {
7 | public InMemoryMergeIntoTest(
8 | InMemoryContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/InMemory/Functional.Update.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchUpdate;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class InMemoryUpdateTest : UpdateTestBase>
6 | {
7 | public InMemoryUpdateTest(
8 | InMemoryContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/InMemory/Functional.UpdateJoin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchUpdateJoin;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class InMemoryUpdateJoinTest : UpdateJoinTestBase>
6 | {
7 | public InMemoryUpdateJoinTest(
8 | InMemoryContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/InMemory/Functional.Upsert.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.Upsert;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class InMemoryUpsertTest : UpsertTestBase>
6 | {
7 | public InMemoryUpsertTest(
8 | InMemoryContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/MySql/Context.Factory.cs:
--------------------------------------------------------------------------------
1 | using Pomelo.EntityFrameworkCore.MySql.Storage;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class MySqlContextFactory : RelationalContextFactoryBase
6 | where TContext : DbContext
7 | {
8 | public ServerVersion ServerVersion { get; private set; }
9 |
10 | protected override string ScriptSplit => ";\r\n\r\n";
11 |
12 | protected override string DropTableCommand => "DROP TABLE IF EXISTS `{0}`";
13 |
14 | protected override void Configure(DbContextOptionsBuilder optionsBuilder)
15 | {
16 | var connectionString =
17 | $"Server=localhost;" +
18 | $"Port=3306;" +
19 | $"Database=efcorebulktest{Suffix};" +
20 | $"User=root;" +
21 | $"Password=Password12!;" +
22 | $"Character Set=utf8;" +
23 | $"TreatTinyAsBoolean=true;";
24 |
25 | ServerVersion = ServerVersion.AutoDetect(connectionString);
26 |
27 | #if EFCORE50 || EFCORE60
28 | optionsBuilder.UseMySql(
29 | connectionString,
30 | ServerVersion,
31 | s => s.UseBulk());
32 | #elif EFCORE31
33 | optionsBuilder.UseMySql(
34 | connectionString,
35 | s => s.UseBulk()
36 | .ServerVersion(ServerVersion));
37 | #endif
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/test/MySql/Context.PrepareDatabase.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | [Collection("DatabaseCollection")]
6 | public class DatabaseCollection :
7 | DatabaseCollection>,
8 | ICollectionFixture
9 | {
10 | public DatabaseCollection() : base(new MySqlContextFactory())
11 | {
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/MySql/EFCore.Bulk.MySql.Tests-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | $(DefineConstants);EFCORE31
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.MySQL.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/MySql/EFCore.Bulk.MySql.Tests-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE50
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.MySQL.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/MySql/EFCore.Bulk.MySql.Tests-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.MySQL.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/MySql/Functional.Delete.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchDelete;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class MySqlDeleteTest : DeleteTestBase>
6 | {
7 | public MySqlDeleteTest(
8 | MySqlContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_ConstantCondition()
14 | {
15 | base.CompiledQuery_ConstantCondition();
16 |
17 | AssertSql(@"
18 | DELETE `i`
19 | FROM `Item_{{schema}}` AS `i`
20 | WHERE (`i`.`ItemId` > 500) AND (`i`.`Price` = 3.0)
21 | ");
22 | }
23 |
24 | public override void CompiledQuery_ContainsSomething()
25 | {
26 | base.CompiledQuery_ContainsSomething();
27 |
28 | AssertSql(@"
29 | DELETE `i`
30 | FROM `Item_{{schema}}` AS `i`
31 | WHERE `i`.`Name` IN ('jyntnytjyntjntnytnt', 'aaa')
32 | ");
33 | }
34 |
35 | public override void CompiledQuery_ParameteredCondition()
36 | {
37 | base.CompiledQuery_ParameteredCondition();
38 |
39 | AssertSql(@"
40 | DELETE `i`
41 | FROM `Item_{{schema}}` AS `i`
42 | WHERE `i`.`Name` = @__nameToDelete
43 | ");
44 | }
45 |
46 | public override void ConstantCondition()
47 | {
48 | base.ConstantCondition();
49 |
50 | AssertSql(@"
51 | DELETE `i`
52 | FROM `Item_{{schema}}` AS `i`
53 | WHERE (`i`.`ItemId` > 500) AND (`i`.`Price` = 124.0)
54 | ");
55 | }
56 |
57 | public override void ContainsAndAlsoEqual()
58 | {
59 | base.ContainsAndAlsoEqual();
60 |
61 | AssertSql(V31, @"
62 | DELETE `i`
63 | FROM `Item_{{schema}}` AS `i`
64 | WHERE `i`.`Description` IN ('info') OR (`i`.`Name` = @__nameToDelete_1)
65 | ");
66 |
67 | AssertSql(V50 | V60, @"
68 | DELETE `i`
69 | FROM `Item_{{schema}}` AS `i`
70 | WHERE (`i`.`Description` = 'info') OR (`i`.`Name` = @__nameToDelete_1)
71 | ");
72 | }
73 |
74 | public override void ContainsSomething()
75 | {
76 | base.ContainsSomething();
77 |
78 | AssertSql(@"
79 | DELETE `i`
80 | FROM `Item_{{schema}}` AS `i`
81 | WHERE `i`.`Description` IN ('info', 'aaa')
82 | ");
83 | }
84 |
85 | public override void EmptyContains()
86 | {
87 | base.EmptyContains();
88 |
89 | AssertSql(V31, @"
90 | DELETE `i`
91 | FROM `Item_{{schema}}` AS `i`
92 | WHERE TRUE = FALSE
93 | ");
94 |
95 | AssertSql(V50 | V60, @"
96 | DELETE `i`
97 | FROM `Item_{{schema}}` AS `i`
98 | WHERE FALSE
99 | ");
100 | }
101 |
102 | public override void ListAny()
103 | {
104 | base.ListAny();
105 |
106 | AssertSql(V31, @"
107 | DELETE `i`
108 | FROM `Item_{{schema}}` AS `i`
109 | WHERE `i`.`Description` IN ('info')
110 | ");
111 |
112 | AssertSql(V50 | V60, @"
113 | DELETE `i`
114 | FROM `Item_{{schema}}` AS `i`
115 | WHERE `i`.`Description` = 'info'
116 | ");
117 | }
118 |
119 | public override void ParameteredCondition()
120 | {
121 | base.ParameteredCondition();
122 |
123 | AssertSql(@"
124 | DELETE `i`
125 | FROM `Item_{{schema}}` AS `i`
126 | WHERE `i`.`Name` = @__nameToDelete_0
127 | ");
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/test/MySql/Functional.InsertInto.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchInsertInto;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class MySqlInsertIntoTest : InsertIntoTestBase>
6 | {
7 | public MySqlInsertIntoTest(
8 | MySqlContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_NormalSelectInto()
14 | {
15 | base.CompiledQuery_NormalSelectInto();
16 |
17 | AssertSql(@"
18 | INSERT INTO `ChangeLog_{{schema}}` (`Description`, `ChangedBy`, `Audit_IsDeleted`)
19 | SELECT CONCAT(COALESCE(`j`.`Server`, @__hh), '666') AS `Description`, `j`.`CompileError` AS `ChangedBy`, TRUE AS `Audit_IsDeleted`
20 | FROM `Judging_{{schema}}` AS `j`
21 | ");
22 | }
23 |
24 | public override void CompiledQuery_WithAbstractType()
25 | {
26 | base.CompiledQuery_WithAbstractType();
27 |
28 | AssertSql(@"
29 | INSERT INTO `Person_{{schema}}` (`Name`, `Class`)
30 | SELECT `p`.`Name`, `p`.`Subject` AS `Class`
31 | FROM `Person_{{schema}}` AS `p`
32 | WHERE `p`.`Discriminator` = 'Student'
33 | ");
34 | }
35 |
36 | public override void CompiledQuery_WithComputedColumn()
37 | {
38 | base.CompiledQuery_WithComputedColumn();
39 |
40 | AssertSql(V31, @"
41 | INSERT INTO `Document_{{schema}}` (`Content`)
42 | SELECT CONCAT(`d`.`Content`, CONVERT(`d`.`ContentLength`, CHAR(11))) AS `Content`
43 | FROM `Document_{{schema}}` AS `d`
44 | ");
45 |
46 | AssertSql(V50, @"
47 | INSERT INTO `Document_{{schema}}` (`Content`)
48 | SELECT CONCAT(`d`.`Content`, CAST(`d`.`ContentLength` AS char) COLLATE utf8mb4_bin) AS `Content`
49 | FROM `Document_{{schema}}` AS `d`
50 | ");
51 |
52 | AssertSql(V60, @"
53 | INSERT INTO `Document_{{schema}}` (`Content`)
54 | SELECT CONCAT(`d`.`Content`, CAST(`d`.`ContentLength` AS char)) AS `Content`
55 | FROM `Document_{{schema}}` AS `d`
56 | ");
57 | }
58 |
59 | public override void NormalSelectInto()
60 | {
61 | base.NormalSelectInto();
62 |
63 | AssertSql(@"
64 | INSERT INTO `ChangeLog_{{schema}}` (`Description`, `ChangedBy`, `Audit_IsDeleted`)
65 | SELECT CONCAT(COALESCE(`j`.`Server`, @__hh_0), '666') AS `Description`, `j`.`CompileError` AS `ChangedBy`, TRUE AS `Audit_IsDeleted`
66 | FROM `Judging_{{schema}}` AS `j`
67 | ");
68 | }
69 |
70 | public override void WithAbstractType()
71 | {
72 | base.WithAbstractType();
73 |
74 | AssertSql(@"
75 | INSERT INTO `Person_{{schema}}` (`Name`, `Class`)
76 | SELECT `p`.`Name`, `p`.`Subject` AS `Class`
77 | FROM `Person_{{schema}}` AS `p`
78 | WHERE `p`.`Discriminator` = 'Student'
79 | ");
80 | }
81 |
82 | public override void WithComputedColumn()
83 | {
84 | base.WithComputedColumn();
85 |
86 | AssertSql(V31, @"
87 | INSERT INTO `Document_{{schema}}` (`Content`)
88 | SELECT CONCAT(`d`.`Content`, CONVERT(`d`.`ContentLength`, CHAR(11))) AS `Content`
89 | FROM `Document_{{schema}}` AS `d`
90 | ");
91 |
92 | AssertSql(V50, @"
93 | INSERT INTO `Document_{{schema}}` (`Content`)
94 | SELECT CONCAT(`d`.`Content`, CAST(`d`.`ContentLength` AS char) COLLATE utf8mb4_bin) AS `Content`
95 | FROM `Document_{{schema}}` AS `d`
96 | ");
97 |
98 | AssertSql(V60, @"
99 | INSERT INTO `Document_{{schema}}` (`Content`)
100 | SELECT CONCAT(`d`.`Content`, CAST(`d`.`ContentLength` AS char)) AS `Content`
101 | FROM `Document_{{schema}}` AS `d`
102 | ");
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/test/MySql/Functional.UpdateJoin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchUpdateJoin;
2 | using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
3 | using Pomelo.EntityFrameworkCore.MySql.Storage;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Tests
6 | {
7 | public class MySqlUpdateJoinTest : UpdateJoinTestBase>
8 | {
9 | public ServerVersion ServerVersion { get; }
10 |
11 | public MySqlUpdateJoinTest(
12 | MySqlContextFactory factory)
13 | : base(factory)
14 | {
15 | ServerVersion = factory.ServerVersion;
16 | }
17 |
18 | public override void CompiledQuery_NormalUpdate()
19 | {
20 | base.CompiledQuery_NormalUpdate();
21 |
22 | AssertSql(@"
23 | UPDATE `ItemA_{{schema}}` AS `i`
24 | INNER JOIN (
25 | SELECT `i0`.`Id`, `i0`.`Value`
26 | FROM `ItemB_{{schema}}` AS `i0`
27 | WHERE `i0`.`Value` = @__aa
28 | ) AS `t` ON `i`.`Id` = `t`.`Id`
29 | SET `i`.`Value` = (`i`.`Value` + `t`.`Value`) - @__cc
30 | WHERE `i`.`Id` = @__bb
31 | ");
32 | }
33 |
34 | public override void LocalTableJoin()
35 | {
36 | base.LocalTableJoin();
37 |
38 | if (ServerVersion.Type == ServerType.MySql && ServerVersion.Version >= new System.Version(8, 0, 19))
39 | {
40 | AssertSql(@"
41 | UPDATE `ItemB_{{schema}}` AS `i`
42 | INNER JOIN (
43 | VALUES
44 | ROW(@__p_0_0_0, @__p_0_0_1),
45 | ROW(@__p_0_1_0, @__p_0_1_1),
46 | ROW(@__p_0_2_0, @__p_0_2_1)
47 | ) AS `cte` (`Id`, `Value`) ON `i`.`Id` = `cte`.`Id`
48 | SET `i`.`Value` = `i`.`Value` + `cte`.`Value`
49 | WHERE `i`.`Id` <> 2
50 | ");
51 | }
52 | else
53 | {
54 | AssertSql(@"
55 | UPDATE `ItemB_{{schema}}` AS `i`
56 | INNER JOIN (
57 | SELECT @__p_0_0_0 AS `Id`, @__p_0_0_1 AS `Value`
58 | UNION SELECT @__p_0_1_0, @__p_0_1_1
59 | UNION SELECT @__p_0_2_0, @__p_0_2_1
60 | ) AS `cte` ON `i`.`Id` = `cte`.`Id`
61 | SET `i`.`Value` = `i`.`Value` + `cte`.`Value`
62 | WHERE `i`.`Id` <> 2
63 | ");
64 | }
65 | }
66 |
67 | public override void NormalUpdate()
68 | {
69 | base.NormalUpdate();
70 |
71 | AssertSql(@"
72 | UPDATE `ItemA_{{schema}}` AS `i`
73 | INNER JOIN (
74 | SELECT `i0`.`Id`, `i0`.`Value`
75 | FROM `ItemB_{{schema}}` AS `i0`
76 | WHERE `i0`.`Value` = 2
77 | ) AS `t` ON `i`.`Id` = `t`.`Id`
78 | SET `i`.`Value` = (`i`.`Value` + `t`.`Value`) - 3
79 | WHERE `i`.`Id` = 1
80 | ");
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/test/PostgreSql/Context.Factory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class NpgsqlContextFactory : RelationalContextFactoryBase
6 | where TContext : DbContext
7 | {
8 | protected override string ScriptSplit => ";\r\n\r\n";
9 |
10 | protected override string DropTableCommand => "DROP TABLE IF EXISTS \"{0}\"";
11 |
12 | protected override void Configure(DbContextOptionsBuilder optionsBuilder)
13 | {
14 | string username = Environment.GetEnvironmentVariable("PGUSER") ?? "postgres";
15 | string password = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "Password12!";
16 |
17 | optionsBuilder.UseNpgsql(
18 | $"User ID={username};" +
19 | $"Password={password};" +
20 | $"Host=localhost;" +
21 | $"Port=5432;" +
22 | $"Database=efcorebulktest{Suffix};" +
23 | $"Pooling=true;",
24 | s => s.UseBulk().UseLegacyDateTimeOffset());
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/PostgreSql/Context.PrepareDatabase.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | [Collection("DatabaseCollection")]
6 | public class DatabaseCollection :
7 | DatabaseCollection>,
8 | ICollectionFixture
9 | {
10 | public DatabaseCollection() : base(new NpgsqlContextFactory())
11 | {
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/PostgreSql/EFCore.Bulk.PostgreSql.Tests-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | $(DefineConstants);EFCORE31
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.PostgreSQL.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/PostgreSql/EFCore.Bulk.PostgreSql.Tests-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE50
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.PostgreSQL.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/PostgreSql/EFCore.Bulk.PostgreSql.Tests-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.PostgreSQL.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/PostgreSql/Functional.InsertInto.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchInsertInto;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class NpgsqlInsertIntoTest : InsertIntoTestBase>
6 | {
7 | public NpgsqlInsertIntoTest(
8 | NpgsqlContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_NormalSelectInto()
14 | {
15 | base.CompiledQuery_NormalSelectInto();
16 |
17 | AssertSql(@"
18 | INSERT INTO ""ChangeLog_{{schema}}"" (""Description"", ""ChangedBy"", ""Audit_IsDeleted"")
19 | SELECT COALESCE(j.""Server"", @__hh) || '666' AS ""Description"", j.""CompileError"" AS ""ChangedBy"", TRUE AS ""Audit_IsDeleted""
20 | FROM ""Judging_{{schema}}"" AS j
21 | ");
22 | }
23 |
24 | public override void CompiledQuery_WithAbstractType()
25 | {
26 | base.CompiledQuery_WithAbstractType();
27 |
28 | AssertSql(@"
29 | INSERT INTO ""Person_{{schema}}"" (""Name"", ""Class"")
30 | SELECT p.""Name"", p.""Subject"" AS ""Class""
31 | FROM ""Person_{{schema}}"" AS p
32 | WHERE p.""Discriminator"" = 'Student'
33 | ");
34 | }
35 |
36 | public override void CompiledQuery_WithComputedColumn()
37 | {
38 | base.CompiledQuery_WithComputedColumn();
39 |
40 | AssertSql(V31 | V50, @"
41 | INSERT INTO ""Document_{{schema}}"" (""Content"")
42 | SELECT d.""Content"" || CAST(d.""ContentLength"" AS text) AS ""Content""
43 | FROM ""Document_{{schema}}"" AS d
44 | ");
45 |
46 | AssertSql(V60, @"
47 | INSERT INTO ""Document_{{schema}}"" (""Content"")
48 | SELECT d.""Content"" || d.""ContentLength""::text AS ""Content""
49 | FROM ""Document_{{schema}}"" AS d
50 | ");
51 | }
52 |
53 | public override void NormalSelectInto()
54 | {
55 | base.NormalSelectInto();
56 |
57 | AssertSql(@"
58 | INSERT INTO ""ChangeLog_{{schema}}"" (""Description"", ""ChangedBy"", ""Audit_IsDeleted"")
59 | SELECT COALESCE(j.""Server"", @__hh_0) || '666' AS ""Description"", j.""CompileError"" AS ""ChangedBy"", TRUE AS ""Audit_IsDeleted""
60 | FROM ""Judging_{{schema}}"" AS j
61 | ");
62 | }
63 |
64 | public override void WithAbstractType()
65 | {
66 | base.WithAbstractType();
67 |
68 | AssertSql(@"
69 | INSERT INTO ""Person_{{schema}}"" (""Name"", ""Class"")
70 | SELECT p.""Name"", p.""Subject"" AS ""Class""
71 | FROM ""Person_{{schema}}"" AS p
72 | WHERE p.""Discriminator"" = 'Student'
73 | ");
74 | }
75 |
76 | public override void WithComputedColumn()
77 | {
78 | base.WithComputedColumn();
79 |
80 | AssertSql(V31 | V50, @"
81 | INSERT INTO ""Document_{{schema}}"" (""Content"")
82 | SELECT d.""Content"" || CAST(d.""ContentLength"" AS text) AS ""Content""
83 | FROM ""Document_{{schema}}"" AS d
84 | ");
85 |
86 | AssertSql(V60, @"
87 | INSERT INTO ""Document_{{schema}}"" (""Content"")
88 | SELECT d.""Content"" || d.""ContentLength""::text AS ""Content""
89 | FROM ""Document_{{schema}}"" AS d
90 | ");
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/test/PostgreSql/Functional.UpdateJoin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchUpdateJoin;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class NpgsqlUpdateJoinTest : UpdateJoinTestBase>
6 | {
7 | public NpgsqlUpdateJoinTest(
8 | NpgsqlContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_NormalUpdate()
14 | {
15 | base.CompiledQuery_NormalUpdate();
16 |
17 | AssertSql(@"
18 | UPDATE ""ItemA_{{schema}}"" AS i
19 | SET ""Value"" = (i.""Value"" + t.""Value"") - @__cc
20 | FROM (
21 | SELECT i0.""Id"", i0.""Value""
22 | FROM ""ItemB_{{schema}}"" AS i0
23 | WHERE i0.""Value"" = @__aa
24 | ) AS t
25 | WHERE (i.""Id"" = @__bb) AND (i.""Id"" = t.""Id"")
26 | ");
27 | }
28 |
29 | public override void LocalTableJoin()
30 | {
31 | base.LocalTableJoin();
32 |
33 | AssertSql(@"
34 | UPDATE ""ItemB_{{schema}}"" AS i
35 | SET ""Value"" = i.""Value"" + cte.""Value""
36 | FROM (
37 | VALUES
38 | (@__p_0_0_0, @__p_0_0_1),
39 | (@__p_0_1_0, @__p_0_1_1),
40 | (@__p_0_2_0, @__p_0_2_1)
41 | ) AS cte (""Id"", ""Value"")
42 | WHERE (i.""Id"" <> 2) AND (i.""Id"" = cte.""Id"")
43 | ");
44 | }
45 |
46 | public override void NormalUpdate()
47 | {
48 | base.NormalUpdate();
49 |
50 | AssertSql(@"
51 | UPDATE ""ItemA_{{schema}}"" AS i
52 | SET ""Value"" = (i.""Value"" + t.""Value"") - 3
53 | FROM (
54 | SELECT i0.""Id"", i0.""Value""
55 | FROM ""ItemB_{{schema}}"" AS i0
56 | WHERE i0.""Value"" = 2
57 | ) AS t
58 | WHERE (i.""Id"" = 1) AND (i.""Id"" = t.""Id"")
59 | ");
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/test/Relational/Context.Factory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Tests
6 | {
7 | public abstract class RelationalContextFactoryBase :
8 | ContextFactoryBase
9 | where TContext : DbContext
10 | {
11 | protected string Suffix { get; }
12 |
13 | protected abstract string ScriptSplit { get; }
14 |
15 | protected abstract string DropTableCommand { get; }
16 |
17 | protected RelationalContextFactoryBase()
18 | {
19 | #if EFCORE31
20 | Suffix = "v31";
21 | #elif EFCORE50
22 | Suffix = "v50";
23 | #elif EFCORE60
24 | Suffix = "v60";
25 | #endif
26 | }
27 |
28 | protected override void PostConfigure(DbContextOptionsBuilder optionsBuilder)
29 | {
30 | base.PostConfigure(optionsBuilder);
31 | optionsBuilder.AddInterceptors(new CommandInterceptor(CommandTracer));
32 | }
33 |
34 | protected override void EnsureCreated(TContext context)
35 | {
36 | if (!context.Database.EnsureCreated())
37 | {
38 | var script = context.Database.GenerateCreateScript();
39 | foreach (var line in script.Trim().Split(ScriptSplit, StringSplitOptions.RemoveEmptyEntries))
40 | {
41 | context.Database.ExecuteSqlRaw(line.Trim());
42 | }
43 | }
44 | }
45 |
46 | protected override void EnsureDeleted(TContext context)
47 | {
48 | List entityTypes = new();
49 |
50 | void RecursiveSearch(IEntityType entityType)
51 | {
52 | foreach (IForeignKey foreignKey in entityType.GetForeignKeys())
53 | {
54 | if (foreignKey.PrincipalEntityType != entityType)
55 | {
56 | if (!entityTypes.Contains(foreignKey.PrincipalEntityType))
57 | {
58 | RecursiveSearch(foreignKey.PrincipalEntityType);
59 | }
60 | }
61 | }
62 |
63 | if (!entityTypes.Contains(entityType))
64 | {
65 | entityTypes.Add(entityType);
66 | }
67 | }
68 |
69 | foreach (IEntityType entityType in context.Model.GetEntityTypes())
70 | {
71 | RecursiveSearch(entityType);
72 | }
73 |
74 | entityTypes.Reverse();
75 | foreach (var item in entityTypes)
76 | {
77 | var tableName = item.GetTableName();
78 | if (tableName != null)
79 | {
80 | context.Database.ExecuteSqlRaw(string.Format(DropTableCommand, tableName));
81 | }
82 | }
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/test/Relational/EFCore.Bulk.Relational.Tests-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | $(DefineConstants);EFCORE31
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Relational.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
19 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/Relational/EFCore.Bulk.Relational.Tests-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE50
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Relational.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
19 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/Relational/EFCore.Bulk.Relational.Tests-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Relational.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
19 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/Specifications/Context.CommandNotifier.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Tests
2 | {
3 | public interface ICommandNotifier
4 | {
5 | string LastCommand { get; set; }
6 |
7 | bool Receiving { get; }
8 |
9 | public void SetLastCommand(string command)
10 | {
11 | if (Receiving)
12 | {
13 | LastCommand = command;
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/Specifications/Context.CommandTracer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
2 | using System;
3 | using Xunit;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Tests
6 | {
7 | public class CommandTracer : ICommandNotifier
8 | {
9 | private string _lastCommand;
10 | private bool _receivingCommand;
11 |
12 | string ICommandNotifier.LastCommand
13 | {
14 | get
15 | {
16 | return _lastCommand;
17 | }
18 |
19 | set
20 | {
21 | _lastCommand = value;
22 | Output.WriteLine(
23 | "------------------------------------------\n" +
24 | $"-- {DateTimeOffset.UtcNow:s} Received Command\n" +
25 | "------------------------------------------\n" +
26 | (value ?? "") +
27 | "\n");
28 | }
29 | }
30 |
31 | bool ICommandNotifier.Receiving => _receivingCommand;
32 |
33 | public void AssertSql(string sql)
34 | {
35 | sql = sql.Trim().Replace("\r", string.Empty).Replace("\n", Environment.NewLine);
36 | Assert.Equal(sql, _lastCommand);
37 | }
38 |
39 | public CommandReceivingDisposable BeginScope()
40 | {
41 | if (_receivingCommand)
42 | {
43 | throw new InvalidOperationException("Cannot create a nested scope.");
44 | }
45 |
46 | return new CommandReceivingDisposable(this);
47 | }
48 |
49 | public sealed class CommandReceivingDisposable : IDisposable
50 | {
51 | private CommandTracer _query;
52 |
53 | public CommandReceivingDisposable(CommandTracer query)
54 | {
55 | query._receivingCommand = true;
56 | _query = query;
57 | }
58 |
59 | public void Dispose()
60 | {
61 | if (_query != null)
62 | {
63 | _query._receivingCommand = false;
64 | }
65 |
66 | _query = null;
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/test/Specifications/Context.Factory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using System;
3 | using System.Linq.Expressions;
4 |
5 | namespace Microsoft.EntityFrameworkCore.Tests
6 | {
7 | public interface IDbContextFactory : IDisposable
8 | where TContext : DbContext
9 | {
10 | ILoggerFactory LoggerFactory { get; }
11 |
12 | CommandTracer CommandTracer { get; }
13 |
14 | string UniqueId { get; }
15 |
16 | DbContextOptions Options { get; }
17 |
18 | object Seed { get; }
19 |
20 | TContext Create();
21 | }
22 |
23 | public interface IDbContextWithSeeds
24 | {
25 | object Seed();
26 | }
27 |
28 | public abstract class ContextFactoryBase :
29 | IDbContextFactory
30 | where TContext : DbContext
31 | {
32 | private Func _factory;
33 | private DbContextOptions _options;
34 |
35 | public ILoggerFactory LoggerFactory { get; }
36 |
37 | public CommandTracer CommandTracer { get; }
38 |
39 | public object Seed { get; }
40 |
41 | public string UniqueId { get; }
42 |
43 | public DbContextOptions Options => _options;
44 |
45 | public virtual TContext Create()
46 | {
47 | return _factory();
48 | }
49 |
50 | protected ContextFactoryBase()
51 | {
52 | var contextType = typeof(TContext);
53 | var constructor = contextType.GetConstructor(new[] { typeof(string), typeof(DbContextOptions) });
54 | if (constructor == null)
55 | {
56 | throw new InvalidOperationException("The DbContext type must have a constructor of .ctor(string schema, DbContextOptions options).");
57 | }
58 |
59 | LoggerFactory = ContextLoggerFactory.Singleton;
60 | CommandTracer = new CommandTracer();
61 | UniqueId = Guid.NewGuid().ToString()[0..6];
62 |
63 | var optionsBuilder = new DbContextOptionsBuilder();
64 | Configure(optionsBuilder);
65 | PostConfigure(optionsBuilder);
66 | _options = optionsBuilder.Options;
67 |
68 | _factory = Expression.Lambda>(
69 | Expression.New(
70 | constructor,
71 | Expression.Constant(UniqueId),
72 | Expression.Constant(_options)))
73 | .Compile();
74 |
75 | using (var context = _factory())
76 | {
77 | EnsureCreated(context);
78 | }
79 |
80 | using (var context = _factory())
81 | {
82 | Seed = (context as IDbContextWithSeeds)?.Seed();
83 | }
84 | }
85 |
86 | protected virtual void PostConfigure(DbContextOptionsBuilder optionsBuilder)
87 | {
88 | optionsBuilder.UseLoggerFactory(LoggerFactory);
89 | }
90 |
91 | protected abstract void Configure(DbContextOptionsBuilder optionsBuilder);
92 |
93 | protected abstract void EnsureCreated(TContext context);
94 |
95 | protected abstract void EnsureDeleted(TContext context);
96 |
97 | public void Dispose()
98 | {
99 | using (var context = _factory())
100 | {
101 | EnsureDeleted(context);
102 | }
103 |
104 | _factory = null;
105 | _options = null;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/test/Specifications/Context.LoggerFactory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public static class ContextLoggerFactory
6 | {
7 | public static ILoggerFactory Singleton { get; } = LoggerFactory.Create(l => l.AddDebug());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/Specifications/Context.PrepareDatabase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace Microsoft.EntityFrameworkCore.Tests
5 | {
6 | public class PrepareContext : DbContext
7 | {
8 | public class HelloWorld
9 | {
10 | public int Id { get; set; }
11 | public string Description { get; set; }
12 | }
13 |
14 | public string DefaultSchema { get; }
15 |
16 | public PrepareContext(string schema, DbContextOptions options)
17 | : base(options)
18 | {
19 | DefaultSchema = schema;
20 | }
21 |
22 | protected override void OnModelCreating(ModelBuilder modelBuilder)
23 | {
24 | modelBuilder.Entity(entity =>
25 | {
26 | entity.ToTable(nameof(HelloWorld) + "_" + DefaultSchema);
27 | });
28 | }
29 | }
30 |
31 | [CollectionDefinition("DatabaseCollection")]
32 | public abstract class DatabaseCollection : IDisposable
33 | where TFactory : class, IDbContextFactory
34 | {
35 | private readonly TFactory _factory;
36 |
37 | protected DatabaseCollection(TFactory factory)
38 | {
39 | _factory = factory;
40 | using var context = _factory.Create();
41 | context.Database.EnsureCreated();
42 | }
43 |
44 | public void Dispose()
45 | {
46 | using var context = _factory.Create();
47 | context.Database.EnsureDeleted();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test/Specifications/Context.QueryTestBase.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
2 | using System;
3 | using System.Diagnostics;
4 | using Xunit;
5 |
6 | namespace Microsoft.EntityFrameworkCore.Tests
7 | {
8 | [Collection("DatabaseCollection")]
9 | public abstract class QueryTestBase : IClassFixture
10 | where TContext : DbContext
11 | where TFactory : class, IDbContextFactory
12 | {
13 | private readonly TFactory _factory;
14 |
15 | protected const DatabaseProvider V31 = DatabaseProvider.Version_31;
16 | protected const DatabaseProvider V50 = DatabaseProvider.Version_50;
17 | protected const DatabaseProvider V60 = DatabaseProvider.Version_60;
18 |
19 | protected TContext CreateContext()
20 | {
21 | return _factory.Create();
22 | }
23 |
24 | protected void AssertSql(string sql)
25 | {
26 | _factory.CommandTracer.AssertSql(sql.Replace("{{schema}}", _factory.UniqueId));
27 | }
28 |
29 | protected void AssertSql(DatabaseProvider version, string sql)
30 | {
31 | #if EFCORE31
32 | if ((version & V31) == V31) AssertSql(sql);
33 | #elif EFCORE50
34 | if ((version & V50) == V50) AssertSql(sql);
35 | #elif EFCORE60
36 | if ((version & V60) == V60) AssertSql(sql);
37 | #endif
38 | }
39 |
40 | protected IDisposable CatchCommand()
41 | {
42 | return _factory.CommandTracer.BeginScope();
43 | }
44 |
45 | protected QueryTestBase(TFactory factory)
46 | {
47 | _factory = factory;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test/Specifications/EFCore.Bulk.Tests-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | $(DefineConstants);EFCORE31
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
24 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/test/Specifications/EFCore.Bulk.Tests-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE50
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
24 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/test/Specifications/EFCore.Bulk.Tests-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
24 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/test/SqlServer/Context.Factory.cs:
--------------------------------------------------------------------------------
1 | namespace Microsoft.EntityFrameworkCore.Tests
2 | {
3 | public class SqlServerContextFactory : RelationalContextFactoryBase
4 | where TContext : DbContext
5 | {
6 | protected override string ScriptSplit => "\r\nGO";
7 |
8 | protected override string DropTableCommand => "DROP TABLE IF EXISTS [{0}]";
9 |
10 | protected override void Configure(DbContextOptionsBuilder optionsBuilder)
11 | {
12 | optionsBuilder.UseSqlServer(
13 | $"Server=(localdb)\\mssqllocaldb;" +
14 | $"Database=efcorebulktest{Suffix};" +
15 | $"Trusted_Connection=True;" +
16 | $"MultipleActiveResultSets=true",
17 | s => s.UseBulk().UseMathExtensions());
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/SqlServer/Context.PrepareDatabase.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | [Collection("DatabaseCollection")]
6 | public class DatabaseCollection :
7 | DatabaseCollection>,
8 | ICollectionFixture
9 | {
10 | public DatabaseCollection() : base(new SqlServerContextFactory())
11 | {
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/SqlServer/EFCore.Bulk.SqlServer.Tests-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | $(DefineConstants);EFCORE31
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.SqlServer.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/SqlServer/EFCore.Bulk.SqlServer.Tests-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE50
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.SqlServer.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/SqlServer/EFCore.Bulk.SqlServer.Tests-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.SqlServer.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
26 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/SqlServer/Functional.Delete.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchDelete;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class SqlServerDeleteTest : DeleteTestBase>
6 | {
7 | public SqlServerDeleteTest(
8 | SqlServerContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_ConstantCondition()
14 | {
15 | base.CompiledQuery_ConstantCondition();
16 |
17 | AssertSql(@"
18 | DELETE [i]
19 | FROM [Item_{{schema}}] AS [i]
20 | WHERE ([i].[ItemId] > 500) AND ([i].[Price] = 3.0)
21 | ");
22 | }
23 |
24 | public override void CompiledQuery_ContainsSomething()
25 | {
26 | base.CompiledQuery_ContainsSomething();
27 |
28 | AssertSql(@"
29 | DELETE [i]
30 | FROM [Item_{{schema}}] AS [i]
31 | WHERE [i].[Name] IN (N'jyntnytjyntjntnytnt', N'aaa')
32 | ");
33 | }
34 |
35 | public override void CompiledQuery_ParameteredCondition()
36 | {
37 | base.CompiledQuery_ParameteredCondition();
38 |
39 | AssertSql(@"
40 | DELETE [i]
41 | FROM [Item_{{schema}}] AS [i]
42 | WHERE [i].[Name] = @__nameToDelete
43 | ");
44 | }
45 |
46 | public override void ConstantCondition()
47 | {
48 | base.ConstantCondition();
49 |
50 | AssertSql(@"
51 | DELETE [i]
52 | FROM [Item_{{schema}}] AS [i]
53 | WHERE ([i].[ItemId] > 500) AND ([i].[Price] = 124.0)
54 | ");
55 | }
56 |
57 | public override void ContainsAndAlsoEqual()
58 | {
59 | base.ContainsAndAlsoEqual();
60 |
61 | AssertSql(V31, @"
62 | DELETE [i]
63 | FROM [Item_{{schema}}] AS [i]
64 | WHERE [i].[Description] IN (N'info') OR ([i].[Name] = @__nameToDelete_1)
65 | ");
66 |
67 | AssertSql(V50 | V60, @"
68 | DELETE [i]
69 | FROM [Item_{{schema}}] AS [i]
70 | WHERE ([i].[Description] = N'info') OR ([i].[Name] = @__nameToDelete_1)
71 | ");
72 | }
73 |
74 | public override void ContainsSomething()
75 | {
76 | base.ContainsSomething();
77 |
78 | AssertSql(@"
79 | DELETE [i]
80 | FROM [Item_{{schema}}] AS [i]
81 | WHERE [i].[Description] IN (N'info', N'aaa')
82 | ");
83 | }
84 |
85 | public override void EmptyContains()
86 | {
87 | base.EmptyContains();
88 |
89 | AssertSql(V31, @"
90 | DELETE [i]
91 | FROM [Item_{{schema}}] AS [i]
92 | WHERE CAST(1 AS bit) = CAST(0 AS bit)
93 | ");
94 |
95 | AssertSql(V50 | V60, @"
96 | DELETE [i]
97 | FROM [Item_{{schema}}] AS [i]
98 | WHERE 0 = 1
99 | ");
100 | }
101 |
102 | public override void ListAny()
103 | {
104 | base.ListAny();
105 |
106 | AssertSql(V31, @"
107 | DELETE [i]
108 | FROM [Item_{{schema}}] AS [i]
109 | WHERE [i].[Description] IN (N'info')
110 | ");
111 |
112 | AssertSql(V50 | V60, @"
113 | DELETE [i]
114 | FROM [Item_{{schema}}] AS [i]
115 | WHERE [i].[Description] = N'info'
116 | ");
117 | }
118 |
119 | public override void ParameteredCondition()
120 | {
121 | base.ParameteredCondition();
122 |
123 | AssertSql(@"
124 | DELETE [i]
125 | FROM [Item_{{schema}}] AS [i]
126 | WHERE [i].[Name] = @__nameToDelete_0
127 | ");
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/test/SqlServer/Functional.InsertInto.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchInsertInto;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class SqlServerInsertIntoTest : InsertIntoTestBase>
6 | {
7 | public SqlServerInsertIntoTest(
8 | SqlServerContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_NormalSelectInto()
14 | {
15 | base.CompiledQuery_NormalSelectInto();
16 |
17 | AssertSql(@"
18 | INSERT INTO [ChangeLog_{{schema}}] ([Description], [ChangedBy], [Audit_IsDeleted])
19 | SELECT COALESCE([j].[Server], @__hh) + N'666' AS [Description], [j].[CompileError] AS [ChangedBy], CAST(1 AS bit) AS [Audit_IsDeleted]
20 | FROM [Judging_{{schema}}] AS [j]
21 | ");
22 | }
23 |
24 | public override void CompiledQuery_WithAbstractType()
25 | {
26 | base.CompiledQuery_WithAbstractType();
27 |
28 | AssertSql(@"
29 | INSERT INTO [Person_{{schema}}] ([Name], [Class])
30 | SELECT [p].[Name], [p].[Subject] AS [Class]
31 | FROM [Person_{{schema}}] AS [p]
32 | WHERE [p].[Discriminator] = N'Student'
33 | ");
34 | }
35 |
36 | public override void CompiledQuery_WithComputedColumn()
37 | {
38 | base.CompiledQuery_WithComputedColumn();
39 |
40 | AssertSql(V31, @"
41 | INSERT INTO [Document_{{schema}}] ([Content])
42 | SELECT [d].[Content] + CONVERT(VARCHAR(11), [d].[ContentLength]) AS [Content]
43 | FROM [Document_{{schema}}] AS [d]
44 | ");
45 |
46 | AssertSql(V50, @"
47 | INSERT INTO [Document_{{schema}}] ([Content])
48 | SELECT [d].[Content] + COALESCE(CONVERT(VARCHAR(11), [d].[ContentLength]), N'') AS [Content]
49 | FROM [Document_{{schema}}] AS [d]
50 | ");
51 |
52 | AssertSql(V60, @"
53 | INSERT INTO [Document_{{schema}}] ([Content])
54 | SELECT [d].[Content] + COALESCE(CONVERT(varchar(11), [d].[ContentLength]), N'') AS [Content]
55 | FROM [Document_{{schema}}] AS [d]
56 | ");
57 | }
58 |
59 | public override void NormalSelectInto()
60 | {
61 | base.NormalSelectInto();
62 |
63 | AssertSql(@"
64 | INSERT INTO [ChangeLog_{{schema}}] ([Description], [ChangedBy], [Audit_IsDeleted])
65 | SELECT COALESCE([j].[Server], @__hh_0) + N'666' AS [Description], [j].[CompileError] AS [ChangedBy], CAST(1 AS bit) AS [Audit_IsDeleted]
66 | FROM [Judging_{{schema}}] AS [j]
67 | ");
68 | }
69 |
70 | public override void WithAbstractType()
71 | {
72 | base.WithAbstractType();
73 |
74 | AssertSql(@"
75 | INSERT INTO [Person_{{schema}}] ([Name], [Class])
76 | SELECT [p].[Name], [p].[Subject] AS [Class]
77 | FROM [Person_{{schema}}] AS [p]
78 | WHERE [p].[Discriminator] = N'Student'
79 | ");
80 | }
81 |
82 | public override void WithComputedColumn()
83 | {
84 | base.WithComputedColumn();
85 |
86 | AssertSql(V31, @"
87 | INSERT INTO [Document_{{schema}}] ([Content])
88 | SELECT [d].[Content] + CONVERT(VARCHAR(11), [d].[ContentLength]) AS [Content]
89 | FROM [Document_{{schema}}] AS [d]
90 | ");
91 |
92 | AssertSql(V50, @"
93 | INSERT INTO [Document_{{schema}}] ([Content])
94 | SELECT [d].[Content] + COALESCE(CONVERT(VARCHAR(11), [d].[ContentLength]), N'') AS [Content]
95 | FROM [Document_{{schema}}] AS [d]
96 | ");
97 |
98 | AssertSql(V60, @"
99 | INSERT INTO [Document_{{schema}}] ([Content])
100 | SELECT [d].[Content] + COALESCE(CONVERT(varchar(11), [d].[ContentLength]), N'') AS [Content]
101 | FROM [Document_{{schema}}] AS [d]
102 | ");
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/test/SqlServer/Functional.UpdateJoin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchUpdateJoin;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class SqlServerUpdateJoinTest : UpdateJoinTestBase>
6 | {
7 | public SqlServerUpdateJoinTest(
8 | SqlServerContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_NormalUpdate()
14 | {
15 | base.CompiledQuery_NormalUpdate();
16 |
17 | AssertSql(@"
18 | UPDATE [i]
19 | SET [i].[Value] = ([i].[Value] + [t].[Value]) - @__cc
20 | FROM [ItemA_{{schema}}] AS [i]
21 | INNER JOIN (
22 | SELECT [i0].[Id], [i0].[Value]
23 | FROM [ItemB_{{schema}}] AS [i0]
24 | WHERE [i0].[Value] = @__aa
25 | ) AS [t] ON [i].[Id] = [t].[Id]
26 | WHERE [i].[Id] = @__bb
27 | ");
28 | }
29 |
30 | public override void LocalTableJoin()
31 | {
32 | base.LocalTableJoin();
33 |
34 | AssertSql(@"
35 | UPDATE [i]
36 | SET [i].[Value] = [i].[Value] + [cte].[Value]
37 | FROM [ItemB_{{schema}}] AS [i]
38 | INNER JOIN (
39 | VALUES
40 | (@__p_0_0_0, @__p_0_0_1),
41 | (@__p_0_1_0, @__p_0_1_1),
42 | (@__p_0_2_0, @__p_0_2_1)
43 | ) AS [cte] ([Id], [Value]) ON [i].[Id] = [cte].[Id]
44 | WHERE [i].[Id] <> 2
45 | ");
46 | }
47 |
48 | public override void NormalUpdate()
49 | {
50 | base.NormalUpdate();
51 |
52 | AssertSql(@"
53 | UPDATE [i]
54 | SET [i].[Value] = ([i].[Value] + [t].[Value]) - 3
55 | FROM [ItemA_{{schema}}] AS [i]
56 | INNER JOIN (
57 | SELECT [i0].[Id], [i0].[Value]
58 | FROM [ItemB_{{schema}}] AS [i0]
59 | WHERE [i0].[Value] = 2
60 | ) AS [t] ON [i].[Id] = [t].[Id]
61 | WHERE [i].[Id] = 1
62 | ");
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/test/Sqlite/Context.Factory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Data.Sqlite;
2 | using System;
3 |
4 | namespace Microsoft.EntityFrameworkCore.Tests
5 | {
6 | public static class SqliteConnectionHolder
7 | {
8 | public static SqliteConnection Singleton { get; }
9 | = new Func(delegate
10 | {
11 | var connection = new SqliteConnection("Filename=:memory:");
12 | connection.Open();
13 | return connection;
14 | })
15 | .Invoke();
16 | }
17 |
18 | public class SqliteContextFactory : RelationalContextFactoryBase
19 | where TContext : DbContext
20 | {
21 | protected override string ScriptSplit => ";\r\n\r\n";
22 |
23 | protected override string DropTableCommand => "DROP TABLE IF EXISTS \"{0}\"";
24 |
25 | protected override void Configure(DbContextOptionsBuilder optionsBuilder)
26 | {
27 | optionsBuilder.UseSqlite(
28 | SqliteConnectionHolder.Singleton,
29 | s => s.UseBulk());
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/test/Sqlite/Context.PrepareDatabase.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | [Collection("DatabaseCollection")]
6 | public class DatabaseCollection :
7 | DatabaseCollection>,
8 | ICollectionFixture
9 | {
10 | public DatabaseCollection() : base(new SqliteContextFactory())
11 | {
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/Sqlite/EFCore.Bulk.Sqlite.Tests-3.1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | $(DefineConstants);EFCORE31
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Sqlite.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
27 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/test/Sqlite/EFCore.Bulk.Sqlite.Tests-5.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE50
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Sqlite.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
27 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/test/Sqlite/EFCore.Bulk.Sqlite.Tests-6.0.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | $(DefineConstants);EFCORE60
6 | false
7 | Microsoft.EntityFrameworkCore.Bulk.Sqlite.Tests
8 | Microsoft.EntityFrameworkCore.Tests
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | <_Parameter1>Microsoft.EntityFrameworkCore.TestUtilities.Xunit.TestPriorityOrderer
27 | <_Parameter2>Microsoft.EntityFrameworkCore.TestUtilities
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/test/Sqlite/Functional.Delete.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchDelete;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class SqliteDeleteTest : DeleteTestBase>
6 | {
7 | public SqliteDeleteTest(
8 | SqliteContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_ConstantCondition()
14 | {
15 | base.CompiledQuery_ConstantCondition();
16 |
17 | AssertSql(@"
18 | DELETE FROM ""Item_{{schema}}"" AS ""i""
19 | WHERE (""i"".""ItemId"" > 500) AND (""i"".""Price"" = '3.0')
20 | ");
21 | }
22 |
23 | public override void CompiledQuery_ContainsSomething()
24 | {
25 | base.CompiledQuery_ContainsSomething();
26 |
27 | AssertSql(@"
28 | DELETE FROM ""Item_{{schema}}"" AS ""i""
29 | WHERE ""i"".""Name"" IN ('jyntnytjyntjntnytnt', 'aaa')
30 | ");
31 | }
32 |
33 | public override void CompiledQuery_ParameteredCondition()
34 | {
35 | base.CompiledQuery_ParameteredCondition();
36 |
37 | AssertSql(@"
38 | DELETE FROM ""Item_{{schema}}"" AS ""i""
39 | WHERE ""i"".""Name"" = @__nameToDelete
40 | ");
41 | }
42 |
43 | public override void ConstantCondition()
44 | {
45 | base.ConstantCondition();
46 |
47 | AssertSql(@"
48 | DELETE FROM ""Item_{{schema}}"" AS ""i""
49 | WHERE (""i"".""ItemId"" > 500) AND (""i"".""Price"" = '124.0')
50 | ");
51 | }
52 |
53 | public override void ContainsAndAlsoEqual()
54 | {
55 | base.ContainsAndAlsoEqual();
56 |
57 | AssertSql(V31, @"
58 | DELETE FROM ""Item_{{schema}}"" AS ""i""
59 | WHERE ""i"".""Description"" IN ('info') OR (""i"".""Name"" = @__nameToDelete_1)
60 | ");
61 |
62 | AssertSql(V50 | V60, @"
63 | DELETE FROM ""Item_{{schema}}"" AS ""i""
64 | WHERE (""i"".""Description"" = 'info') OR (""i"".""Name"" = @__nameToDelete_1)
65 | ");
66 | }
67 |
68 | public override void ContainsSomething()
69 | {
70 | base.ContainsSomething();
71 |
72 | AssertSql(@"
73 | DELETE FROM ""Item_{{schema}}"" AS ""i""
74 | WHERE ""i"".""Description"" IN ('info', 'aaa')
75 | ");
76 | }
77 |
78 | public override void EmptyContains()
79 | {
80 | base.EmptyContains();
81 |
82 | AssertSql(V31, @"
83 | DELETE FROM ""Item_{{schema}}"" AS ""i""
84 | WHERE 1 = 0
85 | ");
86 |
87 | AssertSql(V50 | V60, @"
88 | DELETE FROM ""Item_{{schema}}"" AS ""i""
89 | WHERE 0
90 | ");
91 | }
92 |
93 | public override void ListAny()
94 | {
95 | base.ListAny();
96 |
97 | AssertSql(V31, @"
98 | DELETE FROM ""Item_{{schema}}"" AS ""i""
99 | WHERE ""i"".""Description"" IN ('info')
100 | ");
101 |
102 | AssertSql(V50 | V60, @"
103 | DELETE FROM ""Item_{{schema}}"" AS ""i""
104 | WHERE ""i"".""Description"" = 'info'
105 | ");
106 | }
107 |
108 | public override void ParameteredCondition()
109 | {
110 | base.ParameteredCondition();
111 |
112 | AssertSql(@"
113 | DELETE FROM ""Item_{{schema}}"" AS ""i""
114 | WHERE ""i"".""Name"" = @__nameToDelete_0
115 | ");
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/test/Sqlite/Functional.InsertInto.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchInsertInto;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class SqliteInsertIntoTest : InsertIntoTestBase>
6 | {
7 | public SqliteInsertIntoTest(
8 | SqliteContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_NormalSelectInto()
14 | {
15 | base.CompiledQuery_NormalSelectInto();
16 |
17 | AssertSql(@"
18 | INSERT INTO ""ChangeLog_{{schema}}"" (""Description"", ""ChangedBy"", ""Audit_IsDeleted"")
19 | SELECT COALESCE(""j"".""Server"", @__hh) || '666' AS ""Description"", ""j"".""CompileError"" AS ""ChangedBy"", 1 AS ""Audit_IsDeleted""
20 | FROM ""Judging_{{schema}}"" AS ""j""
21 | ");
22 | }
23 |
24 | public override void CompiledQuery_WithAbstractType()
25 | {
26 | base.CompiledQuery_WithAbstractType();
27 |
28 | AssertSql(@"
29 | INSERT INTO ""Person_{{schema}}"" (""Name"", ""Class"")
30 | SELECT ""p"".""Name"", ""p"".""Subject"" AS ""Class""
31 | FROM ""Person_{{schema}}"" AS ""p""
32 | WHERE ""p"".""Discriminator"" = 'Student'
33 | ");
34 | }
35 |
36 | public override void NormalSelectInto()
37 | {
38 | base.NormalSelectInto();
39 |
40 | AssertSql(@"
41 | INSERT INTO ""ChangeLog_{{schema}}"" (""Description"", ""ChangedBy"", ""Audit_IsDeleted"")
42 | SELECT COALESCE(""j"".""Server"", @__hh_0) || '666' AS ""Description"", ""j"".""CompileError"" AS ""ChangedBy"", 1 AS ""Audit_IsDeleted""
43 | FROM ""Judging_{{schema}}"" AS ""j""
44 | ");
45 | }
46 |
47 | public override void WithAbstractType()
48 | {
49 | base.WithAbstractType();
50 |
51 | AssertSql(@"
52 | INSERT INTO ""Person_{{schema}}"" (""Name"", ""Class"")
53 | SELECT ""p"".""Name"", ""p"".""Subject"" AS ""Class""
54 | FROM ""Person_{{schema}}"" AS ""p""
55 | WHERE ""p"".""Discriminator"" = 'Student'
56 | ");
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/test/Sqlite/Functional.UpdateJoin.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Tests.BatchUpdateJoin;
2 |
3 | namespace Microsoft.EntityFrameworkCore.Tests
4 | {
5 | public class SqliteUpdateJoinTest : UpdateJoinTestBase>
6 | {
7 | public SqliteUpdateJoinTest(
8 | SqliteContextFactory factory)
9 | : base(factory)
10 | {
11 | }
12 |
13 | public override void CompiledQuery_NormalUpdate()
14 | {
15 | base.CompiledQuery_NormalUpdate();
16 |
17 | AssertSql(@"
18 | UPDATE ""ItemA_{{schema}}"" AS ""i""
19 | SET ""Value"" = (""i"".""Value"" + ""t"".""Value"") - @__cc
20 | FROM (
21 | SELECT ""i0"".""Id"", ""i0"".""Value""
22 | FROM ""ItemB_{{schema}}"" AS ""i0""
23 | WHERE ""i0"".""Value"" = @__aa
24 | ) AS ""t""
25 | WHERE (""i"".""Id"" = @__bb) AND (""i"".""Id"" = ""t"".""Id"")
26 | ");
27 | }
28 |
29 | public override void LocalTableJoin()
30 | {
31 | base.LocalTableJoin();
32 |
33 | AssertSql(@"
34 | WITH ""cte"" (""Id"", ""Value"") AS (
35 | VALUES
36 | (@__p_0_0_0, @__p_0_0_1),
37 | (@__p_0_1_0, @__p_0_1_1),
38 | (@__p_0_2_0, @__p_0_2_1)
39 | )
40 | UPDATE ""ItemB_{{schema}}"" AS ""i""
41 | SET ""Value"" = ""i"".""Value"" + ""cte"".""Value""
42 | FROM ""cte""
43 | WHERE (""i"".""Id"" <> 2) AND (""i"".""Id"" = ""cte"".""Id"")
44 | ");
45 | }
46 |
47 | public override void NormalUpdate()
48 | {
49 | base.NormalUpdate();
50 |
51 | AssertSql(@"
52 | UPDATE ""ItemA_{{schema}}"" AS ""i""
53 | SET ""Value"" = (""i"".""Value"" + ""t"".""Value"") - 3
54 | FROM (
55 | SELECT ""i0"".""Id"", ""i0"".""Value""
56 | FROM ""ItemB_{{schema}}"" AS ""i0""
57 | WHERE ""i0"".""Value"" = 2
58 | ) AS ""t""
59 | WHERE (""i"".""Id"" = 1) AND (""i"".""Id"" = ""t"".""Id"")
60 | ");
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/test/TestUtilities/DatabaseProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
4 | {
5 | [Flags]
6 | public enum DatabaseProvider
7 | {
8 | None = 0,
9 |
10 | InMemory_31 = 1 << 0,
11 | InMemory_50 = 1 << 1,
12 | InMemory_60 = 1 << 2,
13 |
14 | SqlServer_31 = 1 << 3,
15 | SqlServer_50 = 1 << 4,
16 | SqlServer_60 = 1 << 5,
17 |
18 | PostgreSQL_31 = 1 << 6,
19 | PostgreSQL_50 = 1 << 7,
20 | PostgreSQL_60 = 1 << 8,
21 |
22 | Sqlite_31 = 1 << 9,
23 | Sqlite_50 = 1 << 10,
24 | Sqlite_60 = 1 << 11,
25 |
26 | MySql_31 = 1 << 12,
27 | MySql_50 = 1 << 13,
28 | MySql_60 = 1 << 14,
29 |
30 | InMemory = InMemory_31 | InMemory_50 | InMemory_60,
31 | SqlServer = SqlServer_31 | SqlServer_50 | SqlServer_60,
32 | PostgreSQL = PostgreSQL_31 | PostgreSQL_50 | PostgreSQL_60,
33 | Sqlite = Sqlite_31 | Sqlite_50 | Sqlite_60,
34 | MySql = MySql_31 | MySql_50 | MySql_60,
35 |
36 | Relational_31 = SqlServer_31 | PostgreSQL_31 | Sqlite_31 | MySql_31,
37 | Relational_50 = SqlServer_50 | PostgreSQL_50 | Sqlite_50 | MySql_50,
38 | Relational_60 = SqlServer_60 | PostgreSQL_60 | Sqlite_60 | MySql_60,
39 | Relational = SqlServer | PostgreSQL | Sqlite | MySql,
40 |
41 | Version_31 = InMemory_31 | Relational_31,
42 | Version_50 = InMemory_50 | Relational_50,
43 | Version_60 = InMemory_60 | Relational_60,
44 | All = InMemory | Relational
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/TestUtilities/DatabaseProviderSkipConditionAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Xunit.Abstractions;
4 | using Xunit.Sdk;
5 |
6 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
7 | {
8 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
9 | public sealed class DatabaseProviderSkipConditionAttribute : Attribute, ITestCondition
10 | {
11 | public DatabaseProvider ExcludedProviders { get; }
12 |
13 | public DatabaseProviderSkipConditionAttribute(DatabaseProvider excludedProviders)
14 | {
15 | ExcludedProviders = excludedProviders;
16 | }
17 |
18 | private static DatabaseProvider GetCurrentProvider(ITestMethod method)
19 | {
20 | var dir = method.Method.Type.Assembly.Name;
21 |
22 | var dp = dir.Contains("InMemory", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.InMemory
23 | : dir.Contains("PostgreSql", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.PostgreSQL
24 | : dir.Contains("SqlServer", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.SqlServer
25 | : dir.Contains("Sqlite", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.Sqlite
26 | : dir.Contains("MySql", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.MySql
27 | : DatabaseProvider.None;
28 |
29 | var ver = dir.Contains("3.1", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.Version_31
30 | : dir.Contains("5.0", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.Version_50
31 | : dir.Contains("6.0", StringComparison.OrdinalIgnoreCase) ? DatabaseProvider.Version_60
32 | : DatabaseProvider.None;
33 |
34 | return dp & ver;
35 | }
36 |
37 | public string SkipReason { get; set; } = "Test cannot run on this provider.";
38 |
39 | public ValueTask IsMetAsync(XunitTestCase testcase)
40 | {
41 | return new ValueTask(
42 | (GetCurrentProvider(testcase.TestMethod) & ExcludedProviders) == DatabaseProvider.None);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/TestUtilities/EFCore.TestUtilities.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | false
6 | Microsoft.EntityFrameworkCore.TestUtilities
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | $(AssemblyName)
17 | $(AssemblyName)
18 | 1.0.0
19 | 1.0.0
20 | 1.0.0.0
21 | 1.0.0.0
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/test/TestUtilities/TestPriorityAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Xunit
4 | {
5 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
6 | public class TestPriorityAttribute : Attribute
7 | {
8 | public TestPriorityAttribute(int priority)
9 | {
10 | Priority = priority;
11 | }
12 |
13 | public int Priority { get; private set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/TestUtilities/TestPriorityOrderer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Xunit;
5 | using Xunit.Abstractions;
6 | using Xunit.Sdk;
7 |
8 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
9 | {
10 | public class TestPriorityOrderer : ITestCaseOrderer
11 | {
12 | public IEnumerable OrderTestCases(IEnumerable testCases) where TTestCase : ITestCase
13 | {
14 | var sortedMethods = new SortedDictionary>();
15 |
16 | foreach (TTestCase testCase in testCases)
17 | {
18 | int priority = 0;
19 |
20 | foreach (IAttributeInfo attr in testCase.TestMethod.Method.GetCustomAttributes((typeof(TestPriorityAttribute).AssemblyQualifiedName)))
21 | priority = attr.GetNamedArgument("Priority");
22 |
23 | GetOrCreate(sortedMethods, priority).Add(testCase);
24 | }
25 |
26 | foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority]))
27 | {
28 | list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name));
29 | foreach (TTestCase testCase in list)
30 | yield return testCase;
31 | }
32 | }
33 |
34 | static TValue GetOrCreate(IDictionary dictionary, TKey key) where TValue : new()
35 | {
36 | TValue result;
37 |
38 | if (dictionary.TryGetValue(key, out result)) return result;
39 |
40 | result = new TValue();
41 | dictionary[key] = result;
42 |
43 | return result;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/ConditionalFactAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using Xunit.Sdk;
6 |
7 | // ReSharper disable once CheckNamespace
8 | namespace Xunit
9 | {
10 | [AttributeUsage(AttributeTargets.Method)]
11 | [XunitTestCaseDiscoverer(
12 | "Microsoft.EntityFrameworkCore.TestUtilities.Xunit.ConditionalFactDiscoverer",
13 | "Microsoft.EntityFrameworkCore.TestUtilities")]
14 | public sealed class ConditionalFactAttribute : FactAttribute
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/ConditionalFactDiscoverer.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using Xunit;
5 | using Xunit.Abstractions;
6 | using Xunit.Sdk;
7 |
8 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
9 | {
10 | ///
11 | /// Used dynamically from .
12 | /// Make sure to update that class if you move this type.
13 | ///
14 | public class ConditionalFactDiscoverer : FactDiscoverer
15 | {
16 | public ConditionalFactDiscoverer(IMessageSink messageSink)
17 | : base(messageSink)
18 | {
19 | }
20 |
21 | protected override IXunitTestCase CreateTestCase(
22 | ITestFrameworkDiscoveryOptions discoveryOptions,
23 | ITestMethod testMethod,
24 | IAttributeInfo factAttribute)
25 | => new ConditionalFactTestCase(
26 | DiagnosticMessageSink,
27 | discoveryOptions.MethodDisplayOrDefault(),
28 | discoveryOptions.MethodDisplayOptionsOrDefault(),
29 | testMethod);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/ConditionalFactTestCase.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Xunit.Abstractions;
8 | using Xunit.Sdk;
9 |
10 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
11 | {
12 | public sealed class ConditionalFactTestCase : XunitTestCase
13 | {
14 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
15 | public ConditionalFactTestCase()
16 | {
17 | }
18 |
19 | public ConditionalFactTestCase(
20 | IMessageSink diagnosticMessageSink,
21 | TestMethodDisplay defaultMethodDisplay,
22 | TestMethodDisplayOptions defaultMethodDisplayOptions,
23 | ITestMethod testMethod,
24 | object[] testMethodArguments = null)
25 | : base(
26 | diagnosticMessageSink,
27 | defaultMethodDisplay,
28 | defaultMethodDisplayOptions,
29 | testMethod,
30 | testMethodArguments)
31 | {
32 | }
33 |
34 | public override async Task RunAsync(
35 | IMessageSink diagnosticMessageSink,
36 | IMessageBus messageBus,
37 | object[] constructorArguments,
38 | ExceptionAggregator aggregator,
39 | CancellationTokenSource cancellationTokenSource)
40 | {
41 | if (await XunitTestCaseExtensions.TrySkipAsync(this, messageBus))
42 | {
43 | return new RunSummary { Total = 1, Skipped = 1 };
44 | }
45 | else
46 | {
47 | var runner = new ContextualTestCaseRunner(
48 | this,
49 | DisplayName,
50 | SkipReason,
51 | constructorArguments,
52 | TestMethodArguments,
53 | messageBus,
54 | aggregator,
55 | cancellationTokenSource);
56 |
57 | return await runner.RunAsync();
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/ConditionalTheoryAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using Xunit.Sdk;
6 |
7 | // ReSharper disable once CheckNamespace
8 | namespace Xunit
9 | {
10 | [AttributeUsage(AttributeTargets.Method)]
11 | [XunitTestCaseDiscoverer(
12 | "Microsoft.EntityFrameworkCore.TestUtilities.Xunit.ConditionalTheoryDiscoverer",
13 | "Microsoft.EntityFrameworkCore.TestUtilities")]
14 | public sealed class ConditionalTheoryAttribute : TheoryAttribute
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/ConditionalTheoryDiscoverer.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System.Collections.Generic;
5 | using Xunit;
6 | using Xunit.Abstractions;
7 | using Xunit.Sdk;
8 |
9 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
10 | {
11 | ///
12 | /// Used dynamically from .
13 | /// Make sure to update that class if you move this type.
14 | ///
15 | public class ConditionalTheoryDiscoverer : TheoryDiscoverer
16 | {
17 | public ConditionalTheoryDiscoverer(IMessageSink messageSink)
18 | : base(messageSink)
19 | {
20 | }
21 |
22 | protected override IEnumerable CreateTestCasesForTheory(
23 | ITestFrameworkDiscoveryOptions discoveryOptions,
24 | ITestMethod testMethod,
25 | IAttributeInfo theoryAttribute)
26 | {
27 | yield return new ConditionalTheoryTestCase(
28 | DiagnosticMessageSink,
29 | discoveryOptions.MethodDisplayOrDefault(),
30 | discoveryOptions.MethodDisplayOptionsOrDefault(),
31 | testMethod);
32 | }
33 |
34 | protected override IEnumerable CreateTestCasesForDataRow(
35 | ITestFrameworkDiscoveryOptions discoveryOptions,
36 | ITestMethod testMethod,
37 | IAttributeInfo theoryAttribute,
38 | object[] dataRow)
39 | {
40 | yield return new ConditionalFactTestCase(
41 | DiagnosticMessageSink,
42 | discoveryOptions.MethodDisplayOrDefault(),
43 | discoveryOptions.MethodDisplayOptionsOrDefault(),
44 | testMethod,
45 | dataRow);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/ConditionalTheoryTestCase.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Xunit.Abstractions;
8 | using Xunit.Sdk;
9 |
10 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
11 | {
12 | public sealed class ConditionalTheoryTestCase : XunitTheoryTestCase
13 | {
14 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
15 | public ConditionalTheoryTestCase()
16 | {
17 | }
18 |
19 | public ConditionalTheoryTestCase(
20 | IMessageSink diagnosticMessageSink,
21 | TestMethodDisplay defaultMethodDisplay,
22 | TestMethodDisplayOptions defaultMethodDisplayOptions,
23 | ITestMethod testMethod)
24 | : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod)
25 | {
26 | }
27 |
28 | public override async Task RunAsync(
29 | IMessageSink diagnosticMessageSink,
30 | IMessageBus messageBus,
31 | object[] constructorArguments,
32 | ExceptionAggregator aggregator,
33 | CancellationTokenSource cancellationTokenSource)
34 | => await XunitTestCaseExtensions.TrySkipAsync(this, messageBus)
35 | ? new RunSummary { Total = 1, Skipped = 1 }
36 | : await base.RunAsync(
37 | diagnosticMessageSink,
38 | messageBus,
39 | constructorArguments,
40 | aggregator,
41 | cancellationTokenSource);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/ITestCondition.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System.Threading.Tasks;
5 | using Xunit.Sdk;
6 |
7 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
8 | {
9 | public interface ITestCondition
10 | {
11 | ValueTask IsMetAsync(XunitTestCase testcase);
12 |
13 | string SkipReason { get; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/Output.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using Xunit.Abstractions;
3 |
4 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
5 | {
6 | public static class Output
7 | {
8 | private static readonly AsyncLocal asyncLocal;
9 |
10 | static Output()
11 | {
12 | asyncLocal = new AsyncLocal();
13 | }
14 |
15 | internal static void SetInstance(ITestOutputHelper testOutputHelper)
16 | {
17 | asyncLocal.Value = testOutputHelper;
18 | }
19 |
20 | public static void WriteLine(string message)
21 | {
22 | asyncLocal.Value?.WriteLine(message);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/PlatformSkipConditionAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Runtime.InteropServices;
6 | using System.Threading.Tasks;
7 | using Xunit.Sdk;
8 |
9 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
10 | {
11 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
12 | public sealed class PlatformSkipConditionAttribute : Attribute, ITestCondition
13 | {
14 | private readonly TestPlatform _excludedPlatforms;
15 |
16 | public PlatformSkipConditionAttribute(TestPlatform excludedPlatforms)
17 | {
18 | _excludedPlatforms = excludedPlatforms;
19 | }
20 |
21 | public ValueTask IsMetAsync(XunitTestCase testcase)
22 | => new ValueTask(CanRunOnThisPlatform(_excludedPlatforms));
23 |
24 | public string SkipReason { get; set; } = "Test cannot run on this platform.";
25 |
26 | private static bool CanRunOnThisPlatform(TestPlatform excludedFrameworks)
27 | {
28 | if (excludedFrameworks == TestPlatform.None)
29 | {
30 | return true;
31 | }
32 |
33 | if (excludedFrameworks.HasFlag(TestPlatform.Windows)
34 | && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
35 | {
36 | return false;
37 | }
38 |
39 | if (excludedFrameworks.HasFlag(TestPlatform.Linux)
40 | && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
41 | {
42 | return false;
43 | }
44 |
45 | return !excludedFrameworks.HasFlag(TestPlatform.Mac)
46 | || !RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/TestPlatform.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
7 | {
8 | [Flags]
9 | public enum TestPlatform
10 | {
11 | None = 0,
12 | Windows = 1 << 0,
13 | Linux = 1 << 1,
14 | Mac = 1 << 2
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/UseCultureAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Globalization;
6 | using System.Reflection;
7 | using Xunit.Sdk;
8 |
9 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
10 | {
11 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
12 | public sealed class UseCultureAttribute : BeforeAfterTestAttribute
13 | {
14 | private CultureInfo _originalCulture;
15 | private CultureInfo _originalUiCulture;
16 |
17 | public UseCultureAttribute(string culture)
18 | : this(culture, culture)
19 | {
20 | }
21 |
22 | public UseCultureAttribute(string culture, string uiCulture)
23 | {
24 | Culture = new CultureInfo(culture);
25 | UiCulture = new CultureInfo(uiCulture);
26 | }
27 |
28 | public CultureInfo Culture { get; }
29 |
30 | public CultureInfo UiCulture { get; }
31 |
32 | public override void Before(MethodInfo methodUnderTest)
33 | {
34 | _originalCulture = CultureInfo.CurrentCulture;
35 | _originalUiCulture = CultureInfo.CurrentUICulture;
36 | CultureInfo.CurrentCulture = Culture;
37 | CultureInfo.CurrentUICulture = UiCulture;
38 | }
39 |
40 | public override void After(MethodInfo methodUnderTest)
41 | {
42 | CultureInfo.CurrentCulture = _originalCulture;
43 | CultureInfo.CurrentUICulture = _originalUiCulture;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/TestUtilities/Xunit/XunitTestCaseExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Collections.Concurrent;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Threading.Tasks;
9 | using Xunit.Abstractions;
10 | using Xunit.Sdk;
11 |
12 | namespace Microsoft.EntityFrameworkCore.TestUtilities.Xunit
13 | {
14 | public static class XunitTestCaseExtensions
15 | {
16 | private static readonly ConcurrentDictionary> _typeAttributes
17 | = new ConcurrentDictionary>();
18 |
19 | private static readonly ConcurrentDictionary> _assemblyAttributes
20 | = new ConcurrentDictionary>();
21 |
22 | public static async ValueTask TrySkipAsync(XunitTestCase testCase, IMessageBus messageBus)
23 | {
24 | var method = testCase.Method;
25 | var type = testCase.TestMethod.TestClass.Class;
26 | var assembly = type.Assembly;
27 |
28 | var skipReasons = new List();
29 | var attributes =
30 | _assemblyAttributes.GetOrAdd(
31 | assembly.Name,
32 | a => assembly.GetCustomAttributes(typeof(ITestCondition)).ToList())
33 | .Concat(
34 | _typeAttributes.GetOrAdd(
35 | type.Name,
36 | t => type.GetCustomAttributes(typeof(ITestCondition)).ToList()))
37 | .Concat(method.GetCustomAttributes(typeof(ITestCondition)))
38 | .OfType()
39 | .Select(attributeInfo => (ITestCondition)attributeInfo.Attribute);
40 |
41 | foreach (var attribute in attributes)
42 | {
43 | if (!await attribute.IsMetAsync(testCase))
44 | {
45 | skipReasons.Add(attribute.SkipReason);
46 | }
47 | }
48 |
49 | if (skipReasons.Count > 0)
50 | {
51 | messageBus.QueueMessage(
52 | new TestSkipped(new XunitTest(testCase, testCase.DisplayName), string.Join(Environment.NewLine, skipReasons)));
53 | return true;
54 | }
55 |
56 | return false;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------