Skip to content
This repository has been archived by the owner on May 10, 2020. It is now read-only.

Commit

Permalink
V0.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
chanan committed Dec 6, 2018
1 parent 303093a commit 1e563e8
Show file tree
Hide file tree
Showing 31 changed files with 318 additions and 232 deletions.
6 changes: 0 additions & 6 deletions BlazorDB.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorDB", "src\BlazorDB\Bl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "src\Sample\Sample.csproj", "{DAABB1BD-F735-4AB8-8ECF-70E00048175B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluxorIntegration", "src\FluxorIntegration\FluxorIntegration.csproj", "{9C42034D-6331-4623-B178-7417F9AB1345}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -23,10 +21,6 @@ Global
{DAABB1BD-F735-4AB8-8ECF-70E00048175B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DAABB1BD-F735-4AB8-8ECF-70E00048175B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DAABB1BD-F735-4AB8-8ECF-70E00048175B}.Release|Any CPU.Build.0 = Release|Any CPU
{9C42034D-6331-4623-B178-7417F9AB1345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C42034D-6331-4623-B178-7417F9AB1345}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C42034D-6331-4623-B178-7417F9AB1345}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C42034D-6331-4623-B178-7417F9AB1345}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ In memory, persisted to localstorage, database for .net Blazor browser framework
## Warning
This library like Blazor itself is experimental and API is likely to change.

## Breaking change as of V0.7.0

At this time, you will need to initialize the `Context` prior to using it. I hope that I cna get this done automatically again in a future version:

```
protected async override Task OnInitAsync()
{
await Context.Initialize();
}
```

## Note about the sample project

The Todo page does not work fully. The TodoForm that allows you to edit an item or add a new item is not working correctly. The OnInitAsync in the
child component is not firing. However, since BlazorDB itself it working, I decided to publish the version and figure out the Sample app afterwards.

## Docs

### Install
Expand Down Expand Up @@ -67,6 +83,15 @@ Inject your context into your component:
@inject Context Context
```

Currently, as of v0.7.0, before using the `Context` object you must initialize it. Hopefully, this requirement will go away in a future version:

```
protected async override Task OnInitAsync()
{
await Context.Initialize();
}
```

Create a model and add it to your Context:

```
Expand Down
8 changes: 4 additions & 4 deletions src/BlazorDB/BlazorDB.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
<LangVersion>7.3</LangVersion>
<PackageId>BlazorDB</PackageId>
<Version>0.2.0</Version>
<Version>0.7.0</Version>
<Authors>Chanan Braunstein</Authors>
<Title>Blazor localStorage Database</Title>
<Description>In memory, persisted to localstorage, database for .net Blazor browser framework</Description>
Expand All @@ -23,9 +23,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="BlazorLogger" Version="0.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Browser" Version="0.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="0.4.0" />
<PackageReference Include="BlazorLogger" Version="0.7.0" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Browser" Version="0.7.0" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="0.7.0" />
</ItemGroup>

</Project>
8 changes: 5 additions & 3 deletions src/BlazorDB/IStorageContext.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
namespace BlazorDB
using System.Threading.Tasks;

