Search code examples
entity-framework-6asp.net-4.5glimpseef-bulkinsert

EF.BulkInsert and Glimpse - not playing together well


I have an EF6 / ASP.NET 4.5 Webforms solution up and running, and now I need to add some functionality to allow bulk inserts from Excel files.

I am aware that EF out of the box isn't optimized for bulk operations, so I looked around and found "EF BulkInsert" (https://efbulkinsert.codeplex.com/) to facilitate just this.

I tried it in a test app, and it worked wonderfully - but when I included it in my actual main app, it broke down. When trying to do the actual bulk insert call, the system crashes with an exception:

BulkInsertProviderNotFoundException: BulkInsertProvider not found for 'Glimpse.Ado.AlternateType.GlimpseDbConnection. To register new provider use EntityFramework.BulkInsert.ProviderFactory.Register() method'

Now I'm unsure if this is the fault of Glimpse or EF BulkInsert (or both), and unfortunately, I cannot seem to find any solution - neither of the makers of these pieces of software is providing any insights or workarounds....

Has anyone here stumbled across this same problem, and found a solution for it??


Solution

  • This problem occurs because Glimpse wraps DbConnection and EF BulkInsert extension tries to access it's private field "_connectionString" which is not there. I would blame EF BulkInsert in this case as accessing private members is just bad practice and no developer in Glimpse team could have anticipated that.

    To solve this problem I have written a custom which inherits from EfSqlBulkInsertProviderWithMappedDataReader (default provider):

        public class GlimpseProvider : EfSqlBulkInsertProviderWithMappedDataReader, IEfBulkInsertProvider
        {
    
            private static object GetPrivateFieldValue(object obj, string propName) {
                if (obj == null) throw new ArgumentNullException("obj");
                Type t = obj.GetType();
                FieldInfo fieldInfo = null;
                PropertyInfo propertyInfo = null;
                while (fieldInfo == null && propertyInfo == null && t != null) {
                    fieldInfo = t.GetField(propName,
                        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                    if (fieldInfo == null) {
                        propertyInfo = t.GetProperty(propName,
                            BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                    }
    
                    t = t.BaseType;
                }
                if (fieldInfo == null && propertyInfo == null)
                    throw new ArgumentOutOfRangeException("propName",
                        string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
    
                if (fieldInfo != null)
                    return fieldInfo.GetValue(obj);
    
                return propertyInfo.GetValue(obj, null);
            }
    
            protected override IDbConnection DbConnection {
                get { return (IDbConnection)GetPrivateFieldValue(this.Context.Database.Connection, "InnerConnection"); }
            }
        }
    

    Now Register this provider somewhere. I did it in context OnModelCreating method.

    EntityFramework.BulkInsert.ProviderFactory.Register<GlimpseProvider>("Glimpse.Ado.AlternateType.GlimpseDbConnection");
    

    Be aware that I've tested this only with basic usage of EF BulkInsert.