Easyweb.Site.DataApi 7.2.0

dotnet add package Easyweb.Site.DataApi --version 7.2.0
NuGet\Install-Package Easyweb.Site.DataApi -Version 7.2.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Easyweb.Site.DataApi" Version="7.2.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Easyweb.Site.DataApi --version 7.2.0
#r "nuget: Easyweb.Site.DataApi, 7.2.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Easyweb.Site.DataApi as a Cake Addin
#addin nuget:?package=Easyweb.Site.DataApi&version=7.2.0

// Install Easyweb.Site.DataApi as a Cake Tool
#tool nuget:?package=Easyweb.Site.DataApi&version=7.2.0

Introduction

Easyweb.Site.DataApi provides an easy, accessible, Entity Framework-like IDataSource implementation for the .NET Easyweb.Site-framework, enabling a LINQ-to-API provider to the Easyweb API.

Setup

Add the Nuget-package to your project.

Add your Easyweb API credentials in your appSettings.json like:

  "ApiSettings": {
    "UnionId": XXXX,
    "ClientId": "OAuth2.XXXX.XXXX",
    "ClientSecret": "XXXX-XXXXX-XXXXX"
  },

Register the data source to the service collection either by:

When using Startup.cs adding:

public void ConfigureServices(IServiceCollection services)
{
    // ....

    services.AddEasywebDataSource();

    // ....
}

Or when using Program.cs adding:

using Easyweb.Site.DataApi;

// ...

var builder = WebApplication.CreateBuilder(args);

// ...

builder.Services.AddEasywebDataSource();

// ...

var app = builder.Build();

// ...

Usage

Inject IDataSource wherever you need to query the Easyweb API and use the .Set<TType>() to query your union data source.

A full minimal example of a Program.cs in .NET 7 for testing might look like:

using Easyweb.Site.Core;
using Easyweb.Site.DataApi;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEasywebDataSource();

var app = builder.Build();

string RunDataTests(IDataSource dataSource)
{
    // Hold count of results for each query to get a feel of the amount of queried entitites returned
    var countDictionary = new Dictionary<string, int>();

    // Queries all linkables in the system, meaning all items maintaining an url = all articles, root modules, folders, images and documents
    // The linakble-query does not include any heavy string-data, allowing a quick registry of all available urls and linkable items in the systems to be fetched as an index
    var allLinkables = dataSource.Set<Linkable>().ToList();
    countDictionary.Add(nameof(allLinkables), allLinkables.Count);

    // Query all web page linkables, a subset above containing only articles (no image urls, no document urls)
    var allWebPageinkables = dataSource.Set<WebPageLinkable>().ToList();
    countDictionary.Add(nameof(allWebPageinkables), allWebPageinkables.Count);

    // Query all available web modules globablly available and created in the studio
    var allWebModules = dataSource.Set<WebModule>().ToList();
    countDictionary.Add(nameof(allWebModules), allWebModules.Count);

    // Query all available web templates created in the studio
    var allWebTemplates = dataSource.Set<WebTemplate>().ToList();
    countDictionary.Add(nameof(allWebTemplates), allWebTemplates.Count);

    // Query the first web page having web module id 1 = Home = The start page/home page/Root page
    var startPage = dataSource.Set<WebPage>().FirstOrDefault(wp => wp.WebModuleId == ModuleType.Home.Id) ?? throw new Exception("No start page found");
    countDictionary.Add(nameof(startPage), startPage != null ? 1 : 0);

    // Query all web contents/content data created for the found start page, meaning all content data added in the content mode to the start page article in Easyweb
    var allStartPageWebContents = dataSource.Set<WebContent>().Where(wc => startPage != null && wc.ArticleId == startPage.Id).ToList();
    countDictionary.Add(nameof(allStartPageWebContents), allStartPageWebContents.Count);

    // QUery the top 20 web pages/articles found sorted by their descending label
    var top20PagesByLabelDesc = dataSource.Set<WebPage>().OrderByDescending(wp => wp.Label).Take(20).ToList();
    countDictionary.Add(nameof(top20PagesByLabelDesc), top20PagesByLabelDesc.Count);

    // Resolve their id to a list before quering that list for all their web content data
    var top20PagesByLabelDescIds = top20PagesByLabelDesc.Select(p => p.Id).ToList();
    
    // Query all fo the the top 20 web page/article web contents/content data at once, by queryign available data in an integer list
    var top20PagesByLabelDescWebContents = dataSource.Set<WebContent>().Where(wc => wc.ArticleId != null && top20PagesByLabelDescIds.Contains(wc.ArticleId.Value)).ToList();
    countDictionary.Add(nameof(top20PagesByLabelDescWebContents), top20PagesByLabelDescWebContents.Count);

    // Query web contents/any entity by a text property for a partial string match using the contains string operation for the string property value
    var webContentTextLikeSearchInPagesWebModule = dataSource.Set<WebContent>().Where(wc => wc.WebModuleId == ModuleType.WebPages.Id && wc.Value.Contains("era")).ToList();
    countDictionary.Add(nameof(webContentTextLikeSearchInPagesWebModule), webContentTextLikeSearchInPagesWebModule.Count);

    // Query all web pages ordered by last edited, taking 100 after first skipping 100.
    var skip100take100WebPagesOrderedByEditedDesc = dataSource.Set<WebPage>().OrderByDescending(wp => wp.Edited).Skip(100).Take(100).ToList();
    countDictionary.Add(nameof(skip100take100WebPagesOrderedByEditedDesc), skip100take100WebPagesOrderedByEditedDesc.Count);

    // Serialzie the count-dictionary to get an overview of resulting count for each query.
    return System.Text.Json.JsonSerializer.Serialize(countDictionary);
}

