master - .Net5 version
master_net31 - 3.x version
This is a Blazor library to work with IndexedDB DOM API. It allows query multiple IndexedDb databases simultaneously
The actual API expose the following methods:
Compatibility *Server-side Blazor and client-side Blazor
<script src="https://github.com/amuste/DnetIndexedDb/raw/master/_content/DnetIndexedDb/rxjs.min.js"></script>
<script src="https://github.com/amuste/DnetIndexedDb/raw/master/_content/DnetIndexedDb/dnet-indexeddb.js"></script>
IndexedDbInterop
. public class GridColumnDataIndexedDb : IndexedDbInterop
{
public GridColumnDataIndexedDb(IJSRuntime jsRuntime, IndexedDbOptions<GridColumnDataIndexedDb> options)
:base(jsRuntime, options)
{
}
}
IndexedDbDatabaseModel
.ConfiguredServices
in Startup.cs
.IndexedDbDatabaseModel
instance. services.AddIndexedDbDatabase<GridColumnDataIndexedDb>(options =>
{
options.UseDatabase(GetGridColumnDatabaseModel());
});
To configure the database model, use the following classes. You can find more info about the IndexedDB database here: https://www.w3.org/TR/IndexedDB-2/#database-construct
IndexedDbDatabaseModel
IndexedDbStore
IndexedDbIndex
IndexedDbStoreParameter
public class IndexedDbDatabaseModel
{
public string Name { get; set; }
public int Version { get; set; }
public List<IndexedDbStore> Stores { get; set; } = new List<IndexedDbStore>();
public int DbModelId { get; set; }
}
var indexedDbDatabaseModel = new IndexedDbDatabaseModel
{
Name = "GridColumnData",
Version = 1,
Stores = new List<IndexedDbStore>
{
new IndexedDbStore
{
Name = "tableField",
Key = new IndexedDbStoreParameter
{
KeyPath = "tableFieldId",
AutoIncrement = true
},
Indexes = new List<IndexedDbIndex>
{
new IndexedDbIndex
{
Name = "tableFieldId",
Definition = new IndexedDbIndexParameter
{
Unique = true
}
},
new IndexedDbIndex
{
Name = "attachedProperty",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
},
new IndexedDbIndex
{
Name = "fieldVisualName",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
}, new IndexedDbIndex
{
Name = "hide",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
},
new IndexedDbIndex
{
Name = "isLink",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
},
new IndexedDbIndex
{
Name = "memberOf",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
},
new IndexedDbIndex
{
Name = "tableName",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
},
new IndexedDbIndex
{
Name = "textAlignClass",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
},
new IndexedDbIndex
{
Name = "type",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
},
new IndexedDbIndex
{
Name = "width",
Definition = new IndexedDbIndexParameter
{
Unique = false
}
}
}
}
},
DbModelId = 0
};
Fluent-based Extension methods to make the configuration of the database simplier. It will create the same model as manual configuration but in a more concise syntax.
using DnetIndexedDb.Fluent;
...
services.AddIndexedDbDatabase<SecuritySuiteDataIndexedDb>(options =>
{
var model = new IndexedDbDatabaseModel()
.WithName("Security")
.WithVersion(1)
.WithModelId(0);
model.AddStore("tableFieldDtos")
.WithAutoIncrementingKey("tableFieldId")
.AddUniqueIndex("tableFieldId")
.AddIndex("attachedProperty")
.AddIndex("fieldVisualName")
.AddIndex("hide")
.AddIndex("isLink")
.AddIndex("memberOf")
.AddIndex("tableName")
.AddIndex("textAlignClass")
.AddIndex("type")
.AddIndex("width");
options.UseDatabase(model);
});
[IndexDbKey]
and [IndexDbIndex]
Property Attributes can be used to configure the database based on the given class.
A DataStore will be created matching the name of the class.
public class TableFieldDto
{
[IndexDbKey(AutoIncrement = true)]
public int? TableFieldId { get; set; }
[IndexDbIndex]
public string TableName { get; set; }
[IndexDbIndex]
public string FieldVisualName { get; set; }
[IndexDbIndex]
public string AttachedProperty { get; set; }
[IndexDbIndex]
public bool IsLink { get; set; }
[IndexDbIndex]
public int MemberOf { get; set; }
[IndexDbIndex]
public int Width { get; set; }
[IndexDbIndex]
public string TextAlignClass { get; set; }
[IndexDbIndex]
public bool Hide { get; set; }
[IndexDbIndex]
public string Type { get; set; }
}
...
var indexedDbDatabaseModel = new IndexedDbDatabaseModel()
.WithName("TestAttributes")
.WithVersion(1);
indexedDbDatabaseModel.AddStore<TableFieldDto>();
You can create a service for any indexedDB's database that you want to use in your application. Use the base class IndexedDbInterop
to create your derived class. See code below.
public class GridColumnDataIndexedDb : IndexedDbInterop
{
public GridColumnDataIndexedDb(IJSRuntime jsRuntime, IndexedDbOptions<GridColumnDataIndexedDb> options)
:base(jsRuntime, options)
{
}
}
This follows a similar pattern to what you do when you create a new DbContext in EF core.
You can use the AddIndexedDbDatabase
extension method to register your service in ConfiguredServices
on Startup.cs
. Use the options
builder to add the service Database. See code below.
services.AddIndexedDbDatabase<GridColumnDataIndexedDb>(options =>
{
options.UseDatabase(GetGridColumnDatabaseModel());
});
GetGridColumnDatabaseModel()
return an instance of IndexedDbDatabaseModel
You can also use multiple database, declaring as many service as you want.
services.AddIndexedDbDatabase<SecuritySuiteDataIndexedDb>(options =>
{
options.UseDatabase(GetSecurityDatabaseModel());
});
To use IndexedDB service in a component or page first inject the IndexedDB servicer instance.
@inject GridColumnDataIndexedDb GridColumnDataIndexedDb
IndexedDB store are the equivalent of table in SQL Database. For the API demostrations will use the following class as our store model.
public class TableFieldDto
{
public int TableFieldId { get; set; }
public string TableName { get; set; }
public string FieldVisualName { get; set; }
public string AttachedProperty { get; set; }
public bool IsLink { get; set; }
public int MemberOf { get; set; }
public int Width { get; set; }
public string TextAlignClass { get; set; }
public bool Hide { get; set; }
public string Type { get; set; }
}
The following examples have two overloads of each DataStore level function.
The first is used when you need to manually specify the DataStore name and the store name does not match the Class Name.
The second is used when the Class name and DataStore name match, such as when using the Class Attribute based configuration.
ValueTask<int> OpenIndexedDb()
var result = await GridColumnDataIndexedDb.OpenIndexedDb();
ValueTask<TEntity> AddItems<TEntity>(string objectStoreName, List<TEntity> items)
ValueTask<TEntity> AddItems<TEntity>(List<TEntity> items)
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.AddItems<TableFieldDto>("tableField", items);
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.AddItems<TableFieldDto>(items);
ValueTask<TEntity> GetByKey<TKey, TEntity>(string objectStoreName, TKey key)
ValueTask<TEntity> GetByKey<TKey, TEntity>(TKey key)
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetByKey<int, TableFieldDto>("tableField", 11);
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetByKey<int, TableFieldDto>(11);
ValueTask<string> DeleteByKey<TKey>(string objectStoreName, TKey key)
ValueTask<string> DeleteByKey<TKey, TEntity>(TKey key)
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.DeleteByKey<int>("tableField", 11);
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.DeleteByKey<int, TableFieldDto>(11);
ValueTask<List<TEntity>> GetAll<TEntity>(string objectStoreName)
ValueTask<List<TEntity>> GetAll<TEntity>()
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetAll<TableFieldDto>("tableField");
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetAll<TableFieldDto>();
ValueTask<List<TEntity>> GetRange<TKey, TEntity>(string objectStoreName, TKey lowerBound, TKey upperBound)
ValueTask<List<TEntity>> GetRange<TKey, TEntity>(TKey lowerBound, TKey upperBound)
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetRange<int, TableFieldDto>("tableField", 15, 20);
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetRange<int, TableFieldDto>(15, 20);
ValueTask<List<TEntity>> GetByIndex<TKey, TEntity>(string objectStoreName, TKey lowerBound, TKey upperBound, string dbIndex, bool isRange)
ValueTask<List<TEntity>> GetByIndex<TKey, TEntity>(TKey lowerBound, TKey upperBound, string dbIndex, bool isRange)
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetByIndex<string, TableFieldDto>("tableField", "Name", null, "fieldVisualName", false);
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetByIndex<string, TableFieldDto>("Name", null, "fieldVisualName", false);
ValueTask<TKey> GetMaxKey<TKey>(string objectStoreName)
ValueTask<TKey> GetMaxKey<TKey, TEntity>()
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetMaxKey<string>("tableField");
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetMaxKey<string, TableFieldDto>();
ValueTask<TKey> GetMinKey<TKey>(string objectStoreName)
ValueTask<TKey> GetMinKey<TKey, TEntity>()
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetMinKey<string>("tableField");
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetMinKey<string, TableFieldDto>();
ValueTask<TIndex> GetMaxIndex<TIndex>(string objectStoreName, string dbIndex)
ValueTask<TIndex> GetMaxIndex<TIndex, TEntity>(string dbIndex)
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetMaxIndex<string>("tableField", "fieldVisualName");
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetMaxIndex<string, TableFieldDto>("fieldVisualName");
ValueTask<TIndex> GetMinIndex<TIndex>(string objectStoreName, string dbIndex)
ValueTask<TIndex> GetMinIndex<TIndex, TEntity>(string dbIndex)
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.GetMinIndex<string>("tableField", "fieldVisualName");
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.GetMinIndex<string, TableFieldDto>("fieldVisualName");
ValueTask<string> UpdateItems<TEntity>(string objectStoreName, List<TEntity> items)
ValueTask<string> UpdateItems<TEntity>(List<TEntity> items)
foreach (var item in items)
{
item.FieldVisualName = item.FieldVisualName + "Updated";
}
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.UpdateItems<TableFieldDto>("tableField", items);
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.UpdateItems<TableFieldDto>(items);
ValueTask<string> DeleteAll(string objectStoreName)
ValueTask<string> DeleteAll<TEntity>()
// Manually set DataStore name
var result = await GridColumnDataIndexedDb.DeleteAll("tableField");
OR
// DataStore name inferred from class
var result = await GridColumnDataIndexedDb.DeleteAll<TableFieldDto>();
ValueTask<string> DeleteIndexedDb()
var result = await GridColumnDataIndexedDb.DeleteIndexedDb();