У меня есть пара ЦАПов с родителями и детьми. На основе значений, установленных в родительском DA C, PXCustomSelector в поле дочернего DA C должен предоставить различные наборы параметров, указывающие c для родительского элемента. Эти параметры для селектора исходят из внешних источников данных, и при первом получении они должны быть кэшированы в Acumatica для повышения производительности.
Таким образом, пользователь выбирает или создает родительские данные. Затем в дочерней записи отображаются пользовательские параметры селектора, которые извлекаются из внешнего источника данных (на основе родительских данных), кэшируются и представляются в виде параметров. Добавление дополнительных дочерних записей позволит повторно использовать уже кэшированные параметры. Добавление или переход к другой родительской записи и вызов параметров пользовательского селектора должны представлять различные параметры, указывающие c для этих родительских данных. Однако функция GetRecords () пользовательского селектора не запускается повторно, поэтому опции не обновляются.
Есть ли способ решить эту проблему, поэтому я могу избежать повторной генерации опций каждый раз, когда селектор открывать, но перестраивать их при первом открытии селектора для данной родительской записи?
Ниже приведена упрощенная версия моей реализации, демонстрирующая это поведение (протестировано в 2018R2 и более поздних версиях)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using PX.Data;
namespace CustomSelectorTest
{
// Parent DAC
[Serializable]
public class TestParent : IBqlTable
{
[PXDBString(1, IsKey = true)]
[PXUIField(DisplayName = "ID")]
public virtual string ParentID { get; set; }
public abstract class parentID : IBqlField { }
[PXDBString(50)]
[PXUIField(DisplayName = "Description")]
public virtual string Descr { get; set; }
public abstract class descr : IBqlField { }
}
// Child DAC
[Serializable]
public class TestChild : IBqlTable
{
[PXDBIdentity(IsKey = true)]
[PXParent(typeof(Select<TestParent, Where<TestParent.parentID, Equal<Current<TestChild.parentID>>>>))]
public virtual int? ChildID { get; set; }
public abstract class childID : IBqlField { }
[PXDBString]
[PXDBDefault(typeof(TestParent.parentID))]
public virtual string ParentID { get; set; }
public abstract class parentID : IBqlField { }
[PXDBInt]
[PXUIField(DisplayName = "Selector Value")]
[MyCustomSelector]
public virtual int? Value { get; set; }
public abstract class value : IBqlField { }
[PXDBString(50)]
[PXUIField(DisplayName = "Child Record Data")]
public virtual string Data { get; set; }
public abstract class data : IBqlField { }
}
// DAC wrapper for Options yielded by the Custom Selector
[Serializable]
public class CustomSelectorOption : IBqlTable
{
[PXString(1, IsKey = true)]
[PXUIField(DisplayName = "Parent ID")]
public virtual string ParentID { get; set; }
public abstract class parentID : IBqlField { }
[PXInt(IsKey = true)]
[PXUIField(DisplayName = "Option ID")]
public virtual int? OptionID { get; set; }
public abstract class optionID : IBqlField { }
[PXString(50)]
public virtual string Descr { get; set; }
public abstract class descr : IBqlField { }
}
// CustomSelector attribute to present dynamically created options
public class MyCustomSelectorAttribute : PXCustomSelectorAttribute
{
public MyCustomSelectorAttribute() : base(
typeof(Search<CustomSelectorOption.optionID, Where<CustomSelectorOption.parentID, Equal<Current<TestParent.parentID>>>>),
typeof(CustomSelectorOption.parentID),
typeof(CustomSelectorOption.optionID),
typeof(CustomSelectorOption.descr)
)
{ }
public virtual IEnumerable GetRecords()
{
// Get reference to the Parent DAC
TestParent CurrentParent = (TestParent)this._Graph.Caches<TestParent>().Current;
// Get the already cached Options for the Current Parent...
PXCache OptionsCache = this._Graph.Caches<CustomSelectorOption>();
IEnumerable<CustomSelectorOption> CachedOptions =
OptionsCache.Cached.Cast<CustomSelectorOption>()
.Where(option => option.ParentID == CurrentParent.ParentID);
// If the records for the current Parent have not already been retrieved & cached...
if (CachedOptions.Count() == 0)
{
// Retrieve the external data...
Dictionary<int, string> ExternalOptions = null;
switch (CurrentParent.ParentID)
{
case "A": ExternalOptions = new Dictionary<int, string>() { { 0, "Apple" }, { 1, "Apricot" }, { 2, "Avocado" } }; break;
case "B": ExternalOptions = new Dictionary<int, string>() { { 0, "Banana" }, { 1, "Boysenberry" }, { 5, "Blueberry" } }; break;
case "C": ExternalOptions = new Dictionary<int, string>() { { 10, "Cantaloupe" }, { 20, "Cherry" }, { 30, "Coconut" }, { 40, "Clementine" } }; break;
case "D": ExternalOptions = new Dictionary<int, string>() { { 1, "Date" }, { 3, "Durian" } }; break;
default: throw new PXException("Unable to retrieve external data -- Use Parent ID of A, B, C, or D");
}
PXTrace.WriteInformation(string.Format("Simulated data options retrieved from external source \"{0}\"", CurrentParent.ParentID));
System.Threading.Thread.Sleep(2000);
// Wrap the retrieved data in a SelectorOption DAC and store it in the cache
foreach (KeyValuePair<int, string> option in ExternalOptions)
{
var SelectorOption = new CustomSelectorOption()
{
ParentID = CurrentParent.ParentID,
OptionID = option.Key,
Descr = option.Value
};
// Add it to the Cache
OptionsCache.Insert(SelectorOption);
// Add it to the IEnumerable
CachedOptions.Append(SelectorOption);
}
}
// Yield the options for the selector
foreach(var option in CachedOptions) { yield return option; }
}
}
public class TestGraph : PXGraph<TestGraph, TestParent>
{
public PXSelect<TestParent> Parent;
public PXSelect<TestChild, Where<TestChild.parentID, Equal<Current<TestParent.parentID>>>> Children;
// This event handler clears the CustomSelectorOption cache when changing Parent but no effect on actual options
public virtual void TestParent_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (e.Row == null) return;
var Parent = (TestParent)e.Row;
var cache = this.Caches<CustomSelectorOption>();
var CachedOptions = cache.Cached.Cast<CustomSelectorOption>().Where(option => option.ParentID == Parent.ParentID);
if (CachedOptions.Count() == 0) { cache.Clear(); }
}
}
}
ASPX:
<%@ Page Language="C#" MasterPageFile="~/MasterPages/FormDetail.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="AA101000.aspx.cs" Inherits="Page_AA101000" Title="Untitled Page" %>
<%@ MasterType VirtualPath="~/MasterPages/FormDetail.master" %>
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server">
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" TypeName="CustomSelectorTest.TestGraph" PrimaryView="Parent" />
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" Runat="Server">
<px:PXFormView ID="form" runat="server" DataSourceID="ds" Style="z-index: 100"
Width="100%" DataMember="Parent" TabIndex="1100">
<Template>
<px:PXLayoutRule runat="server" StartRow="True"/>
<px:PXMaskEdit ID="edParentID" runat="server" AlreadyLocalized="False" DataField="ParentID" DefaultLocale="" CommitChanges="true" />
<px:PXTextEdit ID="edDescr" runat="server" AlreadyLocalized="False" DataField="Descr" DefaultLocale="" />
</Template>
</px:PXFormView>
</asp:Content>
<asp:Content ID="cont3" ContentPlaceHolderID="phG" Runat="Server">
<px:PXGrid ID="grid" runat="server" DataSourceID="ds" Style="z-index: 100"
Width="100%" Height="150px" SkinID="Details" TabIndex="1300">
<Levels>
<px:PXGridLevel DataKeyNames="TestChildID" DataMember="Children">
<RowTemplate>
<px:PXSelector ID="edValue" runat="server" DataField="Value" />
<px:PXTextEdit ID="edDescr" runat="server" AlreadyLocalized="False" DataField="Descr" DefaultLocale="" />
</RowTemplate>
<Columns>
<px:PXGridColumn DataField="Value" TextAlign="Right" />
<px:PXGridColumn DataField="Data" Width="180px" />
</Columns>
</px:PXGridLevel>
</Levels>
<AutoSize Container="Window" Enabled="True" MinHeight="150" />
</px:PXGrid>
</asp:Content>
Таблицы:
CREATE TABLE [dbo].[TestParent](
[CompanyID] [int] NOT NULL,
[ParentID] [char](1) NOT NULL,
[Descr] [nvarchar](50) NULL,
CONSTRAINT [PK_TestParent] PRIMARY KEY CLUSTERED ([CompanyID] ASC, [ParentID] ASC));
CREATE TABLE [dbo].[TestChild](
[CompanyID] [int] NOT NULL,
[ChildID] [int] IDENTITY(1,1) NOT NULL,
[ParentID] [char](1) NULL,
[Value] [int] NULL,
[Data] [nvarchar](50) NULL,
CONSTRAINT [PK_TestChild] PRIMARY KEY CLUSTERED ([CompanyID] ASC, [ChildID] ASC));