加速我的物料清单计算

时间:2013-04-15 11:41:47

标签: c# asp.net linq entity-framework

我有一个相当小的MRP应用程序,包括销售订单,物料清单和物料资源计划报告。

销售订单簿只是一个项目,数量和截止日期的列表。 物料清单是每个项目的平面列表,该项目的子组件和数量如下:[ParentItemID] [ComponentItemID] [数量]

然后我还添加了我的采购订单,以便用户可以看到运行余额。

计算它应该是相当微不足道的。

MRP报告在功能上运行良好,但运行大约需要90秒,这对我的用户来说显然是令人沮丧的。我目前正在使用队列来存储数据,我的整个代码如下所示:

    public void MRPCalcBtn_Click(object sender, EventArgs e)
//Calculate MRP Report
{
    //kick off a timer
    DateTime startTime = DateTime.Now;

    //Flip the panels to hide the button
    panel1.Visible = false;
    panel2.Visible = true;
    Page.Title = "MRP Report"; //set the title so we can tell if it's finished from the task bar

    //Create DataTable to hold items
    var dt = new DataTable();

    dt.Columns.Add("ItemID", System.Type.GetType("System.String"));
    dt.Columns.Add("QtyIn", System.Type.GetType("System.Decimal"));
    dt.Columns.Add("QtyOut", System.Type.GetType("System.Decimal"));
    dt.Columns.Add("DueDate", System.Type.GetType("System.DateTime"));

    var db = new CS3Entities(); // should these be calls to DAL?

    //ADD SALES ORDERS
    //---------------------------------------------------------------------------------------------
    //read orderbook in
    var orderBook = (from ob in db.SalesOrderItems
                     where ob.QtyOutstanding > 0
                     //where ob.ItemID == "9783B" //whilst testing, remove in production
                     select new { ob.ItemID, ob.QtyOutstanding, ob.DueDate });

    //create a queue to store temporary work, the line items are Orders
    var q1 = new Queue<OrderItem>();

    foreach (var item in orderBook) //loop through the order book
    {
        //Copy order book to queue to start off with
        var orderBookArray = new OrderItem
                                 {
                                     ItemID = item.ItemID,
                                     QtyIn = 0,
                                     QtyOut = item.QtyOutstanding,
                                     DueDate = item.DueDate
                                 };

        q1.Enqueue(orderBookArray); //adds line item to queue
    }

    //start calculating the BoM items
    try
    {
        do //main working loop
        {
            var testItem = q1.Peek(); //pull out next line item in queue

            var testItemID = testItem.ItemID; //pull out ItemID from line item
            var testQty = testItem.QtyOut; //pull out Qty from line item
            var testDueDate = testItem.DueDate; //pull out ItemID from line item

            //determines Bill OF Materials for each item
            var bills = (
                    from b in db.BillOfMaterials
                    where b.ItemID == testItemID
                    select new { b.ComponentItemID, b.Qty }
                    );

            if (bills.Any()) //if line item has any sub components
            {
                //add each sub comopnent to the queue
                foreach (var bill in bills)
                {
                    var bomArray = new OrderItem
                                       {
                                           ItemID = bill.ComponentItemID,
                                           QtyOut = bill.Qty*testQty,
                                           QtyIn = 0,
                                           DueDate = testDueDate
                                       };

                    q1.Enqueue(bomArray);
                }

                q1.Dequeue(); //Remove item from queue after all sub components have been added
            }
            else //if no items in Bill Of Materials, must be "leaf" component, add it to "final" table
            {
                AddToItems(dt, testItemID, 0, testQty, testDueDate); //add item to data table as QtyOut
                q1.Dequeue(); //Remove item from queue
            }

        } while (q1.Count != 0); //end of do loop, until queue is empty
    }
    catch
    {
        ErrorLbl.Text = "ERROR"; //if something goes wrong
    }

    //Queue should now be empty and DataTable dt holds all final items for purchase

    //ADD PURCHASE ORDERS
    //---------------------------------------------------------------------------------------------
    //read purchase orders in
    var purchaseOrderItems = (from poi in db.PurchaseOrderItems
                              join po in db.PurchaseOrders on poi.PurchaseOrderID equals po.PurchaseOrderID
                              where po.PurchaseOrderCancelled != true && 
                                    po.PurchaseOrderDeleted != true && 
                                    poi.Balance > 0 && 
                                    poi.LineItemComplete != true

                              orderby poi.DueDate ascending 

                              select new { poi.ItemID, poi.Balance, poi.DueDate }).ToList(); //Is a list faster than the database calls?

    foreach (var item in purchaseOrderItems) //loop through the purchase orders and append them to the temp table
    {
        //add purchase order items to data table as QtyIn
        AddToItems(dt, item.ItemID, Convert.ToDecimal(item.Balance), 0, Convert.ToDateTime(item.DueDate));
    }

    //SORT RESULTS
    //---------------------------------------------------------------------------------------------
    //Sort and group the raw data
    var sorteditems = //sort and group the data table to show sum of qty per day per item
        (from p in dt.AsEnumerable()
        orderby p.Field<string>("ItemID") , p.Field<DateTime>("DueDate")
        group p by new {ItemID = p.Field<string>("ItemID"), DueDate = p.Field<DateTime>("DueDate")}
        into g

        select new
                   {
                       g.Key.ItemID,
                       g.Key.DueDate,
                       QtyIn = g.Sum(p => p.Field<decimal>("QtyIn")),
                       QtyOut = g.Sum(p => p.Field<decimal>("QtyOut"))
                   }).ToList();

    //Create second DataTable to hold sorted and grouped report items
    //tried to use same datatable dt but it didn't work, no hardship to instantiate another
    var dt2 = new DataTable();

    dt2.Columns.Add("ItemID", Type.GetType("System.String"));
    dt2.Columns.Add("QtyIn", Type.GetType("System.Decimal"));
    dt2.Columns.Add("QtyOut", Type.GetType("System.Decimal"));
    dt2.Columns.Add("DueDate", Type.GetType("System.DateTime"));

    foreach (var item in sorteditems) //loop through the sorted results
    {
        //Move the sorted and grouped items into the datatable
        AddToItems(dt2, item.ItemID, Convert.ToDecimal(item.QtyIn), Convert.ToDecimal(item.QtyOut), Convert.ToDateTime(item.DueDate));
    }

    Session["MRPTable"] = dt2; //save datatable to session for recall later by repeater

    //Set repeater datasource to unique item list with details
    var itemslist = from si in sorteditems
                    group si by new { si.ItemID } into sig //equivilent to DISTINCT

                    join i in db.Items
                    on sig.Key.ItemID equals i.ItemID

                    select new
                    {
                        i.ItemID,
                        i.ItemName,
                        i.StocktakeCost,
                        i.ItemROP,
                        i.ItemROQ,
                        i.ItemUOM,
                        i.ItemStock
                    };

    Repeater1.DataSource = itemslist; //note repeater contains a GridView that contains the line items
    Repeater1.DataBind(); //Fire the repeater

    //time finished
    DateTime finishTime = DateTime.Now;

    //Set the start time label
    TimeStart.Text = startTime.TimeOfDay.ToString();

    //Set finish time
    TimeFinish.Text = finishTime.TimeOfDay.ToString();

    TimeTaken.Text = (finishTime - startTime).Minutes.ToString(CultureInfo.InvariantCulture) + " Minute(s), " + (finishTime - startTime).Seconds.ToString(CultureInfo.InvariantCulture) + " Seconds";

    //Add to activity log
    ActivityDAL.AddNewLog("MRP Report", "Generated", TimeTaken.Text, "N/A", HttpContext.Current.User.Identity.Name);

}

