Git Product home page Git Product logo

ef-bulk-operations's Introduction

Tanneryd.BulkOperations.EF6/Tanneryd.BulkOperations.EFCore

Nuget packages that extend the DbContext in EF6/EFCore with bulk operations.

Getting Started

Background

Read the CodeProject article Bulk operations using Entity Framework if you are interested in some background.

Prerequisites

The extension is built for, and requires, Entity Framework 6 or EF Core.

Installing

This will make the following methods available on the DbContext.

API

Insert

public class BulkInsertRequest<T>
{
    public IList<T> Entities { get; set; }
    public SqlTransaction Transaction { get; set; }
    public bool UpdateStatistics { get; set; } = false;
    public bool Recursive { get; set; } = false;
    public bool AllowNotNullSelfReferences { get; set; } = false;
    public bool SortUsingClusteredIndex { get; set; } = true;
}
  • When UpdateStatistics is set the command "UPDATE STATISTICS WITH ALL" will be executed after the insert.
  • When Recursive is set to true the entire entity hierarchy will be inserted.
  • When AllowNotNullSelfReferences is set to true, entities with self referencing foreign keys declared as NOT NULL will be properly inserted. But, this will only work if the database user has the required privileges to execute ALTER TABLE <table name> NOCHECK CONSTRAINT ALL and ALTER TABLE <table name> CHECK CONSTRAINT ALL.
  • When SortUsingClusteredIndex is set to true the entities will be sorted according to the clustered index of the target table.
 public static BulkOperationResponse BulkInsertAll<T>(
     this DbContext ctx,
     BulkInsertRequest<T> request)

Update

public class BulkUpdateRequest
{
    public IList Entities { get; set; }
    public string[] UpdatedPropertyNames { get; set; }
    public string[] KeyPropertyNames { get; set; }
    public SqlTransaction Transaction { get; set; }
    public bool InsertIfNew { get; set; }
}
  • If UpdatedPropertyNames is an empty list all non-key mapped columns will be updated, otherwise only the columns specified.
  • If KeyPropertyNames is an empty list the primary key columns will be used to select which rows to update, otherwise the columns specified will be used.
 public static BulkOperationResponse BulkUpdateAll(
     this DbContext ctx,
     BulkUpdateRequest request)

Select

Select
SelectExisting

The select-existing feature provides a way to identify the subset of existing or non-existing items in a local collection where an item is considered as existing if it is equal to an entity saved in the database according to a set of defined key properties. This provides a very efficient way of figuring out which items in your local collection needs to be inserted and which to be updated. The item collection can be of the same type as the EF entity but it does not have to be.

public class BulkSelectRequest<T>
{
	public BulkSelectRequest(string[] keyPropertyNames, 
				 IList<T> items = null,
				 SqlTransaction transaction = null)
	{
        KeyPropertyMappings = keyPropertyNames.Select(n => new KeyPropertyMapping
            {
                ItemPropertyName = n,
                EntityPropertyName = n
            })
            .ToArray();
        Items = items;
        Transaction = transaction;
    }
    public IList<T> Items { get; set; }
    public KeyPropertyMapping[] KeyPropertyMappings { get; set; }
    public SqlTransaction Transaction { get; set; }
   

    public BulkSelectRequest()
    {
        KeyPropertyMappings = new KeyPropertyMapping[0];
        Items = new T[0];
    }
}

public class KeyPropertyMapping
{
    public string ItemPropertyName { get; set; }
    public string EntityPropertyName { get; set; }

    public static KeyPropertyMapping[] IdentityMappings(string[] names)
    {
        return names.Select(n => new KeyPropertyMapping
        {
            ItemPropertyName = n,
            EntityPropertyName = n
        }).ToArray();
    }
}
/// <summary>
/// Given a set of entities we return the subset of these entities
/// that already exist in the database, according to the key selector
/// used.
/// </summary>
/// <typeparam name="T1">The item collection type</typeparam>
/// <typeparam name="T2">The EF entity type</typeparam>
/// <param name="ctx"></param>
/// <param name="request"></param>
public static IList<T1> BulkSelectExisting<T1,T2>(
    this DbContext ctx,
    BulkSelectRequest<T1> request)

