Caches property of graph in Acumatica - acumatica

I noticed that graph.Caches[typeof(MyDac)] is quite useful in Acumatica to manipulate values in other DACs. What would be the proper way to use it, for example when I update a value in another DAC? Do I have to persist right after it, or will it be saved to DB when I run graph.Action.PressSave(), even if I am not using this DAC in any of the view in the graph?

The PXGraph.Caches collections contains DAC objects loaded in memory on the server. In a graph it is accessed directly as this.Caches and in an extension it is accessed through Base.Caches. While not technically acurate it can be thought of as a global DAC cache. The DAC objects contained in DataView.Cache is a subset of the DAC objects in PXGraph.Caches.
The usual pattern for persisting records is by using graph DataViews. The PXGraph.Action.PressSave() will persist the records when the current record of the primary DataView of the graph is properly set.
The primary DataView of the graph is declared in the ASPX PrimaryView property of the PXDataSource element:
<px:PXDataSource ID="ds" runat="server" PrimaryView="Document" TypeName="PX.Objects.SO.SOOrderEntry" >
Setting PrimaryView.Current record in the BLC graph is required for the Save action to persist the DataViews records.
It is also possible to persist DAC objects in DataViews without invoking the Save action. To do so you would perform a CRUD operation and then Persist the DAC objects in the DataView using:
DataView.Insert(DAC)
DataView.Update(DAC)
DataView.Delete(DAC)
DataView.Cache.Persist(PXDBOperation.Insert)
DataView.Cache.Persist(PXDBOperation.Update)
DataView.Cache.Persist(PXDBOperation.Delete)
While it is possible to do the same operations on PXGraph.Caches there are few situation where this is required in user code because using DataView is the preferred method of persisting DAC objects and access to DataViews is available when you have the BLC.
Where PXGraph.Caches is especially useful is when working with PXGraph instead of a BLC, often found in the context of generic code. You can access DataViews of the BLC like SOOrderEntry.Document with a reference pointing to the SOOrderEntry BLC. However if all you got is a reference to the PXGraph base class of the BLC you don't have access to the DataViews. In that case you can still access the DAC objects in PXGraph.Caches. By knowing the type of the DataView DAC objects for example SOOrderEntry.Document is of SOOrder type and will be in PXGraph.Caches[typeof(SOOrder)]. For user code this pattern is more common in custom attributes that have access to the PXGraph object but not the BLC. In BLC it's more common to work on the DataView.Cache instead of PXGraph.Cache.
Another possible scenario where accessing PXGraph.Caches could be useful is when you have many DataViews of the same type. If you want to query all updated DAC objects of a same type that are in multiple DataViews you can itereate on Caches[typeof(DAC)].Updated. It's also quite common to simply fetch a reference to a Cache object using Caches[typeof(DAC)] that will then be passed to another Acumatica framework method by input parameter. In this case it's often a matter of convenience because the same Cache reference could be obtain through DataView.Cache.

Related

Is it possible to create my own custom KvExt table to fully implement and manage User Defined Fields for my custom table and page?

I have a custom table in SQL Server called AnimalBreed and maintenance page.
I wish to add User Defined Field support via the KvExt approach instead of
traditional practices involving either CSAnswers or a CacheExtension of the
DAC and underlying "Usr" fields.
Is this possible?
Can it be done for line-level (child) DACs, such as
existing INLocation and INLocationKvExt?
Thanks!
UPDATE:
It appears the feature needs at least:
ensure your table has the NoteID column as uniqueidentifier datatype and the DAC has the corresponding field: Guid? NoteID and [PXNote()] attribute.
In the ASPX, if not ListView, add the following to the PXDataSource tag:
EnableAttributes="true"
By making these changes, I can Manage User Defined Fields, choose Attributes to include, and I can store values to the KvExt table.
I am using Version = 19.205.0023
Sales Order page observation: if I add two UDFs on SO Order Entry page, one is combobox and one is checkbox, setting their values saves just fine, but then updating the combobox and save leads to loss of the checkbox (from true to false), unless you uncheck and recheck prior to the save. Is this a bug?
Maybe technically possible but definitely not recommended to use undocumented feature like KvExt.
If you need to deploy User Defined Fields on a page which already contains them. Configure them manually and then add them in a customization package in the user defined fields section for deployment as described here:
https://help-2019r2.acumatica.com/Help?ScreenId=ShowWiki&pageid=e01f29d3-b6b1-40f4-a2d1-45c9d01bdde3
Example:

