Skip to content

Commit

Permalink
Fix bug SqlBulkCopy DataReader ordinal - dotnet/corefx#24655
Browse files Browse the repository at this point in the history
  • Loading branch information
julien committed Mar 10, 2018
1 parent b4d310c commit 79458d4
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 25 deletions.
3 changes: 1 addition & 2 deletions src/QueryBuilder.Core/Database/IDatabaseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ public interface IDatabaseContext<TConnection, TTransaction>
where TConnection : class
where TTransaction : class
{
// TODO move it elsewhere
TTransaction BeginTransaction();
ITransactionScope<TTransaction> BeginTransaction();

TConnection GetConnection();
}
Expand Down
11 changes: 11 additions & 0 deletions src/QueryBuilder.Core/Database/ITransactionScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace QueryBuilder.Core.Database
{
public interface ITransactionScope<TTransaction> : IDisposable
{
TTransaction Current { get; }

void Commit();
}
}
30 changes: 30 additions & 0 deletions src/QueryBuilder.EFCore/Database/DbContextTransactionScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Data.Common;
using Microsoft.EntityFrameworkCore.Storage;
using QueryBuilder.Core.Database;

namespace QueryBuilder.EFCore.Database
{
public class DbContextTransactionScope<TTransaction>
: ITransactionScope<TTransaction>
where TTransaction : DbTransaction
{
private readonly IDbContextTransaction _dbContextTransaction;
public TTransaction Current => (TTransaction)_dbContextTransaction.GetDbTransaction();

public DbContextTransactionScope(IDbContextTransaction dbContextTransaction)
{
_dbContextTransaction = dbContextTransaction ?? throw new ArgumentNullException(nameof(dbContextTransaction));
}

public void Commit()
{
_dbContextTransaction.Commit();
}

public void Dispose()
{
_dbContextTransaction?.Dispose();
}
}
}
7 changes: 6 additions & 1 deletion src/QueryBuilder.EFCore/Database/EFCoreContextAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ public EFCoreContextAdapter(DatabaseFacade dbFacade)
_dbFacade = dbFacade ?? throw new ArgumentNullException(nameof(dbFacade));
}

public TTransaction BeginTransaction() => (TTransaction) _dbFacade.BeginTransaction().GetDbTransaction();
public ITransactionScope<TTransaction> BeginTransaction() {
IDbContextTransaction transaction = _dbFacade.CurrentTransaction
?? _dbFacade.BeginTransaction();

return new DbContextTransactionScope<TTransaction>(transaction);
}

public int ExecuteCommand(string query, IEnumerable<object> parameters) => _dbFacade.ExecuteSqlCommand(query, parameters);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity.Core.EntityClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QueryBuilder.Core.Database;

namespace QueryBuilder.EntityFramework.Database
{
public class DbTransactionTransactionScope<TTransaction>
: ITransactionScope<TTransaction>
where TTransaction : DbTransaction
{
private readonly DbTransaction _dbTransaction;
public TTransaction Current => (TTransaction)((EntityTransaction)_dbTransaction).StoreTransaction;

public DbTransactionTransactionScope(DbTransaction dbTransaction)
{
_dbTransaction = dbTransaction ?? throw new ArgumentNullException(nameof(dbTransaction));
}

public void Commit()
{
_dbTransaction.Commit();
}

public void Dispose()
{
_dbTransaction?.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ public ObjectContextDatabaseAdapter(ObjectContext objectContext)
_objectContext = objectContext ?? throw new ArgumentNullException(nameof(objectContext));
}

public TTransaction BeginTransaction()
public ITransactionScope<TTransaction> BeginTransaction()
{
return ((EntityTransaction)_objectContext.Connection.BeginTransaction()).StoreTransaction as TTransaction;
DbTransaction dbTransaction = _objectContext.Connection.BeginTransaction();

return new DbTransactionTransactionScope<TTransaction>(dbTransaction);
}

public TConnection GetConnection()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Database\DbTransactionTransactionScope.cs" />
<Compile Include="Database\ObjectContextDatabaseAdapter.cs" />
<Compile Include="Helpers\IQueryableHelpers.cs" />
<Compile Include="Mappings\EntityTypeMappingAdapter.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,20 @@ public BulkCopyDataReaderBase(IEnumerable<string> columns)
{
ThrowHelper.ThrowIfNullOrEmpty(columns, nameof(columns));

Columns = new ReadOnlyCollection<string>(columns.ToList());
Columns = columns.Select((value, index) => Tuple.Create(index, value))
.ToList()
.AsReadOnly();
IsClosed = false;
}

public IReadOnlyCollection<string> Columns { get; private set; }
public IReadOnlyCollection<Tuple<int, string>> Columns { get; private set; }
public int FieldCount => Columns.Count;
public int GetOrdinal(string name)
{
ThrowHelper.ThrowIfNullOrWhiteSpace(name, nameof(name));

int index = 0;

using(IEnumerator<string> columnEnumerator = Columns.GetEnumerator())
{
while (columnEnumerator.MoveNext())
if (columnEnumerator.Current == name) return index;
else index++;
}

throw new ArgumentOutOfRangeException();
Tuple<int, string> column = Columns.First(c => c.Item2 == name);
return column.Item1;
}

public bool IsClosed { get; private set; }
Expand Down
8 changes: 6 additions & 2 deletions src/QueryBuilder.SqlServer/Bulk/DataReader/IBulkDataReader.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Data;

namespace QueryBuilder.SqlServer.Bulk.DataReader
{
public interface IBulkDataReader : IDataReader
{
IReadOnlyCollection<string> Columns { get; }
/// <summary>
/// The ordinal and name of the columns
/// </summary>
IReadOnlyCollection<Tuple<int, string>> Columns { get; }
}
}
13 changes: 7 additions & 6 deletions src/QueryBuilder.SqlServer/Bulk/SqlBulkCopyExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ public void Write(string tableName, IBulkDataReader dataReader)
if(sqlConn.State != ConnectionState.Open)
sqlConn.Open();

using (SqlTransaction sqlTransaction = _sqlContext.BeginTransaction())
using (ITransactionScope<SqlTransaction> sqlTransaction = _sqlContext.BeginTransaction())
{
Write(tableName, dataReader, sqlConn, sqlTransaction);

Write(tableName, dataReader, sqlConn, sqlTransaction.Current);
sqlTransaction.Commit();
}
}
Expand All @@ -37,10 +36,12 @@ public virtual void Write(string tableName, IBulkDataReader dataReader, SqlConne
{
using (var bulkCopy = new SqlBulkCopy(sqlConn, SqlBulkCopyOptions.KeepIdentity, transaction))
{
foreach (string columnName in dataReader.Columns)
// TODO use ordinal when adding mapping instead of column name
foreach (Tuple<int, string> column in dataReader.Columns)
{
// use ordinal when adding mapping instead of column name
// bug will be solved in Standard Library 2.1 https://github.com/dotnet/corefx/pull/24655
bulkCopy.ColumnMappings.Add(columnName, columnName);
bulkCopy.ColumnMappings.Add(column.Item1, column.Item2);
}

bulkCopy.DestinationTableName = tableName;
bulkCopy.WriteToServer(dataReader);
Expand Down

0 comments on commit 79458d4

Please sign in to comment.