Delete

DeleteExisting

NOT IMPLEMENTED

DeleteNotExisting
    public class BulkDeleteRequest<T>
    {
        public SqlCondition[] SqlConditions { get; set; }

        public KeyPropertyMapping[] KeyPropertyMappings { get; set; }
        public IList<T> Items { get; set; }
        public SqlTransaction Transaction { get; set; }
        public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromMinutes(1);

        public BulkDeleteRequest(
            SqlCondition[] sqlConditions,
            string[] keyPropertyNames,
            IList<T> items = null,
            SqlTransaction transaction = null)
        {
            SqlConditions = sqlConditions;
            KeyPropertyMappings = KeyPropertyMapping.IdentityMappings(keyPropertyNames);
            Items = items;
            Transaction = transaction;
        }

        public BulkDeleteRequest()
        {
            KeyPropertyMappings = new KeyPropertyMapping[0];
            Items = new T[0];
        }
    }
        /// <summary>
        /// The bulk delete request contains a SqlCondition. It has
        /// a list of column name/column value pairs and will be used
        /// to build an AND where clause. This method will delete any
        /// rows in the database that matches this SQL condition unless
        /// it also matches one of the supplied entities according to
        /// the key selector used.
        ///
        /// !!! IMPORTANT !!!
        /// MAKE SURE THAT YOU FULLY UNDERSTAND THIS LOGIC
        /// BEFORE USING THE BULK DELETE METHOD SO THAT YOU
        /// DO NOT END UP WITH AN EMPTY DATABASE.
        /// 
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="ctx"></param>
        /// <param name="request"></param>
        public static void BulkDeleteNotExisting<T1, T2>(
            this DbContext ctx,
            BulkDeleteRequest<T1> request)

Release history

2.0.5 (2023-11-22)
  • Added .NET8 target for EF Core.
2.0.3 (2023-09-05)
  • Updated the README file to better reflect that there are two packages, one for EF6 and one for EF Core. No functional changes at all.
2.0.2 (2023-08-10)
  • BulkInsert to tables having a DateTime foreign key did not work as expected.
2.0.1 (2023-08-07)
  • Fixed a bug in BulkUpdateAll when column names are not identical with entity property names.
2.0.0 (2023-06-19)
  • Merged EF6 and EF Core packages into one solution and one github project.
1.4.1 (2021-11-12)
  • Added support for retrieving values from the database (updating the local entities that have matching data in the database) when doing a BulkSelectExisting.
1.4.0 (2020-06-15)
  • Added experimental support for TPH table inheritance.
  • The package now targets both netstandard2.1 and net45
  • Fixed a bug when using computed columns in tables without identity primary keys (reported and resolved by https://github.com/hzahradnik)
1.3.0 (2019-12-21)
  • Bugfix: More fixes related to parsing table names in some very specific situations.
  • Added support for the recompile option and for deleting the query plan cache.
1.2.7 (2019-09-13)
  • Bugfix: Issue #18 - Bug when parsing table names. (not completely fixed in 1.2.5)
  • Added documentation for BulkDeleteNotExisting
1.2.5 (2019-07-15)
  • Bugfix: Issue #18 - Bug when parsing table names.
  • Bugfix: Sorting on a clustered index did not work for tables with schemas.
  • Bugfix: BulkSelectExisting and BulkSelectNotExisting sometimes returned duplicates.
1.2.4 (2019-05-26)
  • Bugfix: Issue #16 - BulkInsert is not thread safety.
1.2.3 (2019-03-29)
  • Bugfix: Join tables with Guid keys misbehaved.
  • Added method BulkDeleteNotExisting
1.2.2 (2018-12-01)
  • Bugfix: BulkSelect did not work properly with null columns.
  • Bugfix: Contexts using lazy loading and thus dynamic proxies did not work as expected.
  • Bugfix: Tables with Guid primary keys did not work as expected in some situations.

Built With

  • Visual Studio 2022

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Authors

  • Måns Tånneryd

License

This project is licensed under the Apache License - see the LICENSE.md file for details.

ef-bulk-operations's People

Contributors

chralex avatar domi2120 avatar mtanneryd avatar mtanneryd-marketmath avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ef-bulk-operations's Issues

Database generated Guid as Primary Keys

Hello,

Thank you for the great work. These helpers that you put together are really helpful.
Any plans to support database generated Guid as Primary Keys?
Or any suggestions on how to do it?

Thank you :)

