diff --git a/E2eTest/Entities/DatabaseInfo.cs b/E2eTest/Entities/DatabaseInfo.cs
index c6e58c9..919599f 100644
--- a/E2eTest/Entities/DatabaseInfo.cs
+++ b/E2eTest/Entities/DatabaseInfo.cs
@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace E2eTest.Entities;
+namespace E2eTest.Entities;
internal sealed class DatabaseInfo
{
public string? Name { get; set; }
diff --git a/E2eTest/Extensions/AssertExtensions.cs b/E2eTest/Extensions/AssertExtensions.cs
index 8049d24..a6a3030 100644
--- a/E2eTest/Extensions/AssertExtensions.cs
+++ b/E2eTest/Extensions/AssertExtensions.cs
@@ -1,14 +1,5 @@
-using E2eTest.Entities;
-using Microsoft.AspNetCore.Mvc.RazorPages;
-using Microsoft.Playwright;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Text;
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
-using System.Threading.Tasks;
-using System.Xml.Linq;
namespace E2eTest.Extensions;
internal static class AssertExtensions
diff --git a/E2eTest/Extensions/PageExtensions.cs b/E2eTest/Extensions/PageExtensions.cs
index 6a3891a..e0b5d6c 100644
--- a/E2eTest/Extensions/PageExtensions.cs
+++ b/E2eTest/Extensions/PageExtensions.cs
@@ -1,12 +1,5 @@
using E2eTest.Entities;
-using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Playwright;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Xml.Linq;
namespace E2eTest.Extensions;
internal static class PageExtensions
diff --git a/E2eTest/SingleRecordBasicTest.cs b/E2eTest/SingleRecordBasicTest.cs
index 23b9018..ade0d14 100644
--- a/E2eTest/SingleRecordBasicTest.cs
+++ b/E2eTest/SingleRecordBasicTest.cs
@@ -1,5 +1,4 @@
-using E2eTest.Entities;
-using E2eTest.Extensions;
+using E2eTest.Extensions;
using E2eTestWebApp.TestPages;
namespace E2eTest;
diff --git a/E2eTest/TestBase.cs b/E2eTest/TestBase.cs
index ac47f2c..178f981 100644
--- a/E2eTest/TestBase.cs
+++ b/E2eTest/TestBase.cs
@@ -1,12 +1,10 @@
// works with TestPageBase in E2eTestWebApp
-using E2eTest.Entities;
using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Playwright;
using System.Linq.Expressions;
using System.Reflection;
-using System.Text.Json;
+
namespace E2eTest;
diff --git a/E2eTest/WhereTest.cs b/E2eTest/WhereTest.cs
index 4fcffd1..13643b3 100644
--- a/E2eTest/WhereTest.cs
+++ b/E2eTest/WhereTest.cs
@@ -1,5 +1,4 @@
-using E2eTest.Entities;
-using E2eTest.Extensions;
+using E2eTest.Extensions;
using E2eTestWebApp.TestPages;
namespace E2eTest;
diff --git a/E2eTestWebApp/Program.cs b/E2eTestWebApp/Program.cs
index 5ea7563..ae3a29c 100644
--- a/E2eTestWebApp/Program.cs
+++ b/E2eTestWebApp/Program.cs
@@ -1,5 +1,4 @@
using Magic.IndexedDb;
-using Magic.IndexedDb.Extensions;
using System.Diagnostics;
namespace E2eTestWebApp;
diff --git a/E2eTestWebApp/TestPages/OpenTestPage.cs b/E2eTestWebApp/TestPages/OpenTestPage.cs
index 1660515..74a9973 100644
--- a/E2eTestWebApp/TestPages/OpenTestPage.cs
+++ b/E2eTestWebApp/TestPages/OpenTestPage.cs
@@ -1,6 +1,4 @@
using Magic.IndexedDb;
-using Magic.IndexedDb.Helpers;
-using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components;
namespace E2eTestWebApp.TestPages;
diff --git a/E2eTestWebApp/TestPages/SingleRecordBasicTestPage.cs b/E2eTestWebApp/TestPages/SingleRecordBasicTestPage.cs
index 2f9ca62..f604636 100644
--- a/E2eTestWebApp/TestPages/SingleRecordBasicTestPage.cs
+++ b/E2eTestWebApp/TestPages/SingleRecordBasicTestPage.cs
@@ -1,9 +1,6 @@
using Magic.IndexedDb;
using Magic.IndexedDb.Helpers;
-using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components;
-using System.Runtime.CompilerServices;
-using System.Text.Json.Serialization;
using Magic.IndexedDb.SchemaAnnotations;
using System.Text.Json;
diff --git a/E2eTestWebApp/TestPages/TestPageBase.razor.cs b/E2eTestWebApp/TestPages/TestPageBase.razor.cs
index 16cf2c5..0f86056 100644
--- a/E2eTestWebApp/TestPages/TestPageBase.razor.cs
+++ b/E2eTestWebApp/TestPages/TestPageBase.razor.cs
@@ -1,10 +1,3 @@
-using Magic.IndexedDb;
-using Magic.IndexedDb.Helpers;
-using Microsoft.AspNetCore.Components.Rendering;
-using Microsoft.AspNetCore.Components;
-using System.Diagnostics;
-using System.Text.Json;
-
namespace E2eTestWebApp.TestPages;
partial class TestPageBase
diff --git a/Magic.IndexedDb/Cache.cs b/Magic.IndexedDb/Cache.cs
index 1f5d556..5eb32f9 100644
--- a/Magic.IndexedDb/Cache.cs
+++ b/Magic.IndexedDb/Cache.cs
@@ -1,16 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace Magic.IndexedDb;
-namespace Magic.IndexedDb
+internal static class Cache
{
- internal static class Cache
- {
- ///
- /// this is the wwwroot path of "./magicDB.js" for importing the script
- ///
- public const string MagicDbJsImportPath = "./magicDB.js";
- }
-}
+ ///
+ /// this is the wwwroot path of "./magicDB.js" for importing the script
+ ///
+ public const string MagicDbJsImportPath = "./magicDB.js";
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Extensions/MagicCompoundExtension.cs b/Magic.IndexedDb/Extensions/MagicCompoundExtension.cs
index 04773a8..7771f6e 100644
--- a/Magic.IndexedDb/Extensions/MagicCompoundExtension.cs
+++ b/Magic.IndexedDb/Extensions/MagicCompoundExtension.cs
@@ -1,23 +1,17 @@
using Magic.IndexedDb.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Linq.Expressions;
-using System.Text;
-using System.Threading.Tasks;
-namespace Magic.IndexedDb
+namespace Magic.IndexedDb;
+
+internal static class MagicCompoundExtension
{
- internal static class MagicCompoundExtension
+ public static IMagicCompoundIndex CreateIndex(params Expression>[] keySelectors)
{
- public static IMagicCompoundIndex CreateIndex(params Expression>[] keySelectors)
- {
- return InternalMagicCompoundIndex.Create(keySelectors);
- }
+ return InternalMagicCompoundIndex.Create(keySelectors);
+ }
- public static IMagicCompoundKey CreateKey(bool autoIncrement, params Expression>[] keySelectors)
- {
- return InternalMagicCompoundKey.Create(autoIncrement, keySelectors);
- }
+ public static IMagicCompoundKey CreateKey(bool autoIncrement, params Expression>[] keySelectors)
+ {
+ return InternalMagicCompoundKey.Create(autoIncrement, keySelectors);
}
-}
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Extensions/MagicJsChunkProcessor.cs b/Magic.IndexedDb/Extensions/MagicJsChunkProcessor.cs
index 34415bd..996bf39 100644
--- a/Magic.IndexedDb/Extensions/MagicJsChunkProcessor.cs
+++ b/Magic.IndexedDb/Extensions/MagicJsChunkProcessor.cs
@@ -1,91 +1,83 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.Concurrent;
-namespace Magic.IndexedDb.Extensions
+namespace Magic.IndexedDb.Extensions;
+
+public static class MagicJsChunkProcessor
{
- public static class MagicJsChunkProcessor
+ private static readonly ConcurrentDictionary>> _chunkedMessages = new();
+ private static readonly ConcurrentDictionary> _instanceCompleteItems = new();
+ private static readonly ConcurrentDictionary> _instanceOrderedItems = new();
+
+ public static void RegisterInstance(string instanceId)
{
- private static readonly ConcurrentDictionary>> _chunkedMessages = new();
- private static readonly ConcurrentDictionary> _instanceCompleteItems = new();
- private static readonly ConcurrentDictionary> _instanceOrderedItems = new();
+ _chunkedMessages.TryAdd(instanceId, new ConcurrentDictionary>());
+ _instanceCompleteItems.TryAdd(instanceId, new ConcurrentQueue());
+ _instanceOrderedItems.TryAdd(instanceId, new SortedDictionary());
+ }
- public static void RegisterInstance(string instanceId)
+ public static void AddChunk(string instanceId, string chunkInstanceId, int yieldOrderIndex, string chunk, int chunkIndex, int totalChunks)
+ {
+ // ✅ Ensure the instance exists before proceeding
+ var messageStore = _chunkedMessages.GetOrAdd(instanceId, _ => new ConcurrentDictionary>());
+ var orderedItems = _instanceOrderedItems.GetOrAdd(instanceId, _ => new SortedDictionary());
+ var completedQueue = _instanceCompleteItems.GetOrAdd(instanceId, _ => new ConcurrentQueue());
+
+ if (chunkInstanceId == "STREAM_COMPLETE")
{
- _chunkedMessages.TryAdd(instanceId, new ConcurrentDictionary>());
- _instanceCompleteItems.TryAdd(instanceId, new ConcurrentQueue());
- _instanceOrderedItems.TryAdd(instanceId, new SortedDictionary());
+ completedQueue.Enqueue("STREAM_COMPLETE");
+ return;
}
- public static void AddChunk(string instanceId, string chunkInstanceId, int yieldOrderIndex, string chunk, int chunkIndex, int totalChunks)
+ // ✅ Ensure chunkInstanceId exists
+ var messageChunks = messageStore.GetOrAdd(chunkInstanceId, _ => new Dictionary());
+ messageChunks[chunkIndex] = chunk;
+
+ // ✅ Check if all chunks are received
+ if (messageChunks.Count == totalChunks)
{
- // ✅ Ensure the instance exists before proceeding
- var messageStore = _chunkedMessages.GetOrAdd(instanceId, _ => new ConcurrentDictionary>());
- var orderedItems = _instanceOrderedItems.GetOrAdd(instanceId, _ => new SortedDictionary());
- var completedQueue = _instanceCompleteItems.GetOrAdd(instanceId, _ => new ConcurrentQueue());
+ var fullMessage = string.Join("", messageChunks.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value));
- if (chunkInstanceId == "STREAM_COMPLETE")
+ lock (orderedItems)
{
- completedQueue.Enqueue("STREAM_COMPLETE");
- return;
+ orderedItems[yieldOrderIndex] = fullMessage;
}
- // ✅ Ensure chunkInstanceId exists
- var messageChunks = messageStore.GetOrAdd(chunkInstanceId, _ => new Dictionary());
- messageChunks[chunkIndex] = chunk;
-
- // ✅ Check if all chunks are received
- if (messageChunks.Count == totalChunks)
- {
- var fullMessage = string.Join("", messageChunks.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value));
-
- lock (orderedItems)
- {
- orderedItems[yieldOrderIndex] = fullMessage;
- }
-
- messageStore.TryRemove(chunkInstanceId, out _); // ✅ Corrected removal
- }
+ messageStore.TryRemove(chunkInstanceId, out _); // ✅ Corrected removal
}
+ }
- public static string? GetCompletedItem(string instanceId)
- {
- var orderedItems = _instanceOrderedItems.GetOrAdd(instanceId, _ => new SortedDictionary());
- var completedQueue = _instanceCompleteItems.GetOrAdd(instanceId, _ => new ConcurrentQueue());
+ public static string? GetCompletedItem(string instanceId)
+ {
+ var orderedItems = _instanceOrderedItems.GetOrAdd(instanceId, _ => new SortedDictionary());
+ var completedQueue = _instanceCompleteItems.GetOrAdd(instanceId, _ => new ConcurrentQueue());
- lock (orderedItems)
+ lock (orderedItems)
+ {
+ // If we still have items to return, prioritize them
+ if (orderedItems.Count > 0)
{
- // If we still have items to return, prioritize them
- if (orderedItems.Count > 0)
- {
- var firstKey = orderedItems.Keys.First();
- var message = orderedItems[firstKey];
+ var firstKey = orderedItems.Keys.First();
+ var message = orderedItems[firstKey];
- orderedItems.Remove(firstKey);
- return message;
- }
+ orderedItems.Remove(firstKey);
+ return message;
}
-
- // Only return "STREAM_COMPLETE" if ALL items have been returned
- if (completedQueue.TryPeek(out var completedMarker) && completedMarker == "STREAM_COMPLETE")
- {
- completedQueue.TryDequeue(out _); // Remove completion marker now that we're truly done
- return "STREAM_COMPLETE";
- }
-
- return null;
}
-
-
- public static void RemoveInstance(string instanceId)
+ // Only return "STREAM_COMPLETE" if ALL items have been returned
+ if (completedQueue.TryPeek(out var completedMarker) && completedMarker == "STREAM_COMPLETE")
{
- _chunkedMessages.TryRemove(instanceId, out _);
- _instanceCompleteItems.TryRemove(instanceId, out _);
- _instanceOrderedItems.TryRemove(instanceId, out _);
+ completedQueue.TryDequeue(out _); // Remove completion marker now that we're truly done
+ return "STREAM_COMPLETE";
}
+
+ return null;
+ }
+
+ public static void RemoveInstance(string instanceId)
+ {
+ _chunkedMessages.TryRemove(instanceId, out _);
+ _instanceCompleteItems.TryRemove(instanceId, out _);
+ _instanceOrderedItems.TryRemove(instanceId, out _);
}
-}
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Extensions/MagicJsInvoke.cs b/Magic.IndexedDb/Extensions/MagicJsInvoke.cs
index c25d6bb..cf6eaf3 100644
--- a/Magic.IndexedDb/Extensions/MagicJsInvoke.cs
+++ b/Magic.IndexedDb/Extensions/MagicJsInvoke.cs
@@ -2,254 +2,238 @@
using Magic.IndexedDb.Interfaces;
using Magic.IndexedDb.Models;
using Microsoft.JSInterop;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
-using static System.Runtime.InteropServices.JavaScript.JSType;
-namespace Magic.IndexedDb.Extensions
+namespace Magic.IndexedDb.Extensions;
+
+internal class MagicJsInvoke
{
- internal class MagicJsInvoke
+ private readonly IJSObjectReference _jsModule;
+ private readonly long _jsMessageSizeBytes;
+
+ public MagicJsInvoke(IJSObjectReference jsModule, long jsMessageSizeBytes)
{
- private readonly IJSObjectReference _jsModule;
- private readonly long _jsMessageSizeBytes;
+ _jsModule = jsModule;
+ _jsMessageSizeBytes = jsMessageSizeBytes;
+ }
- public MagicJsInvoke(IJSObjectReference jsModule, long jsMessageSizeBytes)
- {
- _jsModule = jsModule;
- _jsMessageSizeBytes = jsMessageSizeBytes;
- }
+ internal async Task CallJsAsync(string modulePath, string functionName,
+ CancellationToken token, params ITypedArgument[] args)
+ {
+ await MagicVoidStreamJsAsync(modulePath, functionName, token, args);
+ }
- internal async Task CallJsAsync(string modulePath, string functionName,
- CancellationToken token, params ITypedArgument[] args)
- {
- await MagicVoidStreamJsAsync(modulePath, functionName, token, args);
- }
+ internal async Task CallJsAsync(string modulePath, string functionName,
+ CancellationToken token, params ITypedArgument[] args)
+ {
- internal async Task CallJsAsync(string modulePath, string functionName,
- CancellationToken token, params ITypedArgument[] args)
- {
+ return await MagicStreamJsAsync(modulePath, functionName, token, args) ?? default;
+ }
- return await MagicStreamJsAsync(modulePath, functionName, token, args) ?? default;
- }
+ ///
+ internal async Task CallInvokeDefaultJsAsync(string modulePath, string functionName,
+ params object[] args)
+ {
- ///
- internal async Task CallInvokeDefaultJsAsync(string modulePath, string functionName,
- params object[] args)
- {
+ var throwAway = await CallInvokeDefaultJsAsync(modulePath, functionName, args);
- var throwAway = await CallInvokeDefaultJsAsync(modulePath, functionName, args);
+ return;
+ }
- return;
- }
+ ///
+ /// Utilizes InvokeAsync normally with no special serialization or streaming.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal async Task CallInvokeDefaultJsAsync(string modulePath, string functionName,
+ params object[] args)
+ {
+ return await TrueCallInvokeDefaultJsAsync(modulePath, functionName, false, args);
+ }
- ///
- /// Utilizes InvokeAsync normally with no special serialization or streaming.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- internal async Task CallInvokeDefaultJsAsync(string modulePath, string functionName,
- params object[] args)
- {
- return await TrueCallInvokeDefaultJsAsync(modulePath, functionName, false, args);
- }
+ private async Task TrueCallInvokeDefaultJsAsync(string modulePath, string functionName,
+ bool isVoid,
+ params object[] args)
+ {
+ var response = await _jsModule.InvokeAsync("JsHandler", isVoid, modulePath, functionName, args);
+ return response;
+ }
- private async Task TrueCallInvokeDefaultJsAsync(string modulePath, string functionName,
- bool isVoid,
- params object[] args)
- {
- var response = await _jsModule.InvokeAsync("JsHandler", isVoid, modulePath, functionName, args);
- return response;
- }
+ internal async Task CallInvokeVoidDefaultJsAsync(string modulePath, string functionName,
+ params object[] args)
+ {
+ await TrueCallInvokeDefaultJsAsync(modulePath, functionName, true, args);
+ }
- internal async Task CallInvokeVoidDefaultJsAsync(string modulePath, string functionName,
- params object[] args)
+ internal async IAsyncEnumerable CallYieldJsAsync(
+ string modulePath,
+ string functionName,
+ [EnumeratorCancellation] CancellationToken token,
+ params ITypedArgument[] args)
+ {
+ await foreach (var item in MagicYieldJsAsync(modulePath, functionName, token, args)
+ .WithCancellation(token)) // Ensure cancellation works in the async stream
{
- await TrueCallInvokeDefaultJsAsync(modulePath, functionName, true, args);
+ yield return item; // Yield items as they arrive
}
+ }
- internal async IAsyncEnumerable CallYieldJsAsync(
- string modulePath,
- string functionName,
- [EnumeratorCancellation] CancellationToken token,
- params ITypedArgument[] args)
- {
- await foreach (var item in MagicYieldJsAsync(modulePath, functionName, token, args)
- .WithCancellation(token)) // Ensure cancellation works in the async stream
- {
- yield return item; // Yield items as they arrive
- }
- }
+ private async Task MagicStreamJsAsync(string modulePath, string functionName, CancellationToken token, params ITypedArgument[] args)
+ {
+ return await TrueMagicStreamJsAsync(modulePath, functionName, token, false, args);
+ }
+ private async Task TrueMagicStreamJsAsync(string modulePath, string functionName,
+ CancellationToken token, bool isVoid, params ITypedArgument[] args)
+ {
+ var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
- private async Task MagicStreamJsAsync(string modulePath, string functionName, CancellationToken token, params ITypedArgument[] args)
- {
- return await TrueMagicStreamJsAsync(modulePath, functionName, token, false, args);
- }
- private async Task TrueMagicStreamJsAsync(string modulePath, string functionName,
- CancellationToken token, bool isVoid, params ITypedArgument[] args)
+ var package = new MagicJsPackage
{
- var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
-
- var package = new MagicJsPackage
- {
- YieldResults = false,
- ModulePath = modulePath,
- MethodName = functionName,
- Parameters = MagicSerializationHelper.SerializeObjectsToString(args, settings),
- IsVoid = isVoid
- };
+ YieldResults = false,
+ ModulePath = modulePath,
+ MethodName = functionName,
+ Parameters = MagicSerializationHelper.SerializeObjectsToString(args, settings),
+ IsVoid = isVoid
+ };
- string instanceId = Guid.NewGuid().ToString();
+ string instanceId = Guid.NewGuid().ToString();
#if DEBUG
- package.IsDebug = true;
+ package.IsDebug = true;
#else
package.IsDebug = false;
#endif
- using var stream = new MemoryStream();
- await using (var writer = new StreamWriter(stream, leaveOpen: true))
- {
- await MagicSerializationHelper.SerializeObjectToStreamAsync(writer, package, settings);
- }
+ using var stream = new MemoryStream();
+ await using (var writer = new StreamWriter(stream, leaveOpen: true))
+ {
+ await MagicSerializationHelper.SerializeObjectToStreamAsync(writer, package, settings);
+ }
- // ✅ Immediately release reference to `package`
- package = null;
- GC.Collect();
- GC.WaitForPendingFinalizers();
+ // ✅ Immediately release reference to `package`
+ package = null;
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
- stream.Position = 0;
+ stream.Position = 0;
- var streamRef = new DotNetStreamReference(stream);
+ var streamRef = new DotNetStreamReference(stream);
- // Send to JS
- var responseStreamRef = await _jsModule.InvokeAsync("streamedJsHandler",
- streamRef, instanceId, DotNetObjectReference.Create(this), _jsMessageSizeBytes);
+ // Send to JS
+ var responseStreamRef = await _jsModule.InvokeAsync("streamedJsHandler",
+ streamRef, instanceId, DotNetObjectReference.Create(this), _jsMessageSizeBytes);
- // 🚀 Convert the stream reference back to JSON in C#
- await using var responseStream = await responseStreamRef.OpenReadStreamAsync(long.MaxValue, token);
- using var reader = new StreamReader(responseStream);
+ // 🚀 Convert the stream reference back to JSON in C#
+ await using var responseStream = await responseStreamRef.OpenReadStreamAsync(long.MaxValue, token);
+ using var reader = new StreamReader(responseStream);
- string jsonResponse = await reader.ReadToEndAsync();
- return MagicSerializationHelper.DeserializeObject(jsonResponse, settings);
- }
+ string jsonResponse = await reader.ReadToEndAsync();
+ return MagicSerializationHelper.DeserializeObject(jsonResponse, settings);
+ }
- private async IAsyncEnumerable MagicYieldJsAsync(
- string modulePath, string functionName,
- [EnumeratorCancellation] CancellationToken token,
- params ITypedArgument[] args)
- {
- var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
+ private async IAsyncEnumerable MagicYieldJsAsync(
+ string modulePath, string functionName,
+ [EnumeratorCancellation] CancellationToken token,
+ params ITypedArgument[] args)
+ {
+ var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
- var package = new MagicJsPackage
- {
- ModulePath = modulePath,
- MethodName = functionName,
- Parameters = MagicSerializationHelper.SerializeObjectsToString(args, settings),
- IsVoid = false,
- YieldResults = true
- };
+ var package = new MagicJsPackage
+ {
+ ModulePath = modulePath,
+ MethodName = functionName,
+ Parameters = MagicSerializationHelper.SerializeObjectsToString(args, settings),
+ IsVoid = false,
+ YieldResults = true
+ };
- string instanceId = Guid.NewGuid().ToString();
+ string instanceId = Guid.NewGuid().ToString();
- using var stream = new MemoryStream();
- await using (var writer = new StreamWriter(stream, leaveOpen: true))
- {
- await MagicSerializationHelper.SerializeObjectToStreamAsync(writer, package, settings);
- }
+ using var stream = new MemoryStream();
+ await using (var writer = new StreamWriter(stream, leaveOpen: true))
+ {
+ await MagicSerializationHelper.SerializeObjectToStreamAsync(writer, package, settings);
+ }
- stream.Position = 0;
- var streamRef = new DotNetStreamReference(stream);
+ stream.Position = 0;
+ var streamRef = new DotNetStreamReference(stream);
- // Call JS with our instanceId
- await _jsModule.InvokeVoidAsync("streamedJsHandler", token, streamRef, instanceId, DotNetObjectReference.Create(this));
+ // Call JS with our instanceId
+ await _jsModule.InvokeVoidAsync("streamedJsHandler", token, streamRef, instanceId, DotNetObjectReference.Create(this));
- MagicJsChunkProcessor.RegisterInstance(instanceId);
+ MagicJsChunkProcessor.RegisterInstance(instanceId);
- bool isCompleted = false;
+ bool isCompleted = false;
- try
+ try
+ {
+ while (!isCompleted)
{
- while (!isCompleted)
+ string? completedItem;
+ try
{
- string? completedItem;
- try
- {
- completedItem = MagicJsChunkProcessor.GetCompletedItem(instanceId);
- }
- catch (Exception queueError)
+ completedItem = MagicJsChunkProcessor.GetCompletedItem(instanceId);
+ }
+ catch (Exception queueError)
+ {
+ MagicJsChunkProcessor.RemoveInstance(instanceId);
+ throw new InvalidOperationException($"Failed to retrieve chunk for instance {instanceId}.", queueError);
+ }
+
+ if (completedItem != null)
+ {
+ if (completedItem == "STREAM_COMPLETE")
{
- MagicJsChunkProcessor.RemoveInstance(instanceId);
- throw new InvalidOperationException($"Failed to retrieve chunk for instance {instanceId}.", queueError);
+ isCompleted = true;
+ break;
}
- if (completedItem != null)
+ T? deserializedItem;
+ try
{
- if (completedItem == "STREAM_COMPLETE")
- {
- isCompleted = true;
- break;
- }
-
- T? deserializedItem;
- try
- {
- deserializedItem = MagicSerializationHelper.DeserializeObject(completedItem, settings);
- }
- catch (Exception deserializationError)
- {
- MagicJsChunkProcessor.RemoveInstance(instanceId);
- throw new InvalidOperationException($"Failed to deserialize chunk for instance {instanceId}.", deserializationError);
- }
-
- yield return deserializedItem;
+ deserializedItem = MagicSerializationHelper.DeserializeObject(completedItem, settings);
}
- else
+ catch (Exception deserializationError)
{
- await Task.Delay(15, token);
+ MagicJsChunkProcessor.RemoveInstance(instanceId);
+ throw new InvalidOperationException($"Failed to deserialize chunk for instance {instanceId}.", deserializationError);
}
+
+ yield return deserializedItem;
+ }
+ else
+ {
+ await Task.Delay(15, token);
}
- }
- finally
- {
- // Ensure cleanup happens even if an error occurs
- MagicJsChunkProcessor.RemoveInstance(instanceId);
}
}
-
-
-
- [JSInvokable("ProcessJsChunk")]
- public Task ProcessJsChunk(string instanceId, string chunkInstanceId, int yieldOrderIndex, string chunk, int chunkIndex, int totalChunks)
+ finally
{
- if (chunkInstanceId == "STREAM_COMPLETE")
- {
- MagicJsChunkProcessor.AddChunk(instanceId, "STREAM_COMPLETE", -1, "", 0, 1);
- return Task.CompletedTask;
- }
-
- MagicJsChunkProcessor.AddChunk(instanceId, chunkInstanceId, yieldOrderIndex, chunk, chunkIndex, totalChunks);
- return Task.CompletedTask;
+ // Ensure cleanup happens even if an error occurs
+ MagicJsChunkProcessor.RemoveInstance(instanceId);
}
+ }
-
-
-
-
- private async Task MagicVoidStreamJsAsync(string modulePath, string functionName, CancellationToken token, params ITypedArgument[] args)
+ [JSInvokable("ProcessJsChunk")]
+ public Task ProcessJsChunk(string instanceId, string chunkInstanceId, int yieldOrderIndex, string chunk, int chunkIndex, int totalChunks)
+ {
+ if (chunkInstanceId == "STREAM_COMPLETE")
{
- await TrueMagicStreamJsAsync(modulePath, functionName, token, true, args);
+ MagicJsChunkProcessor.AddChunk(instanceId, "STREAM_COMPLETE", -1, "", 0, 1);
+ return Task.CompletedTask;
}
- }
+ MagicJsChunkProcessor.AddChunk(instanceId, chunkInstanceId, yieldOrderIndex, chunk, chunkIndex, totalChunks);
+ return Task.CompletedTask;
+ }
-}
+ private async Task MagicVoidStreamJsAsync(string modulePath, string functionName, CancellationToken token, params ITypedArgument[] args)
+ {
+ await TrueMagicStreamJsAsync(modulePath, functionName, token, true, args);
+ }
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Extensions/MagicTableTools.cs b/Magic.IndexedDb/Extensions/MagicTableTools.cs
index 4f59ec6..68f65bb 100644
--- a/Magic.IndexedDb/Extensions/MagicTableTools.cs
+++ b/Magic.IndexedDb/Extensions/MagicTableTools.cs
@@ -1,29 +1,22 @@
using Magic.IndexedDb.Interfaces;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Linq.Expressions;
-using System.Text;
-using System.Threading.Tasks;
-namespace Magic.IndexedDb
+namespace Magic.IndexedDb;
+
+public class MagicTableTool where T : class, IMagicTableBase, new()
{
- public class MagicTableTool where T : class, IMagicTableBase, new()
+ protected IMagicCompoundIndex CreateCompoundIndex(params Expression>[] keySelectors)
{
- protected IMagicCompoundIndex CreateCompoundIndex(params Expression>[] keySelectors)
- {
- return MagicCompoundExtension.CreateIndex(keySelectors);
- }
-
- protected IMagicCompoundKey CreateCompoundKey(params Expression>[] keySelectors)
- {
- return MagicCompoundExtension.CreateKey(false, keySelectors);
- }
+ return MagicCompoundExtension.CreateIndex(keySelectors);
+ }
- protected IMagicCompoundKey CreatePrimaryKey(Expression> keySelector, bool autoIncrement)
- {
- return MagicCompoundExtension.CreateKey(autoIncrement, keySelector);
- }
+ protected IMagicCompoundKey CreateCompoundKey(params Expression>[] keySelectors)
+ {
+ return MagicCompoundExtension.CreateKey(false, keySelectors);
}
-}
+ protected IMagicCompoundKey CreatePrimaryKey(Expression> keySelector, bool autoIncrement)
+ {
+ return MagicCompoundExtension.CreateKey(autoIncrement, keySelector);
+ }
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Extensions/MagicUtilities.cs b/Magic.IndexedDb/Extensions/MagicUtilities.cs
index 6a68e5c..401d800 100644
--- a/Magic.IndexedDb/Extensions/MagicUtilities.cs
+++ b/Magic.IndexedDb/Extensions/MagicUtilities.cs
@@ -1,39 +1,32 @@
using Magic.IndexedDb.Models;
using Microsoft.JSInterop;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-namespace Magic.IndexedDb.Extensions
-{
- internal class MagicUtilities : IMagicUtilities
- {
- readonly IJSObjectReference _jsModule;
- private readonly long _jsMessageSizeBytes;
+namespace Magic.IndexedDb.Extensions;
- ///
- /// Ctor
- ///
- ///
- ///
- public MagicUtilities(IJSObjectReference jsRuntime, long jsMessageSizeBytes)
- {
- this._jsModule = jsRuntime;
- _jsMessageSizeBytes = jsMessageSizeBytes;
- }
+internal class MagicUtilities : IMagicUtilities
+{
+ readonly IJSObjectReference _jsModule;
+ private readonly long _jsMessageSizeBytes;
+ ///
+ /// Ctor
+ ///
+ ///
+ ///
+ public MagicUtilities(IJSObjectReference jsRuntime, long jsMessageSizeBytes)
+ {
+ this._jsModule = jsRuntime;
+ _jsMessageSizeBytes = jsMessageSizeBytes;
+ }
- ///
- /// Returns Mb
- ///
- ///
- public Task GetStorageEstimateAsync(CancellationToken cancellationToken = default)
- {
- return new MagicJsInvoke(_jsModule, _jsMessageSizeBytes).
- CallJsAsync(Cache.MagicDbJsImportPath,
+ ///
+ /// Returns Mb
+ ///
+ ///
+ public Task GetStorageEstimateAsync(CancellationToken cancellationToken = default)
+ {
+ return new MagicJsInvoke(_jsModule, _jsMessageSizeBytes).
+ CallJsAsync(Cache.MagicDbJsImportPath,
IndexedDbFunctions.GET_STORAGE_ESTIMATE, cancellationToken, [])!;
- }
}
-}
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Extensions/ServiceCollectionExtensions.cs b/Magic.IndexedDb/Extensions/ServiceCollectionExtensions.cs
index 9b86b3a..4815a70 100644
--- a/Magic.IndexedDb/Extensions/ServiceCollectionExtensions.cs
+++ b/Magic.IndexedDb/Extensions/ServiceCollectionExtensions.cs
@@ -1,54 +1,43 @@
using Magic.IndexedDb.Factories;
-using Magic.IndexedDb.Helpers;
-using Magic.IndexedDb.Interfaces;
-using Magic.IndexedDb.Models;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.JSInterop;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-namespace Magic.IndexedDb
+namespace Magic.IndexedDb;
+
+public enum BlazorInteropMode : long
{
- public enum BlazorInteropMode : long
- {
- ///
- /// SignalR default interop send/receive is 32 KB.
- /// This will default to 31 KB for safety.
- ///
- SignalR = 31 * 1024, // 31 KB in bytes
+ ///
+ /// SignalR default interop send/receive is 32 KB.
+ /// This will default to 31 KB for safety.
+ ///
+ SignalR = 31 * 1024, // 31 KB in bytes
+
+ ///
+ /// WASM default interop send/receive is 16 MB.
+ /// This will default to 15 MB for safety.
+ ///
+ WASM = 15 * 1024 * 1024 // 15 MB in bytes
+}
- ///
- /// WASM default interop send/receive is 16 MB.
- /// This will default to 15 MB for safety.
- ///
- WASM = 15 * 1024 * 1024 // 15 MB in bytes
+public static class ServiceCollectionExtensions
+{
+ public static IServiceCollection AddMagicBlazorDB(this IServiceCollection services,
+ BlazorInteropMode interoptMode, bool isDebug)
+ {
+ return services.AddMagicBlazorDB((long)interoptMode, isDebug);
}
- public static class ServiceCollectionExtensions
+ public static IServiceCollection AddMagicBlazorDB(this IServiceCollection services,
+ long jsMessageSizeBytes, bool isDebug)
{
- public static IServiceCollection AddMagicBlazorDB(this IServiceCollection services,
- BlazorInteropMode interoptMode, bool isDebug)
- {
- return services.AddMagicBlazorDB((long)interoptMode, isDebug);
- }
+ services.AddScoped(sp =>
+ new MagicDbFactory(sp.GetRequiredService(), jsMessageSizeBytes));
- public static IServiceCollection AddMagicBlazorDB(this IServiceCollection services,
- long jsMessageSizeBytes, bool isDebug)
+ if (isDebug)
{
- services.AddScoped(sp =>
- new MagicDbFactory(sp.GetRequiredService(), jsMessageSizeBytes));
-
- if (isDebug)
- {
- Magic.IndexedDb.Helpers.MagicValidator.ValidateTables();
- }
-
- return services;
+ Magic.IndexedDb.Helpers.MagicValidator.ValidateTables();
}
+
+ return services;
}
-}
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Extensions/SharedQueryExtensions.cs b/Magic.IndexedDb/Extensions/SharedQueryExtensions.cs
index 280db6c..ba4b63b 100644
--- a/Magic.IndexedDb/Extensions/SharedQueryExtensions.cs
+++ b/Magic.IndexedDb/Extensions/SharedQueryExtensions.cs
@@ -1,107 +1,100 @@
using Magic.IndexedDb.Helpers;
using Magic.IndexedDb.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
-namespace Magic.IndexedDb.Extensions
+namespace Magic.IndexedDb.Extensions;
+
+internal static class SharedQueryExtensions
{
- internal static class SharedQueryExtensions
- {
- internal static MagicQuery Take(MagicQuery magicQuery, int amount)
+ internal static MagicQuery Take(MagicQuery magicQuery, int amount)
where T : class
+ {
+ var _MagicQuery = new MagicQuery(magicQuery);
+ var smq = new StoredMagicQuery
{
- var _MagicQuery = new MagicQuery(magicQuery);
- var smq = new StoredMagicQuery
- {
- additionFunction = MagicQueryFunctions.Take,
- intValue = amount
- };
-
- _MagicQuery.StoredMagicQueries.Add(smq);
- return _MagicQuery;
- }
+ additionFunction = MagicQueryFunctions.Take,
+ intValue = amount
+ };
- public static MagicQuery TakeLast(MagicQuery magicQuery, int amount) where T : class
- {
- var _MagicQuery = new MagicQuery(magicQuery);
- _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
- {
- additionFunction = MagicQueryFunctions.Take_Last,
- intValue = amount
- });
- return _MagicQuery;
- }
+ _MagicQuery.StoredMagicQueries.Add(smq);
+ return _MagicQuery;
+ }
- public static MagicQuery Skip(MagicQuery magicQuery, int amount) where T : class
+ public static MagicQuery TakeLast(MagicQuery magicQuery, int amount) where T : class
+ {
+ var _MagicQuery = new MagicQuery(magicQuery);
+ _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
{
- var _MagicQuery = new MagicQuery(magicQuery);
- _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
- {
- additionFunction = MagicQueryFunctions.Skip,
- intValue = amount
- });
- return _MagicQuery;
- }
+ additionFunction = MagicQueryFunctions.Take_Last,
+ intValue = amount
+ });
+ return _MagicQuery;
+ }
- public static MagicQuery OrderBy(MagicQuery magicQuery, Expression> predicate) where T : class
+ public static MagicQuery Skip(MagicQuery magicQuery, int amount) where T : class
+ {
+ var _MagicQuery = new MagicQuery(magicQuery);
+ _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
{
- var memberExpression = GetMemberExpressionFromLambda(predicate);
- var propertyInfo = memberExpression.Member as PropertyInfo;
-
- if (propertyInfo == null)
- throw new ArgumentException("The expression must represent a single property access.");
+ additionFunction = MagicQueryFunctions.Skip,
+ intValue = amount
+ });
+ return _MagicQuery;
+ }
- MagicPropertyEntry mpe = PropertyMappingCache.GetPropertyEntry(propertyInfo);
+ public static MagicQuery OrderBy(MagicQuery magicQuery, Expression> predicate) where T : class
+ {
+ var memberExpression = GetMemberExpressionFromLambda(predicate);
+ var propertyInfo = memberExpression.Member as PropertyInfo;
- if (!mpe.PrimaryKey && !mpe.Indexed && !mpe.UniqueIndex)
- {
- // Intentionally preserved your comment
- // throw new ArgumentException(...);
- }
+ if (propertyInfo == null)
+ throw new ArgumentException("The expression must represent a single property access.");
- var _MagicQuery = new MagicQuery(magicQuery);
- _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
- {
- additionFunction = MagicQueryFunctions.Order_By,
- property = mpe.JsPropertyName
- });
+ MagicPropertyEntry mpe = PropertyMappingCache.GetPropertyEntry(propertyInfo);
- return _MagicQuery;
+ if (!mpe.PrimaryKey && !mpe.Indexed && !mpe.UniqueIndex)
+ {
+ // Intentionally preserved your comment
+ // throw new ArgumentException(...);
}
- public static MagicQuery OrderByDescending(MagicQuery magicQuery, Expression> predicate) where T : class
+ var _MagicQuery = new MagicQuery(magicQuery);
+ _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
{
- var memberExpression = GetMemberExpressionFromLambda(predicate);
- var propertyInfo = memberExpression.Member as PropertyInfo;
+ additionFunction = MagicQueryFunctions.Order_By,
+ property = mpe.JsPropertyName
+ });
- if (propertyInfo == null)
- throw new ArgumentException("The expression must represent a single property access.");
+ return _MagicQuery;
+ }
- var _MagicQuery = new MagicQuery(magicQuery);
- _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
- {
- additionFunction = MagicQueryFunctions.Order_By_Descending,
- property = PropertyMappingCache.GetJsPropertyName(propertyInfo)
- });
+ public static MagicQuery OrderByDescending(MagicQuery magicQuery, Expression> predicate) where T : class
+ {
+ var memberExpression = GetMemberExpressionFromLambda(predicate);
+ var propertyInfo = memberExpression.Member as PropertyInfo;
- return _MagicQuery;
- }
+ if (propertyInfo == null)
+ throw new ArgumentException("The expression must represent a single property access.");
- private static MemberExpression GetMemberExpressionFromLambda(Expression> expression)
+ var _MagicQuery = new MagicQuery(magicQuery);
+ _MagicQuery.StoredMagicQueries.Add(new StoredMagicQuery
{
- if (expression.Body is MemberExpression m)
- return m;
+ additionFunction = MagicQueryFunctions.Order_By_Descending,
+ property = PropertyMappingCache.GetJsPropertyName(propertyInfo)
+ });
- if (expression.Body is UnaryExpression u && u.Operand is MemberExpression um)
- return um;
+ return _MagicQuery;
+ }
- throw new ArgumentException("The expression must represent a single property access.");
- }
+ private static MemberExpression GetMemberExpressionFromLambda(Expression> expression)
+ {
+ if (expression.Body is MemberExpression m)
+ return m;
+
+ if (expression.Body is UnaryExpression u && u.Operand is MemberExpression um)
+ return um;
+
+ throw new ArgumentException("The expression must represent a single property access.");
}
-}
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Factories/IMagicIndexedDb.cs b/Magic.IndexedDb/Factories/IMagicIndexedDb.cs
index e6963fe..4d2cdec 100644
--- a/Magic.IndexedDb/Factories/IMagicIndexedDb.cs
+++ b/Magic.IndexedDb/Factories/IMagicIndexedDb.cs
@@ -2,57 +2,56 @@
using Magic.IndexedDb.LinqTranslation.Interfaces;
using Magic.IndexedDb.Models;
-namespace Magic.IndexedDb
+namespace Magic.IndexedDb;
+
+public interface IMagicIndexedDb
{
- public interface IMagicIndexedDb
- {
-
- ///
- /// Allows manually inserting database names and schema names via strings.
- /// Please be careful, Magic IndexDB can't protect you from potential issues
- /// if you use this.
- ///
- ///
- ///
- ///
- ///
- //ValueTask> QueryOverride(string? databaseNameOverride = null,
- // string? schemaNameOverride = null) where T : class, IMagicTableBase, new();
-
- ///
- /// Opens a ready query to utilize IndexDB database and capabilities utilizing LINQ to IndexDB.
- /// Use example: IMagicQuery query = await _MagicDb.Query();
- ///
- ///
- ///
- ///
- ///
- ValueTask> Query()
- where T : class, IMagicTableBase, new();
-
- ///
- /// Opens a query for a table to a specified database.
- ///
- ///
- ///
- ///
- ValueTask> Query(Func dbSetSelector) where T : class, IMagicTableBase, new();
-
- ///
- /// Utilize any Database you want, but be careful that it's assigned!
- /// Highly suggested you utilize `Query(Func dbSetSelector)`
- ///
- ///
- ///
- ///
- //ValueTask> Query(IndexedDbSet indexedDbSet)
- // where T : class, IMagicTableBase, new();
-
- // I think this should be under a utilities functionality?
- Task GetStorageEstimateAsync(CancellationToken cancellationToken = default);
-
- //ValueTask Database();
-
- ValueTask Database(IndexedDbSet indexedDbSet);
- }
+
+ ///
+ /// Allows manually inserting database names and schema names via strings.
+ /// Please be careful, Magic IndexDB can't protect you from potential issues
+ /// if you use this.
+ ///
+ ///
+ ///
+ ///
+ ///
+ //ValueTask> QueryOverride(string? databaseNameOverride = null,
+ // string? schemaNameOverride = null) where T : class, IMagicTableBase, new();
+
+ ///
+ /// Opens a ready query to utilize IndexDB database and capabilities utilizing LINQ to IndexDB.
+ /// Use example: IMagicQuery query = await _MagicDb.Query();
+ ///
+ ///
+ ///
+ ///
+ ///
+ ValueTask> Query()
+ where T : class, IMagicTableBase, new();
+
+ ///
+ /// Opens a query for a table to a specified database.
+ ///
+ ///
+ ///
+ ///
+ ValueTask> Query(Func dbSetSelector) where T : class, IMagicTableBase, new();
+
+ ///
+ /// Utilize any Database you want, but be careful that it's assigned!
+ /// Highly suggested you utilize `Query(Func dbSetSelector)`
+ ///
+ ///
+ ///
+ ///
+ //ValueTask> Query(IndexedDbSet indexedDbSet)
+ // where T : class, IMagicTableBase, new();
+
+ // I think this should be under a utilities functionality?
+ Task GetStorageEstimateAsync(CancellationToken cancellationToken = default);
+
+ //ValueTask Database();
+
+ ValueTask Database(IndexedDbSet indexedDbSet);
}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Factories/MagicDbFactory.cs b/Magic.IndexedDb/Factories/MagicDbFactory.cs
index 3915b60..bd6cf92 100644
--- a/Magic.IndexedDb/Factories/MagicDbFactory.cs
+++ b/Magic.IndexedDb/Factories/MagicDbFactory.cs
@@ -1,34 +1,29 @@
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Magic.IndexedDb.Models;
using Magic.IndexedDb.Helpers;
using Magic.IndexedDb.Interfaces;
using Magic.IndexedDb.Extensions;
-using System.Collections.Concurrent;
-using System.Reflection;
using Magic.IndexedDb.LinqTranslation.Interfaces;
-using System.Diagnostics;
-using System.Threading;
-
-namespace Magic.IndexedDb.Factories
-{
- internal class MagicDbFactory : IMagicIndexedDb, IAsyncDisposable
- {
- // null value indicates that the factory is disposed
- Lazy>? _jsModule;
- Lazy> _magicJsManager;
- private readonly long _jsMessageSizeBytes;
- public long JsMessageSizeBytes => _jsMessageSizeBytes;
-
- public MagicDbFactory(IJSRuntime jSRuntime, long jsMessageSizeBytes)
- {
- _jsMessageSizeBytes = jsMessageSizeBytes;
- this._jsModule = new(() => jSRuntime.InvokeAsync(
+
+namespace Magic.IndexedDb.Factories;
+
+internal class MagicDbFactory : IMagicIndexedDb, IAsyncDisposable
+{
+ // null value indicates that the factory is disposed
+ Lazy>? _jsModule;
+ Lazy> _magicJsManager;
+ private readonly long _jsMessageSizeBytes;
+ public long JsMessageSizeBytes => _jsMessageSizeBytes;
+
+ public MagicDbFactory(IJSRuntime jSRuntime, long jsMessageSizeBytes)
+ {
+ _jsMessageSizeBytes = jsMessageSizeBytes;
+ this._jsModule = new(() => jSRuntime.InvokeAsync(
"import",
"./_content/Magic.IndexedDb/magicDbMethods.js").AsTask(),
- isThreadSafe: true);
+ isThreadSafe: true);
- this._magicJsManager = new(async () =>
+ this._magicJsManager = new(async () =>
{
var jsModule = await this._jsModule.Value;
@@ -59,124 +54,123 @@ public MagicDbFactory(IJSRuntime jSRuntime, long jsMessageSizeBytes)
return manager;
},
isThreadSafe: true);
- }
-
- public async ValueTask DisposeAsync()
- {
- var js = _jsModule;
- _jsModule = null;
+ }
- if (js is null || !js.IsValueCreated)
- return;
+ public async ValueTask DisposeAsync()
+ {
+ var js = _jsModule;
+ _jsModule = null;
- IJSObjectReference module;
- try
- {
- module = await js.Value;
- }
- catch
- {
- return;
- }
+ if (js is null || !js.IsValueCreated)
+ return;
- try
- {
- var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
- await module.InvokeVoidAsync(IndexedDbFunctions.CLOSE_ALL, timeout.Token);
- }
- catch
- {
- // do nothing
- }
-
- try
- {
- await module.DisposeAsync();
- }
- catch
- {
- // do nothing
- }
+ IJSObjectReference module;
+ try
+ {
+ module = await js.Value;
+ }
+ catch
+ {
+ return;
}
- ///
- /// Get storage estimate using the shared JS module.
- ///
- public async Task GetStorageEstimateAsync(CancellationToken cancellationToken = default)
+ try
+ {
+ var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+ await module.InvokeVoidAsync(IndexedDbFunctions.CLOSE_ALL, timeout.Token);
+ }
+ catch
{
- ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+ // do nothing
+ }
- var jsModule = await this._jsModule.Value;
- var magicUtility = new MagicUtilities(jsModule, _jsMessageSizeBytes);
- return await magicUtility.GetStorageEstimateAsync();
+ try
+ {
+ await module.DisposeAsync();
}
- [Obsolete("Not fully implemented yet until full migration protocol finished.")]
- public async ValueTask> Query(IndexedDbSet indexedDbSet)
- where T : class, IMagicTableBase, new()
+ catch
{
- ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+ // do nothing
+ }
+ }
- // Get database name and schema name
- string databaseName = indexedDbSet.DatabaseName;
- string schemaName = SchemaHelper.GetTableName();
+ ///
+ /// Get storage estimate using the shared JS module.
+ ///
+ public async Task GetStorageEstimateAsync(CancellationToken cancellationToken = default)
+ {
+ ObjectDisposedException.ThrowIf(this._jsModule is null, this);
- return await QueryOverride(databaseName, schemaName);
- }
+ var jsModule = await this._jsModule.Value;
+ var magicUtility = new MagicUtilities(jsModule, _jsMessageSizeBytes);
+ return await magicUtility.GetStorageEstimateAsync();
+ }
+ [Obsolete("Not fully implemented yet until full migration protocol finished.")]
+ public async ValueTask> Query(IndexedDbSet indexedDbSet)
+ where T : class, IMagicTableBase, new()
+ {
+ ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+ // Get database name and schema name
+ string databaseName = indexedDbSet.DatabaseName;
+ string schemaName = SchemaHelper.GetTableName();
+
+ return await QueryOverride(databaseName, schemaName);
+ }
- public async ValueTask> Query(
- Func dbSetSelector)
- where T : class, IMagicTableBase, new()
- {
- ObjectDisposedException.ThrowIf(this._jsModule is null, this);
- // Create an instance of T to access `DbSets`
- var modelInstance = new T();
+ public async ValueTask> Query(
+ Func dbSetSelector)
+ where T : class, IMagicTableBase, new()
+ {
+ ObjectDisposedException.ThrowIf(this._jsModule is null, this);
- // Retrieve the IndexedDbSet using the provided predicate
- IndexedDbSet selectedDbSet = dbSetSelector(modelInstance);
+ // Create an instance of T to access `DbSets`
+ var modelInstance = new T();
- // Get database name and schema name
- string databaseName = selectedDbSet.DatabaseName;
- string schemaName = SchemaHelper.GetTableName();
+ // Retrieve the IndexedDbSet using the provided predicate
+ IndexedDbSet selectedDbSet = dbSetSelector(modelInstance);
+
+ // Get database name and schema name
+ string databaseName = selectedDbSet.DatabaseName;
+ string schemaName = SchemaHelper.GetTableName();
#pragma warning disable CS0618
- return await QueryOverride(databaseName, schemaName);
+ return await QueryOverride(databaseName, schemaName);
#pragma warning restore CS0618
- }
-
- ///
- /// Query the database for a given type. Automatically opens the database if needed.
- ///
- public async ValueTask> Query()
- where T : class, IMagicTableBase, new()
- {
- ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+ }
- string databaseName = SchemaHelper.GetDefaultDatabaseName();
- string schemaName = SchemaHelper.GetTableName();
- var dbManager = await this._magicJsManager.Value;
+ ///
+ /// Query the database for a given type. Automatically opens the database if needed.
+ ///
+ public async ValueTask> Query()
+ where T : class, IMagicTableBase, new()
+ {
+ ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+
+ string databaseName = SchemaHelper.GetDefaultDatabaseName();
+ string schemaName = SchemaHelper.GetTableName();
+ var dbManager = await this._magicJsManager.Value;
#pragma warning disable CS0618
- return await QueryOverride(databaseName, schemaName);
+ return await QueryOverride(databaseName, schemaName);
#pragma warning restore CS0618
- }
+ }
- [Obsolete("Not decided if this will be built in further or removed")]
- public async ValueTask> QueryOverride(string databaseNameOverride, string schemaNameOverride)
- where T: class, IMagicTableBase, new ()
- {
- ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+ [Obsolete("Not decided if this will be built in further or removed")]
+ public async ValueTask> QueryOverride(string databaseNameOverride, string schemaNameOverride)
+ where T: class, IMagicTableBase, new ()
+ {
+ ObjectDisposedException.ThrowIf(this._jsModule is null, this);
- var dbManager = await this._magicJsManager.Value;
- return dbManager.Query(databaseNameOverride, schemaNameOverride);
- }
+ var dbManager = await this._magicJsManager.Value;
+ return dbManager.Query(databaseNameOverride, schemaNameOverride);
+ }
- public async ValueTask Database(IndexedDbSet indexedDbSet)
- {
- ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+ public async ValueTask Database(IndexedDbSet indexedDbSet)
+ {
+ ObjectDisposedException.ThrowIf(this._jsModule is null, this);
- var dbManager = await this._magicJsManager.Value;
- return dbManager.Database(dbManager, indexedDbSet);
- }
+ var dbManager = await this._magicJsManager.Value;
+ return dbManager.Database(dbManager, indexedDbSet);
}
}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Helpers/AttributeHelpers.cs b/Magic.IndexedDb/Helpers/AttributeHelpers.cs
index a968eba..481d851 100644
--- a/Magic.IndexedDb/Helpers/AttributeHelpers.cs
+++ b/Magic.IndexedDb/Helpers/AttributeHelpers.cs
@@ -1,78 +1,71 @@
using Magic.IndexedDb.Interfaces;
-using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-namespace Magic.IndexedDb.Helpers
+namespace Magic.IndexedDb.Helpers;
+
+public class PrimaryKeys
{
- public class PrimaryKeys
- {
- public string JsName { get; set; }
- public object Value { get; set; }
- }
- public static class AttributeHelpers
- {
- private static readonly ConcurrentDictionary _primaryKeyCache = new();
+ public string JsName { get; set; }
+ public object Value { get; set; }
+}
+public static class AttributeHelpers
+{
+ private static readonly ConcurrentDictionary _primaryKeyCache = new();
- public static List GetPrimaryKeys(T item) where T : class
- {
- if (item is null)
- throw new ArgumentNullException(nameof(item));
+ public static List GetPrimaryKeys(T item) where T : class
+ {
+ if (item is null)
+ throw new ArgumentNullException(nameof(item));
- var primaryKeyProps = GetPrimaryKeyProperties(typeof(T)).PropertyInfos;
+ var primaryKeyProps = GetPrimaryKeyProperties(typeof(T)).PropertyInfos;
- return primaryKeyProps
- .Select(p => new PrimaryKeys
- {
- JsName = PropertyMappingCache.GetJsPropertyName(p), // Convert this if needed (e.g., camelCase conversion)
- Value = p.GetValue(item)!
- })
- .ToList();
- }
+ return primaryKeyProps
+ .Select(p => new PrimaryKeys
+ {
+ JsName = PropertyMappingCache.GetJsPropertyName(p), // Convert this if needed (e.g., camelCase conversion)
+ Value = p.GetValue(item)!
+ })
+ .ToList();
+ }
- public static Type[] GetPrimaryKeyTypes() where T : IMagicTableBase
- {
- return GetPrimaryKeyProperties(typeof(T)).PropertyInfos.Select(p => p.PropertyType).ToArray();
- }
+ public static Type[] GetPrimaryKeyTypes() where T : IMagicTableBase
+ {
+ return GetPrimaryKeyProperties(typeof(T)).PropertyInfos.Select(p => p.PropertyType).ToArray();
+ }
- public static void ValidatePrimaryKey(object[] keys) where T : IMagicTableBase
- {
- var expectedTypes = GetPrimaryKeyTypes();
+ public static void ValidatePrimaryKey(object[] keys) where T : IMagicTableBase
+ {
+ var expectedTypes = GetPrimaryKeyTypes();
- if (keys.Length != expectedTypes.Length)
- throw new ArgumentException($"Invalid number of keys. Expected: {expectedTypes.Length}, received: {keys.Length}.");
+ if (keys.Length != expectedTypes.Length)
+ throw new ArgumentException($"Invalid number of keys. Expected: {expectedTypes.Length}, received: {keys.Length}.");
- for (int i = 0; i < keys.Length; i++)
+ for (int i = 0; i < keys.Length; i++)
+ {
+ if (keys[i] == null || !expectedTypes[i].IsInstanceOfType(keys[i]))
{
- if (keys[i] == null || !expectedTypes[i].IsInstanceOfType(keys[i]))
- {
- throw new ArgumentException($"Invalid key type at index {i}. Expected: {expectedTypes[i]}, received: {keys[i]?.GetType()}.");
- }
+ throw new ArgumentException($"Invalid key type at index {i}. Expected: {expectedTypes[i]}, received: {keys[i]?.GetType()}.");
}
}
+ }
- private static IMagicCompoundKey GetPrimaryKeyProperties(Type type)
+ private static IMagicCompoundKey GetPrimaryKeyProperties(Type type)
+ {
+ return _primaryKeyCache.GetOrAdd(type, t =>
{
- return _primaryKeyCache.GetOrAdd(type, t =>
- {
- if (!typeof(IMagicTableBase).IsAssignableFrom(t))
- throw new InvalidOperationException($"Type '{t.Name}' must implement IMagicTableBase.");
+ if (!typeof(IMagicTableBase).IsAssignableFrom(t))
+ throw new InvalidOperationException($"Type '{t.Name}' must implement IMagicTableBase.");
- var instance = Activator.CreateInstance(t) as IMagicTableBase;
- if (instance == null)
- throw new InvalidOperationException($"Unable to create an instance of '{t.Name}'.");
+ var instance = Activator.CreateInstance(t) as IMagicTableBase;
+ if (instance == null)
+ throw new InvalidOperationException($"Unable to create an instance of '{t.Name}'.");
- var compoundKey = instance.GetKeys();
- if (compoundKey == null || compoundKey.PropertyInfos.Length == 0)
- throw new InvalidOperationException($"Type '{t.Name}' must have at least one primary key.");
+ var compoundKey = instance.GetKeys();
+ if (compoundKey == null || compoundKey.PropertyInfos.Length == 0)
+ throw new InvalidOperationException($"Type '{t.Name}' must have at least one primary key.");
- return compoundKey;
- });
- }
+ return compoundKey;
+ });
}
-}
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Helpers/ExpandoToTypeConverter.cs b/Magic.IndexedDb/Helpers/ExpandoToTypeConverter.cs
index c8387c2..ea4c139 100644
--- a/Magic.IndexedDb/Helpers/ExpandoToTypeConverter.cs
+++ b/Magic.IndexedDb/Helpers/ExpandoToTypeConverter.cs
@@ -1,46 +1,42 @@
-using System.Dynamic;
-using System.Linq.Expressions;
+using System.Linq.Expressions;
-namespace Magic.IndexedDb.Helpers
+namespace Magic.IndexedDb.Helpers;
+
+public static class ExpandoToTypeConverter
{
- public static class ExpandoToTypeConverter
- {
- private static readonly Dictionary> PropertySetters = new();
- private static readonly Dictionary NonConcreteTypeCache = new();
+ private static readonly Dictionary> PropertySetters = new();
+ private static readonly Dictionary NonConcreteTypeCache = new();
- private static readonly bool IsConcrete;
- private static readonly bool HasParameterlessConstructor;
+ private static readonly bool IsConcrete;
+ private static readonly bool HasParameterlessConstructor;
+
+ static ExpandoToTypeConverter()
+ {
+ Type type = typeof(T);
+ IsConcrete = !(type.IsAbstract || type.IsInterface);
+ HasParameterlessConstructor = type.GetConstructor(Type.EmptyTypes) != null;
- static ExpandoToTypeConverter()
+ if (IsConcrete && HasParameterlessConstructor)
{
- Type type = typeof(T);
- IsConcrete = !(type.IsAbstract || type.IsInterface);
- HasParameterlessConstructor = type.GetConstructor(Type.EmptyTypes) != null;
-
- if (IsConcrete && HasParameterlessConstructor)
- {
- PrecomputePropertySetters(type);
- }
+ PrecomputePropertySetters(type);
}
+ }
- private static void PrecomputePropertySetters(Type type)
+ private static void PrecomputePropertySetters(Type type)
+ {
+ foreach (var prop in type.GetProperties().Where(p => p.CanWrite))
{
- foreach (var prop in type.GetProperties().Where(p => p.CanWrite))
- {
- var targetExp = Expression.Parameter(type);
- var valueExp = Expression.Parameter(typeof(object));
+ var targetExp = Expression.Parameter(type);
+ var valueExp = Expression.Parameter(typeof(object));
- var convertedValueExp = Expression.Convert(valueExp, prop.PropertyType);
+ var convertedValueExp = Expression.Convert(valueExp, prop.PropertyType);
- var propertySetterExp = Expression.Lambda>(
- Expression.Assign(Expression.Property(targetExp, prop), convertedValueExp),
- targetExp, valueExp
- );
+ var propertySetterExp = Expression.Lambda>(
+ Expression.Assign(Expression.Property(targetExp, prop), convertedValueExp),
+ targetExp, valueExp
+ );
- PropertySetters[prop.Name] = propertySetterExp.Compile();
- }
+ PropertySetters[prop.Name] = propertySetterExp.Compile();
}
}
-
-
-}
+}
\ No newline at end of file
diff --git a/Magic.IndexedDb/Helpers/ExpressionFlattener.cs b/Magic.IndexedDb/Helpers/ExpressionFlattener.cs
index 544d7e7..cfc6ee7 100644
--- a/Magic.IndexedDb/Helpers/ExpressionFlattener.cs
+++ b/Magic.IndexedDb/Helpers/ExpressionFlattener.cs
@@ -1,261 +1,254 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Magic.IndexedDb.Helpers
+using System.Linq.Expressions;
+
+namespace Magic.IndexedDb.Helpers;
+
+///
+/// IndexDB requires flattening complex nested OR statements as it's not supported by default.
+/// Flattening resolves this issue but without optimizations flattened methods will call
+/// large numbers of redundant queries without some help.
+///
+public static class ExpressionFlattener
{
- ///
- /// IndexDB requires flattening complex nested OR statements as it's not supported by default.
- /// Flattening resolves this issue but without optimizations flattened methods will call
- /// large numbers of redundant queries without some help.
- ///
- public static class ExpressionFlattener
+ public static Expression> FlattenAndOptimize(Expression> expr)
{
- public static Expression> FlattenAndOptimize(Expression> expr)
- {
- // Step 1: Flatten OrElse structures
- var flattenedBody = FlattenOrElseRecursive(expr.Body);
+ // Step 1: Flatten OrElse structures
+ var flattenedBody = FlattenOrElseRecursive(expr.Body);
- // Step 2: Optimize the expression (deduplicate and simplify)
- var optimizedBody = OptimizeExpression(flattenedBody);
+ // Step 2: Optimize the expression (deduplicate and simplify)
+ var optimizedBody = OptimizeExpression(flattenedBody);
- return Expression.Lambda>(optimizedBody, expr.Parameters);
- }
+ return Expression.Lambda>(optimizedBody, expr.Parameters);
+ }
- private static Expression FlattenOrElseRecursive(Expression expr)
+ private static Expression FlattenOrElseRecursive(Expression expr)
+ {
+ if (expr is BinaryExpression binaryExpr)
{
- if (expr is BinaryExpression binaryExpr)
+ if (binaryExpr.NodeType == ExpressionType.OrElse)
{
- if (binaryExpr.NodeType == ExpressionType.OrElse)
- {
- var terms = new HashSet