3

将实体框架与 SQL Server Compact Edition 一起使用时出现内存泄漏。我的情况:

我有一个大约 600MByte 大的文件。我逐行阅读,创建一个实体类并将其添加到 SQL Server CE 数据库中。内存因此增长得非常快。Gen 0 Collections 计数器和 Gen 2 Heap Size 增长非常快(来自 Process Explorer 的信息)。如果我理解正确的 Gen 2 Heap 是用于大对象的。我认为我的实体类是一个大对象。所以 Entity Framework 保存我的对象而不释放它们。我已经尝试将它们分离并调用 GC.Collect(2) 但它没有帮助。

首先,我阅读了这条线。然后在解析行后创建一个对象。然后将其添加到数据库。这是我的数据库代码:

DBEntities dbConnection = new DBEntities();
dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
dbConnection.SaveChanges();
//  dbConnection.Detach(MyObjectCreatedFromTheLine);
//  dbConnection.Dispose();
MyObjectCreatedFromTheLine = null;
dbConnection = null;

我还读到创建的实体类(MyObjectCreatedFromTheLine)属于DbContext。所以我为每一行调用这段代码,每次都创建一个新的上下文。

我究竟做错了什么?

4

4 回答 4

4

我在尝试使用实体框架将 50,000 多条记录插入 SQL 数据库时遇到了这个问题。实体框架不适用于大型批量操作(大型插入或删除操作),因此我最终使用了 System.Data.SqlClient.SqlBulkCopy 库,它更高效、更快。我什至编写了以下帮助函数来自动映射,因此我不必手动构造 SQL 插入语句。(它与类型无关!我认为)。

基本上工作流程是: IList<MyEntityType> -> DataTable -> SqlBulkCopy

public static void BulkInsert<T>(string connection, string tableName, IList<T> list)
    {
        using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls))
        {
            bulkCopy.BatchSize = list.Count;
            bulkCopy.DestinationTableName = tableName;
            bulkCopy.BulkCopyTimeout = 3000;

            var table = new DataTable();
            var props = TypeDescriptor.GetProperties(typeof(T))
                //Dirty hack to make sure we only have system data types 
                //i.e. filter out the relationships/collections
                                       .Cast<PropertyDescriptor>()
                                       .Where(propertyInfo => propertyInfo.PropertyType.Namespace.Equals("System"))
                                       .ToArray();

            foreach (var propertyInfo in props)
            {
                bulkCopy.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name);
                table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
            }

            var values = new object[props.Length];
            foreach (var item in list)
            {
                for (var i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(item);
                }

                table.Rows.Add(values);
            }

            bulkCopy.WriteToServer(table);
        }
    }

在我的示例中,我从 15-20 分钟插入到不到一分钟。

于 2012-07-12T14:28:06.980 回答
0

我不认为通过数据上下文添加大量实体是最好的方法。对于每个创建的对象,您都会消耗内存,因为数据上下文有一个内部 1 级缓存,其中对象会一直保留到上下文被释放。

我不太了解EF,也不知道每次持久化一个对象时是否可以清除缓存。但是,我宁愿选择根本不使用 EF 来执行大量插入。

相反,使用SqlBulkCopy类。它应该可以解决您的内存问题,而且它比您使用 EF 和每个对象插入所能实现的任何东西都快一个数量级。

于 2012-07-12T12:51:51.777 回答
0

让你的 DBEntities dbConnection = new DBEntities() 退出循环!?

在每次迭代中创建新的对象上下文既荒谬又无关紧要。

此外,分配需要更多时间,尤其是对于这样的大对象,更不用说内存开销和可能是问题的释放。

于 2013-06-27T09:19:51.600 回答
0

我觉得你的方法不对。只需创建一个DBEntities对象即可保存所有更改。像下面这样的东西可能会起作用;

using(DBEntities dbConnection = new DBEntities())
{
    foreach(MyObjectCreatedFromTheLine entity in ListOfMyObjectCreatedFromTheLine)
    {
        dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
    }
    dbConnection.SaveChanges();
}

您正在为DBEntities每个实体创建一个新对象,这根本不对。只是将 dbConnection 设置为 null 并不意味着对象已被释放或垃圾收集器不会收集它。事实上,你只是将引用设置为 null,对象还在内存中,垃圾收集器会收集该对象。

于 2012-07-12T11:46:27.790 回答