Use of PXProjection: Overriding a Projection of an Existing DAC
You override the PXProjection attribute of an existing DAC when you need a DAC to behave as if it were declared with a different projection query—without modifying the original projection query of the DAC.
At runtime, each graph maintains a PXCache instance for the DAC. The
cache uses an interceptor to determine which BQL command is executed. If you need
different projection behavior in a specific graph, you override this
interceptor.
You implement the override at the graph level by:
- Replacing the interceptor instance stored in the PXCache. For projection DACs, this instance is a PXProjectionAttribute object responsible for supplying the effective BQL command.
- Adjusting the necessary field mappings through CacheAttached handlers.
Implementing the Override of a Projection
To override the PXProjectionAttribute attribute of an existing DAC, you perform the following general steps:
- Find all the graphs that are currently using the projection.
- Declare a graph extension for each graph.Tip: If you plan to use the same overriding logic for all the graphs that are currently using the projection, we recommend that you declare a generic graph extension instead of declaring a graph extension separately for each graph. This helps you avoid code duplication. For details, see the Declaring a Generic Graph Extension section.
- Place the overriding logic inside the Initialize method of
each graph extension. This logic must:
- Preserve existing Interceptor.Child object, to avoid breaking any nested behavior.
- Replace the
Interceptorobject with a new PXProjectionAttribute that defines the new projection query. - Update BqlSelect to use the new interceptor.
- Declare the cacheAttached event handler to adjust the mapping of the fields of the projection DAC.
The following section shows you how you can implement the override of a projection for an existing DAC by using the steps described above.
Overriding the Projection of the MyProjection DAC
Suppose that you have the following DAC called MyProjection with a
PXProjectionAttribute attribute declared on it.
[PXProjection(typeof(Select<...Old Projection Query...>))] public class MyProjection: PXBqlTable { #region DocID public abstract class docID: PX.Data.BQL.BqlInt.Field<docID> { } [PXDBInt(IsKey = true, BqlField = typeof(SomeDAC.docID))] public virtual int? DocID { get; set; } #endregion ... }
Further suppose that you want to override the projection query in the
MyProjection DAC and change the mapping of its single
DocID field. After you successfully implement the override, the
MyProjection DAC should essentially behave as if it was
originally declared, as shown below.
[PXProjection(typeof(Select<...New Projection Query...>))] public class MyProjection: PXBqlTable { #region DocID public abstract class docID: PX.Data.BQL.BqlInt.Field<docID> { } [PXDBInt(IsKey = true, BqlField = typeof(AnotherDAC.uniqueID))] public virtual int? DocID { get; set; } #endregion ... }
Notice that the DocID field's mapping has been changed from
SomeDAC.docID field to AnotherDAC.uniqueID
field.
To implement the required changes, you must first find all the graphs that are using
the MyProjection DAC. Suppose that the
MyProjection DAC is used only in one graph named
MyGraph, as shown below.
public class MyGraph : PXGraph<MyGraph>
{
public PXSelect<MyProjection> MyView;
}
To override the PXProjectionAttribute attribute, you need to
declare a graph extension (MyGraphProjectionOverrideExt in this
example) for the MyGraph graph. You must place the overriding logic
inside the Initialize method. You change the mapping of a
projection DAC field by declaring the cacheAttached event handler
for the field with a new set of attributes. The following code shows the graph
extension for MyGraph that overrides the
PXProjectionAttribute of the MyProjection
DAC and changes the mapping of its DocID field.
public class MyGraphProjectionOverrideExt : PXGraphExtension<MyGraph>
{
// Overriding the projection query by directly modifying the PXCache graph cache
// that corresponds to the MyProjection DAC
public override void Initialize()
{
var cache = Base.Caches<MyProjection>();
var child = cache.Interceptor.Child;
Type newProjectionQueryType = typeof(Select<...New Projection Query...>);
cache.Interceptor = new PXProjectionAttribute(newProjectionQueryType);
cache.BqlSelect = cache.Interceptor.GetTableCommand();
if (child != null)
cache.Interceptor.Child = child;
}
// Overriding the DocID field's mapping.
[PXCustomize.PXDBAttribute(BqlField = typeof(AnotherDAC.uniqueID))]
protected virtual void _(Events.CacheAttached<MyProjection.docID> e) { }
}
After the initialization of the graph extension completes, the
MyProjection DAC behaves in MyGraph as if it
had been declared with the new projection query. The override affects only
MyGraph because the graph extension is only applied to this
graph. If other graphs use the MyProjection DAC, they will still
use the old projection query.
If you want to apply different overriding logic to each of the other graphs that use the same projection DAC, you need to create a separate graph extension for each of them. However, if you want to apply the same overriding logic to each of the other graphs that use the same projection DAC, you can create a generic graph extension. This approach is described in the next section.
public class MyGraphExtensionForAllGraphs : PXGraphExtension<PXGraph> {}You
can then declare your overriding logic in this graph extension—which will be
applied to all graphs in the system at runtime. However, using this approach will
affect the whole system and will consume additional system resources unnecessarily.
In most cases, only a few graphs in the system use a particular projection DAC. If
you use this approach, the rest of the graphs will have a redundant graph extension
that does nothing. This is why we strongly discourage you from declaring graph
extensions that will be applied to all graphs at runtime. You should declare graph
extensions for only graphs that actually use the projection DAC you’re
overriding.Declaring a Generic Graph Extension
If you want to apply the same overriding logic to other graphs that use the same projection DAC, you need to create the same graph extension (as shown in the preceding section) for each of them. However, this requires you to duplicate a lot of code. Instead, we recommend that you declare a generic graph extension that will serve as a base type for your graph extensions, which extend the concrete graphs that use the same projection DAC. You add the overriding logic to this generic graph extension.
Suppose that the MyProjection DAC is used in multiple graphs and you
want to use the same overriding logic for all these graphs. You can declare a
generic graph extension and place the overriding logic in it as follows.
public abstract class ProjectionOverrideExt<TGraph> : PXGraphExtension<TGraph>
where TGraph : PXGraph, new()
{
// Overriding the projection query by directly modifying the PXCache graph cache
// that corresponds to the MyProjection DAC
public override void Initialize()
{
var cache = Base.Caches<MyProjection>();
var child = cache.Interceptor.Child;
Type newProjectionQueryType = typeof(Select<...New Projection Query...>);
cache.Interceptor = new PXProjectionAttribute(newProjectionQueryType);
cache.BqlSelect = cache.Interceptor.GetTableCommand();
if (child != null)
cache.Interceptor.Child = child;
}
// Overriding the DocID field's mapping.
[PXCustomize.PXDBAttribute(BqlField = typeof(AnotherDAC.uniqueID))]
protected virtual void _(Events.CacheAttached<MyProjection.docID> e) { }
}
Notice that the ProjectionOverrideExt graph extension is abstract
and has a generic TGraph parameter. You can easily duplicate the
overriding logic for all the graphs that use the MyProjection DAC
by creating derived graph extensions that substitute TGraph with
the concrete graph type, as shown below.
public class MyGraphProjectionOverrideExt : ProjectionOverrideExt<MyGraph> { }
public class MyAnotherGraphProjectionOverrideExt : ProjectionOverrideExt<MyAnotherGraph> { }