namespace BlazorDB
{
public interface IStorageContext
{
int SaveChanges();
void LogToConsole();
Task<int> SaveChanges();
Task LogToConsole();
}
}
21 changes: 14 additions & 7 deletions src/BlazorDB/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace BlazorDB
{
Expand All @@ -26,14 +28,19 @@ public static IServiceCollection AddBlazorDB(this IServiceCollection serviceColl
private static void Scan(IServiceCollection serviceCollection, Assembly assembly)
{
var types = ScanForContexts(serviceCollection, assembly);
RegisterBlazorDb(serviceCollection, types);
}

private static void RegisterBlazorDb(IServiceCollection serviceCollection, IEnumerable<Type> types)
{
serviceCollection.AddSingleton(StorageManager);
foreach (var contextType in types)
StorageManager.LoadContextFromStorageOrCreateNew(serviceCollection, contextType);

foreach (var type in types)
{
serviceCollection.AddSingleton(type, s =>
{
var jsRuntime = s.GetRequiredService<IJSRuntime>();
var instance = Activator.CreateInstance(type);
var smProp = type.GetProperty("StorageManager", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
smProp.SetValue(instance, StorageManager);
return instance;
});
}
}

private static IEnumerable<Type> ScanForContexts(IServiceCollection serviceCollection, Assembly assembly)
Expand Down
8 changes: 4 additions & 4 deletions src/BlazorDB/Storage/IStorageManager.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System;
using System.Threading.Tasks;

namespace BlazorDB.Storage
{
public interface IStorageManager
{
int SaveContextToLocalStorage(StorageContext context);
void LoadContextFromStorageOrCreateNew(IServiceCollection serviceCollection, Type contextType);
Task<int> SaveContextToLocalStorage(StorageContext context);
Task LoadContextFromLocalStorage(StorageContext context);
}
}
15 changes: 8 additions & 7 deletions src/BlazorDB/Storage/Logger.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;

namespace BlazorDB.Storage
{
Expand All @@ -10,23 +11,23 @@ internal static class Logger
private const string Normal = "color: black; font-style: normal;";
internal static bool LogDebug { get; set; } = true;

internal static void LogStorageSetToConsole(Type type, object list)
internal async static Task LogStorageSetToConsole(Type type, object list)
{
if (!LogDebug) return;
BlazorLogger.Logger.Log($"StorageSet<{type.GetGenericArguments()[0].Name}>: %o", list);
await BlazorLogger.Logger.Log($"StorageSet<{type.GetGenericArguments()[0].Name}>: %o", list);
}

internal static void StartContextType(Type contextType, bool loading = true)
internal async static Task StartContextType(Type contextType, bool loading = true)
{
if (!LogDebug) return;
var message = loading ? " loading" : " log";
BlazorLogger.Logger.GroupCollapsed($"Context{message}: %c{contextType.Namespace}.{contextType.Name}", Blue);
var message = loading ? "loading" : "log";
await BlazorLogger.Logger.GroupCollapsed($"Context {message}: %c{contextType.Namespace}.{contextType.Name}", Blue);
}

internal static void ContextSaved(Type contextType)
internal async static Task ContextSaved(Type contextType)
{
if (!LogDebug) return;
BlazorLogger.Logger.GroupCollapsed($"Context %csaved: %c{contextType.Namespace}.{contextType.Name}", Green,
await BlazorLogger.Logger.GroupCollapsed($"Context %csaved: %c{contextType.Namespace}.{contextType.Name}", Green,
Blue);
}

Expand Down
8 changes: 4 additions & 4 deletions src/BlazorDB/Storage/StorageManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;

namespace BlazorDB.Storage
{
Expand All @@ -8,14 +8,14 @@ internal class StorageManager : IStorageManager
private readonly StorageManagerLoad _storageManagerLoad = new StorageManagerLoad();
private readonly StorageManagerSave _storageManagerSave = new StorageManagerSave();

public int SaveContextToLocalStorage(StorageContext context)
public Task<int> SaveContextToLocalStorage(StorageContext context)
{
return _storageManagerSave.SaveContextToLocalStorage(context);
}

public void LoadContextFromStorageOrCreateNew(IServiceCollection serviceCollection, Type contextType)
public Task LoadContextFromLocalStorage(StorageContext context)
{
_storageManagerLoad.LoadContextFromStorageOrCreateNew(serviceCollection, contextType);
return _storageManagerLoad.LoadContextFromLocalStorage(context);
}
}
}
51 changes: 20 additions & 31 deletions src/BlazorDB/Storage/StorageManagerLoad.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,39 @@
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.AspNetCore.Blazor;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace BlazorDB.Storage
{
internal class StorageManagerLoad
{
public void LoadContextFromStorageOrCreateNew(IServiceCollection serviceCollection, Type contextType)
public async Task LoadContextFromLocalStorage(StorageContext context)
{
Logger.StartContextType(contextType);
var contextType = context.GetType();
await Logger.StartContextType(contextType);
var storageSets = StorageManagerUtil.GetStorageSets(contextType);
var stringModels = LoadStringModels(contextType, storageSets);
var stringModels = await LoadStringModels(contextType, storageSets);
//PrintStringModels(stringModels);
stringModels = ScanNonAssociationModels(storageSets, stringModels);
stringModels = ScanAssociationModels(storageSets, stringModels);
stringModels = DeserializeModels(stringModels, storageSets);
//PrintStringModels(stringModels);
var context = CreateContext(contextType, stringModels);
RegisterContext(serviceCollection, contextType, context);
await EnrichContext(context, contextType, stringModels);
Logger.EndGroup();
}

private static object CreateContext(Type contextType,
private static async Task EnrichContext(StorageContext context, Type contextType,
IReadOnlyDictionary<Type, Dictionary<int, SerializedModel>> stringModels)
{
var context = Activator.CreateInstance(contextType);
foreach (var prop in contextType.GetProperties())
if (prop.PropertyType.IsGenericType &&
prop.PropertyType.GetGenericTypeDefinition() == typeof(StorageSet<>))
{
var modelType = prop.PropertyType.GetGenericArguments()[0];
var storageSetType = StorageManagerUtil.GenericStorageSetType.MakeGenericType(modelType);
var storageTableName = Util.GetStorageTableName(contextType, modelType);
var metadata = StorageManagerUtil.LoadMetadata(storageTableName);
var metadata = await StorageManagerUtil.LoadMetadata(storageTableName);
if (stringModels.ContainsKey(modelType))
{
var map = stringModels[modelType];
Expand All @@ -46,14 +45,11 @@ private static object CreateContext(Type contextType,
{
Logger.LoadModelInContext(modelType, 0);
}

var storageSet = metadata != null
? LoadStorageSet(storageSetType, contextType, modelType, stringModels[modelType])
: CreateNewStorageSet(storageSetType, contextType);
prop.SetValue(context, storageSet);
}

return context;
}

private static Dictionary<Type, Dictionary<int, SerializedModel>> DeserializeModels(
Expand Down Expand Up @@ -251,7 +247,7 @@ private static string FixAssociationsInStringModels(SerializedModel stringModel,

private static string ReplaceListWithAssociationList(string serializedModel, string propName, string strList)
{
var propStart = serializedModel.IndexOf($"\"{propName}\":[", StringComparison.Ordinal);
var propStart = serializedModel.IndexOf($"\"{propName.ToCamelCase()}\":[", StringComparison.Ordinal);
var start = serializedModel.IndexOf('[', propStart) + 1;
var end = serializedModel.IndexOf(']', start);
var result = StorageManagerUtil.ReplaceString(serializedModel, start, end, strList);
Expand All @@ -262,13 +258,13 @@ private static bool TryGetIdListFromSerializedModel(string serializedModel, stri
out List<int> idList)
{
var list = new List<int>();
if (serializedModel.IndexOf($"\"{propName}\":null", StringComparison.Ordinal) != -1)
if (serializedModel.IndexOf($"\"{propName.ToCamelCase()}\":null", StringComparison.Ordinal) != -1)
{
idList = list;
return false;
}

var propStart = serializedModel.IndexOf($"\"{propName}\":[", StringComparison.Ordinal);
var propStart = serializedModel.IndexOf($"\"{propName.ToCamelCase()}\":[", StringComparison.Ordinal);
var start = serializedModel.IndexOf('[', propStart) + 1;
var end = serializedModel.IndexOf(']', start);
var stringlist = serializedModel.Substring(start, end - start);
Expand Down Expand Up @@ -297,7 +293,7 @@ private static bool IsScanDone(Dictionary<Type, Dictionary<int, SerializedModel>
return done;
}

private static Dictionary<Type, Dictionary<int, SerializedModel>> LoadStringModels(Type contextType,
private static async Task<Dictionary<Type, Dictionary<int, SerializedModel>>> LoadStringModels(Type contextType,
IEnumerable<PropertyInfo> storageSets)
{
var stringModels = new Dictionary<Type, Dictionary<int, SerializedModel>>();
Expand All @@ -306,12 +302,12 @@ private static Dictionary<Type, Dictionary<int, SerializedModel>> LoadStringMode
var modelType = prop.PropertyType.GetGenericArguments()[0];
var map = new Dictionary<int, SerializedModel>();
var storageTableName = Util.GetStorageTableName(contextType, modelType);
var metadata = StorageManagerUtil.LoadMetadata(storageTableName);
var metadata = await StorageManagerUtil.LoadMetadata(storageTableName);
if (metadata == null) continue;
foreach (var guid in metadata.Guids)
{
var name = $"{storageTableName}-{guid}";
var serializedModel = BlazorDBInterop.GetItem(name, false);
var serializedModel = await BlazorDBInterop.GetItem(name, false);
var id = FindIdInSerializedModel(serializedModel);
map.Add(id, new SerializedModel {StringModel = serializedModel});
}
Expand All @@ -325,13 +321,13 @@ private static Dictionary<Type, Dictionary<int, SerializedModel>> LoadStringMode
//TODO: Verify that the found id is at the top level in case of nested objects
private static bool TryGetIdFromSerializedModel(string serializedModel, string propName, out int id)
{
if (serializedModel.IndexOf($"\"{propName}\":null", StringComparison.Ordinal) != -1)
if (serializedModel.IndexOf($"\"{propName.ToCamelCase()}\":null", StringComparison.Ordinal) != -1)
{
id = -1;
return false;
}

var propStart = serializedModel.IndexOf($"\"{propName}\":", StringComparison.Ordinal);
var propStart = serializedModel.IndexOf($"\"{propName.ToCamelCase()}\":", StringComparison.Ordinal);
var start = serializedModel.IndexOf(':', propStart);
id = GetIdFromString(serializedModel, start);
return true;
Expand All @@ -340,7 +336,7 @@ private static bool TryGetIdFromSerializedModel(string serializedModel, string p
//TODO: Verify that the found id is at the top level in case of nested objects
private static int FindIdInSerializedModel(string serializedModel)
{
var start = serializedModel.IndexOf($"\"{StorageManagerUtil.Id}\":", StringComparison.Ordinal);
var start = serializedModel.IndexOf($"\"{StorageManagerUtil.Id.ToCamelCase()}\":", StringComparison.Ordinal);
return GetIdFromString(serializedModel, start);
}

Expand Down Expand Up @@ -368,7 +364,7 @@ private static int GetIdFromString(string stringToSearch, int startFrom = 0)

private static string ReplaceIdWithAssociation(string result, string name, int id, string stringModel)
{
var stringToFind = $"\"{name}\":{id}";
var stringToFind = $"\"{name.ToCamelCase()}\":{id}";
var nameIndex = result.IndexOf(stringToFind, StringComparison.Ordinal);
var index = result.IndexOf(id.ToString(), nameIndex, StringComparison.Ordinal);
result = StorageManagerUtil.ReplaceString(result, index, index + id.ToString().Length, stringModel);
Expand Down Expand Up @@ -398,20 +394,13 @@ private static object DeserializeModel(Type modelType, string value)
var model = genericMethod.Invoke(new JsonWrapper(), new object[] {value});
return model;
}

private static void RegisterContext(IServiceCollection serviceCollection, Type type, object context)
{
serviceCollection.AddSingleton(
type,
context);
}
}

internal class JsonWrapper
{
public T Deserialize<T>(string value)
{
return JsonUtil.Deserialize<T>(value);
return Json.Deserialize<T>(value);
}
}
}
Loading

0 comments on commit 1e563e8

Please sign in to comment.