【问题标题】:SQLBulkCopy inserts using Entity Framework with Foreign Key objectSQLBulkCopy 使用带有外键对象的实体框架插入
【发布时间】:2014-01-31 00:54:00
【问题描述】:

我使用 EF6 作为 ETL 工具的加载层。我承认有更好的工具(例如 SSIS、直接 SQL 查询等),但是由于转换的复杂性,它需要在代码中完成,并且目标数据库是从 EF 模型构建的。批处理中插入的记录数可以超过 100,000 条记录。这并不是非常慢(使用DBContext.AddRange() 方法),但内存使用率非常高(超过1GB

例如,我有以下数据类(在内存中创建)

    public class Foo
    {
        public long FooID { get; set; }
        public string SomeProperty { get; set; }
        public decimal AverageFlightSpeedOfUnladenSwallow { get; set; }
        public IEnumerable<Bar> Bars { get; set; }
    }

    public class Bar
    {
        public long BarID { get; set; }
        public Foo Foo { get; set; }
        public long FooID { get; set; }
        public string FavoriteColour { get; set; }
    }
dbContext.Foos.AddRange(ListOfFoos); //Pre constructed list of Foos
dbContext.Bars.AddRange(ListOfBars); //Pre constructed list of Bars (parent Foo items populated, FooID is not)
dbContext.SaveChanges();

我正在考虑使用 LINQ 实体数据阅读器将IList&lt;Foo&gt; 转换为数据阅读器,以便我可以使用 SQLBulkCopy (SqlBulkCopy and Entity Framework, http://archive.msdn.microsoft.com/LinqEntityDataReader/Release/ProjectReleases.aspx?ReleaseId=389)。

要求
List&lt;Bar&gt; 将没有父 Foo 类的 ID。实体框架处理得很好,但我不确定如何在 SqlBulkCopy 中获得相同的功能。有什么办法可以完成吗?

【问题讨论】:

    标签: c# sql-server entity-framework sqlbulkcopy


    【解决方案1】:

    不,使用 SQL 批量复制没有直接的方法。

    SQL Bulkcopy 非常接近数据库,因此速度非常快。 ORM 处理 FK/PK 关系,但缺点是速度慢。

    根据您的数据模型,您可以在这个问题中执行类似的操作:填充批量数据表

    SQL Bulkcopy YYYYMMDD problem

    【讨论】:

    • 嘿 Pleun,我实际上找到了一个很好的解决方案。我能够在 EntityFramework Lists 上获得 SQLBulkInsert 速度。你知道我可以在哪里上传解决方案吗?它有点脏,但应该适用于大多数情况
    【解决方案2】:

    所以,

    如果您的 EF 用户能够更改数据库上的架构,您可以采用这种解决问题的方法:

    1. 向表中添加 GUID 列
    2. 通过 GUID 识别内存中的每个对象
    3. 通过批量插入插入值,包括识别 GUID
    4. 选择它们并将它们映射到插入的 GUID
    5. 删除 GUID 列

    这里有一些代码可以做到这一点。它有点脏而且没有优化,但它把原来的任务最小化到了 30MB 的内存和 1 分钟的处理时间

    public static class ForeignKeyBulkInsert
    {
        private const string GUID_COLUMN_NAME = "GUID_SURROGATE_KEY";
    
        public static string GetTableName<T>(this ObjectContext context) where T : class
        {
            string sql = context.CreateObjectSet<T>().ToTraceString();
            Regex regex = new Regex("FROM (?<table>.*) AS");
            Match match = regex.Match(sql);
    
            string table = match.Groups["table"].Value;
            return table;
        }
    
        public static void AddRange<TEntity>(this DbContext db, IEnumerable<TEntity> range, bool importForeignKeyIDs = false)
            where TEntity : class
        {
            Dictionary<Guid, TEntity> lookup = new Dictionary<Guid, TEntity>();
    
            var objectContext = ((IObjectContextAdapter)db).ObjectContext;
            var os = objectContext.CreateObjectSet<TEntity>();
            bool hasAutoGeneratedKey = os.EntitySet.ElementType.KeyProperties.Any();
            Type entityType = typeof(TEntity);
    
            if (importForeignKeyIDs)
            {
                var foreignKeyProperties = os.EntitySet.ElementType.NavigationProperties.Where(x => x.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One);
                foreach (var foreignKeyProperty in foreignKeyProperties)
                {
                    var foreignKeyIdProperty = foreignKeyProperty.GetDependentProperties().First();
                    var parentKeyProperty = foreignKeyProperty.ToEndMember.GetEntityType().KeyMembers.First();
                    PropertyInfo foreignKeyPropertyInfo = null;
                    Type parentType = null;
                    PropertyInfo parentKeyPropertyInfo = null;
                    PropertyInfo foreignKeyIdPropertyInfo = null;
                    foreach (var item in range)
                    {
                        entityType.GetProperty(foreignKeyProperty.Name).GetValue(item);
                        if (foreignKeyPropertyInfo == null)
                            foreignKeyPropertyInfo = entityType.GetProperty(foreignKeyProperty.Name);
                        if (parentType == null)
                            parentType = foreignKeyPropertyInfo.GetValue(item).GetType();
                        if (parentKeyPropertyInfo == null)
                            parentKeyPropertyInfo = parentType.GetProperty(parentKeyProperty.Name);
                        if (foreignKeyIdPropertyInfo == null)
                            foreignKeyIdPropertyInfo = entityType.GetProperty(foreignKeyIdProperty.Name);
    
                        var foreignKey = foreignKeyPropertyInfo.GetValue(item);
                        if (foreignKey == null)
                            break;
    
                        var parentKey = parentKeyPropertyInfo.GetValue(foreignKey);
                        foreignKeyIdPropertyInfo.SetValue(item, parentKey);
                    }
                }
            }
    
            string tableName = objectContext.GetTableName<TEntity>();
            var entityReader = range.AsDataReader(GUID_COLUMN_NAME, lookup);
    
            if (hasAutoGeneratedKey)
            {
                try
                {
                    db.Database.ExecuteSqlCommand(string.Format("ALTER TABLE {0} ADD [{1}] uniqueidentifier null", tableName, GUID_COLUMN_NAME));
                }
                catch (Exception)
                {
                    db.Database.ExecuteSqlCommand(string.Format("ALTER TABLE {0} DROP COLUMN [{1}]", tableName, GUID_COLUMN_NAME));
                    db.Database.ExecuteSqlCommand(string.Format("ALTER TABLE {0} ADD [{1}] uniqueidentifier null", tableName, GUID_COLUMN_NAME));
                }
            }
            try
            {
                var connection = db.Database.Connection as SqlConnection;
                connection.Open();
                using (SqlBulkCopy cpy = new SqlBulkCopy(connection))
                {
                    cpy.BulkCopyTimeout = 0;
                    cpy.DestinationTableName = tableName;
                    cpy.WriteToServer(entityReader);
                    connection.Close();
                }
    
                if (hasAutoGeneratedKey)
                {
                    db.Database.Connection.Open();
                    var comm = db.Database.Connection.CreateCommand();
                    comm.CommandText = string.Format("SELECT * FROM {0} WHERE [{1}] is not null", tableName, GUID_COLUMN_NAME);
                    try
                    {
                        using (var reader = comm.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                Guid surrogateKey = Guid.Parse(reader[GUID_COLUMN_NAME].ToString());
                                TEntity entity = lookup[surrogateKey];
                                var keyProperty = entityType.GetProperty(os.EntitySet.ElementType.KeyMembers.First().Name);
                                keyProperty.SetValue(entity, reader[keyProperty.Name]);
                            }
                        }
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    finally
                    {
                        //This should never occur
                        db.Database.Connection.Close();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                if (hasAutoGeneratedKey)
                    db.Database.ExecuteSqlCommand(string.Format("ALTER TABLE {0} DROP COLUMN [{1}]", tableName, GUID_COLUMN_NAME));
            }
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-17
      • 1970-01-01
      • 1970-01-01
      • 2011-11-28
      • 1970-01-01
      • 2017-01-30
      • 1970-01-01
      • 2011-02-02
      • 1970-01-01
      相关资源
      最近更新 更多