处理foreach循环的执行时间过长

Excessive execution time processing foreach loop

我正在编写一些代码(不是我自己的代码)来更新 UCommerce 中的项目。这个特定的代码块更新了产品的 UDF。我遇到执行速度问题。通过 foreach 循环的每次迭代都比前一次慢。当执行开始时,它每秒循环 5-6 次,但是当它处理了 500 次迭代时,它已经减慢到每 5-6 秒 1 次,现在是 910 项并且每 10-11 秒减慢到 1 次。就代码而言,它在提交事务和刷新会话之前处理 200 (_batchsize) 个项目。

内存、CPU 和磁盘 IO 看起来都很好。没有过度使用 CPU,有足够的可用内存并且没有磁盘瓶颈。当 运行ning 时,应用内存使用量稳定在 350Mb 左右。

无论是 运行 来自 IDE 还是编译的 exe,我都看到了同样的问题。

我试过减小批大小,我也试过为每个项目提交事务和刷新会话,但这几乎没有什么区别。我只是想知道是否有人可以建议我可以尝试什么,甚至可以确定可能的问题区域。

public class ccProductUDFs
{
    public string ProductName { get; set; }
    public string FieldName { get; set; }
    public string DataType { get; set; }
    public string DisplayName { get; set; }
    public string SKU { get; set; }
    public bool Facet { get; set; }
    public bool Searchable { get; set; }
    public bool RenderInEditor { get; set; }
    public string DefinitionName { get; set; }
    public string DefinitionDescription { get; set; }
    public string UdfValue { get; set; }
    public bool UdfValueHasChanged { get; set; }
    public bool UdfFieldHasChanged { get; set; }
    public bool UdfFieldDeleted { get; set; }
    public bool DisplayOnSite { get; set; }
    public string CultureCode { get; set; }

    public override string ToString()
    {
        return string.Format("{0} {1} : {2} = {3}", this.SKU, this.ProductName, this.DisplayName, this.UdfValue);
    }
}

//This will load approx. 173000 item for a SQL database
 List<ccProductUDFs> listudfs = ccProductUDFsData.Load(DBConfiguration.GetDBContext());
//_batchsize is set to 200