任何人都可以看到我可以提高报告的速度以及我在哪里发生任何明显的错误。

正如我所说,报告工作正常,但速度很慢。

任何帮助表示赞赏

2 个答案:

答案 0 :(得分:1)

var bills = (from b in db.BillOfMaterials
             where b.ItemID == testItemID
             select new { b.ComponentItemID, b.Qty });

上面的代码应该在do {} while()循环之外,否则db.BillOfMaterials将循环遍历while循环运行次数

public void MRPCalcBtn_Click(object sender, EventArgs e)
//Calculate MRP Report
{
    //kick off a timer
    DateTime startTime = DateTime.Now;

    //Flip the panels to hide the button
    panel1.Visible = false;
    panel2.Visible = true;
    Page.Title = "MRP Report"; //set the title so we can tell if it's finished from the task bar

    //Create DataTable to hold items
    var dt = new DataTable();

    dt.Columns.Add("ItemID", System.Type.GetType("System.String"));
    dt.Columns.Add("QtyIn", System.Type.GetType("System.Decimal"));
    dt.Columns.Add("QtyOut", System.Type.GetType("System.Decimal"));
    dt.Columns.Add("DueDate", System.Type.GetType("System.DateTime"));

    var db = new CS3Entities(); // should these be calls to DAL?

    //ADD SALES ORDERS
    //---------------------------------------------------------------------------------------------
    //read orderbook in
    var orderBook = (from ob in db.SalesOrderItems
                     where ob.QtyOutstanding > 0
                     //where ob.ItemID == "9783B" //whilst testing, remove in production
                     select new OrderItem
                                 {
                                     ItemID = ob.ItemID,
                                     QtyIn = 0,
                                     QtyOut = ob.QtyOutstanding,
                                     DueDate = ob.DueDate
                                 };);

    //create a queue to store temporary work, the line items are Orders
    var q1 = new Queue<OrderItem>();

    foreach (var item in orderBook) //loop through the order 
        q1.Enqueue(orderBookArray); //adds line item to queue

    //determines Bill OF Materials for each item
    var billDict = db.BillOfMaterials.
                   GroupBy(b => b.ItemID).
                   ToDictionary(
                       g => g.Key, 
                       g => g.Select(b => new { b.ComponentItemID, b.Qty }).ToList()
                   );

    //start calculating the BoM items
    try
    {
        do //main working loop
        {
            var testItem = q1.Peek(); //pull out next line item in queue

            var testItemID = testItem.ItemID; //pull out ItemID from line item
            var testQty = testItem.QtyOut; //pull out Qty from line item
            var testDueDate = testItem.DueDate; //pull out ItemID from line item

            if (billDict.ContainsKey(testItemID)) //if line item has any sub components
            {
                var bills = billDict[testItemID];
                //add each sub comopnent to the queue
                foreach (var bill in bills)
                {
                    var bomArray = new OrderItem
                                       {
                                           ItemID = bill.ComponentItemID,
                                           QtyOut = bill.Qty*testQty,
                                           QtyIn = 0,
                                           DueDate = testDueDate
                                       };

                    q1.Enqueue(bomArray);
                }
            }
            else //if no items in Bill Of Materials, must be "leaf" component, add it to "final" table
            {
                AddToItems(dt, testItemID, 0, testQty, testDueDate); //add item to data table as QtyOut
            }

            q1.Dequeue(); //Remove item from queue

        } while (q1.Count != 0); //end of do loop, until queue is empty
    }
    catch
    {
        ErrorLbl.Text = "ERROR"; //if something goes wrong
    }

    //Queue should now be empty and DataTable dt holds all final items for purchase

    //ADD PURCHASE ORDERS
    //---------------------------------------------------------------------------------------------
    //read purchase orders in
    var purchaseOrderItems = (from poi in db.PurchaseOrderItems
                              join po in db.PurchaseOrders on poi.PurchaseOrderID equals po.PurchaseOrderID
                              where po.PurchaseOrderCancelled != true && 
                                    po.PurchaseOrderDeleted != true && 
                                    poi.Balance > 0 && 
                                    poi.LineItemComplete != true

                              orderby poi.DueDate ascending 

                              select new { poi.ItemID, poi.Balance, poi.DueDate }).ToList(); //Is a list faster than the database calls?

    foreach (var item in purchaseOrderItems) //loop through the purchase orders and append them to the temp table
    {
        //add purchase order items to data table as QtyIn
        AddToItems(dt, item.ItemID, Convert.ToDecimal(item.Balance), 0, Convert.ToDateTime(item.DueDate));
    }

    //SORT RESULTS
    //---------------------------------------------------------------------------------------------
    //Sort and group the raw data
    var sorteditems = //sort and group the data table to show sum of qty per day per item
        (from p in dt.AsEnumerable()
        orderby p.Field<string>("ItemID") , p.Field<DateTime>("DueDate")
        group p by new {ItemID = p.Field<string>("ItemID"), DueDate = p.Field<DateTime>("DueDate")}
        into g

        select new
                   {
                       g.Key.ItemID,
                       g.Key.DueDate,
                       QtyIn = g.Sum(p => p.Field<decimal>("QtyIn")),
                       QtyOut = g.Sum(p => p.Field<decimal>("QtyOut"))
                   }).ToList();

    //Create second DataTable to hold sorted and grouped report items
    //tried to use same datatable dt but it didn't work, no hardship to instantiate another
    var dt2 = new DataTable();

    dt2.Columns.Add("ItemID", Type.GetType("System.String"));
    dt2.Columns.Add("QtyIn", Type.GetType("System.Decimal"));
    dt2.Columns.Add("QtyOut", Type.GetType("System.Decimal"));
    dt2.Columns.Add("DueDate", Type.GetType("System.DateTime"));

    foreach (var item in sorteditems) //loop through the sorted results
    {
        //Move the sorted and grouped items into the datatable
        AddToItems(dt2, item.ItemID, Convert.ToDecimal(item.QtyIn), Convert.ToDecimal(item.QtyOut), Convert.ToDateTime(item.DueDate));
    }

    Session["MRPTable"] = dt2; //save datatable to session for recall later by repeater

    //Set repeater datasource to unique item list with details
    var itemslist = from si in sorteditems
                    group si by new { si.ItemID } into sig //equivilent to DISTINCT

                    join i in db.Items
                    on sig.Key.ItemID equals i.ItemID

                    select new
                    {
                        i.ItemID,
                        i.ItemName,
                        i.StocktakeCost,
                        i.ItemROP,
                        i.ItemROQ,
                        i.ItemUOM,
                        i.ItemStock
                    };

    Repeater1.DataSource = itemslist; //note repeater contains a GridView that contains the line items
    Repeater1.DataBind(); //Fire the repeater

    //time finished
    DateTime finishTime = DateTime.Now;

    //Set the start time label
    TimeStart.Text = startTime.TimeOfDay.ToString();

    //Set finish time
    TimeFinish.Text = finishTime.TimeOfDay.ToString();

    TimeTaken.Text = (finishTime - startTime).Minutes.ToString(CultureInfo.InvariantCulture) + " Minute(s), " + (finishTime - startTime).Seconds.ToString(CultureInfo.InvariantCulture) + " Seconds";

    //Add to activity log
    ActivityDAL.AddNewLog("MRP Report", "Generated", TimeTaken.Text, "N/A", HttpContext.Current.User.Identity.Name);

}

答案 1 :(得分:0)

找到答案,修复错误加入并将报告从90秒减少到5