Exponential slowdown on parallel executions

Hello.

We are using this library for bulk insert operations in our .NET 4.7 application. We noticed that, when multiple users. So, I typed out a little application which inserts million of entries in a chunks of 4000 items, you can find it in this repo https://github.com/Hugibeer/TannerydSample
I was shocked to see how slow it is when executing bulk insert operation on multiple threads, in https://github.com/Hugibeer/TannerydSample/tree/master/Measurements you can see that, when executing this operaiton in 10 threads, execution per thread is slowed down to 235 seconds, or so. It seems there is something seriously throttling these operations.

I ran 1000 iterations on SQLBulkCopy and SQL procedur inserts, measurements with rudimentary analysis can be read here https://github.com/Hugibeer/TannerydSample/tree/master/AutomatedMeasurements
I am currently running 10 iterations of only ef6 bulk operations and will add those files to the AutomatedMeasurements folder in the repo.

Just to make things clear, this isn't "hate post", I only want to raise an attention to you to these details.

Thank you

BulkSelectExisting threw error: int is incompatible with uniqueidentifier

Ran into this error using BulkSelectExisting with Tanneryd.BulkOperations.EF6/1.2.3-beta3. The table I am searching has a Guid for a Primary Key. Also I am using Database First EF6. I feel bad for finding holes in this awesome project.

System.Data.SqlClient.SqlException: Operand type clash: int is incompatible with uniqueidentifier
    at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader()
   at Tanneryd.BulkOperations.EF6.DbContextExtensions.DoBulkSelectExisting[T1,T2](DbContext ctx, BulkSelectRequest`1 request)
   at Tanneryd.BulkOperations.EF6.DbContextExtensions.BulkSelectExisting[T1,T2](DbContext ctx, BulkSelectRequest`1 request)

Incorrectly setting generated identity values on entities after insert

Since there is no way of knowing in which order identity column values are generated when doing a bulk insert using SqlBulkCopy incorrect primary keys have been set on the entities, in memory, after insert in some situations. When doing a recursive bulk insert this also means that the foreign key relationsships will be incorrectly set in the database. In release 1.1.0-beta5 this has hopefully been fixed using a combination of temp tables and bulk copy.

SELECT COUNT(*) FROM {tableName.Fullname} performance

If a table has an int primary key and has a large number of rows (e.g. > 100 million) then the SELECT COUNT(*) has a negative impact on performance because of the scan on the table. For me it's adding in 4-5 seconds per bulk insert.

Looking at the code it looks like the count is only used to see if there's any records in the table for setting the nextId. You could change from a "SELECT COUNT(*) FROM ...." to a "SELECT TOP 1 1 FROM ..." and then handle the null as a zero if the table doesn't have any records.

BulkUpdateAll on view with instead of trigger, throws error

Hi,
I'm currently using BulkUpdateAll to update view with an instead of trigger.
I'm aware that this is somehow a strange usage but there's no easy workaround.

Problem

The BulkUpdateAll will try to do UPDATE t0 SET {properties} FROM to JOIN {temp table}, but due to SQLServer limitations this is not permitted for Views with instead of triggers, and throws the following error:

UPDATE is not allowed because the statement updates view "" which participates in a join and has an INSTEAD OF UPDATE trigger.

