Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions Magic.IndexedDb/Extensions/MagicJsInvoke.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Magic.IndexedDb.Helpers;
using Magic.IndexedDb.Interfaces;
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 MagicJsInvoke
{
private readonly IJSObjectReference _jsModule;

public MagicJsInvoke(IJSObjectReference jsModule)
{
_jsModule = jsModule;
}

internal async Task<T?> MagicStreamJsAsync<T>(string functionName, CancellationToken token, params ITypedArgument[] args)
{
return await TrueMagicStreamJsAsync<T>(functionName, token, false, args);
}
private async Task<T?> TrueMagicStreamJsAsync<T>(string functionName, CancellationToken token, bool isVoid, params ITypedArgument[] args)
{
var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };

var package = new MagicJsPackage
{
MethodName = functionName,
Parameters = MagicSerializationHelper.SerializeObjectsToString(args, settings),
IsVoid = isVoid
};

#if DEBUG
package.IsDebug = true;
#endif

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();

stream.Position = 0;

var streamRef = new DotNetStreamReference(stream);

// Send to JS
var responseStreamRef = await _jsModule.InvokeAsync<IJSStreamReference>("streamedJsHandler", token, streamRef);

// 🚀 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<T>(jsonResponse, settings);
}

internal async Task MagicVoidStreamJsAsync(string functionName, CancellationToken token, params ITypedArgument[] args)
{
await TrueMagicStreamJsAsync<bool>(functionName, token, true, args);
}
}


}
2 changes: 1 addition & 1 deletion Magic.IndexedDb/Factories/MagicDbFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public MagicDbFactory(IServiceProvider serviceProvider, IJSRuntime jSRuntime)
_serviceProvider = serviceProvider;
this._jsRuntime = new(() => jSRuntime.InvokeAsync<IJSObjectReference>(
"import",
"./_content/Magic.IndexedDb/magicDB.js").AsTask());
"./_content/Magic.IndexedDb/magicDbMethods.js").AsTask());
}
public async ValueTask DisposeAsync()
{
Expand Down
21 changes: 21 additions & 0 deletions Magic.IndexedDb/Helpers/MagicSerializationHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Magic.IndexedDb.Interfaces;
using Magic.IndexedDb.Models;
using System.Linq;
using System.Text;
using System.Text.Json;

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

public static string[] SerializeObjectsToString(ITypedArgument[] objs, MagicJsonSerializationSettings? settings = null)
{
return objs.Select(arg => arg.SerializeToJsonString(settings)).ToArray();
}

public static JsonElement SerializeObjectToJsonElement<T>(T value, MagicJsonSerializationSettings? settings = null)
{
if (settings == null)
Expand All @@ -33,6 +39,21 @@ public static JsonElement SerializeObjectToJsonElement<T>(T value, MagicJsonSeri
return doc.RootElement.Clone(); // Clone to prevent disposal issues
}

public static async Task SerializeObjectToStreamAsync<T>(StreamWriter writer, T value, MagicJsonSerializationSettings? settings = null)
{
if (settings == null)
settings = new MagicJsonSerializationSettings();

if (value == null)
throw new ArgumentNullException(nameof(value), "Object cannot be null");

var options = settings.GetOptionsWithResolver<T>();
string jsonString = JsonSerializer.Serialize(value, options); // Use your serializer

await writer.WriteAsync(jsonString);
await writer.FlushAsync();
}

public static string SerializeObject<T>(T value, MagicJsonSerializationSettings? settings = null)
{
if (settings == null)
Expand Down
5 changes: 4 additions & 1 deletion Magic.IndexedDb/Helpers/PropertyMappingCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ internal static void EnsureTypeIsCached(Type type)
// Initialize the dictionary for this type
var propertyEntries = new Dictionary<string, MagicPropertyEntry>(StringComparer.OrdinalIgnoreCase);

bool hasMagicTableAttribute = type.IsDefined(typeof(MagicTableAttribute), inherit: true);

List<MagicPropertyEntry> newMagicPropertyEntry = new List<MagicPropertyEntry>();
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
{
Expand All @@ -419,7 +421,8 @@ internal static void EnsureTypeIsCached(Type type)
property.IsDefined(typeof(MagicIndexAttribute), inherit: true),
property.IsDefined(typeof(MagicUniqueIndexAttribute), inherit: true),
property.IsDefined(typeof(MagicPrimaryKeyAttribute), inherit: true),
property.IsDefined(typeof(MagicNotMappedAttribute), inherit: true)
property.IsDefined(typeof(MagicNotMappedAttribute), inherit: true),
hasMagicTableAttribute || property.IsDefined(typeof(MagicNameAttribute), inherit: true)
);
newMagicPropertyEntry.Add(magicEntry);
propertyEntries[propertyKey] = magicEntry; // Store property entry with string key
Expand Down
20 changes: 6 additions & 14 deletions Magic.IndexedDb/IndexDbManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Magic.IndexedDb.Interfaces;
using static System.Runtime.InteropServices.JavaScript.JSType;
using Microsoft.Extensions.Options;
using Magic.IndexedDb.Extensions;

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

internal async Task CallJsAsync(string functionName, CancellationToken token, params ITypedArgument[] args)
{
var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
object[] serializedArgs = MagicSerializationHelper.SerializeObjects(args, settings);
await this._jsModule.InvokeVoidAsync(functionName, token, serializedArgs);
var magicJsInvoke = new MagicJsInvoke(_jsModule);

await magicJsInvoke.MagicVoidStreamJsAsync(functionName, token, args);
}

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

var ss = typeof(T);

var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };
object[] serializedArgs = MagicSerializationHelper.SerializeObjects(args, settings);
// Invoke JavaScript function and retrieve result as a JsonElement to avoid type mismatches
var resultJsonElement = await _jsModule.InvokeAsync<JsonElement>(functionName, token, serializedArgs);
var magicJsInvoke = new MagicJsInvoke(_jsModule);

// Convert JsonElement to a JSON string for custom deserialization
string resultJson = resultJsonElement.GetRawText();

var result = MagicSerializationHelper.DeserializeObject<T>(resultJson, settings);
return result;
return await magicJsInvoke.MagicStreamJsAsync<T>(functionName, token, args) ?? default;
}
}
}
1 change: 1 addition & 0 deletions Magic.IndexedDb/Interfaces/ITypedArgument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public interface ITypedArgument
{
string Serialize(); // Still needed for some cases
JsonElement SerializeToJsonElement(MagicJsonSerializationSettings? settings = null); // Ensures proper object passing
string SerializeToJsonString(MagicJsonSerializationSettings? settings = null);
}
}
17 changes: 17 additions & 0 deletions Magic.IndexedDb/Models/MagicJsPackage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Magic.IndexedDb.Models
{
internal class MagicJsPackage
{
public string MethodName { get; set; }
public string?[]? Parameters { get; set; }
public bool IsVoid { get; set; } = false;

public bool IsDebug { get; set; } = false;
}
}
8 changes: 6 additions & 2 deletions Magic.IndexedDb/Models/Structs/MagicPropertyEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ public struct MagicPropertyEntry
/// Constructor for initializing MagicPropertyEntry while reducing memory footprint.
/// </summary>
public MagicPropertyEntry(PropertyInfo property, IColumnNamed? columnNamedAttribute,
bool indexed, bool uniqueIndex, bool primaryKey, bool notMapped)
bool indexed, bool uniqueIndex, bool primaryKey, bool notMapped,
bool overrideNeverCamel = false)
{
Property = property;
_columnNamedAttribute = columnNamedAttribute;
Indexed = indexed;
UniqueIndex = uniqueIndex;
PrimaryKey = primaryKey;
NotMapped = notMapped;
OverrideNeverCamel = overrideNeverCamel;

IsComplexType = PropertyMappingCache.IsComplexType(property.PropertyType);

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

public object? DefaultValue { get; }

public bool OverrideNeverCamel { get; }

/// <summary>
/// If any Magic attribute was placed on a property. We never
/// camel case if that's the current json setting. These must stay
Expand All @@ -67,7 +71,7 @@ public bool NeverCamelCase
{
get
{
if (PrimaryKey || UniqueIndex || Indexed)
if (PrimaryKey || UniqueIndex || Indexed || OverrideNeverCamel)
return true;
else
return false;
Expand Down
5 changes: 5 additions & 0 deletions Magic.IndexedDb/Models/TypedArgument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public JsonElement SerializeToJsonElement(MagicJsonSerializationSettings? settin
{
return MagicSerializationHelper.SerializeObjectToJsonElement(Value, settings);
}

public string SerializeToJsonString(MagicJsonSerializationSettings? settings = null)
{
return MagicSerializationHelper.SerializeObject(Value, settings);
}
}

}
28 changes: 28 additions & 0 deletions Magic.IndexedDb/SchemaAnnotations/MagicNameAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Magic.IndexedDb.Interfaces;
using Magic.IndexedDb.SchemaAnnotations;

namespace Magic.IndexedDb.SchemaAnnotations
{
[AttributeUsage(AttributeTargets.Property)]
public class MagicNameAttribute : Attribute, IColumnNamed
{
public string ColumnName { get; }

public MagicNameAttribute(string columnName)
{
if (!string.IsNullOrWhiteSpace(columnName))
{
ColumnName = columnName;
}
else
{
throw new Exception("You have a MagicName attribute with no column name string provided!");
}
}
}
}
Loading