处理大量物品

时间:2014-03-17 17:45:32

标签: c# asp.net-mvc collections

在我的一种申请方法中,我处理了很多我必须同时保存的条目:

foreach (CategoryType category in apiCall.CategoryList)
{
    EBAY_CATEGORY itemToAdd = new EBAY_CATEGORY
        {
            CATEGORY_ID = int.Parse(category.CategoryID),
            CATEGORY_LEVEL = category.CategoryLevel,
            NAME = category.CategoryName,
            LEAF = category.LeafCategory,
            EXPIRED = category.Expired,
            VIRTUAL = category.Virtual
        };

    int? parentId = Int32.Parse(category.CategoryParentID[0]);

    if (parentId != null)
    {
        itemToAdd.PARENTID = parentId;
    }

    db.EBAY_CATEGORY.Add(itemToAdd);

    db.Entry(itemToAdd).State = EntityState.Added;

    db.SaveChanges();
}

(是的,我目前正在与eBay打交道)。当这个循环开始时,我已经在一系列复杂项目中下载了大约20 000个项目。

所以你看到我发起了foreach以确保我保存了数据库中的每个条目,但由于它进行了大量操作,因此也需要花费很多时间。处理这些物品的最佳方法是什么?

编辑因此,根据下面的一些非常有用的建议,这是我的新代码!

using (TransactionScope scope = new TransactionScope())
{
    MyContext db = null;

    try
    {
        db = new MyContext();

        db.Database.Connection.Open();

        db.Configuration.AutoDetectChangesEnabled = false;
        db.Configuration.ValidateOnSaveEnabled = false;

        int count = 0;

        ApiContext context = GeteBayApiContext();

        GetCategoriesCall apiCall = new GetCategoriesCall(context)
        {
            EnableCompression = true,
            ViewAllNodes = true
        };

        apiCall.DetailLevelList.Add(DetailLevelCodeType.ReturnAll);

        apiCall.GetCategories();

        foreach (CategoryType category in apiCall.CategoryList)
        {
            EBAY_CATEGORY itemToAdd = new EBAY_CATEGORY
            {
                CATEGORY_ID = int.Parse(category.CategoryID),
                CATEGORY_LEVEL = category.CategoryLevel,
                NAME = category.CategoryName,
                LEAF = category.LeafCategory,
                EXPIRED = category.Expired,
                VIRTUAL = category.Virtual
            };

            int? parentId = Int32.Parse(category.CategoryParentID[0]);

            if (parentId != null)
            {
                itemToAdd.PARENTID = parentId;
            }
            count++;
            db = AddToContext(db, itemToAdd, count, 2000, true);
        }

        db.SaveChanges();
    }
    finally 
    {
        if (db != null)
        {
            db.Dispose();
        }
    }

    scope.Complete();
}

AddToContext方法:

private static MyContext AddToContext(MyContext context,                                              EBAY_CATEGORY itemToAdd,int count,int commitCount,

bool recreateContext)
{
    context.Set<EBAY_CATEGORY>().Add(itemToAdd);

    if (count%commitCount == 0)
    {
        context.SaveChanges();

        if (recreateContext)
        {
            context.Dispose();
            context = new MyContext();
            context.Configuration.AutoDetectChangesEnabled = false;
            context.Configuration.ValidateOnSaveEnabled = false;
        }
    }

    return context;
}

它运行良好且速度更快,但每次插入大量数据后调用Savechanges方法时,都会出现以下错误:

The transaction associated with the current connection has completed but has not been disposed.  The transaction must be disposed before the connection can be used to execute SQL statements.

这发生在大量2000个数据条目之后,或者有时在几次100个数据条目之后。我不明白什么是错的。

2 个答案:

答案 0 :(得分:2)

不要在循环中使用db.SaveChanges()并使用数据库事务。

如果您有太多未提交的更改,则可能会收到OutOfMemory异常,如此处所示Fastest Way of Inserting in Entity Framework

所以你必须使用modulo来db.SaveChanges()每个x条目。取决于您的数据有多大,无论您是否需要它。

答案 1 :(得分:0)

您可以批量执行数据库更新以提高速度。我不确定你在这里使用哪个数据库,但大多数允许相当大的批次。