Skip to content

Commit 7a8b86c

Browse files
Merge pull request #53 from magiccodingman/MagicCodingMan/InvokeRefactor
Merging because it's stable after my testing. Some unit tests would be super helpful, but I want to get this into release and NuGet within the next 72 hours personally. Lots of really important fixes and abilities.
2 parents 8025e65 + b05b83f commit 7a8b86c

File tree

14 files changed

+408
-344
lines changed

14 files changed

+408
-344
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using Magic.IndexedDb.Helpers;
2+
using Magic.IndexedDb.Interfaces;
3+
using Magic.IndexedDb.Models;
4+
using Microsoft.JSInterop;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
11+
namespace Magic.IndexedDb.Extensions
12+
{
13+
internal class MagicJsInvoke
14+
{
15+
private readonly IJSObjectReference _jsModule;
16+
17+
public MagicJsInvoke(IJSObjectReference jsModule)
18+
{
19+
_jsModule = jsModule;
20+
}
21+
22+
internal async Task<T?> MagicStreamJsAsync<T>(string functionName, CancellationToken token, params ITypedArgument[] args)
23+
{
24+
return await TrueMagicStreamJsAsync<T>(functionName, token, false, args);
25+
}
26+
private async Task<T?> TrueMagicStreamJsAsync<T>(string functionName, CancellationToken token, bool isVoid, params ITypedArgument[] args)
27+
{
28+
var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
29+
30+
var package = new MagicJsPackage
31+
{
32+
MethodName = functionName,
33+
Parameters = MagicSerializationHelper.SerializeObjectsToString(args, settings),
34+
IsVoid = isVoid
35+
};
36+
37+
#if DEBUG
38+
package.IsDebug = true;
39+
#endif
40+
41+
using var stream = new MemoryStream();
42+
await using (var writer = new StreamWriter(stream, leaveOpen: true))
43+
{
44+
await MagicSerializationHelper.SerializeObjectToStreamAsync(writer, package, settings);
45+
}
46+
47+
// ✅ Immediately release reference to `package`
48+
package = null;
49+
GC.Collect();
50+
GC.WaitForPendingFinalizers();
51+
52+
stream.Position = 0;
53+
54+
var streamRef = new DotNetStreamReference(stream);
55+
56+
// Send to JS
57+
var responseStreamRef = await _jsModule.InvokeAsync<IJSStreamReference>("streamedJsHandler", token, streamRef);
58+
59+
// 🚀 Convert the stream reference back to JSON in C#
60+
await using var responseStream = await responseStreamRef.OpenReadStreamAsync(long.MaxValue, token);
61+
using var reader = new StreamReader(responseStream);
62+
63+
string jsonResponse = await reader.ReadToEndAsync();
64+
return MagicSerializationHelper.DeserializeObject<T>(jsonResponse, settings);
65+
}
66+
67+
internal async Task MagicVoidStreamJsAsync(string functionName, CancellationToken token, params ITypedArgument[] args)
68+
{
69+
await TrueMagicStreamJsAsync<bool>(functionName, token, true, args);
70+
}
71+
}
72+
73+
74+
}

