Adds Oracle sample provider to EFCore.sln.
- Adds README.md for the sample.
This commit is contained in:
Родитель
38ba45f925
Коммит
d5ab432da3
12
EFCore.sln
12
EFCore.sln
|
@ -61,6 +61,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Benchmarks", "benchm
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Benchmarks.EFCore", "test\EFCore.Benchmarks.EFCore\EFCore.Benchmarks.EFCore.csproj", "{8B289879-9CF1-4EFD-AD4B-781AEA94BDBE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{C41FD56B-38CD-40E3-89E6-071A6C58E36C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OracleProvider", "OracleProvider", "{EA847C1D-EC25-442B-A940-460969577CD5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OracleProvider", "samples\OracleProvider\src\OracleProvider\OracleProvider.csproj", "{9511216F-EC10-480D-AA8D-F72312C96962}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -175,6 +181,10 @@ Global
|
|||
{8B289879-9CF1-4EFD-AD4B-781AEA94BDBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8B289879-9CF1-4EFD-AD4B-781AEA94BDBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8B289879-9CF1-4EFD-AD4B-781AEA94BDBE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9511216F-EC10-480D-AA8D-F72312C96962}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9511216F-EC10-480D-AA8D-F72312C96962}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9511216F-EC10-480D-AA8D-F72312C96962}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9511216F-EC10-480D-AA8D-F72312C96962}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -207,6 +217,8 @@ Global
|
|||
{935B51B9-A9B9-4DA2-93A2-663D3BCEAA83} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{E4F44442-7AD2-480D-AD6B-AAC4424F5FA7} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{8B289879-9CF1-4EFD-AD4B-781AEA94BDBE} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{EA847C1D-EC25-442B-A940-460969577CD5} = {C41FD56B-38CD-40E3-89E6-071A6C58E36C}
|
||||
{9511216F-EC10-480D-AA8D-F72312C96962} = {EA847C1D-EC25-442B-A940-460969577CD5}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {285A5EB4-BCF4-40EB-B9E1-DF6DBCB5E705}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# Sample - Oracle Provider
|
||||
|
||||
**NB: This Oracle provider is a sample and is not production quality.**
|
||||
|
||||
## Limitations of the sample Oracle provider:
|
||||
|
||||
- Only works on full .NET - utilizes official Oracle managed ADO provider.
|
||||
- Does not fully support Migrations. Only enough has been implemented to enable the runtime functional tests.
|
||||
- Scaffolding is not supported.
|
||||
- Update batching is not supported.
|
||||
- OracleTransientExceptionDetector needs to be updated (impl. copied from SqlServer provider).
|
||||
- OracleTypeMapper is implemented but likely needs more tuning.
|
||||
- No Oracle specific features are "lit up".
|
||||
|
||||
## What works:
|
||||
|
||||
- All query tests passing (aside from a few failing due to various Oracle specific issues).
|
||||
- All runtime tests passing (aside from those directly related to the limitations above).
|
||||
|
||||
## Running the tests
|
||||
|
||||
1) Install [Oracle Database 12c Release 2 (12.2.0.1.0) - Standard Edition 2](http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html)
|
||||
- When installing, ensure to enable pluggable databases - the sample relies on a specific pluggable database.
|
||||
|
||||
2) Use a shell to connect via SQLPlus:
|
||||
|
||||
```
|
||||
> sqlplus / as sysdba
|
||||
```
|
||||
|
||||
3) Create a pluggable database used to host the EF test databases:
|
||||
|
||||
```
|
||||
CREATE PLUGGABLE DATABASE ef
|
||||
ADMIN USER ef_pdb_admin IDENTIFIED BY ef_pdb_admin
|
||||
ROLES = (DBA)
|
||||
FILE_NAME_CONVERT = ('\pdbseed\', '\pdb_ef\');
|
||||
```
|
||||
|
||||
4) Open the pluggable database:
|
||||
|
||||
```
|
||||
ALTER PLUGGABLE DATABASE ef OPEN;
|
||||
```
|
|
@ -9,6 +9,7 @@
|
|||
<TargetFrameworks>net461</TargetFrameworks>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<CodeAnalysisRuleSet>..\..\..\..\src\EFCore.ruleset</CodeAnalysisRuleSet>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -24,11 +25,6 @@
|
|||
<None Remove="Scaffolding\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Update\Internal\OracleModificationCommandBatch.cs" />
|
||||
<Compile Remove="Update\Internal\SqlServerModificationCommandBatch.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\EFCore.Relational\EFCore.Relational.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -40,10 +36,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="App.config">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>App.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Properties\OracleStrings.Designer.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>OracleStrings.Designer.cs</LastGenOutput>
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Update.Internal
|
||||
{
|
||||
public class SqlServerModificationCommandBatch : AffectedCountModificationCommandBatch
|
||||
{
|
||||
private const int DefaultNetworkPacketSizeBytes = 4096;
|
||||
private const int MaxScriptLength = 65536 * DefaultNetworkPacketSizeBytes / 2;
|
||||
private const int MaxParameterCount = 2100;
|
||||
private const int MaxRowCount = 1000;
|
||||
private int _parameterCount = 1; // Implicit parameter for the command text
|
||||
private readonly int _maxBatchSize;
|
||||
private readonly List<ModificationCommand> _bulkInsertCommands = new List<ModificationCommand>();
|
||||
private int _commandsLeftToLengthCheck = 50;
|
||||
|
||||
public SqlServerModificationCommandBatch(
|
||||
[NotNull] IRelationalCommandBuilderFactory commandBuilderFactory,
|
||||
[NotNull] ISqlGenerationHelper sqlGenerationHelper,
|
||||
// ReSharper disable once SuggestBaseTypeForParameter
|
||||
[NotNull] ISqlServerUpdateSqlGenerator updateSqlGenerator,
|
||||
[NotNull] IRelationalValueBufferFactoryFactory valueBufferFactoryFactory,
|
||||
[CanBeNull] int? maxBatchSize)
|
||||
: base(
|
||||
commandBuilderFactory,
|
||||
sqlGenerationHelper,
|
||||
updateSqlGenerator,
|
||||
valueBufferFactoryFactory)
|
||||
{
|
||||
if (maxBatchSize.HasValue
|
||||
&& maxBatchSize.Value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(maxBatchSize), RelationalStrings.InvalidMaxBatchSize);
|
||||
}
|
||||
|
||||
_maxBatchSize = Math.Min(maxBatchSize ?? int.MaxValue, MaxRowCount);
|
||||
}
|
||||
|
||||
protected new virtual ISqlServerUpdateSqlGenerator UpdateSqlGenerator => (ISqlServerUpdateSqlGenerator)base.UpdateSqlGenerator;
|
||||
|
||||
protected override bool CanAddCommand(ModificationCommand modificationCommand)
|
||||
{
|
||||
if (ModificationCommands.Count >= _maxBatchSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var additionalParameterCount = CountParameters(modificationCommand);
|
||||
|
||||
if (_parameterCount + additionalParameterCount >= MaxParameterCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_parameterCount += additionalParameterCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool IsCommandTextValid()
|
||||
{
|
||||
if (--_commandsLeftToLengthCheck < 0)
|
||||
{
|
||||
var commandTextLength = GetCommandText().Length;
|
||||
if (commandTextLength >= MaxScriptLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var avarageCommandLength = commandTextLength / ModificationCommands.Count;
|
||||
var expectedAdditionalCommandCapacity = (MaxScriptLength - commandTextLength) / avarageCommandLength;
|
||||
_commandsLeftToLengthCheck = Math.Max(1, expectedAdditionalCommandCapacity / 4);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override int GetParameterCount()
|
||||
=> _parameterCount;
|
||||
|
||||
private static int CountParameters(ModificationCommand modificationCommand)
|
||||
{
|
||||
var parameterCount = 0;
|
||||
foreach (var columnModification in modificationCommand.ColumnModifications)
|
||||
{
|
||||
if (columnModification.UseCurrentValueParameter)
|
||||
{
|
||||
parameterCount++;
|
||||
}
|
||||
|
||||
if (columnModification.UseOriginalValueParameter)
|
||||
{
|
||||
parameterCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return parameterCount;
|
||||
}
|
||||
|
||||
protected override void ResetCommandText()
|
||||
{
|
||||
base.ResetCommandText();
|
||||
_bulkInsertCommands.Clear();
|
||||
}
|
||||
|
||||
protected override string GetCommandText()
|
||||
=> base.GetCommandText() + GetBulkInsertCommandText(ModificationCommands.Count);
|
||||
|
||||
private string GetBulkInsertCommandText(int lastIndex)
|
||||
{
|
||||
if (_bulkInsertCommands.Count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var stringBuilder = new StringBuilder();
|
||||
var resultSetMapping = UpdateSqlGenerator.AppendBulkInsertOperation(stringBuilder, _bulkInsertCommands, lastIndex - _bulkInsertCommands.Count);
|
||||
|
||||
for (var i = lastIndex - _bulkInsertCommands.Count; i < lastIndex; i++)
|
||||
{
|
||||
CommandResultSet[i] = resultSetMapping;
|
||||
}
|
||||
|
||||
if (resultSetMapping != ResultSetMapping.NoResultSet)
|
||||
{
|
||||
CommandResultSet[lastIndex - 1] = ResultSetMapping.LastInResultSet;
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
protected override void UpdateCachedCommandText(int commandPosition)
|
||||
{
|
||||
var newModificationCommand = ModificationCommands[commandPosition];
|
||||
|
||||
if (newModificationCommand.EntityState == EntityState.Added)
|
||||
{
|
||||
if (_bulkInsertCommands.Count > 0
|
||||
&& !CanBeInsertedInSameStatement(_bulkInsertCommands[0], newModificationCommand))
|
||||
{
|
||||
CachedCommandText.Append(GetBulkInsertCommandText(commandPosition));
|
||||
_bulkInsertCommands.Clear();
|
||||
}
|
||||
_bulkInsertCommands.Add(newModificationCommand);
|
||||
|
||||
LastCachedCommandIndex = commandPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
CachedCommandText.Append(GetBulkInsertCommandText(commandPosition));
|
||||
_bulkInsertCommands.Clear();
|
||||
|
||||
base.UpdateCachedCommandText(commandPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanBeInsertedInSameStatement(ModificationCommand firstCommand, ModificationCommand secondCommand)
|
||||
=> string.Equals(firstCommand.TableName, secondCommand.TableName, StringComparison.Ordinal)
|
||||
&& string.Equals(firstCommand.Schema, secondCommand.Schema, StringComparison.Ordinal)
|
||||
&& firstCommand.ColumnModifications.Where(o => o.IsWrite).Select(o => o.ColumnName).SequenceEqual(
|
||||
secondCommand.ColumnModifications.Where(o => o.IsWrite).Select(o => o.ColumnName))
|
||||
&& firstCommand.ColumnModifications.Where(o => o.IsRead).Select(o => o.ColumnName).SequenceEqual(
|
||||
secondCommand.ColumnModifications.Where(o => o.IsRead).Select(o => o.ColumnName));
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче