Beta3 introduces some fundamental changes which will require minor updates in your code base. These changes are done to provide a more consistent experience while also doing internal refactoring to centralize to a common query engine regardless of the entry point. In the remainder of this article you'll see a lot of context
use: in this case this is a PnPContext
which was obtained via the PnPContextFactory
as explained in the overview article and show below:
using (var context = await pnpContextFactory.CreateAsync("SiteToWorkWith"))
{
}
Working with PnP Core SDK starts from a PnPContext which provides you with access to the connected Site, Web and Team but which also acts as an in memory domain model. Before Beta 3 all data you requested got loaded into the PnPContext. Any Get
call on either a model (e.g. doing context.Web.GetAsync(p => p.Title)
) or a collection (e.g. doing context.Web.Lists.GetAsync(p => p.Title, p => p.TemplateType)
) loaded data into the PnPContext whereas now the default behavior for Get
calls is to load the data into a variable. Loading data into the PnPContext is still possible using the new Load
calls.
When requesting a model you can choose whether data is loaded into the PnPContext or not:
Pre Beta3 | Beta3 and later | Loads data into PnPContext |
---|---|---|
GetAsync() | LoadAsync() | Yes |
Get() | Load() | Yes |
GetBatchAsync() | LoadBatchAsync() | Yes |
GetBatch() | LoadBatch() | Yes |
N/A | GetAsync() | No |
N/A | Get() | No |
N/A | GetBatchAsync() | No |
N/A | GetBatch() | No |
Note
When doing a Load
specifying a model collection (e.g. context.Web.LoadAsync(p => p.Lists)
) then all the data is loaded into the PnPContext. This approach is the only option to load a collection into the PnPContext, and unless it is really needed, you should avoid this technique and rather use paging.
Loads initiated from a collection always return data into a variable and never into the PnPContext because using this approach you can also apply filters (using Where
), or other query methods, which would result in an incomplete dataset in the PnPContext. You can now chain methods (Where
, QueryProperties
, etc.) with a fluent syntax and trigger the collection query execution once you are ready, using methods like ToList
or ToListAsync
, or enumerating the result of the query.
Pre Beta3 | Beta3 and later | Loads data into PnPContext |
---|---|---|
GetAsync() | ToListAsync() | No |
Get() | ToList() | No |
GetBatchAsync() | AsBatchAsync() | No |
GetBatch() | AsBatch() | No |
GetFirstOrDefaultAsync() | FirstOrDefaultAsync() | No |
GetFirstOrDefault() | FirstOrDefault() | No |
GetFirstOrDefaultBatchAsync() | Use GetBatchAsync() with filter | No |
GetFirstOrDefaultBatch() | Use GetBatch() with filter | No |
Starting from Beta3 you can easily leverage a LINQ custom query provider that translates your LINQ queries into actual REST queries for Microsoft Graph or for Microsoft SharePoint REST APIs.
For example, to get the items of a SharePoint list, filtered by title and just loading their Id and Title fields, you can write a query like the following one:
var query = (from i in context.Web.Lists.GetByTitle(listName).Items
where i.Title == itemTitle
select i)
.QueryProperties(l => l.Id, l => l.Title);
You will then be able to consume the collection either using an enumeration constructor or a method like ToList
.
Here you can see an example using an enumeration:
foreach (var item in query)
{
Console.WriteLine($"{item.Id} - {item.Title}");
}
While here you can see how to use the ToList
method.
var queryResult = query.ToList();
The above query can also be written just using the LINQ Extension Methods, rather than the LINQ query syntax, like it is illustrated in the following code excerpt:
var query = context.Web.Lists.GetByTitle(listName).Items
.Where(i => i.Title == itemTitle)
.QueryProperties(l => l.Id, l => l.Title);
Under the cover, there will be exactly the same query model, regardless the syntax you like to use.
Important
Since Beta3, whenever you enumerate a query or whenever you define a new query, the query provider will execute the query online targeting the back-end APIs, and you will always get fresh data.
If you don't want to execute an online query targeting the back-end APIs, and you rather want to use data that you already loaded in memory executing a previous query, you can use the AsRequested()
method applied to a pre-loaded collection. As such, you will be able to enumerate the already in-memory data, eventually using LINQ to Objects or any other object browsing technique of your choice. In fact, the AsRequested()
method casts the collection to an IEnumerable
and gives you access to the in-memory copy of data.
// Load all lists in the PnPContext
await context.Web.LoadAsync(p => p.Lists);
// Query the loaded lists via LINQ to Objects, in memory with no additional query on the backend APIs
var documentLibraries = context.Web.Lists.AsRequested().Where(p => p.TemplateType == ListTemplateType.DocumentLibrary);
// Iterate over the loaded lists
foreach(var list in context.Web.Lists.AsRequested())
{
// do something with the list
}
Note
As of beta3 you need to use QueryProperties
instead of LoadProperties
when you want to specify the properties to load into the model.