Multiple Inserts with an Error case

Sep 11, 2014 at 2:51 PM
Edited Sep 11, 2014 at 2:52 PM
Hi

If I set a non null product attribute such as Name, in the imported collection, to null then the Catch statement is activated and I log the error.

The issue is that all other products after the null name product in the collection fail 'Validation failed for one or more entities' even though they are valid.

I believe it is because of the item that caused the error, it's still in the UnitOfWork and thinks it needs processing still.

How can I remove it or what am I doing wrong please?
var ImportedProducts= new[]
                {
                    new ImportedProduct {Name= 'One'},
                    new ImportedProduct {Name= null},
                    new ImportedProduct {Name= 'Three'}
                };

foreach(var item in ImportedProducts) {
    Try
     {
        var prod = new Product();
        prod.Name = item.Name;
        prod.ObjectState = ObjectState.Added;
        _productService.Insert(prod );
        _unitOfWork.SaveChanges();
    } 
    catch (Exception ex)
    {
        LogError();
    }
}
Thx

Anthony
Sep 12, 2014 at 11:55 AM
Have written an 'Integration Test'.
Hope this helps you and you can take a look why this fails please...
[TestMethod]
        public void InsertManyProducts()
        {
            using (IDataContextAsync northwindFakeContext = new NorthwindContext())
            using (IUnitOfWork unitOfWork = new UnitOfWork(northwindFakeContext, _repositoryProvider))
            {
                var newProducts = new[]
                {
                    new Product {ProductName = "One", Discontinued = false, ObjectState = ObjectState.Added},
                    //ProductName is invalid as > 40 chars
                    new Product {ProductName = "12345678901234567890123456789012345678901234567890", Discontinued = true, ObjectState = ObjectState.Added},
                    new Product {ProductName = "Three", Discontinued = true, ObjectState = ObjectState.Added},
                    new Product {ProductName = "Four", Discontinued = true, ObjectState = ObjectState.Added},
                    new Product {ProductName = "Five", Discontinued = true, ObjectState = ObjectState.Added}
                };

                foreach (var item in newProducts)
                {
                    try
                    {
                        unitOfWork.Repository<Product>().Insert(item);
                        unitOfWork.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        //LogErrToDB
                    }
                }

                var product = unitOfWork.Repository<Product>()
                    .Query(x => x.ProductName == "Three")
                   .Select()
                   .SingleOrDefault();

                Assert.IsNotNull(product);
            }
        }
Coordinator
Sep 12, 2014 at 10:10 PM
Can you confirm you are on the latest version?

Your Repository.Pattern.Ef6.Repository.cs should have this method:

https://genericunitofworkandrepositories.codeplex.com/SourceControl/latest#main/Source/Repository.Pattern.Ef6/Repository.cs
private void SyncObjectGraph(object entity)
        {
            // Set tracking state for child collections
            foreach (var prop in entity.GetType().GetProperties())
            {
                // Apply changes to 1-1 and M-1 properties
                var trackableRef = prop.GetValue(entity, null) as IObjectState;
                if (trackableRef != null && trackableRef.ObjectState == ObjectState.Added)
                {
                    _dbSet.Attach((TEntity)entity);
                    _context.SyncObjectState((IObjectState)entity);
                }

                // Apply changes to 1-M properties
                var items = prop.GetValue(entity, null) as IList<IObjectState>;
                if (items == null) continue;

                foreach (var item in items)
                    SyncObjectGraph(item);
            }
        }
We have a similar integration test working:
https://genericunitofworkandrepositories.codeplex.com/SourceControl/latest#main/Sample/Northwind.Test/IntegrationTests/CustomerRepository_Tests.cs
Sep 13, 2014 at 2:10 PM
I downloaded v3.3.2 and constructed the above unit test to replicate the issue I have in my code. I'm not too sure if it is anything to do with SyncObjectGraph though as the example I posted doesn't use graphs in anyway.
Coordinator
Sep 19, 2014 at 6:30 AM
I'm not sure if this is native EF behavior, however to fail on any inserts after one of them has failed. Because we are not doing anything that manages failed entities due to validation, you should be validating the entity before the insert though vs. inserting and anticipating EF to do this workflow for you, which is insert all validated products from your collection and omit the one that fails validation.
Marked as answer by lelong37 on 9/18/2014 at 11:30 PM
Coordinator
Oct 1, 2014 at 5:09 PM
Looked into this, you will need to either fix the validation error or detach it from the DbSet, this is by design EF to fail the rest of the inserts in DbSet.
Oct 1, 2014 at 5:16 PM
Hi Lelong

Yes I concluded the same. I have to recreate the UnitOfWork and the service I'm using (not sure how to detach my object).

Currently though I'm doing what you originally suggested and I'm validating before inserting. This has given rise to another problem which isn't really in the scope of this question but Validate.TryValidateObject is exponentially slowing down.

http://stackoverflow.com/questions/26090528/why-is-validator-tryvalidateobject-slowing-down-exponentially(http://stackoverflow.com/questions/26090528/why-is-validator-tryvalidateobject-slowing-down-exponentially)

Many thanks for your reply!