mtanneryd / ef-bulk-operations

Bulk operations for Entity Framework 6
Apache License 2.0
80 stars 30 forks source link

InvalidOperationExceptions with BulkInsertAll #5

Closed thomasgalliker closed 6 years ago

thomasgalliker commented 6 years ago

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 `

mtanneryd commented 6 years ago

Hello Thomas!

I could not find your perf test code on github so I was not able to try it out. There are two places in GetMappings where I use Single(). The first time is when I try to get the entity set maps out of the context. This has never failed for me but if you are using edmx rather then DbContext that might explain it. The ef6-bulk-operations package has only been tested with DbContext. The second use of Single() occurs if there is a many-to-many relationship. Do you have such a relationship in the employee or in any dependent class?

thomasgalliker commented 6 years ago

DbContext is a code-first context in this case. I prepared a branch called 'PerformanceTests' (https://github.com/thomasgalliker/EntityFramework.Toolkit/tree/PerformanceTests) in which I created a test 'PerformanceTests' with the failing bulk insert. (https://github.com/thomasgalliker/EntityFramework.Toolkit/blob/PerformanceTests/%20EntityFramework.Toolkit.Tests/PerformanceTests.cs). The test case is called 'AddEntitiesPerformanceTest_BulkInsert'.

Would be great if you could quickly look at it - but don't waste a bunch of time on it :) If you want me to create a fork from your repo, I can do so too.

Thanks. Thomas

mtanneryd commented 6 years ago

I'll have a look this evening. Thanks for the input!

mtanneryd commented 6 years ago

There seems to be a problem with your context. When you configure Employee and Student who both inherit from Person the DbContext apparently do not create an entity set map for those two entity classes even though they are mapped to tables. My guess is that this has something to do with those two classes inheriting their primary key. Exactly where the mapping of Employee and Student ends up being stored I do not know. I'll investigate more later.

thomasgalliker commented 6 years ago

I‘ll have a look at it too. I got to work your code from codeproject with the EntityFramework.Toolkit DbContext. However, there was some change necessary. I will try to get it work with your latest github source.

thomasgalliker commented 6 years ago

The point with the inherited classes is clearly my issue. Thanks. Furthermore, I found that it is probably a good idea to put all calls to ExecuteReader() into a using(...) statement so that the readers get disposed after executing. This way I resolved the error „There is already an open DataReader associated with this Command which must be closed first.“

mtanneryd commented 6 years ago

Good idea with the using statements. Fixed that. Thanks!

Selvasli commented 4 years ago

Hi @mtanneryd, I also thank you for the effort you put into this library. I have 3 classes as mentioned below. and I get the error 'Sequence contains no matching element' after running the "bulkinsertall" method. Thanks for your help (I am not very good in english)

**var ctx = new ApplicationDbContext();
var ins=GetAllInstitution();
var request = new bulkinsertrequest<institution>
            {
                entities = ins,
                recursive = false,
                allownotnullselfreferences = true
            };
            ctx.bulkinsertall(request);**
public abstract  class BaseEntity
    {
        public BaseEntity()
        {
            DateCreated = DateTime.Now;
        }
        public int Id { set; get; }
        [Display(Name = "Oluşturma Tarihi")]
        public DateTime DateCreated { set; get; }
        public virtual bool IsDeleted { get; set; }
    }
[Table("Corporations")]
public abstract class Corporation : BaseEntity
{
    [Required(ErrorMessage ="{0} Alan zorunlu")]
    [Display(Name = "Adı")]
    public string Name { get; set; }
    [Required(ErrorMessage = "{0} Alan zorunlu")]
    public int OrganizationId { get; set; }
    [Display(Name = "Şehir")]
    [Required]
    public int CityId { get; set; }
    public City City { get; set; }
    public ICollection<CorporateAgreement> Agreements { set; get; }
    [NotMapped]
    public bool IsExpired
    {
        get
        {
            if (Agreements != null && Agreements.Any(z => !z.IsDeleted))
            {
                return false;
            }
            return true;
        }
    }
}

 [Table("Institution")]
    public class Institution:Corporation
    {
        public string InstitutionMainType { get; set; }
        public string InstitutionConnectedType { get; set; }
        public DateTime? ClosedDate { get; set; }
        public DateTime? OpenedDate { get; set; }
    }