#1 Dynamic DataSources Tutorial
Error Showing Content - please login as admin for details.
Error Showing Content - please login as admin for details.
This is a 10-line super-simple example of a DataSource called Basic101
. It will only return a single item with the answer to the meaning of life 😉. Afterwards we'll explain in more detail what's happening.
-
-
⬇️ Result | Source ➡️
List of Data in the CSV DataSource (1)
-
Hello from Basic101 - the Answer: 42
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource with the name "Basic101"
var basic101 = Kit.Data.GetSource(name: "Basic101");
}
<h3>List of Data in the CSV DataSource (@basic101.List.Count())</h3>
<ul>
@foreach (var item in AsList(basic101)) {
<li>
<strong>@item.Title</strong> - the Answer: @item.TheAnswer
</li>
}
</ul>
Source Code of Basic101.cs
public class Basic101 : Custom.DataSource.DataSource16
{
public Basic101(MyServices services) : base(services)
{
ProvideOut(() => new {
Title = "Hello from Basic101",
TheAnswer = 42,
});
}
}
This is the same sample as before, but with a lot more explanations. Here's what's happening:
- Using
Kit.Data.GetSource(name: "...")
we retrieve the DataSource using the name
- The name references a file with the same name
Basics101.cs
located in the DataSources
folder of the current App.
- The rest of the magic is explained in the source code of the DataSource - see below.
-
-
⬇️ Result | Source ➡️
List of Data in the CSV DataSource (1)
-
Hello from Basic101-Commented - the Answer: 42
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource with the name "Basic101Commented"
var basic101c = Kit.Data.GetSource(name: "Basic101Commented");
}
<h3>List of Data in the CSV DataSource (@basic101c.List.Count())</h3>
<ul>
@foreach (var item in AsList(basic101c)) {
<li>
<strong>@item.Title</strong> - the Answer: @item.TheAnswer
</li>
}
</ul>
Error Showing Content - please login as admin for details.
Return a list of items numbered 1...5 with random Guid identifier.
-
-
⬇️ Result | Source ➡️
List of Data in the DataSource (5)
-
Hello from ListBasic (1 / 872f19ee-5f61-4a43-8c23-077359615dd7) - Fav Number: 2742
-
Hello from ListBasic (2 / 57c28b9d-3064-4727-ac17-90c5a7d90aa1) - Fav Number: 2742
-
Hello from ListBasic (3 / 4846244d-0b89-4dc4-8633-59267da94fc9) - Fav Number: 2742
-
Hello from ListBasic (4 / eae6664a-7bb5-40c2-a5c0-369bfe30eef3) - Fav Number: 2742
-
Hello from ListBasic (5 / 678a930e-d245-4851-a961-34ae92e8aa8f) - Fav Number: 2742
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource with the name "Basic101"
var basic101 = Kit.Data.GetSource(name: "ListBasic");
}
<h3>List of Data in the DataSource (@basic101.List.Count())</h3>
<ul>
@foreach (var item in AsList(basic101)) {
<li>
<strong>@item.Title</strong> (@item.EntityId / @item.EntityGuid) - Fav Number: @item.FavoriteNumber
</li>
}
</ul>
Source Code of ListBasic.cs
// This sample uses some LINQ
using System.Linq;
public class ListBasic : Custom.DataSource.DataSource16
{
public ListBasic(MyServices services) : base(services)
{
ProvideOut(() =>
// For demo, create a few of these items using numbers 1 to 5
Enumerable.Range(1, 5).Select(i => new {
// Property with name "Id" is automatically used for the EntityId
Id = i,
// Property with name "Guid" is automatically used for the EntityGuid
Guid = System.Guid.NewGuid(),
Title = "Hello from ListBasic",
FavoriteNumber = 2742,
})
);
}
}
-
-
⬇️ Result | Source ➡️
Data in the DataSource (1)
- Current Temperature: 20.1
- WindSpeed: 6.3
- WindDirection: 211
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource with the name "DataFromWebService"
var weather = Kit.Data.GetSource(name: "DataFromWebService");
}
<h3>Data in the DataSource (@weather.List.Count())</h3>
<ul>
@foreach (var item in AsList(weather)) {
<li>Current Temperature: @item.Temperature</li>
<li>WindSpeed: @item.WindSpeed</li>
<li>WindDirection: @item.WindDirection</li>
}
</ul>
Source Code of DataFromWebservice.cs
using System.Net.Http;
using System.Linq;
using System.Text.Json.Serialization; // For JsonPropertyName
public class DataFromWebService : Custom.DataSource.DataSource16
{
public DataFromWebService(MyServices services) : base(services)
{
ProvideOut(() => {
var response = new HttpClient()
.GetAsync("https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true")
.GetAwaiter()
.GetResult();
response.EnsureSuccessStatusCode();
var responseBody = response.Content.ReadAsStringAsync()
.GetAwaiter()
.GetResult();
var result = Kit.Convert.Json.To<WeatherData>(responseBody);
return new {
Temperature = result.Current.Temperature,
WindSpeed = result.Current.WindSpeed,
WindDirection = result.Current.WindDirection,
};
});
}
}
// Helper classes for JSON deserialization
// Note that we're not using most of the properties, but we have them for completeness of the tutorial
public class WeatherData
{
public double Latitude { get; set; }
public double Longitude { get; set; }
[JsonPropertyName("generationtime_ms")]
public double GenerationTimeMs { get; set; }
[JsonPropertyName("utc_offset_seconds")]
public int UtcOffsetSeconds { get; set; }
public string Timezone { get; set; }
[JsonPropertyName("timezone_abbreviation")]
public string TimezoneAbbreviation { get; set; }
public double Elevation { get; set; }
[JsonPropertyName("current_weather")]
public CurrentWeather Current { get; set; }
}
public class CurrentWeather
{
public double Temperature { get; set; }
public double WindSpeed { get; set; }
public double WindDirection { get; set; }
public int WeatherCode { get; set; }
public int Is_Day { get; set; }
public string Time { get; set; }
}
Error Showing Content - please login as admin for details.
This simple example will get a DataSource from a file, and pass some configuration options. Specifically we'll give it the AmountOfItems
and FavoriteColor
.
-
-
⬇️ Result | Source ➡️
List of Data in the WithConfig DataSource (3)
-
Hello from WithConfig #1 - Favorite Color: dark blue
-
Hello from WithConfig #2 - Favorite Color: dark blue
-
Hello from WithConfig #3 - Favorite Color: dark blue
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource with the name "WithConfig"
// and set some configuration options
var withConfig = Kit.Data.GetSource(name: "WithConfig", parameters: new {
AmountOfItems = 3,
FavoriteColor = "dark blue"
});
}
<h3>List of Data in the WithConfig DataSource (@withConfig.List.Count())</h3>
<ul>
@foreach (var item in AsList(withConfig)) {
<li>
<strong>@item.Title</strong> - Favorite Color: @item.FavoriteColor
</li>
}
</ul>
Source Code of WithConfig.cs
using System.Linq;
using ToSic.Eav.DataSource; // This namespace is for the [Configuration] attribute
public class WithConfig : Custom.DataSource.DataSource16
{
public WithConfig(MyServices services) : base(services, "My.Magic")
{
ProvideOut(() => {
var result = Enumerable.Range(1, AmountOfItems).Select(i => new {
Title = "Hello from WithConfig #" + i,
FavoriteColor,
});
return result;
});
}
// This attribute [Configuration] creates a configuration "FavoriteColor"
// In this example [Configuration] already knows about the fallback.
// * The property getter calls Configuration.GetThis()
// * GetThis() will automatically use the property name "FavoriteColor" to look up the config
// * Calling an empty GetThis() will always return a string,
// so it's ideal for string-properties where the Fallback was specified before
[Configuration(Fallback = "magenta")]
public string FavoriteColor { get { return Configuration.GetThis(); } }
// This attribute [Configuration] creates configuration "AmountOfItems"
// * The property getter calls Configuration.GetThis()
// * GetThis() will automatically use the property name "AmountOfItems" to look up the config
// and will use the default value of 1 if it was not specified
[Configuration]
public int AmountOfItems { get { return Configuration.GetThis(1); } }
}
Error Showing Content - please login as admin for details.
This example has a DataSource which receives data from an <em>upstream</em> source. The upstream source is the list of all authors in this App. Our DataSource will then filter this list, and only keep the authors with an odd ID.
-
-
⬇️ Result | Source ➡️
Data from the KeepOdd DataSource (2)
-
Terry (ID: 45475)
-
Ed (ID: 45485)
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Get the stream of all authors
var authors = App.Data["Persons"];
// Create the Dynamic DataSource "KeepOdd" and attach the authors to it
var keepOdd = Kit.Data.GetSource(name: "KeepOdd", attach: authors);
}
<h3>Data from the KeepOdd DataSource (@keepOdd.List.Count())</h3>
<ul>
@foreach (var item in AsList(keepOdd)) {
<li>
<strong>@item.EntityTitle</strong> (ID: @item.EntityId)
</li>
}
</ul>
Source Code of KeepOdd.cs
using System.Linq;
public class KeepOdd : Custom.DataSource.DataSource16
{
public KeepOdd(MyServices services) : base(services)
{
ProvideOut(() => {
// Make sure we have an In stream - otherwise return an error
var inStream = TryGetIn();
if (inStream == null) return Error.TryGetInFailed();
// Return only odd items
return inStream.Where(e => e.EntityId % 2 == 1);
});
}
}
If your DataSource requires attached data (In
) you may get hard-to-debug errors. Because of this, the sample also has error handling for this. The following code uses the KeepOdd
but forgets to attach the in.
-
-
-
⬇️ Result | Source ➡️
Data from the KeepOdd DataSource (1)
-
Error: Stream 'Default' not found (ID: 0)
Message:
This DataSource needs the stream 'Default' on the In to work, but it couldn't find it.
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource "KeepOdd" and forget to attach the In
// note: debug:false is only used to ensure developers don't see a real exception
// this is just so the tutorial is easier to manage
var keepOdd = Kit.Data.GetSource(name: "KeepOdd", debug: false);
}
<h3>Data from the KeepOdd DataSource (@keepOdd.List.Count())</h3>
<ul>
@foreach (var item in AsList(keepOdd)) {
<li>
<strong>@item.EntityTitle</strong> (ID: @item.EntityId)
<br>
<strong>Message: </strong>
@item.Message
</li>
}
</ul>
Source Code of KeepOdd.cs
using System.Linq;
public class KeepOdd : Custom.DataSource.DataSource16
{
public KeepOdd(MyServices services) : base(services)
{
ProvideOut(() => {
// Make sure we have an In stream - otherwise return an error
var inStream = TryGetIn();
if (inStream == null) return Error.TryGetInFailed();
// Return only odd items
return inStream.Where(e => e.EntityId % 2 == 1);
});
}
}
Important: Note that the DataSource also has code to handle errors if the In was not attached.
Error Showing Content - please login as admin for details.
The default stream is called Default
so you don't need to specify it. In the following example, we have a second stream called Settings
.
-
-
⬇️ Result | Source ➡️
Data in the stream "Settings"
- PageSize: 50
- ShowStuff: True
List of Data in the Default stream (5)
-
Hello from ListMultiStream (1 / 86d07e73-21b0-432d-b380-90c80f89569c) - Fav Number: 2742
-
Hello from ListMultiStream (2 / 6de5bf35-6763-4bcf-a498-347fe4e1b171) - Fav Number: 2742
-
Hello from ListMultiStream (3 / 92ee3f1e-1400-4d73-82e0-c2cd1dc9bd95) - Fav Number: 2742
-
Hello from ListMultiStream (4 / 749b7f06-d768-4f16-9938-97a353641d0d) - Fav Number: 2742
-
Hello from ListMultiStream (5 / d78022b7-539a-4e9c-98f2-eb28aaf72550) - Fav Number: 2742
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource with the name "ListMultiStream"
var multiStream = Kit.Data.GetSource(name: "ListMultiStream");
var settings = AsDynamic(multiStream["Settings"].List.FirstOrDefault());
}
<h3>Data in the stream "Settings"</h3>
<ul>
<li>PageSize: @settings.PageSize</li>
<li>ShowStuff: @settings.ShowStuff</li>
</ul>
<h3>List of Data in the Default stream (@multiStream.List.Count())</h3>
<ul>
@foreach (var item in AsList(multiStream)) {
<li>
<strong>@item.Title</strong> (@item.EntityId / @item.EntityGuid) - Fav Number: @item.FavoriteNumber
</li>
}
</ul>
Source Code of ListMultiStream.cs
// This sample uses Enumerable.Repeat, which needs this namespace
using System.Linq;
public class ListMultiStream : Custom.DataSource.DataSource16
{
public ListMultiStream(MyServices services) : base(services)
{
// First stream "Settings" containing just one item
ProvideOut(() => new {
Title = "Settings Entity",
PageSize = 50,
ShowStuff = true,
}, name: "Settings");
// A second stream on the normal (unnamed) "Default"
ProvideOut(() => Enumerable.Range(1, 5).Select(i => new {
Id = i,
guid = System.Guid.NewGuid(),
Title = "Hello from ListMultiStream",
FavoriteNumber = 2742,
})
);
}
}
In some cases it makes sense to provide multiple streams which were processed at the same time. For example, when splitting data or when providing a stream Folders
and Files
which are still related.
The following sample takes the original Persons
and splits them into odd/even streams in one preparation step.
-
-
⬇️ Result | Source ➡️
List of Data in the All stream (6)
-
Douglas (#45474)
-
Terry (#45475)
-
Neil (#45476)
-
George (#45480)
-
Raphael (#45484)
-
Ed (#45485)
List of Data in the Odd stream (2)
-
Terry (#45475)
-
Ed (#45485)
List of Data in the Even stream (4)
-
Douglas (#45474)
-
Neil (#45476)
-
George (#45480)
-
Raphael (#45484)
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
var oddEven = Kit.Data.GetSource(name: "SplitOddEven", attach: App.Data["Persons"]);
var all = oddEven.List;
var odd = oddEven["Odd"].List;
var even = oddEven["Even"].List;
}
<h3>List of Data in the All stream (@all.Count())</h3>
<ul>
@foreach (var item in AsList(all)) {
<li>
<strong>@item.EntityTitle</strong> (#@item.EntityId)
</li>
}
</ul>
<h3>List of Data in the Odd stream (@odd.Count())</h3>
<ul>
@foreach (var item in AsList(odd)) {
<li>
<strong>@item.EntityTitle</strong> (#@item.EntityId)
</li>
}
</ul>
<h3>List of Data in the Even stream (@even.Count())</h3>
<ul>
@foreach (var item in AsList(even)) {
<li>
<strong>@item.EntityTitle</strong> (#@item.EntityId)
</li>
}
</ul>
Source Code of SplitOddEven.cs
using System.Linq;
public class SplitOddEven : Custom.DataSource.DataSource16
{
public SplitOddEven(MyServices services) : base(services)
{
ProvideOut(() => TryGetIn());
ProvideOut(() => Split().Odd, name: "Odd");
ProvideOut(() => Split().Even, name: "Even");
}
private Cache Split() {
// If already cached (eg. it's retrieving Even after Odd was already retrieved), return cache
if (_cache != null)
return _cache;
// Make sure we have an In stream - otherwise return an error
var inStream = TryGetIn();
if (inStream == null) return new Cache { Odd = Error.TryGetInFailed(), Even = Error.TryGetInFailed() };
// Build cache so we don't have to re-calculate when retrieving other streams
return _cache = new Cache {
Odd = inStream.Where(e => e.EntityId % 2 == 1),
Even = inStream.Where(e => e.EntityId % 2 == 0)
};
}
private Cache _cache;
private class Cache {
public object Odd;
public object Even;
}
}
Error Showing Content - please login as admin for details.
The Authors
DataSource demonstrates how to get all App data and then filter it using an inner EntityTypeFilter
DataSource.
-
-
⬇️ Result | Source ➡️
Items in the DataSource (6)
-
Douglas
-
Terry
-
Neil
-
George
-
Raphael
-
Ed
@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade
@using System.Linq
@using ToSic.Eav.DataSources
@{
// Create the Dynamic DataSource with the name "Basic101"
var authors = Kit.Data.GetSource(name: "Authors");
}
<h3>Items in the DataSource (@authors.List.Count())</h3>
<ul>
@foreach (var item in AsList(authors)) {
<li>
<strong>@item.EntityTitle</strong>
</li>
}
</ul>
Source Code of Authors.cs
using System.Linq;
using ToSic.Eav.DataSources;
public class Authors : Custom.DataSource.DataSource16
{
public Authors(MyServices services) : base(services)
{
ProvideOut(() => {
// Get the app data
var appData = Kit.Data.GetAppSource();
// Get the Content-Type Filter DataSource
var contentTypeFilter = Kit.Data.GetSource<EntityTypeFilter>(attach: appData, parameters: new { TypeName = "Persons"});
// Return all the items after filtering
return contentTypeFilter.List;
});
}
}
#1 Dynamic DataSources Tutorial