From 9a93eaf57a9818fa3c7d3f208b5acda70daaf4eb Mon Sep 17 00:00:00 2001
From: Ard van der Marel <109339598+Ard2025@users.noreply.github.com>
Date: Wed, 4 Jun 2025 09:30:59 +0200
Subject: [PATCH 1/3] refactor to file scoped namespace
---
Magic.IndexedDb/Cache.cs | 17 +-
.../Extensions/MagicCompoundExtension.cs | 21 +-
.../Extensions/MagicJsChunkProcessor.cs | 121 +-
Magic.IndexedDb/Extensions/MagicJsInvoke.cs | 353 +++--
Magic.IndexedDb/Extensions/MagicTableTools.cs | 30 +-
Magic.IndexedDb/Extensions/MagicUtilities.cs | 49 +-
.../Extensions/ServiceCollectionExtensions.cs | 61 +-
.../Extensions/SharedQueryExtensions.cs | 145 ++-
Magic.IndexedDb/Factories/IMagicIndexedDb.cs | 103 +-
Magic.IndexedDb/Factories/MagicDbFactory.cs | 217 ++--
Magic.IndexedDb/Helpers/AttributeHelpers.cs | 101 +-
.../Helpers/ExpandoToTypeConverter.cs | 59 +-
.../Helpers/ExpressionFlattener.cs | 394 +++---
.../Helpers/MagicSerializationHelper.cs | 141 +-
Magic.IndexedDb/Helpers/MagicValidator.cs | 151 ++-
.../Helpers/PropertyMappingCache.cs | 882 +++++++------
Magic.IndexedDb/Helpers/SchemaHelper.cs | 375 +++---
Magic.IndexedDb/IndexDbManager.cs | 489 ++++---
Magic.IndexedDb/IndexedDbFunctions.cs | 59 +-
Magic.IndexedDb/Interfaces/IColumnNamed.cs | 12 +-
.../Interfaces/IMagicCompoundIndex.cs | 13 +-
.../Interfaces/IMagicCompoundKey.cs | 15 +-
.../Interfaces/IMagicConnectRepository.cs | 9 +-
.../Interfaces/IMagicRepository.cs | 9 +-
Magic.IndexedDb/Interfaces/IMagicTable.cs | 11 +-
Magic.IndexedDb/Interfaces/IMagicTableBase.cs | 23 +-
Magic.IndexedDb/Interfaces/IMagicUtilities.cs | 9 +-
Magic.IndexedDb/Interfaces/ITypedArgument.cs | 15 +-
.../Extensions/MagicCursorExtension.cs | 103 +-
.../Extensions/MagicQueryExtensions.cs | 269 ++--
.../Extensions/UniversalExpressionBuilder.cs | 1148 ++++++++---------
.../Interfaces/IMagicCursor.cs | 55 +-
.../Interfaces/IMagicCursorSkip.cs | 15 +-
.../Interfaces/IMagicCursorStage.cs | 37 +-
.../Interfaces/IMagicDatabase.cs | 33 +-
.../Interfaces/IMagicExecute.cs | 43 +-
.../LinqTranslation/Interfaces/IMagicQuery.cs | 87 +-
.../Interfaces/IMagicQueryFinal.cs | 37 +-
.../Interfaces/IMagicQueryOrderable.cs | 47 +-
.../Interfaces/IMagicQueryOrderableTable.cs | 21 +-
.../Interfaces/IMagicQueryPaginationTake.cs | 39 +-
.../Interfaces/IMagicQueryStaging.cs | 37 +-
.../Models/LogicalFilterNode.cs | 109 +-
.../LinqTranslation/Models/MagicCursor.cs | 77 +-
.../Models/MagicDatabaseGlobal.cs | 11 +-
.../Models/MagicDatabaseScoped.cs | 59 +-
.../LinqTranslation/Models/MagicQuery.cs | 259 ++--
.../LinqTranslation/Models/MagicWhereLinq.cs | 9 +-
Magic.IndexedDb/MagicEncryptAttribute.cs | 25 +-
Magic.IndexedDb/Models/DbMigration.cs | 15 +-
.../Models/DbMigrationInstruction.cs | 15 +-
Magic.IndexedDb/Models/DbStore.cs | 21 +-
Magic.IndexedDb/Models/IndexFilterValue.cs | 21 +-
Magic.IndexedDb/Models/IndexedDbSet.cs | 15 +-
.../Models/InternalMagicCompoundIndex.cs | 139 +-
.../Models/InternalMagicCompoundKey.cs | 173 ++-
.../Models/MagicContractResolver.cs | 707 +++++-----
Magic.IndexedDb/Models/MagicException.cs | 12 +-
Magic.IndexedDb/Models/MagicJsPackage.cs | 21 +-
.../Models/MagicJsonSerializationSettings.cs | 53 +-
Magic.IndexedDb/Models/PredicateVisitor.cs | 133 +-
Magic.IndexedDb/Models/StoreRecord.cs | 15 +-
Magic.IndexedDb/Models/StoreSchema.cs | 43 +-
Magic.IndexedDb/Models/StoredMagicQuery.cs | 55 +-
.../Models/Structs/MagicAttributesEntry.cs | 59 +-
.../Models/Structs/MagicPropertyEntry.cs | 261 ++--
.../Structs/MagicPropertySearchEntry.cs | 61 +-
.../Models/Structs/MagicTableEntry.cs | 23 +-
Magic.IndexedDb/Models/TypedArgument.cs | 42 +-
.../UniversalOperations/AndFilterGroup.cs | 23 +-
.../UniversalOperations/FilterCondition.cs | 75 +-
.../UniversalOperations/NestedOrFilter.cs | 43 +-
.../UniversalOperations/OrFilterGroup.cs | 23 +-
Magic.IndexedDb/Models/UpdateRecord.cs | 9 +-
.../SchemaAnnotations/MagicIndexAttribute.cs | 35 +-
.../SchemaAnnotations/MagicNameAttribute.cs | 29 +-
.../MagicNotMappedAttribute.cs | 7 +-
.../MagicPrimaryKeyAttribute.cs | 47 +-
.../SchemaAnnotations/MagicTableAttribute.cs | 31 +-
.../MagicUniqueIndexAttribute.cs | 35 +-
Magic.IndexedDb/SnapshotBuilder/BuildTools.cs | 129 +-
Magic.IndexedDb/Testing/Helpers/AllPaths.cs | 165 ++-
.../Testing/Helpers/MagicQueryPathWalker.cs | 465 ++++---
Magic.IndexedDb/Testing/Helpers/TestHelper.cs | 233 ++--
.../Testing/Models/TestResponse.cs | 13 +-
TestServer/TestServer/Models/DbNames.cs | 11 +-
TestServer/TestServer/Models/Person.cs | 127 +-
.../TestServer/Repository/IndexDbContext.cs | 15 +-
TestWasm/Models/DbNames.cs | 11 +-
TestWasm/Models/Person.cs | 129 +-
TestWasm/Repository/IndexDbContext.cs | 15 +-
91 files changed, 4924 insertions(+), 5034 deletions(-)
diff --git a/Magic.IndexedDb/Cache.cs b/Magic.IndexedDb/Cache.cs
index 1f5d556..bae4680 100644
--- a/Magic.IndexedDb/Cache.cs
+++ b/Magic.IndexedDb/Cache.cs
@@ -4,13 +4,12 @@
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..0c6068f 100644
--- a/Magic.IndexedDb/Extensions/MagicCompoundExtension.cs
+++ b/Magic.IndexedDb/Extensions/MagicCompoundExtension.cs
@@ -6,18 +6,17 @@
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..934459f 100644
--- a/Magic.IndexedDb/Extensions/MagicJsChunkProcessor.cs
+++ b/Magic.IndexedDb/Extensions/MagicJsChunkProcessor.cs
@@ -5,87 +5,86 @@
using System.Text;
using System.Threading.Tasks;
-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];
-
- orderedItems.Remove(firstKey);
- return message;
- }
- }
+ var firstKey = orderedItems.Keys.First();
+ var message = orderedItems[firstKey];
- // 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";
+ orderedItems.Remove(firstKey);
+ return message;
}
+ }
- return null;
+ // 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)
- {
- _chunkedMessages.TryRemove(instanceId, out _);
- _instanceCompleteItems.TryRemove(instanceId, out _);
- _instanceOrderedItems.TryRemove(instanceId, out _);
- }
+
+ 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..d676779 100644
--- a/Magic.IndexedDb/Extensions/MagicJsInvoke.cs
+++ b/Magic.IndexedDb/Extensions/MagicJsInvoke.cs
@@ -11,245 +11,242 @@
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 };
+ YieldResults = false,
+ ModulePath = modulePath,
+ MethodName = functionName,
+ Parameters = MagicSerializationHelper.SerializeObjectsToString(args, settings),
+ IsVoid = isVoid
+ };
- var package = new MagicJsPackage
- {
- 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);
- }
}
+ 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)
+ [JSInvokable("ProcessJsChunk")]
+ public Task ProcessJsChunk(string instanceId, string chunkInstanceId, int yieldOrderIndex, string chunk, int chunkIndex, int totalChunks)
+ {
+ if (chunkInstanceId == "STREAM_COMPLETE")
{
- if (chunkInstanceId == "STREAM_COMPLETE")
- {
- MagicJsChunkProcessor.AddChunk(instanceId, "STREAM_COMPLETE", -1, "", 0, 1);
- return Task.CompletedTask;
- }
-
- MagicJsChunkProcessor.AddChunk(instanceId, chunkInstanceId, yieldOrderIndex, chunk, chunkIndex, totalChunks);
+ 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);
- }
- }
-
-}
+ 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..8622079 100644
--- a/Magic.IndexedDb/Extensions/MagicTableTools.cs
+++ b/Magic.IndexedDb/Extensions/MagicTableTools.cs
@@ -6,24 +6,22 @@
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..70833b5 100644
--- a/Magic.IndexedDb/Extensions/MagicUtilities.cs
+++ b/Magic.IndexedDb/Extensions/MagicUtilities.cs
@@ -6,34 +6,33 @@
using System.Text;
using System.Threading.Tasks;
-namespace Magic.IndexedDb.Extensions
+namespace Magic.IndexedDb.Extensions;
+
+internal class MagicUtilities : IMagicUtilities
{
- internal class MagicUtilities : IMagicUtilities
- {
- readonly IJSObjectReference _jsModule;
- private readonly long _jsMessageSizeBytes;
+ readonly IJSObjectReference _jsModule;
+ private readonly long _jsMessageSizeBytes;
- ///
- /// Ctor
- ///
- ///
- ///
- public MagicUtilities(IJSObjectReference jsRuntime, long jsMessageSizeBytes)
- {
- this._jsModule = jsRuntime;
- _jsMessageSizeBytes = 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..6a44635 100644
--- a/Magic.IndexedDb/Extensions/ServiceCollectionExtensions.cs
+++ b/Magic.IndexedDb/Extensions/ServiceCollectionExtensions.cs
@@ -12,43 +12,42 @@
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..1cc4ad7 100644
--- a/Magic.IndexedDb/Extensions/SharedQueryExtensions.cs
+++ b/Magic.IndexedDb/Extensions/SharedQueryExtensions.cs
@@ -9,99 +9,98 @@
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..0e86a6d 100644
--- a/Magic.IndexedDb/Factories/MagicDbFactory.cs
+++ b/Magic.IndexedDb/Factories/MagicDbFactory.cs
@@ -10,25 +10,25 @@
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 +59,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;
- }
-
- try
- {
- var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
- await module.InvokeVoidAsync(IndexedDbFunctions.CLOSE_ALL, timeout.Token);
- }
- catch
- {
- // do nothing
- }
+ if (js is null || !js.IsValueCreated)
+ return;
- 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();
- public async ValueTask> Query(
- Func dbSetSelector)
- where T : class, IMagicTableBase, new()
- {
- ObjectDisposedException.ThrowIf(this._jsModule is null, this);
+ 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();
+ // Create an instance of T to access `DbSets`
+ var modelInstance = new T();
- // Retrieve the IndexedDbSet using the provided predicate
- IndexedDbSet selectedDbSet = dbSetSelector(modelInstance);
+ // 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();
+ // 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..b8792f4 100644
--- a/Magic.IndexedDb/Helpers/AttributeHelpers.cs
+++ b/Magic.IndexedDb/Helpers/AttributeHelpers.cs
@@ -7,72 +7,71 @@
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..50ae100 100644
--- a/Magic.IndexedDb/Helpers/ExpandoToTypeConverter.cs
+++ b/Magic.IndexedDb/Helpers/ExpandoToTypeConverter.cs
@@ -1,46 +1,43 @@
using System.Dynamic;
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..a7a55bc 100644
--- a/Magic.IndexedDb/Helpers/ExpressionFlattener.cs
+++ b/Magic.IndexedDb/Helpers/ExpressionFlattener.cs
@@ -5,257 +5,255 @@
using System.Text;
using System.Threading.Tasks;
-namespace Magic.IndexedDb.Helpers
+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