Skip to content

Commit

Permalink
Add a driver to support Microsoft.Data.SqlClient provider (#2216)
Browse files Browse the repository at this point in the history
Co-authored-by: Frédéric Delaporte <12201973+fredericDelaporte@users.noreply.github.com>
  • Loading branch information
hazzik and fredericDelaporte committed Dec 23, 2019
1 parent 3eb16b0 commit c8c52aa
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 10 deletions.
6 changes: 2 additions & 4 deletions src/NHibernate.Test/Async/NHSpecificTest/NH2420/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,8 @@ public async Task ShouldBeAbleToReleaseSuppliedConnectionAfterDistributedTransac
new DummyEnlistment(),
EnlistmentOptions.None);

if (Sfi.ConnectionProvider.Driver.GetType() == typeof(OdbcDriver))
connection = new OdbcConnection(connectionString);
else
connection = new SqlConnection(connectionString);
connection = Sfi.ConnectionProvider.Driver.CreateConnection();
connection.ConnectionString = connectionString;

await (connection.OpenAsync());
using (s = Sfi.WithOptions().Connection(connection).OpenSession())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,22 @@ public class MSSQLExceptionConverterExample : ISQLExceptionConverter

public Exception Convert(AdoExceptionContextInfo exInfo)
{
SqlException sqle = ADOExceptionHelper.ExtractDbException(exInfo.SqlException) as SqlException;
if(sqle != null)
var dbEx = ADOExceptionHelper.ExtractDbException(exInfo.SqlException);
if (dbEx is SqlException sqle)
{
if (sqle.Number == 547)
return new ConstraintViolationException(exInfo.Message, sqle.InnerException, exInfo.Sql, null);
if (sqle.Number == 208)
return new SQLGrammarException(exInfo.Message, sqle.InnerException, exInfo.Sql);
}

if(dbEx is Microsoft.Data.SqlClient.SqlException msSqle)
{
if (msSqle.Number == 547)
return new ConstraintViolationException(exInfo.Message, msSqle.InnerException, exInfo.Sql, null);
if (msSqle.Number == 208)
return new SQLGrammarException(exInfo.Message, msSqle.InnerException, exInfo.Sql);
}
return SQLStateConverter.HandledNonSpecificException(exInfo.SqlException, exInfo.Message, exInfo.Sql);
}

Expand Down
6 changes: 2 additions & 4 deletions src/NHibernate.Test/NHSpecificTest/NH2420/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,8 @@ public void ShouldBeAbleToReleaseSuppliedConnectionAfterDistributedTransaction()
new DummyEnlistment(),
EnlistmentOptions.None);

if (Sfi.ConnectionProvider.Driver.GetType() == typeof(OdbcDriver))
connection = new OdbcConnection(connectionString);
else
connection = new SqlConnection(connectionString);
connection = Sfi.ConnectionProvider.Driver.CreateConnection();
connection.ConnectionString = connectionString;

connection.Open();
using (s = Sfi.WithOptions().Connection(connection).OpenSession())
Expand Down
1 change: 1 addition & 0 deletions src/NHibernate.Test/NHibernate.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="1.0.19269.1" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.109.2" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.8.11" />
<PackageReference Include="NSubstitute" Version="3.1.0" />
Expand Down
209 changes: 209 additions & 0 deletions src/NHibernate/Driver/MicrosoftDataSqlClientDriver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using NHibernate.AdoNet;
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.SqlTypes;
using NHibernate.Util;