Magic.IndexedDb/Factories/MagicDbFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public MagicDbFactory(IServiceProvider serviceProvider, IJSRuntime jSRuntime)
1515
_serviceProvider = serviceProvider;
1616
this._jsRuntime = new(() => jSRuntime.InvokeAsync<IJSObjectReference>(
1717
"import",
18-
"./_content/Magic.IndexedDb/magicDB.js").AsTask());
18+
"./_content/Magic.IndexedDb/magicDbMethods.js").AsTask());
1919
}
2020
public async ValueTask DisposeAsync()
2121
{

Magic.IndexedDb/Helpers/MagicSerializationHelper.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Magic.IndexedDb.Interfaces;
22
using Magic.IndexedDb.Models;
33
using System.Linq;
4+
using System.Text;
45
using System.Text.Json;
56

67
namespace Magic.IndexedDb.Helpers
@@ -17,6 +18,11 @@ public static object[] SerializeObjects(ITypedArgument[] objs, MagicJsonSerializ
1718
return objs.Select(arg => arg.SerializeToJsonElement(settings)).Cast<object>().ToArray();
1819
}
1920

21+
public static string[] SerializeObjectsToString(ITypedArgument[] objs, MagicJsonSerializationSettings? settings = null)
22+
{
23+
return objs.Select(arg => arg.SerializeToJsonString(settings)).ToArray();
24+
}
25+
2026
public static JsonElement SerializeObjectToJsonElement<T>(T value, MagicJsonSerializationSettings? settings = null)
2127
{
2228
if (settings == null)
@@ -33,6 +39,21 @@ public static JsonElement SerializeObjectToJsonElement<T>(T value, MagicJsonSeri
3339
return doc.RootElement.Clone(); // Clone to prevent disposal issues
3440
}
3541

42+
public static async Task SerializeObjectToStreamAsync<T>(StreamWriter writer, T value, MagicJsonSerializationSettings? settings = null)
43+
{
44+
if (settings == null)
45+
settings = new MagicJsonSerializationSettings();
46+
47+
if (value == null)
48+
throw new ArgumentNullException(nameof(value), "Object cannot be null");
49+
50+
var options = settings.GetOptionsWithResolver<T>();
51+
string jsonString = JsonSerializer.Serialize(value, options); // Use your serializer
52+
53+
await writer.WriteAsync(jsonString);
54+
await writer.FlushAsync();
55+
}
56+
3657
public static string SerializeObject<T>(T value, MagicJsonSerializationSettings? settings = null)
3758
{
3859
if (settings == null)

Magic.IndexedDb/Helpers/PropertyMappingCache.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ internal static void EnsureTypeIsCached(Type type)
397397
// Initialize the dictionary for this type
398398
var propertyEntries = new Dictionary<string, MagicPropertyEntry>(StringComparer.OrdinalIgnoreCase);
399399

400+
bool hasMagicTableAttribute = type.IsDefined(typeof(MagicTableAttribute), inherit: true);
401+
400402
List<MagicPropertyEntry> newMagicPropertyEntry = new List<MagicPropertyEntry>();
401403
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
402404
{
@@ -419,7 +421,8 @@ internal static void EnsureTypeIsCached(Type type)
419421
property.IsDefined(typeof(MagicIndexAttribute), inherit: true),
420422
property.IsDefined(typeof(MagicUniqueIndexAttribute), inherit: true),
421423
property.IsDefined(typeof(MagicPrimaryKeyAttribute), inherit: true),
422-
property.IsDefined(typeof(MagicNotMappedAttribute), inherit: true)
424+
property.IsDefined(typeof(MagicNotMappedAttribute), inherit: true),
425+
hasMagicTableAttribute || property.IsDefined(typeof(MagicNameAttribute), inherit: true)
423426
);
424427
newMagicPropertyEntry.Add(magicEntry);
425428
propertyEntries[propertyKey] = magicEntry; // Store property entry with string key

Magic.IndexedDb/IndexDbManager.cs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Magic.IndexedDb.Interfaces;
1212
using static System.Runtime.InteropServices.JavaScript.JSType;
1313
using Microsoft.Extensions.Options;
14+
using Magic.IndexedDb.Extensions;
1415

1516
namespace Magic.IndexedDb
1617
{
@@ -559,26 +560,17 @@ public Task ClearTableAsync<T>(CancellationToken cancellationToken = default) wh
559560

560561
internal async Task CallJsAsync(string functionName, CancellationToken token, params ITypedArgument[] args)
561562
{
562-
var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
563-
object[] serializedArgs = MagicSerializationHelper.SerializeObjects(args, settings);
564-
await this._jsModule.InvokeVoidAsync(functionName, token, serializedArgs);
563+
var magicJsInvoke = new MagicJsInvoke(_jsModule);
564+
565+
await magicJsInvoke.MagicVoidStreamJsAsync(functionName, token, args);
565566
}
566567

567568
internal async Task<T> CallJsAsync<T>(string functionName, CancellationToken token, params ITypedArgument[] args)
568569
{
569570

570-
var ss = typeof(T);
571-
572-
var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
573-
object[] serializedArgs = MagicSerializationHelper.SerializeObjects(args, settings);
574-
// Invoke JavaScript function and retrieve result as a JsonElement to avoid type mismatches
575-
var resultJsonElement = await _jsModule.InvokeAsync<JsonElement>(functionName, token, serializedArgs);
571+
var magicJsInvoke = new MagicJsInvoke(_jsModule);
576572

577-
// Convert JsonElement to a JSON string for custom deserialization
578-
string resultJson = resultJsonElement.GetRawText();
579-
580-
var result = MagicSerializationHelper.DeserializeObject<T>(resultJson, settings);
581-
return result;
573+
return await magicJsInvoke.MagicStreamJsAsync<T>(functionName, token, args) ?? default;
582574
}
583575
}
584576
}

Magic.IndexedDb/Interfaces/ITypedArgument.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ public interface ITypedArgument
1212
{
1313
string Serialize(); // Still needed for some cases
1414
JsonElement SerializeToJsonElement(MagicJsonSerializationSettings? settings = null); // Ensures proper object passing
15+
string SerializeToJsonString(MagicJsonSerializationSettings? settings = null);
1516
}
1617
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Magic.IndexedDb.Models
8+
{
9+
internal class MagicJsPackage
10+
{
11+
public string MethodName { get; set; }
12+
public string?[]? Parameters { get; set; }
13+
public bool IsVoid { get; set; } = false;
14+
15+
public bool IsDebug { get; set; } = false;
16+
}
17+
}

Magic.IndexedDb/Models/Structs/MagicPropertyEntry.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@ public struct MagicPropertyEntry
1818
/// Constructor for initializing MagicPropertyEntry while reducing memory footprint.
1919
/// </summary>
2020
public MagicPropertyEntry(PropertyInfo property, IColumnNamed? columnNamedAttribute,
21-
bool indexed, bool uniqueIndex, bool primaryKey, bool notMapped)
21+
bool indexed, bool uniqueIndex, bool primaryKey, bool notMapped,
22+
bool overrideNeverCamel = false)
2223
{
2324
Property = property;
2425
_columnNamedAttribute = columnNamedAttribute;
2526
Indexed = indexed;
2627
UniqueIndex = uniqueIndex;
2728
PrimaryKey = primaryKey;
2829
NotMapped = notMapped;
30+
OverrideNeverCamel = overrideNeverCamel;
2931

3032
IsComplexType = PropertyMappingCache.IsComplexType(property.PropertyType);
3133

@@ -58,6 +60,8 @@ public MagicPropertyEntry(PropertyInfo property, IColumnNamed? columnNamedAttrib
5860

5961
public object? DefaultValue { get; }
6062

63+
public bool OverrideNeverCamel { get; }
64+
6165
/// <summary>
6266
/// If any Magic attribute was placed on a property. We never
6367
/// camel case if that's the current json setting. These must stay
@@ -67,7 +71,7 @@ public bool NeverCamelCase
6771
{
6872
get
6973
{
70-
if (PrimaryKey || UniqueIndex || Indexed)
74+
if (PrimaryKey || UniqueIndex || Indexed || OverrideNeverCamel)
7175
return true;
7276
else
7377
return false;

Magic.IndexedDb/Models/TypedArgument.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public JsonElement SerializeToJsonElement(MagicJsonSerializationSettings? settin
2727
{
2828
return MagicSerializationHelper.SerializeObjectToJsonElement(Value, settings);
2929
}
30+
31+
public string SerializeToJsonString(MagicJsonSerializationSettings? settings = null)
32+
{
33+
return MagicSerializationHelper.SerializeObject(Value, settings);
34+
}
3035
}
3136

3237
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Magic.IndexedDb.Interfaces;
7+
using Magic.IndexedDb.SchemaAnnotations;
8+
9+
namespace Magic.IndexedDb.SchemaAnnotations
10+
{
11+
[AttributeUsage(AttributeTargets.Property)]
12+
public class MagicNameAttribute : Attribute, IColumnNamed
13+
{
14+
public string ColumnName { get; }
15+
16+
public MagicNameAttribute(string columnName)
17+
{
18+
if (!string.IsNullOrWhiteSpace(columnName))
19+
{
20+
ColumnName = columnName;
21+
}
22+
else
23+
{
24+
throw new Exception("You have a MagicName attribute with no column name string provided!");
25+
}
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)