并发Acumatica API上下文导致GetSchema()

时间:2015-10-26 20:34:11

标签: acumatica

我们有一个Web服务接受来自外部网站的请求,然后使用Acumatica API根据请求添加或更新客户记录。当我们一次收到一个请求时,这工作正常。问题是外部站点批量处理他们的请求,然后同时发送多个请求。这意味着我们最终会同时运行两个或多个请求,这意味着一次发生多个登录和多个上下文。这最终几乎总是产生一个模糊的"对象引用未设置为对象的实例"在许多GetSchema()调用之一。我还看到了一些锁定违规错误,例如:"错误#147:另一个进程添加了' CSAnswers'记录。您的更改将会丢失。"

我在下面创建了一个测试用例,可以通过向发出所有API调用的同一个网页发送3个异步Web请求来复制此事件。另一个问题是,在我没有运行一段时间之后运行它时似乎总是会产生错误。如果我再次立即运行它,那么它通常会成功。这让我觉得可能会在后续调用中缓存某些内容,因此运行速度更快,然后不会自行运行???我不知道,我尝试添加一些延迟,看看是否会在后续运行中更频繁地发生,但它没有。

有人知道Acumatica API是否肯定不支持异步/同步上下文?我只看到了this post on automated scheduling where Gabriel mentioned some thread safety concerns,但不确定这是一回事。

代码是VB中的两个ASP.Net页面。 Default.aspx只是用于创建对CreateReservation.aspx的单个和同时调用的一些按钮。

我们正在使用Acumatica版本4.20.2063和IIS 8.5,我认为管道是.net 4.0集成的。谢谢!

Default.aspx.vb:

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="AcumaticaTesting._Default" Async="true" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
    <asp:Label runat="server" ID="lblMessage" ForeColor="Red"></asp:Label>
    <br />
<asp:Button runat="server" ID="btnStartOne" Text="Run One" OnClick="btnStartOne_Click" />
    <br />
<asp:Button runat="server" ID="btnStartAsynch" Text="Run Three (Asynchronous)" OnClick="btnStartAsynch_Click" />
</div>
</form>
</body>
</html>

Default.aspx.vb(相关方法)

Protected m_webRequest1 As WebClient
Protected m_webRequest2 As WebClient
Protected m_webRequest3 As WebClient
Protected m_webAddress As String = "http://localhost:61343/CreateReservation.aspx"

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

End Sub

Protected Sub btnStartOne_Click(sender As Object, e As EventArgs)
    Dim uri As Uri = New Uri(m_webAddress)

    m_webRequest1 = New WebClient()
    AddHandler m_webRequest1.OpenReadCompleted, AddressOf OpenReadCallback
    m_webRequest1.OpenReadAsync(uri)
End Sub

Protected Sub btnStartAsynch_Click(sender As Object, e As EventArgs)
    Dim uri As Uri = New Uri(m_webAddress)

    m_webRequest1 = New WebClient()
    AddHandler m_webRequest1.OpenReadCompleted, AddressOf OpenReadCallback
    m_webRequest1.OpenReadAsync(Uri)
    Threading.Thread.Sleep(CInt(Int(50))) ' milliseconds

    m_webRequest2 = New WebClient()
    AddHandler m_webRequest2.OpenReadCompleted, AddressOf OpenReadCallback
    m_webRequest2.OpenReadAsync(uri)
    Threading.Thread.Sleep(CInt(Int(50))) ' milliseconds

    m_webRequest3 = New WebClient()
    AddHandler m_webRequest3.OpenReadCompleted, AddressOf OpenReadCallback
    m_webRequest3.OpenReadAsync(uri)
End Sub

' THIS IS JUST A CALLBACK FOR THE ASYNCHRONOUS CALLS, ALL IT DOES IS SET A STATUS MESSAGE
Protected Sub OpenReadCallback(sender As Object, e As OpenReadCompletedEventArgs)
    Dim reply As Stream = Nothing
    Dim s As StreamReader = Nothing

    Try

        reply = CType(e.Result, Stream)
        s = New StreamReader(reply)
        Console.WriteLine(s.ReadToEnd())
    Finally

        If Not s Is Nothing Then

            s.Close()
        End If

        If Not reply Is Nothing Then

            reply.Close()
        End If
    End Try
    lblMessage.Text = "Received result"
