Fluent API Best Practices for Children Nodes
I had a question regarding the best practice to acquire the children nodes for a specific page within the front end site map. Consider this code snippet :
1.
using
(var fluent = App.WorkWith())
2.
3.
var parentPage = fluent.Pages()
4.
.Where(node => node.Id == parentPageId).Get().FirstOrDefault();
5.
var childrenPages = parentPage.Nodes;
6.
Hi James,
There is a problem with the plural pages facade. I would recommend that you use the 'native' API:
var parentPageId = Guid.Empty;
// set to some real ID
var parentPage2 = PageManager.GetManager().GetPageNodes().Where(p => p.Id == parentPageId).FirstOrDefault();
if
(parentPage2 !=
null
)
var childNodes = parentPage2.Nodes;
If you do know that there is a page by this ID, you will really speed thigs up by taking advantage of OpenAcces' first-level cache:
var parentPageId = Guid.Empty;
// set to some real ID
var childNodes2 = PageManager.GetManager().GetPageNode(parentPageId).Nodes;
The difference between the two is that the first query always goes to the DB, while the second takes advantage of OA's first-level cache (e.g. when you retrieve items by just their PK and not a full-fledged query).
We are aware that there are performance issues, and we are working on them. I do not know how many issues will be addressed in the next Q, but we have dedicated a lot of our internal resources to performance improvements.
Greetings,
This was incredibly valuable information and EXACTLY what we were needing! I had optimized some fluent API queries that were taking SECONDS to run down to about 400-800ms and now with your change all of our queries are running in 10-20ms which is absolutely phenomenal!
Thanks so much for the help and insight, we will be changing out the rest of our queries over the coming days and will report back if anything else looks strange.
Thanks again.
James
I can get a PageManaer() a couple of different ways. Are the sfPm1 and sfPm2 objects below, the same w respect to performance?
var sfPm1 = PageManager.GetManager().
using (var fluent = App.WorkWith())
var pageFacade = fluent.Page();
var sfPm2 = pageFacade.PageManager;
pageFacade.CreateNewStandardPage(myPageNode)
I can focus my question now that I have dug into this a little more. The way that we are currently creating PageNodes is using memory to such an extent that we can only create 20 or so at a time. The memory usage of the IIS process continues to increase until the machine has no more to provide. We would like to create 180 pages at a time.
What is the best way to create pages?
The following code block is what we are currently doing. It is based on code that this forum gave us several months back.
using (var fluent = App.WorkWith())
var pageFacade = fluent.Page();
var pm = pageFacade.PageManager;
LoadVars(pm, sfModelPageId, parentGuid);
pageFacade.CreateNewStandardPage(parentNode)
.Do(pn =>
pn.Title = childPageName;
pn.UrlName = childPageName;
pn.Page.Title = childPageName;
pn.InheritsPermissions = true;
// pn.Ordinal = theOrdinal;
)
.CheckOut() // creates a draft version and locks the page
.SetTemplateTo(guidTemplate)
.Publish()
.SaveAndContinue()
.SaveChanges();
Hello Phil,
If you get the manager through the pages facade, it will be in a named transaction. In Sitefinity, named transactions are an artificial way to work in transaction with the objects of several providers/managers. The only difference to the end user is that you can no longer call SaveChanges on the manager, you will have to invoke TransactionManager.CommitTransaction(transactionName), where transactionName is the name of the named transaction. You can always check if a provider is working in a named transaction mode by checking the value of Manager.Provider.TransactionName. If it is null or empty, you work in a regular transaction (per-manager). Otherwise, you should pass that value to TransactionManager.CommitTransaction.
For this to work, the underlying providers must implement FlushTransaction, RollbackTransaction and CommitTransaction (and in the stock providers they are implemented via decorators).
As far as page creation performance - I can't really tell, as I haven't really tested. In fact, that's what a team of ours is doing this Q, but they are profiling other areas in the pages API/services. I can offer some general advice, however:
const
int
gcCollectFrequency = 100;
const
int
commitFrequency = 100;
var fluentPages = App.WorkWith().Page();
for
(
int
i = 0; i < pagesToCreate.Count; i++)
// your logic for creating pages
if
(i > 0)
if
(i % commitFrequency == 0)
fluentPages.SaveChanges();
fluentPages = App.WorkWith().Page();
// create a new named transaction
if
(i % gcCollectFrequency == 0)
GC.Collect();