//processUdFs  is a Boolean = true stored in AppSettings

    #region Update any UDFs...

    _startrange = 0;
    started = DateTime.Now;
    timeTaken = TimeSpan.MinValue;
    timeLeft = TimeSpan.MinValue;
    doneCount = 0;
    _totalCount = listudfs.Count();
    while (_processUdFs && _startrange < listudfs.Count())
    {
        int sz = listudfs.Count() - _startrange >= _batchsize ? _batchsize : listudfs.Count() - _startrange;
        List<ccProductUDFs> shallowlist = listudfs.GetRange(_startrange, sz);
        _startrange = _startrange + sz;

            _session = SessionContext.Session;
            // create a response list to hold results of uploads...
            List<ccResponse> response_categoryUDFs = new List<ccResponse>();

            // start the transaction
            using (var tx = _session.BeginTransaction())
            {
                _dbcontext.BeginTransaction();
                int counter = 0;
                int listcount = listudfs.Count();

                // loop through each remaining UDF
                // these are UDFs where the FIELD or the VALUE has changed, and we have not dealt with it as part of the product
                foreach (ccProductUDFs udf in shallowlist)
                {
                    if (ShowLog(verbose, (_startrange - sz) + ++counter, listcount))
                    {
                        TimeSpan elapsed = (DateTime.Now - started);
                        WriteLogV2("product udfs {0} of {1}", (_startrange - sz) + counter, listcount, elapsed);


                    }
                    // get the product for this UDF...
                    var product = _session.Query<Product>().FirstOrDefault(a => a.Sku == udf.SKU);


                    if (product != null)
                    {
                        // check that product has a product definition...
                        if (product.ProductDefinition == null)
                        {
                            // product has no definition assigned, so check that the product definition exists in data...
                            ProductDefinition definition = _session.Query<ProductDefinition>().FirstOrDefault(a => a.Description == udf.DefinitionName);
                            if (definition == null)
                            {
                                // product definition doesn;t exist in data, so create it...
                                definition = new ProductDefinition();
                                definition.Description = udf.DefinitionDescription;
                                definition.Name = udf.DefinitionName;

                                // save the changes...
                                _session.SaveOrUpdate((ProductDefinition)definition);


                            }

                            // assign this product definition to the product record...
                            product.ProductDefinition = definition;
                        }

                        // determine if the UDF FIELD exists...
                        ProductDefinitionField definitionfield = product.ProductDefinition.ProductDefinitionFields.FirstOrDefault(a => a.Name == udf.FieldName);
                        if (definitionfield == null)
                        {
                            // the UDF FIELD does NOT exist, so we shall add it.
                            definitionfield = new ProductDefinitionField();
                            definitionfield.Name = udf.FieldName;
                            definitionfield.ProductDefinition = product.ProductDefinition;

                            // locate the data type record and assign it to this UDF FIELD
                            var dt = _session.Query<DataType>().FirstOrDefault(a => a.TypeName == udf.DataType);
                            if (dt != null)
                            {
                                definitionfield.DataType = dt;
                            }

                            // add the UDF FIELD to the product category...
                            product.ProductDefinition.ProductDefinitionFields.Add(definitionfield);

                            // save the changes...
                            _session.SaveOrUpdate((Product)product);

                        }

                        bool changed = definitionfield.Deleted != udf.UdfFieldDeleted;

                        // assign properties to this UDF FIELD...
                        definitionfield.Deleted = udf.UdfFieldDeleted;

                        if (changed)
                        {
                            // save the changes...
                            _session.SaveOrUpdate((ProductDefinitionField)definitionfield);
                        }

                        // determine if the UDF VALUE record exists...
                        ProductProperty property = product.ProductProperties.FirstOrDefault(a => a.ProductDefinitionField.Name == definitionfield.Name && a.Product.Id == product.ProductId);
                        if (property == null)
                        {
                            // the UDF VALUE does NOT exist, so we shall add it.
                            property = new ProductProperty();
                            property.ProductDefinitionField = definitionfield;
                            property.Product = product;

                            // add the UDF VALUE to the product category...
                            product.ProductProperties.Add(property);
                        }

                        changed = false;

                        string v = udf.UdfValue == null ? string.Empty : udf.UdfValue.Trim();

                        changed = property.Value != v;

                        // assign properties to this UDF FIELD...
                        property.Value = v;

                        if (changed)
                        {
                            // save the changes...
                            _session.SaveOrUpdate((ProductProperty)property);

                            // save the changes...
                            _session.SaveOrUpdate((Product)product);
                        }

                        // update the response with a successful result...
                        response_categoryUDFs.Add(new ccResponse(udf.SKU, udf.FieldName, ccCategoryUDFsData.Source, true, "", 0));
                    }
                    object[] prodparam =
                                {
                                    _dbcontext.NewParameter("@Sku", udf.SKU),
                                    _dbcontext.NewParameter("@udfName", udf.FieldName)
                                };
                    _dbcontext.ExecuteNonQuery(CommandType.Text,
                        "UPDATE [LOAD_ProductUdfValues] SET HasChanged = 0 WHERE ProductId = @Sku And FieldName = @udfName",
                        prodparam);

                    TimeSpan ts = DateTime.Now - started;
                    doneCount++;
                    Console.WriteLine("Done {0} of {1} in {2}", doneCount, _totalCount, ts.ToString(@"hh\:mm\:ss"));
                }

                try
                {
                    // commit all changes...
                    tx.Commit();
                    _dbcontext.CommitTransaction();
                }
                catch (Exception ex)
                {
                    // Error Handler
                    tx.Rollback();
                    _dbcontext.RollbackTransaction();
                    response_categoryUDFs.Clear();
                    response_categoryUDFs.Add(new ccResponse("", "", ccCatalogData.Source, false, "Commit failed [" + ex.ToString() + "]", 1));
                }
            }

            // send any response...
            ccResponse.Write(_dbcontext, response_categoryUDFs, ccCatalogData.ResponseTarget);

            // tidy up the session before is it disposed...
            _session.Flush();
       // }
    }
    #endregion

如果有人在搜索中找到这个,我已经通过在 _session.Flush();

之后添加 _session.Clear(); 来解决我的问题