Skip to content

Commit

Permalink
Merge pull request #197 from ngvtien/master
Browse files Browse the repository at this point in the history
Addressing Issues #196, #192 and #205
  • Loading branch information
ngvtien authored Feb 10, 2021
2 parents 71e0480 + 42ca3f9 commit 997af4c
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 73 deletions.
4 changes: 2 additions & 2 deletions build/common.props
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<Project>
<PropertyGroup>
<TargetFrameworks>netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;net46;netstandard2.0</TargetFrameworks>
<VersionPrefix>0.19.1</VersionPrefix>
<VersionPrefix>0.19.2</VersionPrefix>
<AssemblyVersion>0.11.0.0</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>dataaction</Authors>
<PackageTags>Sybase ASE Adaptive SAP AseClient DbProvider</PackageTags>
<PackageIcon>icon.png</PackageIcon>
<PackageProjectUrl>https://github.com/DataAction/AdoNetCore.AseClient</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageReleaseNotes>Refer to GitHub - https://github.com/DataAction/AdoNetCore.AseClient/releases/tag/0.19.1</PackageReleaseNotes>
<PackageReleaseNotes>Refer to GitHub - https://github.com/DataAction/AdoNetCore.AseClient/releases/tag/0.19.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/DataAction/AdoNetCore.AseClient</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
Expand Down
7 changes: 5 additions & 2 deletions src/AdoNetCore.AseClient/AseCommandBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,15 @@ private static DataTable GetSchemaTableWithKeyInfo(DbCommand sourceCommand)
if (dataTable != null)
{
// If there is no primary key on the table, then throw MissingPrimaryKeyException.
var isKeyColumn = dataTable.Columns["IsKey"];
var isKeyColumn = dataTable.Columns[SchemaTableColumn.IsKey];
var isUniqueColumn = dataTable.Columns[SchemaTableColumn.IsUnique];

var hasKey = false;

foreach (DataRow columnDescriptorRow in dataTable.Rows)
{
hasKey |= (bool)columnDescriptorRow[isKeyColumn];
hasKey |= (bool)columnDescriptorRow[isKeyColumn] ||
(bool)columnDescriptorRow[isUniqueColumn];

if (hasKey)
{
Expand Down
14 changes: 12 additions & 2 deletions src/AdoNetCore.AseClient/AseConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Data;
using System.Data.Common;
using System.Net.Security;
using AdoNetCore.AseClient.Interface;
using AdoNetCore.AseClient.Internal;

Expand Down Expand Up @@ -274,7 +275,7 @@ public override void Open()
{
var parameters = ConnectionParameters.Parse(_connectionString);

_internal = _connectionPoolManager.Reserve(_connectionString, parameters, _eventNotifier);
_internal = _connectionPoolManager.Reserve(_connectionString, parameters, _eventNotifier, UserCertificateValidationCallback);

InternalConnectionTimeout = parameters.LoginTimeout;
}
Expand Down Expand Up @@ -543,7 +544,10 @@ public bool NamedParameters
#if ENABLE_CLONEABLE_INTERFACE
public object Clone()
{
return new AseConnection(_connectionString, _connectionPoolManager);
return new AseConnection(_connectionString, _connectionPoolManager)
{
UserCertificateValidationCallback = UserCertificateValidationCallback,
};
}
#endif

Expand Down Expand Up @@ -621,6 +625,12 @@ public AseTransaction Transaction
return _transaction;
}
}

/// <summary>
/// Allow consumer to override the default certificate validation
/// </summary>
public RemoteCertificateValidationCallback UserCertificateValidationCallback { get; set; }

}

