Выполнение длинной операции в обработчике события - PullRequest
0 голосов
/ 25 апреля 2019

Мне нужно выполнить некоторую проверку адресов на адресах клиентов, используя сторонний 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;
        }
    }
}

ОБНОВЛЕНИЕ - Новый подход

Как и предполагалось, этот код теперь вызывает LongOperation внутри метода Persist.

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 голосов
/ 25 апреля 2019

PXLongOperation предназначен для использования в контексте обратного вызова PXAction.Обычно это инициируется элементом меню или кнопкой управления, включая встроенные действия, такие как Сохранить.

Запрещается использовать его в любое время при изменении значения на веб-странице.Его следует использовать только в том случае, если значение сохраняется (с помощью действия Сохранить) или другим обработчиком событий PXAction.Вы должны обрабатывать долгосрочную проверку, когда пользователь нажимает кнопку или элемент меню, а не когда он изменяет значение.

Например, встроенная функция проверки адреса запускается только тогда, когда пользователь нажимает кнопку проверкиКнопка адреса, и если требуются проверенные запросы, она также запускается в событии Persist, вызываемом в контексте действия Сохранить, чтобы отменить сохранение в случае сбоя проверки.

Это сделано для того, чтобы пользователь ожидал простого измененияПоле значения формы / сетки не требует длительного времени ожидания проверки, из-за которого пользователь может поверить, что веб-страница не отвечает.Когда пользователь нажимает кнопку «Сохранить» или определенную кнопку «Действие», считается более разумным ожидать более продолжительного времени ожидания.

При этом не рекомендуется, но возможно заключать вызов PXLongOperation в фиктивное действие и асинхроннонажмите невидимую кнопку «Действие», чтобы запустить длинную операцию в правильном контексте из любого обработчика событий (кроме Initialize):

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();
        }
      }
  }
}
...