|
| 1 | +# **Setting Up Magic IndexedDB in Blazor** |
| 2 | + |
| 3 | +## **1. Install the NuGet Package** |
| 4 | + |
| 5 | +The first step is to install the **latest version** of **Magic IndexedDB** from NuGet: |
| 6 | +🔗 **[Magic.IndexedDb on NuGet](https://www.nuget.org/packages/Magic.IndexedDb/)** |
| 7 | + |
| 8 | +Before updating, it's **highly recommended** to review the latest **release notes** to check for any important changes or enhancements: |
| 9 | +🔗 **[Release Notes & Updates](https://github.com/magiccodingman/Magic.IndexedDb/releases)** |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## **2. Register the Magic IndexedDB Service** |
| 14 | + |
| 15 | +Once installed, you must register **Magic IndexedDB** in your Blazor application's dependency injection container. Add the following to your **Program.cs** file: |
| 16 | + |
| 17 | +### **🚀 Default Safe Message Limits** |
| 18 | + |
| 19 | +```csharp |
| 20 | +// Default safe message limit for WASM applications |
| 21 | +builder.Services.AddMagicBlazorDB(BlazorInteropMode.WASM, builder.HostEnvironment.IsDevelopment()); |
| 22 | + |
| 23 | +// Default safe message limit for Blazor Hybrid applications (SignalR mode) |
| 24 | +builder.Services.AddMagicBlazorDB(BlazorInteropMode.SignalR, builder.HostEnvironment.IsDevelopment()); |
| 25 | +``` |
| 26 | + |
| 27 | +### **📏 Custom Message Limit (Advanced)** |
| 28 | + |
| 29 | +If you need to **customize** the message size limit (in bytes), you can specify it manually: |
| 30 | + |
| 31 | +```csharp |
| 32 | +// Custom message limit of 35MB |
| 33 | +long customMessageLimitBytes = 35 * 1024 * 1024; |
| 34 | +builder.Services.AddMagicBlazorDB(customMessageLimitBytes, builder.HostEnvironment.IsDevelopment()); |
| 35 | +``` |
| 36 | + |
| 37 | +### **🔹 Understanding Interop Modes** |
| 38 | + |
| 39 | +|**Interop Mode**|**Use Case**| |
| 40 | +|---|---| |
| 41 | +|`BlazorInteropMode.WASM`|Used for **standalone Blazor WebAssembly applications**| |
| 42 | +|`BlazorInteropMode.SignalR`|Recommended for **Blazor Hybrid applications** where SignalR is used for communication| |
| 43 | + |
| 44 | +The interop mode determines **how** the JavaScript and C# layers communicate. The **message limit** controls **how much data** can be sent between IndexedDB and C#. **A higher limit increases speed but also memory usage**, so tune it based on your needs. |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +## **3. Debug vs. Production Mode** |
| 49 | + |
| 50 | +The second parameter in `AddMagicBlazorDB` is: |
| 51 | + |
| 52 | +```csharp |
| 53 | +builder.HostEnvironment.IsDevelopment() |
| 54 | +``` |
| 55 | + |
| 56 | +This boolean **indicates whether the application is in Debug mode or not**. |
| 57 | + |
| 58 | +### **🛠 Why This Matters?** |
| 59 | + |
| 60 | +- When **enabled in development**, Magic IndexedDB **validates your database schema** at startup. |
| 61 | +- It performs **system reflection-based validation** to **detect potential issues early**. |
| 62 | +- In **production mode**, validation is **skipped** to avoid unnecessary performance overhead. |
| 63 | +- **AOT Compatibility**: Reflection-based validation may not work in **Ahead-of-Time (AOT) compiled scenarios**. Keeping it enabled **only in development** ensures a smooth experience. |
| 64 | + |
| 65 | +> **TL;DR:** In **development mode**, Magic IndexedDB **protects you from mistakes** by validating your setup **before you run into issues**. In **production mode**, it prioritizes **speed and efficiency**. |
| 66 | +
|
| 67 | +--- |
| 68 | + |
| 69 | +## **4. Defining Your IndexedDB Schema** |
| 70 | + |
| 71 | +### **Understanding IndexedDB Repositories** |
| 72 | + |
| 73 | +Unlike traditional databases, **IndexedDB operates differently**. **Think of repositories like defining tables** rather than databases. |
| 74 | + |
| 75 | +To create a schema, **define a repository** in your Blazor project or any referenced project. |
| 76 | + |
| 77 | +--- |
| 78 | + |
| 79 | +### **5. Creating the IndexedDB Context** |
| 80 | + |
| 81 | +Inside your Blazor project (or a referenced project), create a new **C# file** (e.g., `IndexedDbContext.cs`) and **implement `IMagicRepository`**: |
| 82 | + |
| 83 | +```csharp |
| 84 | +public class IndexedDbContext : IMagicRepository |
| 85 | +{ |
| 86 | + public static readonly IndexedDbSet Client = new("Client"); |
| 87 | + public static readonly IndexedDbSet Employee = new("Employee"); |
| 88 | + public static readonly IndexedDbSet Animal = new("Animal"); |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +### **🔍 How It Works** |
| 93 | + |
| 94 | +- The system will **automatically detect** this repository **no matter where it is**, even if it resides in a **referenced project**. It's detected by the **`IMagicRepository`** interface itself being attached. |
| 95 | +- Why this is exists will be shown below. |
| 96 | + |
| 97 | +> **💡 Important:** |
| 98 | +> This setup **alone** is enough to define basic IndexedDB tables. |
| 99 | +> **For complex migration support, additional steps will be needed later**. |
| 100 | +
|
| 101 | +# **Defining Tables in Magic IndexedDB** |
| 102 | + |
| 103 | +## **1. Understanding Tables in Magic IndexedDB** |
| 104 | + |
| 105 | +Tables in **Magic IndexedDB** are **universally reusable** across any IndexedDB database you deploy. Defining a table is as simple as **creating a C# class** that represents your data and appending it with the appropriate **Magic IndexedDB interfaces and tools**. |
| 106 | + |
| 107 | +Each table: |
| 108 | + |
| 109 | +- **Defines its schema** with properties. |
| 110 | +- **Registers its database associations**. |
| 111 | +- **Supports compound keys, indexes, and unique constraints**. |
| 112 | +- **Automatically migrates when you modify its structure**. |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +## **2. Creating a Table (Basic Example)** |
| 117 | + |
| 118 | +To define a table, you must: |
| 119 | + |
| 120 | +1. Create a **class** that represents your data. |
| 121 | +2. Inherit from `MagicTableTool<T>`. |
| 122 | +3. Implement `IMagicTable<TDbSets>`. |
| 123 | +4. Define indexes, keys, and other configurations as needed. |
| 124 | + |
| 125 | +### **📌 Example: Defining a `Person` Table** |
| 126 | + |
| 127 | +```csharp |
| 128 | +public class Person : MagicTableTool<Person>, IMagicTable<DbSets> |
| 129 | +{ |
| 130 | + public static readonly IndexedDbSet Client = IndexDbContext.Client; |
| 131 | + |
| 132 | + public IMagicCompoundKey GetKeys() => |
| 133 | + CreatePrimaryKey(x => x.Id, true); // Auto-incrementing primary key |
| 134 | +
|
| 135 | + public string GetTableName() => "Person"; |
| 136 | + public IndexedDbSet GetDefaultDatabase() => IndexDbContext.Client; |
| 137 | + |
| 138 | + public DbSets Databases { get; } = new(); |
| 139 | + public sealed class DbSets |
| 140 | + { |
| 141 | + public readonly IndexedDbSet Client = IndexDbContext.Client; |
| 142 | + public readonly IndexedDbSet Employee = IndexDbContext.Employee; |
| 143 | + } |
| 144 | + |
| 145 | + [MagicIndex] // Creates an index on this field |
| 146 | + public string Name { get; set; } |
| 147 | + |
| 148 | + [MagicUniqueIndex("guid")] // Unique constraint |
| 149 | + public Guid UniqueGuid { get; set; } = Guid.NewGuid(); |
| 150 | + |
| 151 | + public int Age { get; set; } |
| 152 | + |
| 153 | + [MagicNotMapped] // Exclude from IndexedDB schema |
| 154 | + public string Secret { get; set; } |
| 155 | +} |
| 156 | +``` |
| 157 | + |
| 158 | +--- |
| 159 | + |
| 160 | +## **3. Breaking Down the Table Definition** |
| 161 | + |
| 162 | +### **🛠 Understanding `DbSets`** |
| 163 | + |
| 164 | +The `<TDbSets>` type parameter in `IMagicTable<TDbSets>` exists to enforce **clean C# code structure**. |
| 165 | + |
| 166 | +- You **define `DbSets` however you like**, but it **must include**: |
| 167 | + |
| 168 | + ```csharp |
| 169 | + public TDbSets Databases { get; } = new(); |
| 170 | + ``` |
| 171 | + |
| 172 | +- This makes the query system **strongly typed**, allowing clean LINQ queries like: |
| 173 | + |
| 174 | + ```csharp |
| 175 | + await _MagicDb.Query<Person>(x => x.Databases.Client); |
| 176 | + ``` |
| 177 | + |
| 178 | + If you're unfamiliar with this pattern, **review the [Introduction Page](https://github.com/magiccodingman/Magic.IndexedDb/blob/master/MagicIndexDbWiki/Getting-Started-Blazor/P1-Introduction.md)**. |
| 179 | + |
| 180 | +--- |
| 181 | + |
| 182 | +## **4. Defining Keys & Indexes** |
| 183 | + |
| 184 | +### **🔑 Setting the Primary Key** |
| 185 | + |
| 186 | +```csharp |
| 187 | +public IMagicCompoundKey GetKeys() => |
| 188 | + CreatePrimaryKey(x => x.Id, true); |
| 189 | +``` |
| 190 | + |
| 191 | +- The **first parameter** is the **primary key property**. |
| 192 | +- The **second parameter** (`true` or `false`) sets **auto-incrementing** behavior. |
| 193 | + |
| 194 | +### **🗝 Defining a Compound Key** |
| 195 | + |
| 196 | +```csharp |
| 197 | +public IMagicCompoundKey GetKeys() => |
| 198 | + CreateCompoundKey(x => x.Field1, x => x.Field2); |
| 199 | +``` |
| 200 | + |
| 201 | +- **Compound keys combine multiple fields** as a unique key. |
| 202 | +- **Auto-incrementing** is **not allowed** on compound keys. |
| 203 | + |
| 204 | +--- |
| 205 | + |
| 206 | +## **5. Additional Table Configurations** |
| 207 | + |
| 208 | +### **📌 `GetTableName()` – Table Naming** |
| 209 | + |
| 210 | +Sets the table name in IndexedDB: |
| 211 | + |
| 212 | +```csharp |
| 213 | +public string GetTableName() => "Person"; |
| 214 | +``` |
| 215 | + |
| 216 | +This lets you **rename** your C# class **without breaking migrations**. |
| 217 | + |
| 218 | +### **📌 `GetDefaultDatabase()` – Default Storage Location** |
| 219 | + |
| 220 | +```csharp |
| 221 | +public IndexedDbSet GetDefaultDatabase() => IndexDbContext.Client; |
| 222 | +``` |
| 223 | + |
| 224 | +Tells the system which **database** this table belongs to by default. |
| 225 | + |
| 226 | +--- |
| 227 | + |
| 228 | +## **6. Using Attributes for IndexedDB Optimization** |
| 229 | + |
| 230 | +### **🔍 `MagicName` – Rename Columns in IndexedDB** |
| 231 | + |
| 232 | +```csharp |
| 233 | +[MagicName("_id")] |
| 234 | +public int Id { get; set; } |
| 235 | +``` |
| 236 | + |
| 237 | +- **Ensures column names stay consistent** in IndexedDB. |
| 238 | +- **Highly recommended** to prevent migration issues when renaming C# properties. |
| 239 | + |
| 240 | +### **📌 `MagicIndex` – Create an Indexed Column** |
| 241 | + |
| 242 | +```csharp |
| 243 | +[MagicIndex] |
| 244 | +public string Name { get; set; } |
| 245 | +``` |
| 246 | + |
| 247 | +Speeds up queries using this field. |
| 248 | + |
| 249 | +### **📌 `MagicUniqueIndex` – Unique Constraints** |
| 250 | + |
| 251 | +```csharp |
| 252 | +[MagicUniqueIndex("guid")] |
| 253 | +public Guid UniqueGuid { get; set; } = Guid.NewGuid(); |
| 254 | +``` |
| 255 | + |
| 256 | +Prevents duplicate values in this column. |
| 257 | + |
| 258 | +### **📌 `MagicNotMapped` – Exclude Fields from IndexedDB** |
| 259 | + |
| 260 | +```csharp |
| 261 | +[MagicNotMapped] |
| 262 | +public string Secret { get; set; } |
| 263 | +``` |
| 264 | + |
| 265 | +Keeps the property **in C# but out of IndexedDB**. |
| 266 | + |
| 267 | +--- |
| 268 | + |
| 269 | +## **7. Nested Objects in IndexedDB** |
| 270 | + |
| 271 | +Yes, **Magic IndexedDB supports nested objects**! |
| 272 | + |
| 273 | +```csharp |
| 274 | +public class Address |
| 275 | +{ |
| 276 | + public string City { get; set; } |
| 277 | + public string State { get; set; } |
| 278 | +} |
| 279 | + |
| 280 | +public class Person : MagicTableTool<Person>, IMagicTable<DbSets> |
| 281 | +{ |
| 282 | + public Address HomeAddress { get; set; } = new Address(); |
| 283 | +} |
| 284 | +``` |
| 285 | + |
| 286 | +- **Fully supported** by the custom-built serializer. |
| 287 | +- **Validations ensure** that your schema remains stable. |
| 288 | + |
| 289 | +--- |
| 290 | + |
| 291 | +## **8. Schema Validation & Protection** |
| 292 | + |
| 293 | +Magic IndexedDB **validates your schema** to **prevent broken tables**: ✔ **Ensures compound keys don’t have forbidden names like `id`**. |
| 294 | +✔ **Warns you if you rename fields without using `[MagicName]`**. |
| 295 | +✔ **Protects you from invalid IndexedDB constraints**. |
| 296 | + |
| 297 | +--- |
| 298 | + |
| 299 | +## **9. Next Steps – Handling Migrations** |
| 300 | + |
| 301 | +Once you’ve defined your tables, the **next critical step is handling migrations**. |
| 302 | +Schema changes need to be **tracked and managed** so you don’t lose data. |
| 303 | + |
| 304 | +🔥 **Learn how to handle migrations:** |
| 305 | +➡ **[Check out the Magic IndexedDB Migrations Guide](https://github.com/magiccodingman/Magic.IndexedDb/blob/master/MagicIndexDbWiki/Getting-Started-Blazor/P3-Migrations.md)** |
0 commit comments