Update EsentDatabase specs

This commit is contained in:
Namhyeon Go 2025-06-26 10:22:07 +09:00
parent 91ce125c7f
commit 82df8c5605
13 changed files with 248 additions and 23 deletions

View File

@ -1,6 +1,6 @@
// Column.cs (WelsonJS.Esent) // Column.cs (WelsonJS.Esent)
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2025 Namhyeon Go, Catswords OSS and WelsonJS Contributors // SPDX-FileCopyrightText: 2025 Namhyeon Go <gnh1201@catswords.re.kr>, Catswords OSS and WelsonJS Contributors
// https://github.com/gnh1201/welsonjs // https://github.com/gnh1201/welsonjs
// //
using System; using System;

View File

@ -1,6 +1,6 @@
// DataStore.cs (WelsonJS.Esent) // DataStore.cs (WelsonJS.Esent)
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2025 Namhyeon Go, Catswords OSS and WelsonJS Contributors // SPDX-FileCopyrightText: 2025 Namhyeon Go <gnh1201@catswords.re.kr>, Catswords OSS and WelsonJS Contributors
// https://github.com/gnh1201/welsonjs // https://github.com/gnh1201/welsonjs
// //
using System; using System;
@ -13,11 +13,13 @@ using Microsoft.Isam.Esent.Interop;
namespace WelsonJS.Esent namespace WelsonJS.Esent
{ {
public class DataStore : IDisposable public class EsentDatabase : IDisposable
{ {
private const string _primaryKeyindexName = "primary"; private const string _primaryKeyindexName = "primary";
private const string _indexNamePrefix = "idx_"; private const string _indexNamePrefix = "idx_";
private const string _databaseName = "metadata.edb";
private readonly ICompatibleLogger _logger;
private static readonly object _lock = new object(); private static readonly object _lock = new object();
private static bool _initialized = false; private static bool _initialized = false;
private static Instance _instance; private static Instance _instance;
@ -30,8 +32,10 @@ namespace WelsonJS.Esent
private readonly Column _primaryKey; private readonly Column _primaryKey;
private readonly Dictionary<string, JET_COLUMNID> _columnIds; private readonly Dictionary<string, JET_COLUMNID> _columnIds;
public DataStore(Schema schema, string workingDirectory) public EsentDatabase(Schema schema, string workingDirectory, ICompatibleLogger logger = null)
{ {
_logger = logger ?? new TraceLogger();
_primaryKey = schema.PrimaryKey; _primaryKey = schema.PrimaryKey;
if (schema == null) if (schema == null)
@ -74,10 +78,10 @@ namespace WelsonJS.Esent
if (_initialized) return; if (_initialized) return;
// set the file path // set the file path
_filePath = Path.Combine(_workingDirectory, "metadata.edb"); _filePath = Path.Combine(_workingDirectory, _databaseName);
// config the instance // config the instance
_instance = new Instance("WelsonJS.Launcher.MetadataStore"); _instance = new Instance(typeof(EsentDatabase).Namespace);
_instance.Parameters.SystemDirectory = _workingDirectory; _instance.Parameters.SystemDirectory = _workingDirectory;
_instance.Parameters.LogFileDirectory = _workingDirectory; _instance.Parameters.LogFileDirectory = _workingDirectory;
_instance.Parameters.TempDirectory = _workingDirectory; _instance.Parameters.TempDirectory = _workingDirectory;
@ -160,7 +164,7 @@ namespace WelsonJS.Esent
} }
catch (EsentColumnNotFoundException) catch (EsentColumnNotFoundException)
{ {
Trace.TraceWarning($"Column '{col.Name}' not found."); _logger.Warn($"Column '{col.Name}' not found.");
} }
} }
} }
@ -201,7 +205,7 @@ namespace WelsonJS.Esent
if (expectSeek != found) if (expectSeek != found)
{ {
Trace.TraceWarning($"[ESENT] Operation skipped. Seek result = {found}, expected = {expectSeek}"); _logger.Warn($"[ESENT] Operation skipped. Seek result = {found}, expected = {expectSeek}");
Api.JetRollback(_session, RollbackTransactionGrbit.None); Api.JetRollback(_session, RollbackTransactionGrbit.None);
return false; return false;
} }
@ -312,7 +316,7 @@ namespace WelsonJS.Esent
case JET_coltyp.LongBinary: case JET_coltyp.LongBinary:
return Api.RetrieveColumn(session, table, columnId); return Api.RetrieveColumn(session, table, columnId);
default: default:
Trace.TraceWarning($"[ESENT] Unsupported RetrieveColumn type: {type}"); _logger.Warn($"[ESENT] Unsupported RetrieveColumn type: {type}");
return null; return null;
} }
} }
@ -323,13 +327,13 @@ namespace WelsonJS.Esent
if (!values.TryGetValue(_primaryKey.Name, out keyValue)) if (!values.TryGetValue(_primaryKey.Name, out keyValue))
{ {
Trace.TraceWarning($"[ESENT] Missing primary key '{_primaryKey.Name}'."); _logger.Warn($"[ESENT] Missing primary key '{_primaryKey.Name}'.");
return false; return false;
} }
if (keyValue == null) if (keyValue == null)
{ {
Trace.TraceWarning("[ESENT] Primary key value cannot be null."); _logger.Warn("[ESENT] Primary key value cannot be null.");
return false; return false;
} }
@ -351,7 +355,7 @@ namespace WelsonJS.Esent
{ {
if (!_columnIds.TryGetValue(kv.Key, out var colid)) if (!_columnIds.TryGetValue(kv.Key, out var colid))
{ {
Trace.TraceWarning($"[ESENT] Column '{kv.Key}' not found in cache."); _logger.Warn($"[ESENT] Column '{kv.Key}' not found in cache.");
continue; continue;
} }
@ -384,7 +388,7 @@ namespace WelsonJS.Esent
Api.SetColumn(session, table, columnId, (byte[])value); Api.SetColumn(session, table, columnId, (byte[])value);
break; break;
default: default:
Trace.TraceWarning($"[ESENT] Unsupported SetColumn type: {type}"); _logger.Warn($"[ESENT] Unsupported SetColumn type: {type}");
break; break;
} }
} }
@ -410,7 +414,7 @@ namespace WelsonJS.Esent
Api.MakeKey(session, table, (byte[])value, MakeKeyGrbit.NewKey); Api.MakeKey(session, table, (byte[])value, MakeKeyGrbit.NewKey);
break; break;
default: default:
Trace.TraceWarning($"[ESENT] Unsupported MakeKey type: {type}"); _logger.Warn($"[ESENT] Unsupported MakeKey type: {type}");
break; break;
} }
} }