End Sub

CreateReservation.aspx(相关方法)

' HELPER METHOD
Protected Function CreateValue(screenField As AcumaticaAPI.Field, newVal As String) As Value
    Return CreateValue(screenField, newVal, False)
End Function

' HELPER METHOD
Protected Function CreateValue(screenField As AcumaticaAPI.Field, newVal As String, addCommit As Boolean) As Value
    Dim theValue As Value = New Value()
    theValue.LinkedCommand = screenField
    theValue.Value = newVal

    If addCommit Then
        theValue.Commit = True
    End If

    Return theValue
End Function

' PAGE_LOAD MAKES ALL THE ACTUAL API CALLS
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim delayAmt As Integer = 4000

    ' Initialize the random-number generator.
    Randomize()
    Dim nameEnd As Integer = Now.Millisecond
    Dim fullName As String = "Doe, John" + nameEnd.ToString()

    ' STEP 1: Login
    Dim context1 As AcumaticaAPI.Screen
    context1 = New AcumaticaAPI.Screen
    context1.CookieContainer = New System.Net.CookieContainer()
    context1.AllowAutoRedirect = True
    context1.EnableDecompression = True
    context1.Timeout = 1000000
    context1.Url = ACUMATICA_URL
    Dim login1 As LoginResult = context1.Login(ACUMATICA_USER, ACUMATICA_PWD)

    ' STEP 2 : See if customer exists
    Dim CR303000 As CR303000Content = context1.CR303000GetSchema()
    context1.CR303000Clear()

    Dim nameFilter As Filter = New Filter()
    nameFilter.Field = CR303000.AccountSummary.BusinessAccountName
    nameFilter.Condition = FilterCondition.Equals
    nameFilter.Value = fullName

    Dim searchfilters() As Filter = {nameFilter}
    Dim searchCommands() As Command = {CR303000.AccountSummary.BusinessAccount, CR303000.DetailsMainContact.Phone1, CR303000.DetailsMainContact.Phone2}
    Dim searchResult As String()() = context1.CR303000Export(searchCommands, searchfilters, 0, False, False)

    ' STEP 3 CREATE CUSTOMER
    Dim AR303000 As AR303000Content = context1.AR303000GetSchema()
    context1.AR303000Clear()

    ' create customer with just name for now
    Dim nameVal As Value = CreateValue(AR303000.CustomerSummary.CustomerName, fullName)

    ' other fields required for Customer
    Dim classVal As Value = CreateValue(AR303000.GeneralInfoFinancialSettings.CustomerClass, "DEFAULT")
    Dim statementCycleVal As Value = CreateValue(AR303000.GeneralInfoFinancialSettings.StatementCycleID, "ENDOFMONTH")
    Dim statementTypeVal As Value = CreateValue(AR303000.BillingSettingsPrintAndEmailSettings.StatementType, "Open Item")
    Dim cashDiscountAccountVal As Value = CreateValue(AR303000.GLAccountsCashDiscountAccount.CashDiscountAccount, "10103")
    Dim creditVerificationVal As Value = CreateValue(AR303000.GeneralInfoCreditVerificationRulesCreditVerification.CreditVerification, "Disabled")

    ' execute insert with just name and required fields
    Dim insertCommands As Command() = {nameVal, classVal, statementCycleVal, statementTypeVal, cashDiscountAccountVal, creditVerificationVal, AR303000.Actions.Save}
    Dim insertResult As AR303000Content() = context1.AR303000Submit(insertCommands)

    ' STEP 4 : Find the newly created Customer record
    Dim CR303000_2 As CR303000Content = context1.CR303000GetSchema()
    context1.CR303000Clear()

    Dim nameFilter_2 As Filter = New Filter()
    nameFilter_2.Field = CR303000_2.AccountSummary.BusinessAccountName
    nameFilter_2.Condition = FilterCondition.Equals
    nameFilter_2.Value = fullName

    Dim searchfilters_2() As Filter = {nameFilter_2}
    Dim searchCommands_2() As Command = {CR303000_2.AccountSummary.BusinessAccount, CR303000_2.DetailsMainContact.Phone1, CR303000_2.DetailsMainContact.Phone2}
    Dim searchResult_2 As String()() = context1.CR303000Export(searchCommands_2, searchfilters_2, 0, False, False)
    Dim newCustomerID As String = searchResult_2(0)(0)

    ' STEP 5 : Add Business Acct fields
    Dim CR303000_3 As CR303000Content = context1.CR303000GetSchema()
    context1.CR303000Clear()

    ' create key field
    Dim baKeyVal As Value = CreateValue(CR303000_3.AccountSummary.BusinessAccount, newCustomerID.ToString())
    Dim baClassIDVal As Value = CreateValue(CR303000_3.DetailsCRM.ClassID, "DEFAULT")

    ' create custom fields to update at same time
    Dim passwordName As Value = CreateValue(CR303000_3.Attributes.Attribute, CUST_ATTRIBUTE_ID_PASSWORD)
    Dim passwordVal As Value = CreateValue(CR303000_3.Attributes.Value, "-------", True)
    Dim secretQuestionName As Value = CreateValue(CR303000_3.Attributes.Attribute, CUST_ATTRIBUTE_ID_SECRET_QUESTION)
    Dim secretQuestionVal As Value = CreateValue(CR303000_3.Attributes.Value, "QQQQQQ", True)
    Dim secretAnswerName As Value = CreateValue(CR303000_3.Attributes.Attribute, CUST_ATTRIBUTE_ID_SECRET_ANSWER)
    Dim secretAnswerVal As Value = CreateValue(CR303000_3.Attributes.Value, "AAAAAAA", True)

    ' execute update
    Dim updateBACommands As Command() = {baKeyVal, baClassIDVal, passwordName, passwordVal, secretQuestionName, secretQuestionVal, secretAnswerName, secretAnswerVal, CR303000_3.Actions.Save}
    Dim updateBAResult As CR303000Content() = context1.CR303000Submit(updateBACommands)
