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
4 changes: 3 additions & 1 deletion Magic.IndexedDb/Extensions/MagicJsInvoke.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ internal async Task CallInvokeVoidDefaultJsAsync(string modulePath, string funct
}

private async IAsyncEnumerable<T?> MagicYieldJsAsync<T>(
string modulePath, string functionName, CancellationToken token, params ITypedArgument[] args)
string modulePath, string functionName,
[EnumeratorCancellation] CancellationToken token,
params ITypedArgument[] args)
{
var settings = new MagicJsonSerializationSettings() { UseCamelCase = true };

Expand Down
2 changes: 1 addition & 1 deletion Magic.IndexedDb/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static IServiceCollection AddMagicBlazorDB(this IServiceCollection servic
long jsMessageSizeBytes, bool isDebug)
{
services.AddScoped<IMagicIndexedDb>(sp =>
new MagicDbFactory(sp, sp.GetRequiredService<IJSRuntime>(), jsMessageSizeBytes));
new MagicDbFactory(sp.GetRequiredService<IJSRuntime>(), jsMessageSizeBytes));

if (isDebug)
{
Expand Down
143 changes: 65 additions & 78 deletions Magic.IndexedDb/Factories/MagicDbFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,60 @@
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
{
internal MagicDbFactory(long jsMessageSizeBytes)
// null value indicates that the factory is disposed
Lazy<Task<IJSObjectReference>>? _jsModule;
Lazy<Task<IndexedDbManager>> _magicJsManager;
public MagicDbFactory(IJSRuntime jSRuntime, long jsMessageSizeBytes)
{
Cache.JsMessageSizeBytes = jsMessageSizeBytes;
}

Lazy<Task<IJSObjectReference>>? _jsRuntime;
readonly IServiceProvider _serviceProvider;
public static IndexedDbManager? _MagicJsManager { get; set; } = null;
private IJSObjectReference? _cachedJsModule; // Shared JS module instance
public MagicDbFactory(IServiceProvider serviceProvider, IJSRuntime jSRuntime, long jsMessageSizeBytes)
{
_serviceProvider = serviceProvider;
this._jsRuntime = new(() => jSRuntime.InvokeAsync<IJSObjectReference>(
this._jsModule = new(() => jSRuntime.InvokeAsync<IJSObjectReference>(
"import",
"./_content/Magic.IndexedDb/magicDbMethods.js").AsTask());
}
"./_content/Magic.IndexedDb/magicDbMethods.js").AsTask(),
isThreadSafe: true);

/// <summary>
/// Get or initialize the shared JavaScript module.
/// </summary>
private async Task<IJSObjectReference> GetJsModuleAsync()
{
if (_cachedJsModule is not null)
return _cachedJsModule;
this._magicJsManager = new(async () =>
{
var jsModule = await this._jsModule.Value;

var dbSchemas = SchemaHelper.GetAllSchemas();
// Create & Open the database (formerly in IndexedDbManager)
var manager = new IndexedDbManager(jsModule);

_cachedJsModule = await _jsRuntime.Value;
return _cachedJsModule;
var dbSets = SchemaHelper.GetAllIndexedDbSets();

if (dbSets != null)
{
foreach (var dbSet in dbSets)
{
await new MagicJsInvoke(jsModule).CallJsAsync(Cache.MagicDbJsImportPath,
IndexedDbFunctions.CREATE_LEGACY, default,
new TypedArgument<DbStore>(new DbStore()
{
Name = dbSet.DatabaseName,
Version = 1,
StoreSchemas = dbSchemas
}));
}
}
else
{
Console.WriteLine("No IndexedDbSet found and/or no found IMagicRepository.");
}
return manager;
},
isThreadSafe: true);
}

public async ValueTask DisposeAsync()
{
var js = _jsRuntime;
_jsRuntime = null;
var js = _jsModule;
_jsModule = null;

if (js is null || !js.IsValueCreated)
return;
Expand Down Expand Up @@ -79,58 +95,23 @@ public async ValueTask DisposeAsync()
}
}

/// <summary>
/// Ensure a database is opened and properly associated with the shared JS module.
/// </summary>
private async ValueTask<IndexedDbManager> GetOrCreateDatabaseAsync(CancellationToken cancellationToken = default)
{
if (_MagicJsManager != null)
return _MagicJsManager; // Return cached instance

var jsModule = await GetJsModuleAsync(); // Ensure shared JS module is ready

var dbSchemas = SchemaHelper.GetAllSchemas();
// Create & Open the database (formerly in IndexedDbManager)
var manager = new IndexedDbManager(jsModule);

var dbSets = SchemaHelper.GetAllIndexedDbSets();

if (dbSets != null)
{
foreach (var dbSet in dbSets)
{
await new MagicJsInvoke(jsModule).CallJsAsync(Cache.MagicDbJsImportPath,
IndexedDbFunctions.CREATE_LEGACY, cancellationToken,
new TypedArgument<DbStore>(new DbStore()
{
Name = dbSet.DatabaseName,
Version = 1,
StoreSchemas = dbSchemas
}));
}
}
else
{
Console.WriteLine("No IndexedDbSet found and/or no found IMagicRepository.");
}
_MagicJsManager = manager; // Cache the opened database
return _MagicJsManager;
}


/// <summary>
/// Get storage estimate using the shared JS module.
/// </summary>
public async Task<QuotaUsage> GetStorageEstimateAsync(CancellationToken cancellationToken = default)
{
var jsModule = await GetJsModuleAsync(); // Shared JS module reference
ObjectDisposedException.ThrowIf(this._jsModule is null, this);

var jsModule = await this._jsModule.Value;
var magicUtility = new MagicUtilities(jsModule);
return await magicUtility.GetStorageEstimateAsync();
}
[Obsolete("Not fully implemented yet until full migration protocol finished.")]
public async ValueTask<IMagicQuery<T>> Query<T>(IndexedDbSet indexedDbSet)
where T : class, IMagicTableBase, new()
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<T>();
Expand All @@ -140,9 +121,11 @@ public async ValueTask<IMagicQuery<T>> Query<T>(IndexedDbSet indexedDbSet)


public async ValueTask<IMagicQuery<T>> Query<T>(
Func<T, IndexedDbSet> dbSetSelector)
where T : class, IMagicTableBase, new()
Func<T, IndexedDbSet> 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();

Expand All @@ -153,39 +136,43 @@ public async ValueTask<IMagicQuery<T>> Query<T>(
string databaseName = selectedDbSet.DatabaseName;
string schemaName = SchemaHelper.GetTableName<T>();

#pragma warning disable CS0618
return await QueryOverride<T>(databaseName, schemaName);
#pragma warning restore CS0618
}

/// <summary>
/// Query the database for a given type. Automatically opens the database if needed.
/// </summary>
public async ValueTask<IMagicQuery<T>> Query<T>()
where T : class, IMagicTableBase, new()
{
{
ObjectDisposedException.ThrowIf(this._jsModule is null, this);

string databaseName = SchemaHelper.GetDefaultDatabaseName<T>();
string schemaName = SchemaHelper.GetTableName<T>();
var dbManager = await GetOrCreateDatabaseAsync();
var dbManager = await this._magicJsManager.Value;
#pragma warning disable CS0618
return await QueryOverride<T>(databaseName, schemaName);
#pragma warning restore CS0618
}

[Obsolete("Not decided if this will be built in further or removed")]
public async ValueTask<IMagicQuery<T>> QueryOverride<T>(string databaseNameOverride, string schemaNameOverride)
where T: class, IMagicTableBase, new ()
{
var dbManager = await GetOrCreateDatabaseAsync(); // Ensure database is open
ObjectDisposedException.ThrowIf(this._jsModule is null, this);

var dbManager = await this._magicJsManager.Value;
return dbManager.Query<T>(databaseNameOverride, schemaNameOverride);
}

public async ValueTask<IMagicDatabaseScoped> Database(IndexedDbSet indexedDbSet)
{
var dbManager = await GetOrCreateDatabaseAsync(); // Ensure database is open
ObjectDisposedException.ThrowIf(this._jsModule is null, this);

var dbManager = await this._magicJsManager.Value;
return dbManager.Database(dbManager, indexedDbSet);
}

//public async ValueTask<IMagicDatabaseScoped> Database()
//{
// throw new Exception("Still working on it.");
//}

}
}
4 changes: 2 additions & 2 deletions Magic.IndexedDb/Helpers/ExpressionFlattener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ private class LogicalExpressionComparer : IEqualityComparer<Expression>, ICompar
{
public static readonly LogicalExpressionComparer Instance = new();

public bool Equals(Expression x, Expression y) => Compare(x, y) == 0;
public bool Equals(Expression? x, Expression? y) => Compare(x, y) is 0;

public int GetHashCode(Expression obj) => obj.ToString().GetHashCode();

public int Compare(Expression x, Expression y)
public int Compare(Expression? x, Expression? y)
{
if (ReferenceEquals(x, y)) return 0;
if (x is null) return -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public interface IMagicExecute<T> where T : class
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
IAsyncEnumerable<T> AsAsyncEnumerable([EnumeratorCancellation] CancellationToken cancellationToken = default);
IAsyncEnumerable<T> AsAsyncEnumerable(CancellationToken cancellationToken = default);

/// <summary>
/// The order you apply does get applied correctly in the query,
Expand Down
8 changes: 4 additions & 4 deletions Magic.IndexedDb/Models/MagicContractResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ private bool IsSimpleJsonNull(JsonElement element)
object? value = ReadPropertyValue(ref reader, mpe, options);
propertyValues[csharpPropertyName] = value;
}
catch (Exception ex)
catch
{

// do nothing
}
}
else
Expand Down Expand Up @@ -324,9 +324,9 @@ private bool SerializeSimple(Utf8JsonWriter writer, object value)
/// <summary>
/// 🔥 Serializes lists (IEnumerable)
/// </summary>
private bool SerializeIEnumerable(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
private bool SerializeIEnumerable(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
{
if (value == null)
if (value is null)
{
writer.WriteNullValue();
return true;
Expand Down
8 changes: 0 additions & 8 deletions TestServer/TestServer/Components/Pages/Home.razor
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,6 @@
{
if (firstRender)
{
try
{

// Targets the default database automatically if called without any parameters.
IMagicQuery<Person> personQuery = await _MagicDb.Query<Person>();
await personQuery.ClearTable();
Expand Down Expand Up @@ -758,11 +755,6 @@


StateHasChanged();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
15 changes: 9 additions & 6 deletions TestServer/TestServer/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
"Logging":
{
"LogLevel":
{
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"DetailedErrors": true
}