Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/b-straub/DexieNET
DexieNET is a .NET wrapper for dexie.js minimalist wrapper for IndexedDB
https://github.com/b-straub/DexieNET
Last synced: about 1 month ago
JSON representation
DexieNET is a .NET wrapper for dexie.js minimalist wrapper for IndexedDB
- Host: GitHub
- URL: https://github.com/b-straub/DexieNET
- Owner: b-straub
- License: apache-2.0
- Created: 2022-12-11T11:12:10.000Z (about 2 years ago)
- Default Branch: master
- Last Pushed: 2024-02-26T07:24:00.000Z (10 months ago)
- Last Synced: 2024-04-10T02:47:33.255Z (8 months ago)
- Language: C#
- Homepage:
- Size: 994 KB
- Stars: 37
- Watchers: 7
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
- RSCG_Examples - Dexie - straub/DexieNET (Contributors Welcome for those / 1. [ThisAssembly](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly) , in the [EnhancementProject](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementproject) category)
- awesome-blazor - DexieNET - ![GitHub stars](https://img.shields.io/github/stars/b-straub/DexieNET?style=flat-square&cacheSeconds=604800) ![last commit](https://img.shields.io/github/last-commit/b-straub/DexieNET?style=flat-square&cacheSeconds=86400) DexieNET aims to be a feature complete .NET wrapper for Dexie.js the famous Javascript IndexedDB wrapper from David Fahlander. It's designed for Blazor and includes an easy to use Razor component. Now with support for DexieCloud in preview. (Libraries & Extensions / 2D/3D Rendering engines)
README
DexieNET
========DexieCloudNET is a .NET wrapper for dexie.js minimalist wrapper for IndexedDB see https://dexie.org with cloud support see https://dexie.org/cloud/ .
*'DexieNET' used with permission of David Fahlander*
*and made with*
[*Support for Open-Source Projects*](https://www.jetbrains.com/community/opensource/#support) !## News
- Released [DexieCloud](https://dexie.org/cloud/)
- Added Push support. Please check the [ReadMe](DexieNETCloudPushServer/README.md)
- Clicking on notifications currently does not work reliable for iOS [notificationclick events in serviceworkers not firing](https://bugs.webkit.org/show_bug.cgi?id=268797)
- Clicking on notifications currently does not work reliable for Chrome on MacOS 15
- Please register with **DexieCloud** and test the [ToDoSample](DexieNETCloudSample). The configuration script can be found here [configure-app.ps1](DexieNETCloudSample/Dexie/configure-app.ps1) (Windows) or here [configure-app.sh](DexieNETCloudSample/Dexie/configure-app.sh) (Nix - jq required).
- Published a new helper library [RxBlazorLight](https://github.com/b-straub/RxBlazorLight)---
## Basic
**DexieNET** aims to be a feature complete .NET wrapper for **Dexie.js** the famous Javascript IndexedDB wrapper from David Fahlander including support for **cloud sync**.
I consists of two parts, a source generator converting a C# record, class, struct to a DB store and a set of wrappers around the well known Dexie.js API constructs such as *Table, WhereClause, Collection*, ...
It's designed to work within a Blazor Webassembly application with minimal effort.
#### Hello World
- create a Blazor WebAssembly
- Add the **DexieNET** Nuget
- Add the HelloWorld Component and update the index*Program.cs*
```c#
using DexieNET;
using YourNamspace.Pages;
....builder.Services.AddDexieNET();
```*HelloWorld.razor*
```razor
@page "/helloWorld"
@using DexieNET.Component
@inherits DexieNETFriends:
@if (_friends is null)
{
Loading...
}
else if (_friends.Count() == 0)
{
No items...
}
else
{
-
Name: @friend.Name, Age: @friend.Age
@foreach (var friend in _friends)
{
}
}
Logs:
@if (_logs is null)
{
Loading...
}
else if (_logs.Count() == 0)
{
No items...
}
else
{
-
Message: @logEntry.Message, TimeStamp: @logEntry.TimeStamp.ToLongTimeString();
@foreach (var logEntry in _logs)
{
}
}
PopulateDatabase
GoodTransaction
FailedTransaction
ClearDatabase
```
*HelloWorld.razor.cs*
```c#
using DexieNET;
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
using System.Xml.Linq;
namespace DexieNETHelloWorld.Pages
{
public interface IFriendsDB : IDBStore { };
public partial record Friend
(
[property: Index] string Name,
[property: Index] int Age
) : IFriendsDB;
public partial record LogEntry
(
[property: Index] string? Message,
[property: Index] DateTime TimeStamp
) : IFriendsDB;
public partial class HelloWorld
{
private IEnumerable? _friends;
private IEnumerable? _logs;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Dexie.Version(1).Stores();
await FillTables();
}
private async Task FillTables()
{
_friends = await Dexie.Friends().ToArray();
_logs = await Dexie.LogEntries().OrderBy(l => l.TimeStamp).Reverse().ToArray();
await InvokeAsync(StateHasChanged);
}
private async Task LogMessage(string? message)
{
await Dexie.Transaction(async _ =>
{
await Dexie.LogEntries().Add(new LogEntry(message, DateTime.Now));
}, TAType.TopLevel);
}
private async Task ClearDatabase()
{
await Dexie.Friends().Clear();
await Dexie.LogEntries().Clear();
await FillTables();
}
private async Task PopulateDatabase()
{
await LogMessage("PopulateDatabase");
Random rand = new();
await Dexie.Friends().Add(new Friend("Jane Doe", rand.Next(1, 99)));
await Dexie.Friends().Add(new Friend("John Doe", rand.Next(1, 99)));
await FillTables();
}
private async Task GoodTransaction()
{
await LogMessage("GoodTransaction");
await Dexie.Transaction(async ta =>
{
Random rand = new();
var key = await Dexie.Friends().Add(new Friend("Luke", rand.Next(1, 99)));
var friend = await Dexie.Friends().Get(key);
if (friend?.Name == "Luke" || ta.Collecting)
// ta.Collecting, this means the first pass of the transaction, in which the table names are collected
// if a second table is hidden behind a conditional statement, it must also be visited in the first pass
{
await Dexie.Friends().Add(new Friend("John", rand.Next(1, 99)));
await Dexie.LogEntries().Add(new LogEntry("TA executed", DateTime.Now));
}
});
await FillTables();
}
private async Task FailedTransaction()
{
await LogMessage("ProvokeFail");
try
{
await Dexie.Transaction(async ta =>
{
await Dexie.Friends().Clear();
var key = await Dexie.Friends().Add(new Friend("Test", 33));
var friend = await Dexie.Friends().Get(key);
if (friend?.Name == "Test" || ta.Collecting)
{
await LogMessage("TA will fail");
}
await Dexie.Friends().Add(friend); // this will fail
});
}
catch (Exception ex)
{
var firstDot = ex.Message.IndexOf('.');
var message = firstDot <= 0 ? ex.Message : ex.Message[..firstDot];
await LogMessage($"TA failed: {message}");
}
await FillTables();
}
}
}
```
## Advanced
### Naming
- the Source Generator will create the following classes from an *IDBStore* derived class, struct, record:
- **PIndentifier** -> Plural of *Identifier* provided by *Humanizer.Core* (English only), be aware the plural form might not be always obvious e.g. Person -> People
- service: *PIndentifier***DB**
- table: *PIndentifier*
```c#
// Record
public partial record Friend
(
[property: Index] string Name,
[property: Index] int Age
) : IDBStore;
......
// Service
builder.Services.AddDexieNET();
......
[Inject]
public IDexieNETService? DB { get; set; }
......
// Table
var table = await DB.Friends();
```
- You can have multiple stores in one database
```c#
[DBName("TestDB")] // optional -> default name = interface name without leading 'I' -> PersonsDB
public interface IPersonsDB : IDBStore
{
}
// Records
[CompoundIndex("FirstName", "LastName")]
public partial record Person
(
[property: Index] string FirstName,
[property: Index] string LastName,
Guid? AddressKey
) : IPersonsDB;
[CompoundIndex("City", "Street")]
[CompoundIndex("Zip", "Street")]
public partial record Address
(
[property: Index] string Street,
[property: Index] string Housenumber,
[property: Index] string City,
[property: Index] string ZIP,
[property: Index] string Country
) : IPersonsDB;
......
// Service
using DexieNET;
.......
builder.Services.AddDexieNET();
// Component
[Inject]
public IDexieNETService? TestDB { get; set; }
......
// Table
var persons = await TestDB.Persons();
var addresses = await TestDB.Addresses();
```
### Transactions
- transactions capture the table names in two passes
- if a second table is hidden behind a conditional statement, it must also be visited in the first pass
- nested transactions contain all required table names
- top level transactions must use *TAType.TopLevel*, the root transaction is implictly a top level transaction
- parallel transactions are composed by a root transaction with *TAType.TopLevel* and top level child transactions
### Samples
The tests from [TestCases](DexieNETTest/TestBase/Test/TestCases) will cover all possible *DexieNET Api* calls. Those calls are as close as possible modelled after the original *Dexie.js* API.