并发Acumatica API上下文导致GetSchema()中出现错误

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

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

我已经在下面创建了一个测试用例,它可以通过向发出所有API调用的同一个网页触发3个异步web请求来复制这种情况。另一个问题是,当我在一段时间没有运行它之后运行它时,它似乎总是产生错误。如果我立即再次运行它,那么它通常会成功。这让我想到,可能在随后的调用中缓存了一些东西,所以它运行得更快,然后不会自行运行???我不知道,我试着增加一些延迟,看看这是否会使它在后续的运行中更频繁地发生,但没有

有人知道Acumatica API是否绝对不支持异步/同时上下文吗?我只看到了,但不确定那是同一件事

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

我们使用的是Acumatica版本4.20.2063和IIS 8.5,我认为管道集成了.NET4.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>
CreateReservation.aspx(相关方法)

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


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


原因是系统在构建屏幕架构时可能出现的竞争条件。此模式仅在应用程序池被回收时构建,这就是为什么在系统闲置一段时间后,您通常会遇到此模式的原因。手动回收应用程序池应强制执行该行为

您正在运行的版本/内部版本号是什么?能否指定您正在使用的IIS版本和IIS管道模式?@Philippe抱歉,Acumatica版本是4.20版。2063@SergRogovtsevIIS 8.5和我认为它的.NET4.0集成了。太棒了!我将研究是否可以升级到该版本或更高版本。非常感谢。
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
' 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
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: