Skip to content

Commit 911b3f1

Browse files
committed
2 parents 412e688 + bcd8bfc commit 911b3f1

File tree

1 file changed

+272
-1
lines changed

1 file changed

+272
-1
lines changed

README.md

Lines changed: 272 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,272 @@
1-
# Magic.IndexedDb
1+
# Magic.IndexedDb
2+
3+
This open source library provides an IndexedDb wrapper for C# and Blazor WebAssembly applications. It simplifies working with IndexedDb and makes it similar to using LINQ to SQL.
4+
5+
Thank you to nwestfall for his BlazorDB work, which was a fork of Blazor.IndexedDB.Framework by Reshiru
6+
7+
https://github.com/nwestfall/BlazorDB
8+
9+
**NOTE:**
10+
This code is still very young. I will be making updates for this code as I come across it. I will try my best to not depreciate or break syntax already in use. But please take note of any version updates before you use my code if you're updating it in the future. As I will not guarentee right now that I won't break your stuff if you download this version and then come back in a year and get the latest version.
11+
12+
## Table of Contents
13+
14+
- [Installation](#installation)
15+
- [Usage](#usage)
16+
- [Setting up the project](#setting-up-the-project)
17+
- [Creating a class with Magic attributes](#creating-a-class-with-magic-attributes)
18+
- [Using the DbManager](#using-the-dbmanager)
19+
- [Attributes](#attributes)
20+
- [Examples](#examples)
21+
22+
## Installation
23+
24+
1. Add the library to your project.
25+
2. Add the following code to your `Program.cs` file:
26+
27+
```csharp
28+
/*
29+
* This is an example encryption key. You must make your own 128 bit or 256 bit
30+
* key! Do not use this example encryption key that I've provided here as that's
31+
* incredibly unsafe!
32+
*/
33+
string EncryptionKey = "zQfTuWnZi8u7x!A%C*F-JaBdRlUkXp2l";
34+
35+
builder.Services.AddBlazorDB(options =>
36+
{
37+
options.Name = DbNames.Client;
38+
options.Version = "1";
39+
options.EncryptionKey = EncryptionKey;
40+
options.StoreSchemas = SchemaHelper.GetAllSchemas("DatabaseName"); // builds entire database schema for you based on attributes
41+
options.DbMigrations = new List<DbMigration>
42+
{
43+
/*
44+
* The DbMigration is not currently working or setup!
45+
* This is an example and idea I'm thinking about, but
46+
* this will very likely be depreciated so do not use or rely
47+
* on any of this syntax right now. If you want to have
48+
* your own migration knowledge. Write JavaScript on the front end
49+
* that will check the indedDb version and then apply migration code
50+
* on the front end if needed. But this is only needed for complex
51+
* migration projects.
52+
*/
53+
new DbMigration
54+
{
55+
FromVersion = "1.1",
56+
ToVersion = "2.2",
57+
Instructions = new List<DbMigrationInstruction>
58+
{
59+
new DbMigrationInstruction
60+
{
61+
Action = "renameStore",
62+
StoreName = "oldStore",
63+
Details = "newStore"
64+
}
65+
}
66+
}
67+
};
68+
});
69+
```
70+
71+
3. Add the following scripts to the end of the body tag in your `Index.html`:
72+
73+
```html
74+
<script src="_content/Magic.IndexedDb/dexie.min.js"></script>
75+
<script src="_content/Magic.IndexedDb/magicDb.js"></script>
76+
```
77+
78+
4. Add the following to your _Import.razor:
79+
80+
```csharp
81+
@using Magic.IndexedDb
82+
@inject IMagicDbFactory _MagicDb
83+
```
84+
85+
### Creating a class with Magic attributes
86+
87+
Define your class with the `MagicTable` attribute and the appropriate magic attributes for each property. For example:
88+
89+
```csharp
90+
[MagicTable("Person", "DatabaseName")]
91+
public class Person
92+
{
93+
[MagicPrimaryKey("id")]
94+
public int _Id { get; set; }
95+
96+
[MagicIndex]
97+
public string Name { get; set; }
98+
99+
[MagicIndex("Age")]
100+
public int _Age { get; set; }
101+
102+
[MagicEncrypt]
103+
public string Secret { get; set; }
104+
105+
[MagicNotMapped]
106+
public string SecretDecrypted { get; set; }
107+
}
108+
```
109+
I highly suggest you always add the string parameters as that sets the IndexedDb column name. By default it'll use the C# class property name. But You should always have some very unique and static set string for the attribute. That way if/when you change the c# property names, class names, or anything, the schema will not care because the code has smart logic to differentiate the C# class property names and the IndexedDb column names that was set in your attribute. This way you can freely change any C# class properties without ever caring about needing to create migration code. You can additionally add or remove columns freely without issue.
110+
111+
## Attributes
112+
113+
- `MagicTable(string, string)`: Associates the class with a table in IndexedDb. The first parameter is the table name, and the second parameter is the database name.
114+
- `MagicPrimaryKey(string)`: Marks the property as the primary key. The parameter is the column name in IndexedDb.
115+
- `MagicIndex(string)`: Creates a searchable index for the property. The parameter is the column name in IndexedDb.
116+
- `MagicUniqueIndex(string)`: Creates a unique index for the property. The parameter is the column name in IndexedDb.
117+
- `MagicEncrypt`: Encrypts the string property when it's stored in IndexedDb.
118+
- `MagicNotMapped`: Excludes the property from being mapped to IndexedDb.
119+
120+
### Using the DbManager
121+
122+
1. Get the `DbManager` for your database:
123+
124+
```csharp
125+
var manager = await _MagicDb.GetDbManager(DbNames.Client);
126+
```
127+
128+
2. Perform operations with the `DbManager`, such as adding, updating, deleting, and querying data.
129+
130+
131+
Here's a grid-style documentation for the methods:
132+
133+
| Method | Description |
134+
| ------ | ----------- |
135+
| `AddRange<T>(IEnumerable<T> records)` | Bulk add records to a table. |
136+
| `ClearTableAsync(string storeName)` | Clear all records in a table. |
137+
| `DeleteDbAsync(string dbName)` | Delete a database by name. |
138+
| `Add<T>(T record, Action<BlazorDbEvent> action)` | Add a single record to a table. |
139+
| `AddRange<T>(IEnumerable<T> records, Action<BlazorDbEvent> action)` | Bulk add records to a table with an action callback. |
140+
| `ClearTable(string storeName, Action<BlazorDbEvent> action)` | Clear all records in a table with an action callback. |
141+
| `ClearTable<T>(Action<BlazorDbEvent> action)` | Clear all records in a table of type `T` with an action callback. |
142+
| `Delete<T>(T item, Action<BlazorDbEvent> action)` | Delete a record of type `T` with an action callback. |
143+
| `DeleteDb(string dbName, Action<BlazorDbEvent> action)` | Delete a database by name with an action callback. |
144+
| `OpenDb(Action<BlazorDbEvent> action)` | Open a database with an action callback. |
145+
| `Update<T>(T item, Action<BlazorDbEvent> action)` | Update a record of type `T` with an action callback. |
146+
| `UpdateRange<T>(IEnumerable<T> items, Action<BlazorDbEvent> action)` | Bulk update records of type `T` with an action callback. |
147+
| `GetAll<T>()` | Get all records of type `T` from a table. |
148+
| `DeleteRange<TResult>(IEnumerable<TResult> items)` | Bulk delete records of type `TResult`. |
149+
| `Decrypt(string EncryptedValue)` | Decrypt an encrypted string. |
150+
| `GetById<TResult>(object key)` | Get a record of type `TResult` by its primary key. |
151+
| `Where<T>(Expression<Func<T, bool>> predicate)` | Query method to allow complex query capabilities for records of type `T`. |
152+
153+
154+
### "Where" Method MagicQuery syntax
155+
156+
| Method | Description |
157+
| ------ | ----------- |
158+
| `Take(int amount)` | Limits the number of records returned by the query to the specified amount. |
159+
| `TakeLast(int amount)` | Returns the last specified number of records in the query. |
160+
| `Skip(int amount)` | Skips the specified number of records in the query result. |
161+
| `OrderBy(Expression<Func<T, object>> predicate)` | Orders the query result by the specified predicate in ascending order. |
162+
| `OrderByDescending(Expression<Func<T, object>> predicate)` | Orders the query result by the specified predicate in descending order. |
163+
| `Execute()` | Executes the MagicQuery and returns the results as an `IEnumerable<T>`. |
164+
165+
These MagicQuery methods allow you to build complex queries similar to standard LINQ in C#. Remember to call the `Execute` method at the end of your MagicQuery to execute the query and retrieve the results.
166+
167+
## Examples
168+
169+
To start using MagicIndexedDb, you need to create a `DbManager` instance for your specific database.
170+
171+
```csharp
172+
private List<Person> allPeople { get; set; } = new List<Person>();
173+
private IEnumerable<Person> WhereExample { get; set; } = Enumerable.Empty<Person>();
174+
175+
protected override async Task OnAfterRenderAsync(bool firstRender)
176+
{
177+
if (firstRender)
178+
{
179+
try
180+
{
181+
var manager = await _MagicDb.GetDbManager(DbNames.Client);
182+
183+
await manager.ClearTable<Person>();
184+
185+
var AllThePeeps = await manager.GetAll<Person>();
186+
if (AllThePeeps.Count() < 1)
187+
{
188+
Person[] persons = new Person[] {
189+
new Person { Name = "Zack", TestInt = 9, _Age = 45, GUIY = Guid.NewGuid(), Secret = "I buried treasure behind my house"},
190+
new Person { Name = "Luna", TestInt = 9, _Age = 35, GUIY = Guid.NewGuid(), Secret = "Jerry is my husband and I had an affair with Bob."},
191+
new Person { Name = "Jerry", TestInt = 9, _Age = 35, GUIY = Guid.NewGuid(), Secret = "My wife is amazing"},
192+
new Person { Name = "Jon", TestInt = 9, _Age = 37, GUIY = Guid.NewGuid(), Secret = "I black mail Luna for money because I know her secret"},
193+
new Person { Name = "Jack", TestInt = 9, _Age = 37, GUIY = Guid.NewGuid(), Secret = "I have a drug problem"},
194+
new Person { Name = "Cathy", TestInt = 9, _Age = 22, GUIY = Guid.NewGuid(), Secret = "I got away with reading Bobs diary."},
195+
new Person { Name = "Bob", TestInt = 3 , _Age = 69, GUIY = Guid.NewGuid(), Secret = "I caught Cathy reading my diary, but I'm too shy to confront her." },
196+
new Person { Name = "Alex", TestInt = 3 , _Age = 80, GUIY = Guid.NewGuid(), Secret = "I'm naked! But nobody can know!" }
197+
};
198+
199+
await manager.AddRange(persons);
200+
}
201+
202+
var allPeopleDecrypted = await manager.GetAll<Person>();
203+
204+
foreach (Person person in allPeopleDecrypted)
205+
{
206+
person.SecretDecrypted = await manager.Decrypt(person.Secret);
207+
allPeople.Add(person);
208+
}
209+
210+
WhereExample = await manager.Where<Person>(x => x.Name.StartsWith("c", StringComparison.OrdinalIgnoreCase)
211+
|| x.Name.StartsWith("l", StringComparison.OrdinalIgnoreCase)
212+
|| x.Name.StartsWith("j", StringComparison.OrdinalIgnoreCase) && x._Age > 35
213+
).OrderBy(x => x._Id).Skip(1).Execute();
214+
215+
StateHasChanged();
216+
}
217+
catch (Exception ex)
218+
{
219+
// Handle exception
220+
}
221+
}
222+
}
223+
```
224+
225+
## Adding Records
226+
227+
To add new records, use the `AddRange` method on the `DbManager` instance. This example adds new `Person` records to the database:
228+
229+
```csharp
230+
if (AllThePeeps.Count() < 1)
231+
{
232+
Person[] persons = new Person[] {
233+
// ... (list of Person objects)
234+
};
235+
236+
await manager.AddRange(persons);
237+
}
238+
```
239+
240+
## Retrieving Records
241+
242+
To retrieve all records of a specific type, use the `GetAll` method on the `DbManager` instance. This example retrieves all `Person` records:
243+
244+
```csharp
245+
var allPeople = await manager.GetAll<Person>();
246+
```
247+
248+
## Decrypting Records
249+
250+
To decrypt a specific property of a record, use the `Decrypt` method on the `DbManager` instance. This example decrypts the `Secret` property of each `Person`:
251+
252+
```csharp
253+
foreach (Person person in allPeople)
254+
{
255+
person.SecretDecrypted = await manager.Decrypt(person.Secret);
256+
}
257+
```
258+
259+
## Querying Records
260+
261+
To query records based on specific conditions, use the `Where` method on the `DbManager` instance. You can chain additional LINQ methods, such as `OrderBy`, `Skip`, and `Execute`, to further refine your query. This example retrieves `Person` records that match certain criteria:
262+
263+
```csharp
264+
var whereExample = await manager.Where<Person>(x => x.Name.StartsWith("c", StringComparison.OrdinalIgnoreCase)
265+
|| x.Name.StartsWith("l", StringComparison.OrdinalIgnoreCase)
266+
|| x.Name.StartsWith("j", StringComparison.OrdinalIgnoreCase) && x._Age > 35
267+
).OrderBy(x => x._Id).Skip(1).Execute();
268+
```
269+
270+
In this example, the query returns `Person` records where the `Name` property starts with "c", "l", or "j" (case-insensitive), and the `_Age` property is greater than 35. The results are ordered by the `_Id` property and the first record is skipped.
271+
272+
These examples demonstrate the basics of using MagicIndexedDb in a Blazor WebAssembly application. You can also perform other operations, such as updating and deleting records, by using the appropriate methods provided by the `DbManager`.

0 commit comments

Comments
 (0)