View File

@ -0,0 +1,17 @@
// ICompatibleLogger.cs (WelsonJS.Esent)
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2025 Namhyeon Go <gnh1201@catswords.re.kr>, Catswords OSS and WelsonJS Contributors
// https://github.com/gnh1201/welsonjs
//
// We use the ICompatibleLogger interface to maintain a BCL-first style.
// This allows for later replacement with logging libraries such as ILogger or Log4Net.
//
namespace WelsonJS.Esent
{
public interface ICompatibleLogger
{
void Info(string message);
void Warn(string message);
void Error(string message);
}
}

View File

@ -0,0 +1,58 @@
# WelsonJS.Esent
WelsonJS.Esent is a library that enables the use of the [ESENT](https://learn.microsoft.com/en-us/windows/win32/extensible-storage-engine/extensible-storage-engine) database (also known as the Extensible Storage Engine or JET Blue).
Although it was developed to support the WelsonJS framework, it can be used in any .NET-based project.
For more details, refer to the [WelsonJS Documentation](https://catswords-oss.rdbl.io/5719744820/5330609327).
## Example code
```csharp
using WelsonJS.Esent;
// connect the database to manage an instances
Schema schema = new Schema("Instances", new List<Column>
{
new Column("InstanceId", typeof(string), 255),
new Column("FirstDeployTime", typeof(DateTime), 1)
});
schema.SetPrimaryKey("InstanceId");
_db = new EsentDatabase(schema, Path.GetTempPath());
// Insert row
try
{
_db.Insert(new Dictionary<string, object>
{
["InstanceId"] = instanceId,
["FirstDeployTime"] = now
}, out _);
}
catch (Exception ex)
{
// Handle exception
}
// find all
var instances = _db.FindAll();
foreach (var instance in instances)
{
try
{
string instanceId = instance["InstanceId"].ToString();
string firstDeployTime = instance.ContainsKey("FirstDeployTime")
? ((DateTime)instance["FirstDeployTime"]).ToString(_dateTimeFormat)
: "Unknown";
Console.WriteLine($"{firstDeployTime}, {instanceId}");
}
catch (Exception ex)
{
// Handle exception
}
}
```
Source code available: https://github.com/gnh1201/welsonjs

View File

@ -1,6 +1,6 @@
// Schema.cs (WelsonJS.Esent) // Schema.cs (WelsonJS.Esent)
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2025 Namhyeon Go, Catswords OSS and WelsonJS Contributors // SPDX-FileCopyrightText: 2025 Namhyeon Go <gnh1201@catswords.re.kr>, Catswords OSS and WelsonJS Contributors
// https://github.com/gnh1201/welsonjs // https://github.com/gnh1201/welsonjs
// //
using System; using System;

View File

@ -0,0 +1,19 @@
// TraceLogger.cs (WelsonJS.Esent)
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2025 Namhyeon Go <gnh1201@catswords.re.kr>, Catswords OSS and WelsonJS Contributors
// https://github.com/gnh1201/welsonjs
//
// We use the ICompatibleLogger interface to maintain a BCL-first style.
// This allows for later replacement with logging libraries such as ILogger or Log4Net.
//
using System.Diagnostics;
namespace WelsonJS.Esent
{
public class TraceLogger : ICompatibleLogger
{
public void Info(string message) => Trace.TraceInformation(message);
public void Warn(string message) => Trace.TraceWarning(message);
public void Error(string message) => Trace.TraceError(message);
}
}

View File

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Platforms>AnyCPU;x86</Platforms>
<Title>WelsonJS.Esent</Title>
<PackageProjectUrl>https://github.com/gnh1201/welsonjs</PackageProjectUrl>
<RepositoryUrl>https://github.com/gnh1201/welsonjs</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>nosql,esent,database</PackageTags>
<AssemblyVersion>0.2.7.55</AssemblyVersion>
<FileVersion>0.2.7.55</FileVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Copyright>2025 Namhyeon Go, Catswords OSS and WelsonJS Contributors</Copyright>
<Description>Enable ESENT database engine</Description>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Authors>Namhyeon Go,Catswords OSS</Authors>
<PackageIcon></PackageIcon>
<PackageReadmeFile></PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\EsentInterop\EsentInterop.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="assets\docs\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="assets\img\logo.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -4,10 +4,40 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<Platforms>AnyCPU;x86</Platforms> <Platforms>AnyCPU;x86</Platforms>
<Title>WelsonJS.Esent</Title> <Title>WelsonJS.Esent</Title>
<PackageProjectUrl>https://github.com/gnh1201/welsonjs</PackageProjectUrl>
<RepositoryUrl>https://github.com/gnh1201/welsonjs</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>nosql,esent,database</PackageTags>
<AssemblyVersion>0.2.7.55</AssemblyVersion>
<FileVersion>0.2.7.55</FileVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Copyright>2025 Namhyeon Go, Catswords OSS and WelsonJS Contributors</Copyright>
<Description>Enable ESENT database engine</Description>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Authors>Namhyeon Go,Catswords OSS</Authors>
<PackageIcon></PackageIcon>
<PackageReadmeFile></PackageReadmeFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\EsentInterop\EsentInterop.csproj" /> <ProjectReference Include="..\EsentInterop\EsentInterop.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="assets\docs\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="assets\img\logo.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="README.md">
<PackagePath>\</PackagePath>
<Pack>True</Pack>
</None>
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,58 @@
# WelsonJS.Esent
WelsonJS.Esent is a library that enables the use of the [ESENT](https://learn.microsoft.com/en-us/windows/win32/extensible-storage-engine/extensible-storage-engine) database (also known as the Extensible Storage Engine or JET Blue).
Although it was developed to support the WelsonJS framework, it can be used in any .NET-based project.
For more details, refer to the [WelsonJS Documentation](https://catswords-oss.rdbl.io/5719744820/5330609327).
## Example code
```csharp
using WelsonJS.Esent;
// connect the database to manage an instances
Schema schema = new Schema("Instances", new List<Column>
{
new Column("InstanceId", typeof(string), 255),
new Column("FirstDeployTime", typeof(DateTime), 1)
});
schema.SetPrimaryKey("InstanceId");
_db = new EsentDatabase(schema, Path.GetTempPath());
// Insert row
try
{
_db.Insert(new Dictionary<string, object>
{
["InstanceId"] = instanceId,
["FirstDeployTime"] = now
}, out _);
}
catch (Exception ex)
{
// Handle exception
}
// find all
var instances = _db.FindAll();
foreach (var instance in instances)
{
try
{
string instanceId = instance["InstanceId"].ToString();
string firstDeployTime = instance.ContainsKey("FirstDeployTime")
? ((DateTime)instance["FirstDeployTime"]).ToString(_dateTimeFormat)
: "Unknown";
Console.WriteLine($"{firstDeployTime}, {instanceId}");
}
catch (Exception ex)
{
// Handle exception
}
}
```
Source code available: https://github.com/gnh1201/welsonjs

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -15,7 +15,7 @@
{ {
if (disposing && (components != null)) if (disposing && (components != null))
{ {
_dataStore?.Dispose(); // dispose the database session _db?.Dispose(); // dispose the database session
components.Dispose(); components.Dispose();
} }
base.Dispose(disposing); base.Dispose(disposing);

View File

@ -18,7 +18,7 @@ namespace WelsonJS.Launcher
private string _entryFileName; private string _entryFileName;
private string _scriptName; private string _scriptName;
private readonly string _dateTimeFormat; private readonly string _dateTimeFormat;
private readonly DataStore _dataStore; private readonly EsentDatabase _db;
public InstancesForm() public InstancesForm()
{ {
@ -37,12 +37,12 @@ namespace WelsonJS.Launcher
new Column("FirstDeployTime", typeof(DateTime), 1) new Column("FirstDeployTime", typeof(DateTime), 1)
}); });
schema.SetPrimaryKey("InstanceId"); schema.SetPrimaryKey("InstanceId");
_dataStore = new DataStore(schema, Program.GetAppDataPath()); _db = new EsentDatabase(schema, Program.GetAppDataPath());
} }
public DataStore GetDataStore() public EsentDatabase GetDatabaseInstance()
{ {
return _dataStore; return _db;
} }
private void InstancesForm_Load(object sender, EventArgs e) private void InstancesForm_Load(object sender, EventArgs e)
@ -53,7 +53,7 @@ namespace WelsonJS.Launcher
private void LoadInstances() private void LoadInstances()
{ {
var instances = _dataStore.FindAll(); var instances = _db.FindAll();
foreach (var instance in instances) foreach (var instance in instances)
{ {
try try
@ -135,7 +135,7 @@ namespace WelsonJS.Launcher
try try
{ {
Directory.Delete(workingDirectory, true); Directory.Delete(workingDirectory, true);
_dataStore.DeleteById(instanceId); _db.DeleteById(instanceId);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -169,7 +169,7 @@ namespace WelsonJS.Launcher
InstancesForm instancesForm = new InstancesForm(); InstancesForm instancesForm = new InstancesForm();
try try
{ {
instancesForm.GetDataStore().Insert(new Dictionary<string, object> instancesForm.GetDatabaseInstance().Insert(new Dictionary<string, object>
{ {
["InstanceId"] = instanceId, ["InstanceId"] = instanceId,
["FirstDeployTime"] = now ["FirstDeployTime"] = now