Solution

One way to solve this problem would be using a MERGE statement instead of the JOIN and maybe even enable the insertion of new elements in case of the flag InsertIfNew is enabled.

Is this something that could be done, or are there advantages in the usage of the UPDATE statement in terms of performance?

Not Signed

When compiling my solution I get the following error as the assembly is not signed.

CSC : error CS8002: Referenced assembly 'Tanneryd.BulkOperations.EF6, Version=1.2.3.0, Culture=neutral, PublicKeyToken=null' does not have a strong name.

BulkUpdateAll with InsertIfNew - Operand type clash: datetime2 is incompatible with int with EF6

Hi,

Thanks for the tool. We are heavily using this as we are processing lots of data. We allow user to upload file and process 1000's of records and this tool really help us from performance wise.

While adding new table to support bulk operation, we are stumbled on below error on one of the table, which has 2 columns of datetime2(7) datatype not null. The same operation works for other table which has 3 columns of datetime2(7) data type but 2 of them are nullable.

BulkInsertAll is working fine for the table.

I have tried to re-create EDMX file but still same error.

Operand type clash: datetime2 is incompatible with int\r\nDatabase name 'tempdb' ignored, referencing object in
tempdb.\r\nDatabase name 'tempdb' ignored, referencing object in tempdb.

Getting this issue on one other table

Implicit conversion from data type datetime to int is not allowed. Use the CONVERT function to run this query.\r\nDatabase name 'tempdb' ignored, referencing object in tempdb.\r\nDatabase name 'tempdb' ignored, referencing object in tempdb.

Can you please check if this is issue or i am doing something wrong.

BulkUpdateRequest sbur = new BulkUpdateRequest();
sbur.KeyPropertyNames = keyPropertyNames;
sbur.UpdatedPropertyNames = updatePropertyNames;
sbur.InsertIfNew = true;
sbur.Entities = entities;
var sburesponse = _saasDB.BulkUpdateAll(sbur);

Thanks
Balwinder

Bulk insert fails when database contains computed columns

I'm getting errors when attempting to insert into a table that contains computed columns. In this case, Shift is a computed column.

Is there a way to 'ignore' these columns for the purpose of the insert?

