1

在精简应用程序以挖掘我面临的性能问题后,我有一个非常基本的数据模型。数据模型存在两个实体,例如 callend Product 和 Category:

class Product {
    public virtual int ID {get;set;}
    public virtual string Name {get;set;}
    public virtual int CategoryID {get;set;}
    public virtual Category Category {get;set;}
}

class Category {
    public virtual int ID {get;set;}
    public virtual string Name {get;set;}
}

当我添加 100 个新产品时,所有产品的引用都使用 CategoryID 属性设置为同一类别,我调用 db.SaveChanges() 可能需要相当长的时间。通过将 AutoDetectChangesEnabled 设置为 false,我能够减少所需的时间。但经过几次运行后,我注意到当数据库中存在的产品数量增加时,插入 100 个新产品所需的时间也会增加:

23416 -->  +/- 7000 ms
25516 -->  +/- 7500 ms

因为我知道表在生产中会变得更大,所以我想知道在我回退到使用原始 Sql 查询或 BulkInsert 之前我的选择是什么。

针对斯坦利和拉斐尔的评论:

在我的测试用例中,ID 属性都是主键,CategoryID 有一个索引。没有触发器等。

我正在执行一个循环 100 次,在其中我初始化一个新的 DbContext,插入 100 个产品并处理 DbContext。

明确一点:我可以创建一个新项目,使用 SqlCe 设置 EF,使用 Code-First 像上面一样构建数据模型并获得相同的结果

在进行了一些其他测试之后,似乎问题出在 Sql Server Compact Edition 上。使用常规的 MSSQL 服务器,观察到的性能问题消失了,现在插入 100 行始终需要大约 50 毫秒。尽管我现在似乎找到了问题的根源,但它仍然没有回答为什么使用外部查询工具在紧凑型数据库中插入 100 行与使用 EF 插入 100 行时没有相同的性能问题。

4

1 回答 1

0

我之前使用过 ETL 的实体框架,并且遇到了内存消耗问题,即使我很努力地处理我的上下文。我发现将垃圾收集作为日常工作的一部分减少了内存消耗,但只是部分减少了。

在使用实体框架进行批量插入或更新时,我不知道有一种方法可以避免 n+1 反模式。如果这种加载数百或数千条记录的场景是您可以控制的并且很少这样做,您可以尝试以下操作:

using (MyDbContext db = new MyDbContext())
{
    // prep your records and add them to your context

    // Save the records
    db.SaveChanges
}

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

这对我有用,因为 ETL 是一次性负载,我们有时间将其延长几天。我不会把这样的东西推到生产环境中,特别是如果你打算让你无法控制的用户或系统有权进行批量插入。

我的团队最终仅将 EF 用于事务性用途,并且我们有一个单独的数据访问层用于批量加载、集成和报告。

我解决这个问题的方法是在尝试保持模型驱动开发的同时,利用 SQL Server 中的 xml 数据类型。我准备对象作为 ICollection 的一部分,将它们序列化为 XML,然后将该字符串作为参数发送到存储过程,如下所示:

create procedure [dbo].[BulkAddProducts]
        @Values xml = null
    as begin
        insert into dbo.Products
            select
                    Products.Product.value('*:Name[1]', 'nvarchar(512)') as Name
                    ,Products.Product.value('*:CategoryID[1]', 'int') as CateogryID
                from @Values.nodes(N'/*:ArrayOfProduct/*:Product') Products(Product)
    end
go

这是一个没有 proc 的查询的工作示例:

declare @Val xml = '
<ArrayOfProduct xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MyAppName.Models">
    <Product>
        <Name>Product1</Name>
        <CategoryID>5</CategoryID>
    </Product>
    <Product>
        <Name>Product2</Name>
        <CategoryID>5</CategoryID>
    </Product>
</ArrayOfProduct>
'
select
        Products.Product.value('*:Name[1]', 'nvarchar(512)') as Name
        ,Products.Product.value('*:CategoryID[1]', 'int') as CateogryID
    from @Val.nodes(N'/*:ArrayOfProduct/*:Product') Products(Product)
go
于 2012-10-05T23:28:55.937 回答