我需要使用第三方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();
}
答案 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();
}
}
}
}