-
Notifications
You must be signed in to change notification settings - Fork 24
Dynamic databases and store schema #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.JSInterop; | ||
|
||
|
@@ -12,6 +13,7 @@ public class IndexedDbManager | |
{ | ||
readonly DbStore _dbStore; | ||
readonly IJSRuntime _jsRuntime; | ||
readonly IDictionary<string, IndexedDbManager> _dbs; | ||
const string InteropPrefix = "window.blazorDB"; | ||
DotNetObjectReference<IndexedDbManager> _objReference; | ||
IDictionary<Guid, WeakReference<Action<BlazorDbEvent>>> _transactions = new Dictionary<Guid, WeakReference<Action<BlazorDbEvent>>>(); | ||
|
@@ -27,11 +29,12 @@ public class IndexedDbManager | |
/// </summary> | ||
/// <param name="dbStore"></param> | ||
/// <param name="jsRuntime"></param> | ||
internal IndexedDbManager(DbStore dbStore, IJSRuntime jsRuntime) | ||
internal IndexedDbManager(DbStore dbStore, IJSRuntime jsRuntime, IDictionary<string, IndexedDbManager> dbs) | ||
{ | ||
_objReference = DotNetObjectReference.Create(this); | ||
_dbStore = dbStore; | ||
_jsRuntime = jsRuntime; | ||
_dbs = dbs; | ||
} | ||
|
||
public List<StoreSchema> Stores => _dbStore.StoreSchemas; | ||
|
@@ -63,6 +66,10 @@ public async Task<Guid> DeleteDb(string dbName, Action<BlazorDbEvent> action = n | |
} | ||
var trans = GenerateTransaction(action); | ||
await CallJavascriptVoid(IndexedDbFunctions.DELETE_DB, trans, dbName); | ||
if (_dbStore.Dynamic) | ||
{ | ||
_dbs.Remove(dbName); | ||
} | ||
return trans; | ||
} | ||
|
||
|
@@ -80,6 +87,57 @@ public async Task<BlazorDbEvent> DeleteDbAsync(string dbName) | |
} | ||
var trans = GenerateTransaction(); | ||
await CallJavascriptVoid(IndexedDbFunctions.DELETE_DB, trans.trans, dbName); | ||
if (_dbStore.Dynamic) | ||
{ | ||
_dbs.Remove(dbName); | ||
} | ||
return await trans.task; | ||
} | ||
|
||
/// <summary> | ||
/// Adds a new store schema to a dynamic database | ||
/// </summary> | ||
/// <param name="storeSchema">New schema to add</param> | ||
/// <returns></returns> | ||
public async Task<Guid> AddSchema(StoreSchema storeSchema, Action<BlazorDbEvent> action = null) | ||
{ | ||
if (!_dbStore.Dynamic) | ||
{ | ||
throw new ArgumentException($"Database is not dynamic. Cannot add schema."); | ||
} | ||
|
||
var trans = GenerateTransaction(action); | ||
await CallJavascriptVoid(IndexedDbFunctions.ADD_SCHEMA, trans, _dbStore, storeSchema); | ||
_dbStore.StoreSchemas.Add(storeSchema); | ||
_dbStore.Version = _dbStore.Version + 1; | ||
|
||
return trans; | ||
} | ||
|
||
/// <summary> | ||
/// Adds a new store schema to a dynamic database | ||
/// Waits for response | ||
/// </summary> | ||
/// <param name="storeSchema">New schema to add</param> | ||
/// <returns></returns> | ||
public async Task<BlazorDbEvent> AddSchemaAsync(StoreSchema storeSchema) | ||
{ | ||
if (!_dbStore.Dynamic) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why just for dynamic? |
||
{ | ||
throw new ArgumentException($"Database is not dynamic. Cannot add schema."); | ||
} | ||
|
||
var trans = GenerateTransaction(); | ||
try | ||
{ | ||
await CallJavascriptVoid(IndexedDbFunctions.ADD_SCHEMA, trans.trans, _dbStore, storeSchema); | ||
_dbStore.StoreSchemas.Add(storeSchema); | ||
_dbStore.Version = _dbStore.Version + 1; | ||
} | ||
catch (JSException jse) | ||
{ | ||
RaiseEvent(trans.trans, true, jse.Message); | ||
} | ||
return await trans.task; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,81 @@ | ||
window.blazorDB = { | ||
databases: [], | ||
createDb: function(dotnetReference, transaction, dbStore) { | ||
if(window.blazorDB.databases.find(d => d.name == dbStore.name) !== undefined) | ||
getDbNames: function () { | ||
return new Promise(resolve => Dexie.getDatabaseNames().then(resolve)); | ||
}, | ||
getDbStoreDynamic: function (dbName) { | ||
return new Promise((resolve, reject) => { | ||
Dexie.getDatabaseNames().then(dbNames => { | ||
if (dbNames.find(n => n === dbName) === undefined) { | ||
console.error("Database not found"); | ||
reject("No database"); | ||
} else { | ||
var db = new Dexie(dbName); | ||
|
||
db.open().then(db => { | ||
const dbStore = { | ||
name: db.name, | ||
version: db.verno, | ||
storeSchemas: db.tables.map(table => { | ||
return { | ||
name: table.name, | ||
primaryKey: table.schema.primKey.name, | ||
primaryKeyAuto: table.schema.primKey.auto, | ||
uniqueIndexes: table.schema.indexes | ||
.filter(index => index.unique) | ||
.map(index => index.name), | ||
indexes: table.schema.indexes | ||
.filter(index => !index.unique) | ||
.map(index => index.name) | ||
}; | ||
}), | ||
dynamic: true | ||
}; | ||
// Problem: when we create a dynamic db, we make it version 1 in createDb() below. | ||
// However, if we close/exit our session without adding any store schemas, | ||
// the next time we call db.open() on this dynamic db, the reported verno is 0, | ||
// rather than 1. Yet browser devtools DOES report the correct version. | ||
// Also, calling indexedDB directly will also report correct version. | ||
// (note, for unrelated reasons, Dexie version is the indexedDB version / 10, | ||
// see https://github.com/dfahlander/Dexie.js/issues/59) | ||
// To fix: when we come across verno = 0, change the version number in dbStore to 1. | ||
// dbStore needs correct version so we can increment it when a store schema is added. | ||
// This is not ideal; need to understand why Dexie reports verno 0 instead of 1. | ||
if (db.verno === 0) { | ||
dbStore.version = 1; | ||
indexedDB.databases().then(databases => { | ||
console.warn(`Dexie reports version of ${db.name} is ${db.verno}, but indexedDB reports version is ${databases.find(db1 => db1.name === db.name).version / 10}. BlazorDB will use indexedDB version number.`); | ||
}); | ||
} | ||
window.blazorDB.databases.push({ | ||
name: dbName, | ||
db: db | ||
}); | ||
resolve(dbStore); | ||
}) | ||
} | ||
}) | ||
}); | ||
}, | ||
addSchema: function (dotnetReference, transaction, dbStore, newStoreSchema) { | ||
if (!dbStore.dynamic || window.blazorDB.databases.find(d => d.name === dbStore.name) === undefined) { | ||
console.error(`Blazor.IndexedDB.Framework - Dynamic database ${dbStore.name} not found`); | ||
dotnetReference.invokeMethodAsync('BlazorDBCallback', transaction, true, `Dynamic database ${dbStore.name} could not be found`); | ||
return; | ||
} | ||
var db = window.blazorDB.databases.find(d => d.name === dbStore.name).db | ||
if (db.tables.map(table => table.name).find(name => name === newStoreSchema.name) !== undefined) { | ||
console.error(`Blazor.IndexedDB.Framework - schema for store ${newStoreSchema.name} already exists`); | ||
dotnetReference.invokeMethodAsync('BlazorDBCallback', transaction, true, 'Schema already exists'); | ||
return; | ||
} | ||
db.close(); | ||
|
||
dbStore.storeSchemas.push(newStoreSchema); | ||
this.createDb(dotnetReference, transaction, dbStore); | ||
}, | ||
createDb: function (dotnetReference, transaction, dbStore) { | ||
if(!dbStore.dynamic && window.blazorDB.databases.find(d => d.name === dbStore.name) !== undefined) | ||
console.warn("Blazor.IndexedDB.Framework - Database already exists"); | ||
|
||
var db = new Dexie(dbStore.name); | ||
|
@@ -32,6 +106,9 @@ window.blazorDB = { | |
|
||
stores[schema.name] = def; | ||
} | ||
if (dbStore.dynamic) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think belongs in |
||
dbStore.version = dbStore.version + 1; | ||
} | ||
db.version(dbStore.version).stores(stores); | ||
if(window.blazorDB.databases.find(d => d.name == dbStore.name) !== undefined) { | ||
window.blazorDB.databases.find(d => d.name == dbStore.name).db = db; | ||
|
@@ -41,7 +118,9 @@ window.blazorDB = { | |
db: db | ||
}); | ||
} | ||
db.open().then(_ => { | ||
db.open().then(db => { | ||
if (dbStore.dynamic) | ||
window.blazorDB.databases.find(d => d.name === dbStore.name).db = db; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this already happen on L113/L114? |
||
dotnetReference.invokeMethodAsync('BlazorDBCallback', transaction, false, 'Database opened'); | ||
}).catch(e => { | ||
console.error(e); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than open the DB here and have your logic in the javascript version to get around the Dexie bug, should we not allowed a DB to be opened if there is no schema? What's the point to open it anyway?
Thinking out loud, maybe in
AddSchema
we detect if any previous schemas and then open after at least 1 schema is added?