namespace NHibernate.Driver
{
/// <summary>
/// A NHibernate Driver for using the SqlClient DataProvider
/// </summary>
public class MicrosoftDataSqlClientDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider, IParameterAdjuster
{
const byte MaxTime = 5;

private static readonly Action<object, SqlDbType> SetSqlDbType = DelegateHelper.BuildPropertySetter<SqlDbType>(System.Type.GetType("Microsoft.Data.SqlClient.SqlParameter, Microsoft.Data.SqlClient", true), "SqlDbType");

private Dialect.Dialect _dialect;

public MicrosoftDataSqlClientDriver()
: base(
"Microsoft.Data.SqlClient",
"Microsoft.Data.SqlClient.SqlConnection",
"Microsoft.Data.SqlClient.SqlCommand")
{
}

/// <summary>
/// MsSql requires the use of a Named Prefix in the SQL statement.
/// </summary>
/// <remarks>
/// <see langword="true" /> because MsSql uses "<c>@</c>".
/// </remarks>
public override bool UseNamedPrefixInSql => true;

/// <summary>
/// MsSql requires the use of a Named Prefix in the Parameter.
/// </summary>
/// <remarks>
/// <see langword="true" /> because MsSql uses "<c>@</c>".
/// </remarks>
public override bool UseNamedPrefixInParameter => true;

/// <summary>
/// The Named Prefix for parameters.
/// </summary>
/// <value>
/// Sql Server uses <c>"@"</c>.
/// </value>
public override string NamedPrefix => "@";

/// <inheritdoc/>
public override bool SupportsMultipleOpenReaders => false;

/// <inheritdoc />
public override bool SupportsMultipleQueries => true;

/// <summary>
/// With read committed snapshot or lower, SQL Server may have not actually already committed the transaction
/// right after the scope disposal.
/// </summary>
public override bool HasDelayedDistributedTransactionCompletion => true;

public override bool RequiresTimeSpanForTime => true;

/// <inheritdoc />
public override DateTime MinDate => DateTime.MinValue;

System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass => typeof(GenericBatchingBatcherFactory);

/// <inheritdoc />
public virtual void AdjustParameterForValue(DbParameter parameter, SqlType sqlType, object value)
{
if (value is string stringVal)
switch (parameter.DbType)
{
case DbType.AnsiString:
case DbType.AnsiStringFixedLength:
parameter.Size = IsAnsiText(parameter, sqlType)
? MsSql2000Dialect.MaxSizeForAnsiClob
: Math.Max(stringVal.Length, sqlType.LengthDefined ? sqlType.Length : parameter.Size);
break;
case DbType.String:
case DbType.StringFixedLength:
parameter.Size = IsText(parameter, sqlType)
? MsSql2000Dialect.MaxSizeForClob
: Math.Max(stringVal.Length, sqlType.LengthDefined ? sqlType.Length : parameter.Size);
break;
}
}

public override void Configure(IDictionary<string, string> settings)
{
base.Configure(settings);

_dialect = Dialect.Dialect.GetDialect(settings);
}

/// <inheritdoc />
protected override void InitializeParameter(DbParameter dbParam, string name, SqlType sqlType)
{
base.InitializeParameter(dbParam, name, sqlType);

// Defaults size/precision/scale
switch (dbParam.DbType)
{
case DbType.AnsiString:
case DbType.AnsiStringFixedLength:
dbParam.Size = IsAnsiText(dbParam, sqlType)
? MsSql2000Dialect.MaxSizeForAnsiClob
: MsSql2000Dialect.MaxSizeForLengthLimitedAnsiString;
break;
case DbType.Binary:
dbParam.Size = IsBlob(dbParam, sqlType)
? MsSql2000Dialect.MaxSizeForBlob
: MsSql2000Dialect.MaxSizeForLengthLimitedBinary;
break;
case DbType.Decimal:
if (_dialect == null)
throw new InvalidOperationException(
"Dialect not available, is this driver used without having been configured?");
dbParam.Precision = _dialect.DefaultCastPrecision;
dbParam.Scale = _dialect.DefaultCastScale;
break;
case DbType.String:
case DbType.StringFixedLength:
dbParam.Size = IsText(dbParam, sqlType)
? MsSql2000Dialect.MaxSizeForClob
: MsSql2000Dialect.MaxSizeForLengthLimitedString;
break;
case DbType.DateTime2:
dbParam.Size = MsSql2000Dialect.MaxDateTime2;
break;
case DbType.DateTimeOffset:
dbParam.Size = MsSql2000Dialect.MaxDateTimeOffset;
break;
case DbType.Xml:
dbParam.Size = MsSql2005Dialect.MaxSizeForXml;
break;
}
switch (sqlType.DbType)
{
case DbType.Time:
SetSqlDbType(dbParam, SqlDbType.Time);
dbParam.Size = MaxTime;
break;
case DbType.Date:
SetSqlDbType(dbParam, SqlDbType.Date);
break;
}

// Do not override the default length for string using data from SqlType, since LIKE expressions needs
// larger columns. https://nhibernate.jira.com/browse/NH-3036

if (sqlType.PrecisionDefined)
{
dbParam.Precision = sqlType.Precision;
dbParam.Scale = sqlType.Scale;
}
}

/// <summary>
/// Interprets if a parameter is a Clob (for the purposes of setting its default size)
/// </summary>
/// <param name="dbParam">The parameter</param>
/// <param name="sqlType">The <see cref="SqlType" /> of the parameter</param>
/// <returns>True, if the parameter should be interpreted as a Clob, otherwise False</returns>
protected static bool IsAnsiText(DbParameter dbParam, SqlType sqlType)
{
return (DbType.AnsiString == dbParam.DbType || DbType.AnsiStringFixedLength == dbParam.DbType) &&
sqlType.LengthDefined && sqlType.Length > MsSql2000Dialect.MaxSizeForLengthLimitedAnsiString;
}

/// <summary>
/// Interprets if a parameter is a Clob (for the purposes of setting its default size)
/// </summary>
/// <param name="dbParam">The parameter</param>
/// <param name="sqlType">The <see cref="SqlType" /> of the parameter</param>
/// <returns>True, if the parameter should be interpreted as a Clob, otherwise False</returns>
protected static bool IsText(DbParameter dbParam, SqlType sqlType)
{
return sqlType is StringClobSqlType ||
(DbType.String == dbParam.DbType || DbType.StringFixedLength == dbParam.DbType) &&
sqlType.LengthDefined && sqlType.Length > MsSql2000Dialect.MaxSizeForLengthLimitedString;
}

/// <summary>
/// Interprets if a parameter is a Blob (for the purposes of setting its default size)
/// </summary>
/// <param name="dbParam">The parameter</param>
/// <param name="sqlType">The <see cref="SqlType" /> of the parameter</param>
/// <returns>True, if the parameter should be interpreted as a Blob, otherwise False</returns>
protected static bool IsBlob(DbParameter dbParam, SqlType sqlType)
{
return sqlType is BinaryBlobSqlType || DbType.Binary == dbParam.DbType && sqlType.LengthDefined &&
sqlType.Length > MsSql2000Dialect.MaxSizeForLengthLimitedBinary;
}

/// <inheritdoc />
public override IResultSetsCommand GetResultSetsCommand(ISessionImplementor session)
{
return new BasicResultSetsCommand(session);
}
}
}

0 comments on commit c8c52aa

Please sign in to comment.