End Sub

我一直得到的完整例外是这个,但它可以在不同的GetSchema()调用中发生:

System.Web.Services.Protocols.SoapException was unhandled by user code
Actor=""
HResult=-2146233087
Lang=""
Message=System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at PX.Api.ScreenUtils.GetScreenInfo(String screenId, Boolean appendDescriptors)
at PX.Api.ScreenUtils.GetScreenInfoWithServiceCommands(Boolean appendDescriptors, String screenID)
at PX.Api.Services.ScreenService.Get(String id, SchemaMode mode)
at PX.Api.Soap.Screen.ScreenGeneric.GetSchema(String screenID)
--- End of inner exception stack trace ---
Node=""
Role=""
Source=System.Web.Services
StackTrace:
   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   at AcumaticaTesting.AcumaticaAPI.Screen.AR303000GetSchema() in C:\Users\Eric\Documents\Visual Studio 2013\Projects\AcumaticaTesting\AcumaticaTesting\Web References\AcumaticaAPI\Reference.vb:line 671
   at AcumaticaTesting.CreateReservation.Page_Load(Object sender, EventArgs e) in C:\Users\Eric\Documents\Visual Studio 2013\Projects\AcumaticaTesting\AcumaticaTesting\CreateReservation.aspx.vb:line 59
   at System.Web.UI.Control.OnLoad(EventArgs e)
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
InnerException: 

1 个答案:

答案 0 :(得分:2)

这是已知的错误,已在5.20.1227版中修复。

原因是系统构建屏幕架构时会发生竞争情况。此模式仅在应用程序池被回收时构建,这就是您在系统空闲一段时间后遇到的原因。手动回收应用程序池应该强制行为发生。