Graph CacheAttached method

I want to make the SOLine Qty field to 1.0 by default, so I have changed at DAC level, by doing this it will be applied to the entire application?
Next, I can do same by writing Graph CacheAttached method also, I think this will apply only to that particular graph and if the same field is used in another graph or a custom new screen this will not affect in that graph.
Please suggest.
You are correct, adding an attribute to SOLine DAC extension will apply this attribute system wide. To limit its scope to a graph, use a CacheAttached.
Here are good reads on this subject :
DAC : https://help.acumatica.com/(W(12))/Wiki/ShowWiki.aspx?pageid=b3d24079-bda4-4f82-9fbd-c444a8bcb733
Append and replace of dacs attributes : http://asiablog.acumatica.com/2017/01/append-and-replace-of-dacs-attributes.html
These concepts are well explained in the T200 certification, which I suggest you do if you haven't already.

Windows 8 XAML - Storing Objects local resource

I am still relatively new to development for Windows Store Apps in XAML/C# and I'm currently dealing with a very random and intermittent problem with an app I have written.
Firstly a quick overview of how my app works - user logs on once a day, downloads data from web service and stores the data in xml files. Each time the app opens/resumes the data is loaded from xml, deserialized and stored in memory in the Application.Resouces Resource Dictionary.
The objects I am storing are my own classes which contain Observable Collections of other classes. I have declared these in App.xaml
<localdata:MyClass x:Key="MyClassResource">
When a page needs this data I reference it using
MyClass myClass = (MyClass)App.Current.Resources["MyClassResource"];
and bind it to controls. The user updates the data and these changes would also be saved to file periodically.
I am now starting to doubt whether this is the correct approach for storing my data.
Every so often the users reports problems with the stored data - I don't have enough details to fully discuss the specific problem right now but I wanted advise on whether it is fine to store my own objects in the Application Resource Dictionary.
There's nothing wrong with your approach. It is actually a very common way to create and access the viewmodel. There is an excellent blog post by Paul Stovell describing different approaches to create and access the viewmodel.
Create viewmodel from code-behind within the view
Inject the viewmodel as dependency into the view
Assign viewmodel to view's DataContext property
Set viewmodel via XAML to DataContext property
Define viewmodel as resource in XAML
Use a view model locator in XAML
DataTemplate property in XAML
DataTemplate and view class in XAML
The referenced article describes all 8 approaches with examples. Your approach is number 5.

Is it possible to retain custom attributes in a class instance after deserialization?

I'm trying to build a custom HTML helper for MVC.NET that would allow me to render object entities (Model Objects) as HTML forms. So I decided to do it using custom attributes such as html input type, readonly flag, css classes, etc. Similar in a way to LINQ Mapping attributes that set database related bindings for Table and Column. So I did write a custom attribute class, applied it to the same entities that I store in the database, but when I retrieve an entity class from a database to display in a View, all of my custom attributes are gone. Is there a way to retain my custom attributes, AFTER they come back from a database?

Accessing Aggregate Entities without Lazy Loading

I want to follow the DDD philosophy and not access entity objects of an aggregate directly. So, i have to call the root object to get the associated entity. But In other cases I dont always want every associated entity to load when the root is called. Is that the purpose of lazy loading?
How do I access entity objects through the root without loading all the associated objects everytime if i disable lazyloading feature of linq?
EDIT:
For example, If I have a Person as the Root Entity, and the Person has Name, Addresses and OwnedProperties. If I want to get a list of People so that I could display their names, I dont necvessarily want to load up Owned Properties every time on the call to the Repository. Conversely, on another page I may want to show a list of OwnedProperties, but do not want the other information to load with the call. what is the simple way of just calling the Person without the owned property entity other than creating a new person object without that owned properties?
I don't thinks that's possible without lazy loading.
Getting all data at once: Eager Loading
Getting data when accessed: Lazy Loading
According to your edit:
What I do in these situations, is create a 'View' class or a 'DTO' class which just contains the properties that I'm interested in.
For instance, I could have a 'PersonView' class which just has a Name property for instance.
Then, using my OR/M mapper (I use NHibernate), I create a HQL query (or Criteria query) which works on my 'Person' entity. Before I execute the query, I tell NHibernate that I want 'PersonView' objects as a result (I specify a projection). Then, NHibernate is smart enough to execute a query that only retrieves the columns that are necessary to populate the PersonView instances.
One way to avoid lazy loading is just using the object 'id'

Resources