Unhandled Exception: System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Shift', table 'tempdb.dbo.#8e9dd922f00c4719b21059e9c4042ccf___________________________________________________________________________________000000000009'; column does not allow nulls. INSERT fails.
The statement has been terminated.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlBulkCopy.RunParser(BulkCopySimpleResultSet bulkCopyHandler)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsyncContinuedOnSuccess(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsyncContinued(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsync(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet internalResults, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalAsync(CancellationToken ctoken)
   at System.Data.SqlClient.SqlBulkCopy.WriteRowSourceToServerAsync(Int32 columnCount, CancellationToken ctoken)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServer(IDataReader reader)
   at Tanneryd.BulkOperations.EF6.DbContextExtensions.DoBulkCopy(DbContext ctx, IList entities, Type t, Mappings mappings, SqlTransaction transaction, Boolean allowNotNullSelfReferences, TimeSpan commandTimeout, BulkInsertResponse response)
   at Tanneryd.BulkOperations.EF6.DbContextExtensions.DoBulkInsertAll(DbContext ctx, IList`1 entities, SqlTransaction transaction, Boolean recursive, Boolean allowNotNullSelfReferences, TimeSpan commandTimeout, Dictionary`2 savedEntities, BulkInsertResponse response)
   at Tanneryd.BulkOperations.EF6.DbContextExtensions.BulkInsertAll[T](DbContext ctx, BulkInsertRequest`1 request)
   at Tanneryd.BulkOperations.EF6.DbContextExtensions.BulkInsertAll[T](DbContext ctx, IList`1 entities, SqlTransaction transaction, Boolean recursive)
   at DataMigrator.Main.Migrate() in D:\Dev\DataMigrator\Modules\Main.vb:line 47
   at DataMigrator.Main.Main() in D:\Dev\DataMigrator\Modules\Main.vb:line 5

BulkSelectNotExisting fails

When the select items have different property names from the entity in question it fails. The key property mapping created incorrectly uses table columns instead of entity properties.

InsertIfDoesntExist

Would it be possible to implement a method that inserts a new records if it doesn't exist based on a specified key. Essentially something like:

_context.BulkUpdateAll(new BulkUpdateRequest
{
     Entities = new[] {new User {UserName = "someUserName}},
     InsertIfNew = true,
     KeyPropertyNames = new[] {"UserName"},
     UpdatedColumnNames = new[] {"UserName"},
     Transaction = null
});

Except that doesn't work, since all UpdatedColumnNames that also exist in KeyPropertyNames are filtered out. Update isn't even needed here, for every entity in the list we would only need:

 IF NOT EXISTS(SELECT 1 FROM Users x where x.UserName = 'someUserName')
  INSERT INTO Users(UserName) VALUES ('someUserName');

Bug When Parsing Table Names

Hello!

I think I've discovered a bug when the extension is parsing out table names.. For my model, when Entity Framework builds the SQL output that the Regex parses in the GetTableName method, for some reason EF is adding an extra space in the FROM clause (between the FROM keyword and the table name), e.g:

FROM  [dbo].[StagingTransactions] AS [Extent1]

When GetTableName parses this, it parses it out as " dbo.StagingTransactions" (with a leading space) and thus returns:
Schema = " dbo"
TableName = "StagingTransactions"

When this is then returned as a TableName object and parsed back into SQL, it's outputting it as [ dbo].[StagingTransactions] which then causes the Temp Table code to fail.

BulkUpdateAll error with use proxy type.

Hi @mtanneryd.

I'm get entities from entity fremwork, modify and BulkUpdateAll(). I have exception in runtime. The problem is that entity fremwork returns a collection of proxy types.

Solution: add stringin DbContextExtensions in GetMappings method on 1794 row
"t = ObjectContext.GetObjectType(t);"

Bulk Delete Documentation

Hi.

Thank you for Tanneryd.BulkOperations.EF6.

I'd like to request the ability to bulk delete records from the database.

Is this documented?

Thank you!

InvalidOperationExceptions with BulkInsertAll

Hi @mtanneryd, thank you for the effort you put into this library. I tried the latest beta version but I could not get it work.

The relevant position of code looks like this: I'm opening a new transaction and trying to recursively insert a list of employeesTestData.

using (var transaction = employeeContext.Database.BeginTransaction()) { var insertRequest = new BulkInsertRequest<Employee> { Recursive = true, Entities = employeesTestData, Transaction = (SqlTransaction)transaction.UnderlyingTransaction }; employeeContext.BulkInsertAll(insertRequest); transaction.Commit(); }

Method call BulkInsertAll(...) throws the following 'Sequence contains no matching element' and one line later, the Commit(...) raises the 'There is already an open DataReader' exception. What am I doing wrong? I forked your code and could fix it to make it work. But I rather think I'm doing something wrong here. Thanks for your help, Thomas.

System.InvalidOperationException HResult=0x80131509 Message=Sequence contains no matching element Source=System.Core StackTrace: at System.Linq.Enumerable.Single[TSource](IEnumerable1 source, Func2 predicate) at Tanneryd.BulkOperations.EF6.DbContextExtensions.GetMappings(DbContext ctx, Type t) at Tanneryd.BulkOperations.EF6.DbContextExtensions.DoBulkInsertAll(DbContext ctx, IList1 entities, SqlTransaction transaction, Boolean recursive, Boolean allowNotNullSelfReferences, Dictionary2 savedEntities, BulkInsertResponse response) at Tanneryd.BulkOperations.EF6.DbContextExtensions.BulkInsertAll[T](DbContext ctx, BulkInsertRequest1 request)
at EntityFramework.Toolkit.Tests.PerformanceTests.AddEntitiesPerformanceTest_BulkInsert(Int32 count) in C:\src\github\thomasgalliker\EntityFramework.Toolkit\ EntityFramework.Toolkit.Tests\PerformanceTests.cs:line 147

System.InvalidOperationException
HResult=0x80131509
Message=There is already an open DataReader associated with this Command which must be closed first.
Source=System.Data
StackTrace:
at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
at System.Data.SqlClient.SqlInternalTransaction.Rollback()
at System.Data.SqlClient.SqlTransaction.Dispose(Boolean disposing)
at System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext](TTarget target, Action2 operation, TInterceptionContext interceptionContext, Action3 executing, Action3 executed)
at System.Data.Entity.Infrastructure.Interception.DbTransactionDispatcher.Dispose(DbTransaction transaction, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.EntityClient.EntityTransaction.Dispose(Boolean disposing)
at System.Data.Entity.DbContextTransaction.Dispose(Boolean disposing)
at System.Data.Entity.DbContextTransaction.Dispose()
at EntityFramework.Toolkit.Tests.PerformanceTests.AddEntitiesPerformanceTest_BulkInsert(Int32 count) in C:\src\github\thomasgalliker\EntityFramework.Toolkit\ EntityFramework.Toolkit.Tests\PerformanceTests.cs:line 150
`

Navigation properties not correctly persisted

First of all, I like your library which seems to solve one of the major issues that exist with Entity Framework. Thanks for the effort !

I have an entity DataEntry which has a collection of DataEntryProperty entities. When I try to bulk insert the DataEntry it will indeed persist the DataEntry and corresponding DataEntryProperty entities, however the column 'DataEntry_Identifier', which is added automatically by EntityFramework, containing the FK relation from the DataEntryProperty to the DataEntry is not stored.

I tried different ways of using the BulkInsert. Also tried attaching the resulted entities within the EF context, but this did not make any difference. When debugging the implementation I was unable to see any reference to the 'DataEntry_Identifier' column being used.

BulkInsert is not thread safety

Helloy, @mtanneryd.

If I execute many BulkInsert in one table in many threads (parrallel executing BulkInsert), I have Exception("More id values generated than we had entities. Something went wrong, try again.").

It happens because the increment of the primary key (user integer) and the number of inserted records are compared, but they may differ when the insertion is parallel.

Async/await support

Hi,

Have you considered adding async/await support?

This line could be replaced by await bulkCopy.WriteToServerAsync(...) ?

BulkCopy Options

When performing bulk inserts, default BulkOptions are used. For instance CheckConstraints default value is to not check.
This may result in invalid database state, for instance, by inserting invalid foreign keys.
This can be avoided by exposing an an option to disable default CheckConstraints behavihour.

Error when bulk updating/inserting to SQL Server Temporal Tables

(Reference for Temporal Tables: https://docs.microsoft.com/en-us/sql/relational-databases/tables/creating-a-system-versioned-temporal-table?view=sql-server-ver15)

I utilize SQL Server Temporal Tables and my time columns have unique names different than the default SysStartTime and SysEndTime.

The error occurs when trying to update or insert to these columns which is not allowed.
2019-12-31 08_34_27-Window

I believe the solution would be to support configuring column names that need to be ignored in the "BulkUpdateAll" and "BulkInsertAll" methods.

I've been utilizing the solution below to support SQL Server Temporal Tables in EF for years.
https://stackoverflow.com/questions/40742142/entity-framework-not-working-with-temporal-table

Regex to get table name gives empty string.

The following Regex that you are using gives empty string:
var regex = new Regex(@"FROM (?<table>[\[\w@$#_\. \]]+) AS \[\w+\]$") and this error is not handled.

The SQL statement generated from my code is:
SELECT [Extent1].[Id] AS [Id], [Extent1].[Identifier] AS [Identifier], [Extent1].[Name] AS [Name], [Extent1].[Latitude] AS [Latitude], [Extent1].[Longitude] AS [Longitude], [Extent1].[FIR] AS [FIR], [Extent1].[UIR] AS [UIR], [Extent1].[ICAO] AS [ICAO], [Extent1].[MagneticVariation] AS [MagneticVariation], [Extent1].[Frequency] AS [Frequency], [Extent1].[CountryName] AS [CountryName], [Extent1].[CountryId] AS [CountryId], [Extent1].[StateId] AS [StateId], [Extent1].[CityId] AS [CityId], [Extent1].[IsActive] AS [IsActive] FROM [dbo].[Points] AS [Extent1] WHERE ([Extent1].[IsActive] = @DynamicFilterParam_000001) OR (@DynamicFilterParam_000002 IS NOT NULL)

Ill generate a pull request fixing this issue and added additional exception handling, please be kind enough to merge this and release on nuget. Thanks.

File: DbContextExtensions.cs
LIne: 2201

Capture

No optimistic concurrency check, when having a RowVersion column and executing the BulkUpdate() method

Hello!

Thank you for a great and fast library!

Unfortunately, I have run into an issue, when using the BulkUpdate() method, it does not enforce the optimistic concurrency check.
When having the 'RowVersion' column, and in concurrent scenarios, BulkUpdate() just overwrites the changes, ignoring the fact that the same version of data was already modified by different source.

Could you help with that please?

BulkUpdateAll fails as update has columns in SQL

i'm trying to update some entities, my first one goes through fine, but the 2nd one throws an exception as the columns in the update statement are blank:
image
any idea what would make this happen?

Problem with BulkInsert when having column name like In or As

Hi @mtanneryd, thank you for the effort you put into this library. I was importing chemical analysis in a Datawaharehouse having column name like "As" and "In" that throw error when they are not between []. There's missing [] in some function of DbContextExtensions that involve ColumnName.

Here's a version of DbContextExtensions that fixes the problem but did it very quick and didn't do many many test with it. Next time I will take the time to do a pullrequest to ease the merge job.

DbContextExtensions.zip

BulkInsertAll failling for table with no non primary key columns

Hi, I'm trying to use your library, but BulkInsertAll gives me an SqlException: Invalid syntax near ','.
I tracked the problem down to this line: https://github.com/mtanneryd/ef6-bulk-operations/blob/7ca96c682037bad9c40de83c0041abe3f8ee5005/Tanneryd.BulkOperations.EF6/Tanneryd.BulkOperations.EF6/DbContextExtensions.cs#L1769
The issue is that 1 of my tables only has a primay key. It means that columnNames is an empty string, and it generates an sql statement like this:

MERGE [dbo].[TraitTargets]
                        USING 
                            (SELECT , rowno
                             FROM   tempdb..#9ada186c0c524a4aa88daa44962c87a4) t (, rowno)
                        ON 1 = 0
                        WHEN NOT MATCHED THEN
                        INSERT ()
                        VALUES ()
                        OUTPUT t.rowno,
                               inserted.[TraitTargetId]; 

which is indeed invalid sql. You should probably check if nonPrimaryKeyColumnMappings has any values.
Any chance for a quick fix?

BulkInsertAll "recursive" entities with Many-to-Many relationships seems to repeatedly insert into related entity table.

Basically when running a Sql Profiler I saw SQL that was inserting into my UnitAmenities over and over. The main entity I am trying to insert is called Units. I also have a join table called Units_UnitAmenities. Primary keys are all Guids.

Here is the SQL that was running over and over. I was hoping the recursion would insert them all at once (bulk) like it normally does on Units without recursion.
INSERT INTO [dbo].[UnitAmenities] ([UnitAmenityId],[Name]) SELECT [UnitAmenityId],[Name] FROM tempdb..#b249e294eb5e413e8561a4faf2a3cd9f AS [t0] WHERE NOT EXISTS ( SELECT [UnitAmenityId] FROM [dbo].[UnitAmenities] AS [t1] WHERE [t0].[UnitAmenityId] = [t1].[UnitAmenityId] )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.