November Happy Hour will be moved to Thursday December 5th.
November Happy Hour will be moved to Thursday December 5th.
Can you show us some code? mainly the calls in FInalizeOrderService and PaymentController.
Are these methods async?
Hi @Surjit Bharath
Below is the code that calls the SaveAsPurchaseOrder method.
The code is not asyncronous
public class FinalizeOrderService : IFinalizeOrderService
{
private readonly ICartService _cartService;
private readonly IOrderRepository _orderRepository;
private static readonly ILogger _logger = LogManager.GetLogger();
public FinalizeOrderService(
ICartService cartService,
IOrderRepository orderRepository)
{
_cartService = cartService;
_orderRepository = orderRepository;
}
public IPurchaseOrder FinalizeOrder(CustomerContact customer = null)
{
// Make sure to execute within transaction
using (var scope = new Mediachase.Data.Provider.TransactionScope())
{
var cart = _cartService.LoadCart(_cartService.DefaultCartName);
cart.CustomerId = EPiServer.Security.PrincipalInfo.CurrentPrincipal.GetContactId();
var orderReference = _orderRepository.SaveAsPurchaseOrder(cart);
IPurchaseOrder po = _orderRepository.Load<IPurchaseOrder>(orderReference.OrderGroupId);
if (customer != null)
{
customer.LastOrder = po.Created;
customer.SaveChanges();
}
// Remove old cart
_orderRepository.Delete(cart.OrderLink);
// Commit changes
scope.Complete();
return po;
}
}
}
Investigating deadlocks is always a complicated mater. I'd suggest to contact developer support service. You might need to provide the XDL file capturing the deadlock.
Remove the transactionscope wrapper. Use a try/catch instead and move everything after the load purchase order out of the try catch.
@Surjit Bharath @Quan Mai
My apologies the cartcontroller code calling this is async. The line in red is were the call is being made to the FinalizeOrderService
private async Task<ActionResult> OriginalProcessPayment(CardConnectPaymentPage currentPage, PaymentForm paymentForm)
{
string message = string.Empty;
var gRecaptchaResponse = Request.Form["g-recaptcha-response"];
var result = await _captchaValidator.ValidateCaptcha(gRecaptchaResponse, "credit_card_submission");
if (!result.Success) //failure
{
if (User.Identity.IsAuthenticated)
{
_logger.Error($"---- ACCOUNT: {_currentContact.Email} failed Recaptcha Score: {result.RiskAnalysis.Score} ----");
ModelState.AddModelError("paymentForm.RecaptchaResponse", SiteHelper.Translate(() => FormResources.RecaptchaError));
}
else
{
_logger.Error($"---- {HttpContext.GetClientIP()} GUEST: failed Recaptcha Score: {result.RiskAnalysis.Score} ----");
ModelState.AddModelError("paymentForm.RecaptchaResponse", SiteHelper.Translate(() => FormResources.RecaptchaError));
}
}
if (TempData.ContainsKey("pickuptype"))
{
paymentForm.PickUpType = TempData["pickuptype"] as string;
TempData.Keep("pickuptype");
}
else
{
return Redirect(SiteHelper.SiteSettings.CartPage.GetUrl());
}
if (TempData.ContainsKey("AlternateInfo"))
{
var pickupperson = TempData["AlternateInfo"] as AlternatePickupPerson;
TempData.Keep("AlternateInfo");
paymentForm.PickUpPersonFirstName = pickupperson.Name;
paymentForm.PickupPersonLastName = string.Empty;
}
if (TempData.ContainsKey("SMS"))
{
paymentForm.SMSPhoneNumber = TempData["SMS"] as string;
TempData.Keep("SMS");
}
var shipment = Cart.GetFirstForm().Shipments.First().ShippingAddress;
var fullName = $"{shipment?.FirstName ?? ""} {shipment?.LastName ?? ""}";
if (!string.IsNullOrEmpty(paymentForm.ExpDate) && !Regex.IsMatch(paymentForm.ExpDate, Shoup.Common.Constants.ExpDateRegEx))
{
ModelState.AddModelError("paymentForm.InvalidExpDateMessage", SiteHelper.Translate(() => FormResources.InvalidExpDateText));
}
if (ModelState.IsValid)
{
if (CartIsNullOrEmpty())
{
return Redirect(Site.SiteSettings.CartPage.GetUrl());
}
var cartTotal = Cart.GetTotal();
var amount = Cart.GetFirstForm().Payments.FirstOrDefault().Amount;
if (Cart.GetFirstForm().Payments.FirstOrDefault().Amount != cartTotal)
{
return Redirect(Site.SiteSettings.CartPage.GetUrl() + "?newCartItems=true");
}
var date = paymentForm.ExpDate.Split('/');
if (Cart.Forms.Count > 0)
{
var initialPayment = Cart.GetFirstForm().Payments.FirstOrDefault();
if (initialPayment != null)
{
CreatePayment(fullName, paymentForm, Cart);
var paymentProcessingResults = Cart.ProcessPayments();
if (!paymentProcessingResults.Any(r => !r.IsSuccessful))
{
var cardConnectReturnValues = paymentProcessingResults.First().Message.Split('|');
var thanksPage = Site.SiteSettings.ThanksPage.GetUrl();
_ccInfo.Last4 = paymentForm.CreditCardNumber.Substring(paymentForm.CreditCardNumber.Length - 4, 4);
_ccInfo.Expires = Convert.ToInt32((date[0].ToString() + date[1].ToString()));
_ccInfo.Type = GetCardType(paymentForm.CreditCardNumber);
Cart.ClearCCDetails(_orderRepository);
var customerComment = (Cart.Notes.Any()) ? Cart.Notes.First().Detail : string.Empty;
var po = _finalizeOrderService.FinalizeOrder(_currentContact);
var orderID = po.OrderLink.OrderGroupId.ToString();
var shipmethod = po.GetFirstShipment().ShippingMethodName;
if (!string.IsNullOrEmpty(shipmethod))
{
shipmethod = shipmethod.ToLower();
}
var customerEmail = "";
if (TempData.ContainsKey("guestEmail"))
{
customerEmail = TempData["guestEmail"] as string;
}
if (User.Identity.IsAuthenticated)
{
var loggedInUser = _contactRepository.Get();
customerEmail = loggedInUser.Email;
if (shipmethod == "instore" || shipmethod == "curbside")
{
fullName = $"{loggedInUser.FirstName} {loggedInUser.LastName}";
}
}
_queueService.InsertNewOrder(
orderID,
po.GetTotal().Amount,
(int)Common.Enumeration.OrderStatus.InQueue,
fullName,
po.GetCustomerPurchaseOrderNumber(),
0,
cardConnectReturnValues[1],
_ccInfo.Expires.ToString(),
_ccInfo.Last4,
cardConnectReturnValues.First(),
cardConnectReturnValues.Last(),
_ccInfo.Type,
customerComment,
shipmethod,
customerEmail,
po.OrderNumber,
paymentForm.PickUpPersonFirstName,
paymentForm.PickupPersonLastName,
paymentForm.SMSPhoneNumber);
try
{
var contact = new OrderContact
{
CustomerCode = _currentContact?.Code ?? string.Empty,
Email = _currentContact?.Email ?? shipment.Email,
FirstName = _currentContact?.FirstName ?? shipment.FirstName,
LastName = _currentContact?.LastName ?? shipment.LastName
};
if (_currentContact != null)
{
_currentContact.SetValue(ContactFieldNames.HasOrders, true);
_currentContact.SaveChanges();
}
var localizedPage = currentPage as ILocalizable;
var shippingOptions = _checkoutService.GetShippingMethodsWithRates(Cart, localizedPage.Language.ToString()).Where(x => x != null).ToList();
var pickupType = shipmethod?.ToLower();
string unfriendlyName = "";
//use friendly name if available
if (shippingOptions.Any(x => String.Equals(x.ShippingMethodName, shipmethod, StringComparison.CurrentCultureIgnoreCase)))
{
pickupType = shippingOptions
.Find(x => String.Equals(x.ShippingMethodName, shipmethod, StringComparison.CurrentCultureIgnoreCase))
.FriendlyName;
unfriendlyName = shippingOptions
.Find(x => String.Equals(x.ShippingMethodName, shipmethod, StringComparison.CurrentCultureIgnoreCase))
.ShippingMethodName;
}
await Task.Run(() =>
{
_emailService.SendNewOrderNotificationEmailToShoup(contact,
po.OrderNumber,
orderID,
po.GetTotal().Amount,
unfriendlyName,
paymentForm.PickUpPersonFirstName,
paymentForm.PickupPersonLastName,
paymentForm.SMSPhoneNumber,
pickupType
);
SendEmailReceipt(po, unfriendlyName);
}).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Error("CardConnectPaymentController => SendNewOrderNotificationEmail " + ex.ToString());
}
thanksPage = UriUtil.AddQueryString(thanksPage, "success", "true");
thanksPage = UriUtil.AddQueryString(thanksPage, "ordernumber", orderID);
thanksPage = UriUtil.AddQueryString(thanksPage, "customerId", po.CustomerId.ToString());
thanksPage = UriUtil.AddQueryString(thanksPage, "POnumber", po.OrderNumber);
thanksPage = UriUtil.AddQueryString(thanksPage, "CartId", Cart.GetCartId().ToString());
ModelState.Clear();
await CreateHubspotInfo(shipment);
Session["pickupType"] = "";
return Redirect(thanksPage);
}
else
{
Cart.ClearCCDetails(_orderRepository);
message = paymentProcessingResults.First().Message;
}
}
else
{
message = "X:NullPayment";
}
}
}
else
{
message = "ModelError";
}
TempData["PaymentError"] = message;
Session["pickupType"] = "";
return RedirectToAction("Index");
}
Version number EpiServer 11.20.17, EPiServer Commerce 13.33.0.0
Has anyone expereniece a SQL deadlock error with the IOrderRepository.SaveAsPurchaseOrder method? The stack track is below:
System.Data.SqlClient.SqlException (0x80131904): Transaction (Process ID 330) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at EPiServer.Data.Providers.SqlTransientErrorsRetryPolicy.Execute[TResult](Func`1 method)
at Mediachase.Data.Provider.SqlDataProvider.ExecuteNonExec(DataCommand command)
at Mediachase.MetaDataPlus.Common.DBHelper.ExecuteNonQuery(String connectionString, CommandType commandType, String commandText, Int32 commandTimeout, DataParameter[] commandParameters)
at Mediachase.MetaDataPlus.Configurator.MetaDataPlusDatabase.StoredProcedure.ExecuteNonQuery(MetaDataContext context)
at Mediachase.MetaDataPlus.Configurator.MetaDataPlusDatabase.UpdateMetaObject(MetaDataContext context, MetaClass metaClass, Int32 metaObjectId, String creatorId, DateTime created, String modifierId, DateTime modified, IEnumerable`1 metaParameters)
at Mediachase.MetaDataPlus.MetaObject.AcceptChanges(MetaDataContext context)
at Mediachase.Commerce.Storage.MetaStorageBase.AcceptChanges(MetaDataContext context, Boolean saveSystem)
at Mediachase.Commerce.Orders.Payment.AcceptChanges()
at Mediachase.Commerce.Storage.MetaStorageCollectionBase`1.AcceptChanges()
at Mediachase.Commerce.Orders.OrderForm.AcceptChanges()
at Mediachase.Commerce.Storage.MetaStorageCollectionBase`1.AcceptChanges()
at Mediachase.Commerce.Orders.OrderGroup.AcceptChanges()
at Mediachase.Commerce.Orders.PurchaseOrder.AcceptChanges()
at EPiServer.Commerce.Order.Internal.PurchaseOrderProvider.Save(IPurchaseOrder purchaseOrder)
at EPiServer.Commerce.Order.Internal.SerializableCartProvider.SaveAsPurchaseOrder(ICart cart)
at EPiServer.Commerce.Order.Internal.DefaultOrderRepository.SaveAsPurchaseOrder(IOrderGroup cart)
at [private namespace].Services.FinalizeOrderService.FinalizeOrder(CustomerContact customer) in [private directory]\Services\Order\FinalizeOrderService.cs:line 32
at [private namespace].Controllers.CardConnectPaymentController.<OriginalProcessPayment>d__23.MoveNext() in [private directory]\Controllers\CardConnectPaymentController.cs:line 589