回调函数以错误的顺序完成

时间:2014-12-06 08:28:13

标签: c# facebook entity-framework callback facebook-canvas

这是一个非常令人困惑的问题,所以我会尽力详细说明。我有一个Facebook画布应用程序需要付款,当用户点击付款按钮时,会发生以下情况:

  1. 我的javascript回调函数被调用并传递了付款ID,因此我将此付款ID和其他信息保存到我的订单数据库。

  2. Facebook拨打我设置的回拨网址,以便在付款完成后通知我。它只给我支付ID,所以我用它在数据库中搜索他们发送的支付ID的行。然后,我使用行中的信息填充我发送给客户的电子邮件,以便他们进行订单确认。

  3. 我的大问题是,由于某种原因,第2步在第1步之前完成,所以当我尝试在数据库中查找付款ID时,它还不存在,所以我无法将电子邮件发送给客户。我该怎么做才能解决这个问题?我在下面的步骤中都有伪代码。

    第1步:

    using (OrderDBContext order = new OrderDBContext())
                {
                    string message = Encryption.SimpleDecryptWithPassword(orderDetails.request_id, GlobalFacebookConfiguration.Configuration.AppId, 0);
                    string[] finalMessage = message.Split('@');
                    int orderID = Convert.ToInt16(finalMessage.ElementAtOrDefault(2));
                    Models.Order row = order.Orders.Where(i => i.ID == orderID).FirstOrDefault();
    
                    switch (orderDetails.status)
                    {
                        case "completed":
                            row.PaymentID = orderDetails.payment_id;
                            row.Currency = orderDetails.currency;
                            row.HashKey = orderDetails.request_id;
                            row.Paid = true;
    
                            order.SaveChanges();
                            return Json(new { message = "Your payment was processed! You will receive a confirmation email soon." }, JsonRequestBehavior.AllowGet);
                        case "initiated":
                            row.PaymentID = orderDetails.payment_id;
                            row.Currency = orderDetails.currency;
                            row.HashKey = orderDetails.request_id;
                            row.Paid = false;
    
                            order.SaveChanges();
                            return Json(new { message = "Your payment is being processed! You will receive a confirmation email as soon as the payment is confirmed." }, JsonRequestBehavior.AllowGet);
                    }
                }
    

    第2步:

    dynamic result = new StreamReader(request.InputStream).ReadToEnd();
                    var items = JsonConvert.DeserializeObject<RootObject>(result);
                    string paymentID;
    
                    if (items.entry != null && items.entry.Count > 0)
                    {
                        paymentID = items.entry[0].id;
                    }
                    else
                    {
                        // logic when items.entry is null or doesn't have any elements
                        paymentID = null;
                    }
    
                    if (PaymentHelper.confirmPayment(paymentID, GlobalFacebookConfiguration.Configuration.AppId, GlobalFacebookConfiguration.Configuration.AppSecret))
                    {
                        // if payment is confirmed then send email to us with the order details
                        // then send confirmation email to user letting them know that we are working on it
                        using (OrderDBContext order = new OrderDBContext())
                        {
                            Order row = order.Orders.Where(i => i.PaymentID == paymentID).FirstOrDefault();
                            SendEmail.sendOrderDetailsToWriter(row);
                            SendEmail.sendOrderDetailsToCustomer(row);
                        }
                    }
    

2 个答案:

答案 0 :(得分:1)

伪代码中的

Semaphore = 0;

function Step1() {
    //do your stuff
    Semaphore++;
}

function Step2() {
    //do your stuff
    Semaphore++;
}

function Step3() {
    while (1) { // for example a setTimer in javascript
        if ( Semaphore >= 2 ) {
            // send email
            break;
        } 
        if ( timeout() ) {
            // send error message
            break;
        }
        sleep(200);
    }
}

Step1.Start();
Step2.Start();
Step3.Start();

你当然可以找到一个允许同步两个任务的javascript框架。这是一个非常简单和天真的同步。

答案 1 :(得分:1)

是什么引发了Facebook的运营?步骤#1中的某些事情会导致步骤#2开始吗?或者两个步骤是一起异步启动的吗?

假设后者(因为那是更困难的情况),你应该这样做:

readonly object o = new object();
bool databaseUpdated;

// user clicked the payment button
void onClick()
{
    databaseUpdated = false;

    StartStep1();
    StartStep2();
}

// completion routines for steps #1 and #2
void stepOneDone()
{
    lock (o)
    {
        // do the database update here...i.e. the code you posted for step #1

       databaseUpdated = true;
       Monitor.Pulse(o);
    }
}

void stepTwoDone()
{
    lock (o)
    {
        while (!databaseUpdated)
        {
            Monitor.Wait(o);
        }

        // Process Facebook response here...i.e. the code you posted for step #2
    }
}

以上使用共享锁来使两个操作相互同步。 databaseUpdated标志当然表示数据库更新是否已完成。如果在数据库更新甚至设法启动之前启动了步骤#2完成(即步骤#2在步骤#1之前获得锁定),它将检查该标志,注意它尚未设置,并且将等待。对Monitor.Wait()的调用会释放锁定,以便步骤#1可以接受锁定。然后步骤#1执行它需要做的事情,设置标志,并向步骤#2线程发出信号,表明它可以继续。

当然,如果步骤#1首先获得锁定,那么步骤#2甚至无法获得锁定。当锁再次可用并且步骤#2可以处理超过lock语句时,将设置标志并且它可以以其快乐的方式进行。 :)

有可能使用新的async / await惯用法来解决问题,但没有更多的背景我无法说出来。以上应该可以肯定。

最后,还有一个小问题:为什么要将步骤#2 result变量声明为dynamic?除ReadToEnd()之外,string方法永远不会返回任何内容。这里使用dynamic充其量是没有意义的,并且由于需要动态绑定,最坏的情况下可能会产生额外开销(取决于C#编译器是否注意到它是无意义的......我不记得了我头脑中的编译规则是什么。)