/// <summary>
Expand Down
28 changes: 18 additions & 10 deletions src/AdoNetCore.AseClient/AseParameterCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,10 @@ public AseParameter Add(string parameterName, AseDbType dbType, int size, Parame
/// <returns>A new <see cref="AseParameter" /> object.</returns>
public AseParameter Add(AseParameter parameter)
{
if (parameter != null)
{
_parameters.Add(parameter);
}
if (parameter == null) throw new ArgumentNullException(nameof(parameter));
if (Contains(parameter)) throw new ArgumentException($"parameter name '{parameter.ParameterName}' is already in the collection");

_parameters.Add(parameter);
return parameter;
}

Expand Down Expand Up @@ -379,6 +379,7 @@ public override int Add(object value)
{
if (value is AseParameter p)
{
if (Contains(p)) throw new ArgumentException($"Parameter name: '{p.ParameterName}' is already registered", nameof(value));
return ((IList)_parameters).Add(p);
}
return -1;
Expand Down Expand Up @@ -438,15 +439,23 @@ public override int IndexOf(object value)
/// Returns -1 when the object does not exist in the <see cref="AseParameterCollection" />.</returns>
public int IndexOf(AseParameter value)
{
if (value != null)
if (value == null) return -1;
for (var i = 0; i < _parameters.Count; i++)
{
for (var i = 0; i < _parameters.Count; i++)
if (string.IsNullOrWhiteSpace(value.ParameterName))
{
if (value == _parameters[i])
{
return i;
}
}
else
{
if (value == _parameters[i] || value.ParameterName == _parameters[i].ParameterName)
{
return i;
}
}
}

return -1;
Expand All @@ -459,6 +468,7 @@ public int IndexOf(AseParameter value)
/// <param name="parameter">The <see cref="AseParameter" /> object to add to the collection.</param>
public override void Insert(int index, object parameter)
{
if (Contains(parameter)) throw new ArgumentException($"parameter name '{parameter}' is already in the collection");
((IList)_parameters).Insert(index, parameter);
}

Expand All @@ -469,6 +479,7 @@ public override void Insert(int index, object parameter)
/// <param name="parameter">The <see cref="AseParameter" /> object to add to the collection.</param>
public void Insert(int index, AseParameter parameter)
{
if (Contains(parameter)) throw new ArgumentException($"parameter name '{parameter.ParameterName}' is already in the collection");
((IList)_parameters).Insert(index, parameter);
}

Expand Down Expand Up @@ -521,10 +532,7 @@ public override void RemoveAt(int index)
/// <param name="index">The starting index of the array.</param>
public override void CopyTo(Array array, int index)
{
if (array != null)
{
((IList)_parameters).CopyTo(array, index);
}
((IList)_parameters).CopyTo(array, index);
}

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion src/AdoNetCore.AseClient/Interface/IConnectionPoolManager.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Net.Security;

namespace AdoNetCore.AseClient.Interface
{
internal interface IConnectionPoolManager
{
IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier);
IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier, RemoteCertificateValidationCallback userCertificateValidationCallback = null);
void Release(string connectionString, IInternalConnection connection);
void ClearPool(string connectionString);
void ClearPools();
Expand Down
7 changes: 4 additions & 3 deletions src/AdoNetCore.AseClient/Internal/ConnectionPoolManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Security;
using AdoNetCore.AseClient.Interface;

namespace AdoNetCore.AseClient.Internal
Expand All @@ -14,14 +15,14 @@ internal sealed class ConnectionPoolManager : IConnectionPoolManager, IEnumerabl

private static readonly ConcurrentDictionary<string, IConnectionPool> Pools = new ConcurrentDictionary<string, IConnectionPool>(StringComparer.OrdinalIgnoreCase);

public IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier)
public IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier, RemoteCertificateValidationCallback userCertificateValidationCallback = null)
{
return Pools.GetOrAdd(connectionString, _ =>
{
#if ENABLE_ARRAY_POOL
var internalConnectionFactory = new InternalConnectionFactory(parameters, BufferPool);
var internalConnectionFactory = new InternalConnectionFactory(parameters, BufferPool, userCertificateValidationCallback);
#else
var internalConnectionFactory = new InternalConnectionFactory(parameters);
var internalConnectionFactory = new InternalConnectionFactory(parameters, userCertificateValidationCallback);
#endif
return new ConnectionPool(parameters, internalConnectionFactory);
Expand Down
74 changes: 42 additions & 32 deletions src/AdoNetCore.AseClient/Internal/FormatItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,50 +64,60 @@ public string ParameterName
/// </summary>
public SerializationType SerializationType { get; set; }

public static FormatItem CreateForParameter(AseParameter parameter, DbEnvironment env, CommandType commandType)
public static FormatItem CreateForParameter(AseParameter parameter, DbEnvironment env, AseCommand command)
{
parameter.AseDbType = TypeMap.InferType(parameter);

var dbType = parameter.DbType;

var length = TypeMap.GetFormatLength(dbType, parameter, env.Encoding);

var format = new FormatItem
var format = command.FormatItem;
var parameterName = parameter.ParameterName ?? command.Parameters.IndexOf(parameter).ToString();
if (!(command.FormatItem != null && command.FormatItem.ParameterName == parameterName &&
command.FormatItem.AseDbType == parameter.AseDbType))
{
AseDbType = parameter.AseDbType,
ParameterName = parameter.ParameterName,
IsOutput = parameter.IsOutput,
IsNullable = parameter.IsNullable,
Length = length,
DataType = TypeMap.GetTdsDataType(dbType, parameter.SendableValue, length, parameter.ParameterName),
UserType = TypeMap.GetUserType(dbType, parameter.SendableValue, length)
};
format = new FormatItem
{
AseDbType = parameter.AseDbType,
ParameterName = parameter.ParameterName,
IsOutput = parameter.IsOutput,
IsNullable = parameter.IsNullable,
Length = length,
DataType = TypeMap.GetTdsDataType(dbType, parameter.SendableValue, length, parameter.ParameterName),
UserType = TypeMap.GetUserType(dbType, parameter.SendableValue, length)
};

//fixup the FormatItem's BlobType for strings and byte arrays
if (format.DataType == TdsDataType.TDS_BLOB)
{
switch (parameter.DbType)
//fixup the FormatItem's BlobType for strings and byte arrays
if (format.DataType == TdsDataType.TDS_BLOB)
{
case DbType.AnsiString:
format.BlobType = BlobType.BLOB_LONGCHAR;
break;
case DbType.String:
format.BlobType = BlobType.BLOB_UNICHAR;
// This is far less than ideal but at the time of addressing this issue whereby if the
// BlobType is a BLOB_UNICHAR then the UserType would need to be 36 when it
// is a stored proc otherwise it would need to be zero (0).
//
// In the future, we'd need to overhaul how TDS_BLOB is structured especially
// around BLOB_UNICHAR and the UserType that it should return in a more consistent way
if (commandType != CommandType.StoredProcedure)
format.UserType = 0;
switch (parameter.DbType)
{
case DbType.AnsiString:
format.BlobType = BlobType.BLOB_LONGCHAR;
break;
case DbType.String:
format.BlobType = BlobType.BLOB_UNICHAR;
// This is far less than ideal but at the time of addressing this issue whereby if the
// BlobType is a BLOB_UNICHAR then the UserType would need to be 36 when it
// is a stored proc otherwise it would need to be zero (0).
//
// In the future, we'd need to overhaul how TDS_BLOB is structured especially
// around BLOB_UNICHAR and the UserType that it should return in a more consistent way
if (command.CommandType != CommandType.StoredProcedure)
format.UserType = 0;

break;
case DbType.Binary:
format.BlobType = BlobType.BLOB_LONGBINARY;
break;
break;
case DbType.Binary:
format.BlobType = BlobType.BLOB_LONGBINARY;
break;
}
}
}
else
{
format.DataType = TypeMap.GetTdsDataType(dbType, parameter.SendableValue, length, parameter.ParameterName);
format.UserType = TypeMap.GetUserType(dbType, parameter.SendableValue, length);
}

//fixup the FormatItem's length,scale,precision for decimals
if (format.IsDecimalType)
Expand Down
6 changes: 1 addition & 5 deletions src/AdoNetCore.AseClient/Internal/InternalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -669,11 +669,7 @@ private IToken[] BuildParameterTokens(AseCommand command)

foreach (var parameter in command.Parameters.SendableParameters)
{
var parameterName = parameter.ParameterName ?? command.Parameters.IndexOf(parameter).ToString();
if (!(command.FormatItem != null && command.FormatItem.ParameterName == parameterName && command.FormatItem.AseDbType == parameter.AseDbType))
{
command.FormatItem = FormatItem.CreateForParameter(parameter, _environment, command.CommandType);
}
command.FormatItem = FormatItem.CreateForParameter(parameter, _environment, command);

formatItems.Add(command.FormatItem);
parameterItems.Add(new ParametersToken.Parameter
Expand Down
14 changes: 10 additions & 4 deletions src/AdoNetCore.AseClient/Internal/InternalConnectionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,23 @@ namespace AdoNetCore.AseClient.Internal
internal class InternalConnectionFactory : IInternalConnectionFactory
{
private readonly IConnectionParameters _parameters;
private readonly RemoteCertificateValidationCallback _userCertificateValidationCallback;


#if ENABLE_ARRAY_POOL
private readonly System.Buffers.ArrayPool<byte> _arrayPool;
#endif
private IPEndPoint _endpoint;

#if ENABLE_ARRAY_POOL
public InternalConnectionFactory(IConnectionParameters parameters, System.Buffers.ArrayPool<byte> arrayPool)
public InternalConnectionFactory(IConnectionParameters parameters, System.Buffers.ArrayPool<byte> arrayPool, RemoteCertificateValidationCallback userCertificateValidationCallback)
#else
public InternalConnectionFactory(IConnectionParameters parameters)
public InternalConnectionFactory(IConnectionParameters parameters, RemoteCertificateValidationCallback userCertificateValidationCallback)
#endif
{
_parameters = parameters;
_userCertificateValidationCallback = userCertificateValidationCallback ?? UserCertificateValidationCallback;

#if ENABLE_ARRAY_POOL
_arrayPool = arrayPool;
#endif
Expand Down Expand Up @@ -109,7 +114,7 @@ private InternalConnection CreateConnection(Socket socket, CancellationToken tok

if (_parameters.Encryption)
{
sslStream = new SslStream(networkStream, false, UserCertificateValidationCallback);
sslStream = new SslStream(networkStream, false, _userCertificateValidationCallback);

var authenticate = sslStream.AuthenticateAsClientAsync(_parameters.Server);

Expand Down Expand Up @@ -175,10 +180,11 @@ private InternalConnection CreateConnectionInternal(Stream networkStream)
#if ENABLE_ARRAY_POOL
return new InternalConnection(_parameters, networkStream, reader, environment, _arrayPool);
#else
return new InternalConnection(_parameters, networkStream, reader, environment);
return new InternalConnection(_parameters, networkStream, reader, environment);
#endif
}


private bool UserCertificateValidationCallback(object sender, X509Certificate serverCertificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var certificateChainPolicyErrors = (sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors;
Expand Down
7 changes: 1 addition & 6 deletions src/AdoNetCore.AseClient/Internal/SchemaTableBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,12 @@ private FillTableResults FillTableFromFormats(DataTable table)
private void TryLoadKeyInfo(DataTable table, string baseTableNameValue, string baseSchemaNameValue, string baseCatalogNameValue)
{
if (_connection == null)
{
throw new InvalidOperationException("Invalid AseCommand.Connection");
}

if (_connection.State != ConnectionState.Open)
{
throw new InvalidOperationException("Invalid AseCommand.Connection.ConnectionState");
}

if (!string.IsNullOrWhiteSpace(baseTableNameValue) &&
!string.IsNullOrWhiteSpace(baseCatalogNameValue))
if (string.IsNullOrWhiteSpace(baseTableNameValue))
return;

using (var command = _connection.CreateCommand())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />

<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
Expand All @@ -34,9 +35,11 @@
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Net.Security" Version="4.3.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Net.Security" Version="4.3.2" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
<PackageReference Include="Sybase.AdoNet4.AseClient">
Expand Down
Loading

0 comments on commit 997af4c

Please sign in to comment.