在事件处理程序中运行长时间操作

时间:2019-04-25 18:58:36

标签: acumatica

我需要使用第三方API对“客户位置”地址进行一些地址验证,以确定该地址是住宅区还是商业区。每当更改地址字段时,都应运行此验证。换句话说,验证应在Address_RowUpdated事件处理程序中运行。

因为该函数正在调用第三方API,所以我认为应该在单独的线程中使用PXLongOperation进行操作,这样它就不会占用地址保存空间,并且在API不可用或返回时会优雅地失败错误。

但是,我不确定是否支持在事件处理程序中运行长操作的体系结构,或者是否可以采用其他方法更好。

这是我的代码。

public class CustomerLocationMaint_Extension : PXGraphExtension<CustomerLocationMaint>
{
    protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
    {
        PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
        if (row != null)
        {
            Location location = this.Base.Location.Current;
            PXCache locationCache = Base.LocationCurrent.Cache;

            PXLongOperation.StartOperation(Base, delegate
            {
                RunCheckResidential(location, locationCache); 
            });

            this.Base.LocationCurrent.Cache.IsDirty = true; 
        }
    }

    protected void RunCheckResidential(Location location, PXCache locationCache)
    {
        string messages = "";

        PX.Objects.CR.Address defAddress = PXSelect<PX.Objects.CR.Address,
            Where<PX.Objects.CR.Address.addressID, Equal<Required<Location.defAddressID>>>>.Select(Base, location.DefAddressID);

        FValidator validator = new FValidator();
        AddressValidationReply reply = validator.Validate(defAddress);
        AddressValidationResult result = reply.AddressResults[0];

        bool isResidential = location.CResedential ?? false; 
        if (result.Classification == FClassificationType.RESIDENTIAL) 
        {
            isResidential = true;
        } else if (result.Classification == FClassificationType.BUSINESS)
        {
            isResidential = false;
        } else
        {
            messages += "Residential classification is: " + result.Classification + "\r\n";
        }
        location.CResedential = isResidential;

        locationCache.Update(location);
        Base.LocationCurrent.Update(location);
        Base.Actions.PressSave();

        // Display relevant messages
        if (reply.HighestSeverity == NotificationSeverityType.SUCCESS)
            String addressCorrection = validator.AddressCompare(result.EffectiveAddress, defAddress);
            if (!string.IsNullOrEmpty(addressCorrection))
                messages += addressCorrection;
        }

        PXSetPropertyException message = new PXSetPropertyException(messages, PXErrorLevel.Warning);
        PXLongOperation.SetCustomInfo(new LocationMessageDisplay(message));
        //throw new PXOperationCompletedException(messages); // Shows message if you hover over the success checkmark, but you have to hover to see it so not ideal

    }

    public class LocationMessageDisplay : IPXCustomInfo
    {
        public void Complete(PXLongRunStatus status, PXGraph graph)
        {
            if (status == PXLongRunStatus.Completed && graph is CustomerLocationMaint)
            {
                ((CustomerLocationMaint)graph).RowSelected.AddHandler<Location>((sender, e) =>
                {
                    Location location = e.Row as Location;
                    if (location != null)
                    {
                        sender.RaiseExceptionHandling<Location.cResedential>(location, location.CResedential, _message);
                    }
                });
            }
        }

        private PXSetPropertyException _message;

        public LocationMessageDisplay(PXSetPropertyException message)
        {
            _message = message;
        }
    }
}

更新-新方法

按照建议,此代码现在在Persist方法中调用LongOperation。

protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
    {
        PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
        if (row != null)
        {
            Location location = Base.Location.Current;
            LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);
            locationExt.UsrResidentialValidated = false;
            Base.LocationCurrent.Cache.IsDirty = true; 
        }
    }   

public delegate void PersistDelegate();
    [PXOverride]
    public virtual void Persist(PersistDelegate baseMethod)
    {
        baseMethod();

        var location = Base.Location.Current;
        PXCache locationCache = Base.LocationCurrent.Cache;
        LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);

        if (locationExt.UsrResidentialValidated == false)
        {
            PXLongOperation.StartOperation(Base, delegate
            {
                CheckResidential(location);
            });
        }
    }

public void CheckResidential(Location location)
    {
        CustomerLocationMaint graph = PXGraph.CreateInstance<CustomerLocationMaint>();

        graph.Clear();
        graph.Location.Current = location;

        LocationExt locationExt = location.GetExtension<LocationExt>();
        locationExt.UsrResidentialValidated = true;

        try
        {
          // Residential code using API (this will change the value of the location.CResedential field)
        } catch (Exception e)
        {
            throw new PXOperationCompletedWithErrorException(e.Message);
        }

        graph.Location.Update(location);
        graph.Persist();
    }

1 个答案:

答案 0 :(得分:0)

PXLongOperation旨在用于PXAction回调的上下文中。通常,这是通过菜单项或按钮控件启动的,包括诸如“保存”之类的内置操作。

这是一种反模式,可在网页中的任何值更改时使用它。仅当持久化值(通过Save操作)或另一个PXAction事件处理程序时才应使用它。当用户单击按钮或菜单项而不是更改值时,您应该进行长期运行的验证。

例如,仅当用户单击“验证地址”按钮时才运行内置的“验证地址”功能,并且如果需要验证的请求,则还会在“保存”操作的上下文中调用的Persist事件中运行该功能,以在以下情况下取消保存:验证失败。

这样做是为了确保用户期望表单/网格值字段中的简单更改不会导致较长的验证等待时间,该等待时间会使用户认为网页没有响应。当用户单击“保存”或特定的“操作”按钮时,期望更长的等待时间被认为是更合理的。

话虽如此,不建议这样做,但可以将您的PXLongOperation调用包装在虚拟Action中,并异步单击不可见的Action按钮,以使长操作在任何事件处理程序(初始化除外)中在适当的上下文中运行:< / p>

using PX.Data;
using System.Collections;

namespace PX.Objects.SO
{
    public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
  {
      public PXAction<SOOrder> TestLongOperation;

      [PXUIField(DisplayName = "Test Long Operation", Visible = false, Visibility = PXUIVisibility.Invisible)]
      [PXButton]
      public virtual IEnumerable testLongOperation(PXAdapter adapter)
      {
        PXLongOperation.StartOperation(Base, delegate () 
        { 
            System.Threading.Thread.Sleep(2000);
            Base.Document.Ask("Operation Done", MessageButtons.OK);
        });

        return adapter.Get();
      }

      public void SOOrder_OrderDesc_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
      {
        if (!PXLongOperation.Exists(Base.UID))
        {
            // Calling Action Button asynchronously so it can run in the context of a PXAction callback
            Base.Actions["TestLongOperation"].PressButton();
        }
      }
  }
}