app.MapGet("/", (IDataSource dataSource) =>
{
    return RunDataTests(dataSource);
});

app.Run();

Resulting in, based on your union data, an output like:

{
    "allLinkables": 3878,
    "allWebPageinkables": 1119,
    "allWebModules": 21,
    "allWebTemplates": 891,
    "startPage": 1,
    "allStartPageWebContents": 345,
    "top20PagesByLabelDesc": 20,
    "top20PagesByLabelDescWebContents": 776,
    "webContentTextLikeSearchInPagesWebModule": 58,
    "skip100take100WebPagesOrderedByEditedDesc": 100
}

Logging

Review how written LINQ-querys are converted into Easyweb API-querys with requesting timers to measure the response time of your query by editing the Logging section of your appSettings.json (or appSettings.Development.json in development):

  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Easyweb.Site.DataApi": "Debug"
    }
  },

Sample output:


Easyweb.Site.DataApi.ApiClient: Debug: Requesting: https://app.easyweb.se/extapi/xxxx/query/webmodules?
Easyweb.Site.DataApi.ApiClient: Debug: Request received in 19 ms and read in 1 ms: https://app.easyweb.se/extapi/xxxx/query/webmodules?
Easyweb.Site.DataApi.ApiClient: Debug: Requesting: https://app.easyweb.se/extapi/xxxx/query/webtemplates?
Easyweb.Site.DataApi.ApiClient: Debug: Request received in 51 ms and read in 5 ms: https://app.easyweb.se/extapi/xxxx/query/webtemplates?
Easyweb.Site.DataApi.ApiClient: Debug: Requesting: https://app.easyweb.se/extapi/xxxx/query/webpages?where=WebModuleId=@p1&take=1&@p1=1
Easyweb.Site.DataApi.ApiClient: Debug: Request received in 35 ms and read in 2 ms: https://app.easyweb.se/extapi/xxxx/query/webpages?where=WebModuleId=@p1&take=1&@p1=1
Easyweb.Site.DataApi.ApiClient: Debug: Requesting: https://app.easyweb.se/extapi/xxxx/query/webcontents?where=WebModuleId=@p1 and ((Value like @p2))&@p1=40&@p2=%era%
Easyweb.Site.DataApi.ApiClient: Debug: Request received in 63 ms and read in 1 ms: https://app.easyweb.se/extapi/xxxx/query/webcontents?where=WebModuleId=@p1 and ((Value like @p2))&@p1=40&@p2=%era%
Easyweb.Site.DataApi.ApiClient: Debug: Requesting: https://app.easyweb.se/extapi/xxxx/query/webpages?orderby=Edited desc&skip=100&take=100
Easyweb.Site.DataApi.ApiClient: Debug: Request received in 28 ms and read in 30 ms: https://app.easyweb.se/extapi/xxxx/query/webpages?orderby=Edited desc&skip=100&take=100

Support & limitations

Most default LINQ-query expressions expected to be available when querying any data source is supported, however expressions and operations on the advanced or more complex side has less support, specifically projection (.Select()) and related grouping (.Join(), .GroupJoin() and similar).

Following dos and don´ts avoid unneccessary headache regarding supported LINQ expression calls.

  • Do keep your data queries simple and execute them by calling .ToList/.FirstOrDefault or similar default execution calls before you perform more complex expressions on the data source (which will then be evaluated client side).

  • Do use common operators and equality expressions on your IDataSource.Set<TType>().ExpressionMethod(...) to query data from server side, such as ´x => x.Property == Y´, ´x => x.PropertyString.Contains(YString)´, ´x => myPredefinedNumericList.Contains(x.Id)´

  • Do combine operators like && and || to create more precise queries, such as: ´x => x.BoolProperty && x.IntProperty == YInteger´, ´x => x.ArticleId == myArticleId || x.ViewKey == myFallbackViewKey´

  • Do use .OrderBy, .OrderByDescending combined with .Skip and .Take to limit and optimize your query results

  • Do not express your constant query expressions with complex evaluations in the lambda body like: ´x => x.Property == (myProperty ?? myOtherFallbackProperty)´ or 'x => x.id == (myVariable == null ? myinteger1 : myinteger2')'. Resolve the result to a variable and use the resuling variable in the query instead.

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
7.2.0 140 9/1/2023
7.1.1 109 8/29/2023
7.1.0 137 8/28/2023
7.1.0-RC1 103 8/27/2023
7.0.0-rc4 196 2/14/2023
6.5.1 405 11/29/2022
6.5.0 293 11/29/2022
6.5.0-rc2 120 11/2/2022