Skip to content

Commit b37b5a9

Browse files
author
MPCoreDeveloper
committed
all changes for performance
1 parent 1ff787c commit b37b5a9

File tree

18 files changed

+1544
-176
lines changed

18 files changed

+1544
-176
lines changed

SharpCoreDB.Demo/Program.cs

Lines changed: 126 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,144 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using SharpCoreDB;
33

4-
// This demo showcases the capabilities of SharpCoreDB, a lightweight, encrypted, file-based database
5-
// that supports basic SQL operations like CREATE TABLE, INSERT, and SELECT with WHERE and ORDER BY clauses.
6-
// SharpCoreDB uses JSON files for persistence, implements Write-Ahead Logging (WAL) for durability,
7-
// and provides user authentication with encrypted storage.
4+
// Serilog setup stub
5+
// Add Serilog packages to your project:
6+
// dotnet add package Serilog
7+
// dotnet add package Serilog.Sinks.Console
8+
// dotnet add package Serilog.Extensions.Logging
9+
using Serilog;
810

911
class Program
1012
{
1113
static void Main(string[] args)
1214
{
13-
Console.WriteLine("SharpCoreDB Demo - A Lightweight Encrypted Database");
14-
Console.WriteLine("===============================================");
15+
// Configure Serilog
16+
Log.Logger = new LoggerConfiguration()
17+
.MinimumLevel.Information()
18+
.WriteTo.Console()
19+
.CreateLogger();
1520

16-
// Clean up any previous demo data
17-
string dbPath = Path.Combine(Path.GetTempPath(), "SharpCoreDBDemo2");
18-
if (Directory.Exists(dbPath))
19-
{
20-
Directory.Delete(dbPath, true);
21-
}
22-
23-
// Step 1: Set up Dependency Injection
24-
// SharpCoreDB uses dependency injection for its services. We use the AddSharpCoreDB extension
25-
// to register all required services, making the database self-contained.
26-
var services = new ServiceCollection();
27-
services.AddSharpCoreDB();
28-
var serviceProvider = services.BuildServiceProvider();
29-
30-
// Step 2: Get the Database from DI
31-
// The database is now resolved from the service provider, with all dependencies configured internally.
32-
var factory = serviceProvider.GetRequiredService<DatabaseFactory>();
33-
string masterPassword = "demoMasterPassword123";
34-
35-
Console.WriteLine($"\nInitializing database at: {dbPath}");
36-
Console.WriteLine($"Master password: {masterPassword}");
37-
var db = factory.Create(dbPath, masterPassword);
38-
39-
// Step 3: Create Tables
40-
Console.WriteLine("--- Creating Tables ---");
41-
db.ExecuteSQL("CREATE TABLE users (id INTEGER, name TEXT)");
42-
string createTestTable = "CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT, active BOOLEAN, created DATETIME, score REAL, bigNum LONG, price DECIMAL, ulid ULID AUTO, guid GUID AUTO)";
43-
Console.WriteLine($"Executing: {createTestTable}");
44-
db.ExecuteSQL(createTestTable);
45-
46-
// Step 4: Insert Data
47-
Console.WriteLine("\n--- Inserting Data ---");
48-
db.ExecuteSQL("INSERT INTO users VALUES (?, ?)", new Dictionary<string, object?> { { "0", 1 }, { "1", "Alice" } });
49-
db.ExecuteSQL("INSERT INTO users VALUES (?, ?)", new Dictionary<string, object?> { { "0", 2 }, { "1", "Bob" } });
50-
db.ExecuteSQL("INSERT INTO users VALUES (?, ?)", new Dictionary<string, object?> { { "0", 3 }, { "1", "Charlie" } });
51-
var newUlid = Ulid.NewUlid().Value;
52-
Console.WriteLine($"Generated ULID: {newUlid}");
53-
var parsedUlid = Ulid.Parse(newUlid);
54-
Console.WriteLine($"Parsed timestamp: {parsedUlid.ToDateTime()}");
55-
db.ExecuteSQL("INSERT INTO test VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
56-
new Dictionary<string, object?> {
57-
{ "0", 1 },
58-
{ "1", "Test1" },
59-
{ "2", true },
60-
{ "3", new DateTime(2023, 1, 1) },
61-
{ "4", 10.5 },
62-
{ "5", 123456789012345L },
63-
{ "6", 99.99m },
64-
{ "7", newUlid },
65-
{ "8", Guid.NewGuid().ToString() }
66-
});
67-
db.ExecuteSQL("INSERT INTO test VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", new Dictionary<string, object?> { { "0", 2 }, { "1", "Test2" }, { "2", null }, { "3", null }, { "4", null }, { "5", null }, { "6", null }, { "7", null }, { "8", null } });
68-
db.ExecuteSQL("INSERT INTO test (id, name) VALUES (?, ?)", new Dictionary<string, object?> { { "0", 3 }, { "1", "AutoTest" } });
69-
Console.WriteLine("Inserted data");
70-
71-
// Step 5: Query Data
72-
Console.WriteLine("\n--- Querying Data ---");
73-
74-
// Select all test
75-
Console.WriteLine("Selecting all test:");
76-
db.ExecuteSQL("SELECT * FROM test");
77-
78-
// Select test where active = 'true'
79-
Console.WriteLine("\nSelecting test where active = 'true':");
80-
db.ExecuteSQL("SELECT * FROM test WHERE active = 'true'");
81-
82-
// Test UPDATE
83-
Console.WriteLine("\nTesting UPDATE:");
84-
db.ExecuteSQL("UPDATE test SET name = ? WHERE id = ?", new Dictionary<string, object?> { { "0", "UpdatedTest" }, { "1", 1 } });
85-
db.ExecuteSQL("SELECT * FROM test WHERE id = ?", new Dictionary<string, object?> { { "0", 1 } });
86-
87-
// Test DELETE
88-
Console.WriteLine("\nTesting DELETE:");
89-
db.ExecuteSQL("DELETE FROM test WHERE id = ?", new Dictionary<string, object?> { { "0", 2 } });
90-
db.ExecuteSQL("SELECT * FROM test");
91-
92-
// Test JOIN
93-
Console.WriteLine("\nTesting JOIN:");
94-
db.ExecuteSQL("SELECT test.id, users.name FROM test JOIN users ON test.id = users.id");
95-
96-
// Test LEFT JOIN
97-
Console.WriteLine("\nTesting LEFT JOIN:");
98-
db.ExecuteSQL("SELECT test.id, users.name FROM test LEFT JOIN users ON test.id = users.id");
99-
100-
// Step 6: Readonly Connection Test
101-
Console.WriteLine("\n--- Readonly Connection Test ---");
102-
var dbReadonly = factory.Create(dbPath, masterPassword, true); // Readonly
103-
Console.WriteLine("Readonly database created");
104-
105-
// Try to insert in readonly (should fail)
10621
try
10722
{
108-
dbReadonly.ExecuteSQL("INSERT INTO test VALUES (?, ?, ?, ?, ?)", new Dictionary<string, object?> { { "0", 3 }, { "1", "Readonly Test" }, { "2", false }, { "3", new DateTime(2023, 1, 2) }, { "4", 20.0 } });
23+
Console.WriteLine("SharpCoreDB Demo - A Lightweight Encrypted Database");
24+
Console.WriteLine("===============================================");
25+
26+
// Clean up any previous demo data
27+
string dbPath = Path.Combine(Path.GetTempPath(), "SharpCoreDBDemo2");
28+
if (Directory.Exists(dbPath))
29+
{
30+
Directory.Delete(dbPath, true);
31+
}
32+
33+
// Step 1: Set up Dependency Injection
34+
// SharpCoreDB uses dependency injection for its services. We use the AddSharpCoreDB extension
35+
// to register all required services, making the database self-contained.
36+
var services = new ServiceCollection();
37+
services.AddSharpCoreDB();
38+
// Add Serilog as the logging provider
39+
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog());
40+
var serviceProvider = services.BuildServiceProvider();
41+
42+
// Step 2: Get the Database from DI
43+
// The database is now resolved from the service provider, with all dependencies configured internally.
44+
var factory = serviceProvider.GetRequiredService<DatabaseFactory>();
45+
string masterPassword = "demoMasterPassword123";
46+
47+
Console.WriteLine($"\nInitializing database at: {dbPath}");
48+
Console.WriteLine($"Master password: {masterPassword}");
49+
var db = factory.Create(dbPath, masterPassword);
50+
51+
// Step 3: Create Tables
52+
Console.WriteLine("--- Creating Tables ---");
53+
db.ExecuteSQL("CREATE TABLE users (id INTEGER, name TEXT)");
54+
string createTestTable = "CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT, active BOOLEAN, created DATETIME, score REAL, bigNum LONG, price DECIMAL, ulid ULID AUTO, guid GUID AUTO)";
55+
Console.WriteLine($"Executing: {createTestTable}");
56+
db.ExecuteSQL(createTestTable);
57+
58+
// Step 4: Insert Data
59+
Console.WriteLine("\n--- Inserting Data ---");
60+
db.ExecuteSQL("INSERT INTO users VALUES (?, ?)", new Dictionary<string, object?> { { "0", 1 }, { "1", "Alice" } });
61+
db.ExecuteSQL("INSERT INTO users VALUES (?, ?)", new Dictionary<string, object?> { { "0", 2 }, { "1", "Bob" } });
62+
db.ExecuteSQL("INSERT INTO users VALUES (?, ?)", new Dictionary<string, object?> { { "0", 3 }, { "1", "Charlie" } });
63+
var newUlid = Ulid.NewUlid().Value;
64+
Console.WriteLine($"Generated ULID: {newUlid}");
65+
var parsedUlid = Ulid.Parse(newUlid);
66+
Console.WriteLine($"Parsed timestamp: {parsedUlid.ToDateTime()}");
67+
db.ExecuteSQL("INSERT INTO test VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
68+
new Dictionary<string, object?> {
69+
{ "0", 1 },
70+
{ "1", "Test1" },
71+
{ "2", true },
72+
{ "3", new DateTime(2023, 1, 1) },
73+
{ "4", 10.5 },
74+
{ "5", 123456789012345L },
75+
{ "6", 99.99m },
76+
{ "7", newUlid },
77+
{ "8", Guid.NewGuid().ToString() }
78+
});
79+
db.ExecuteSQL("INSERT INTO test VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", new Dictionary<string, object?> { { "0", 2 }, { "1", "Test2" }, { "2", null }, { "3", null }, { "4", null }, { "5", null }, { "6", null }, { "7", null }, { "8", null } });
80+
db.ExecuteSQL("INSERT INTO test (id, name) VALUES (?, ?)", new Dictionary<string, object?> { { "0", 3 }, { "1", "AutoTest" } });
81+
Console.WriteLine("Inserted data");
82+
83+
// Step 5: Query Data
84+
Console.WriteLine("\n--- Querying Data ---");
85+
86+
// Select all test
87+
Console.WriteLine("Selecting all test:");
88+
db.ExecuteSQL("SELECT * FROM test");
89+
90+
// Select test where active = 'true'
91+
Console.WriteLine("\nSelecting test where active = 'true':");
92+
db.ExecuteSQL("SELECT * FROM test WHERE active = 'true'");
93+
94+
// Test UPDATE
95+
Console.WriteLine("\nTesting UPDATE:");
96+
db.ExecuteSQL("UPDATE test SET name = ? WHERE id = ?", new Dictionary<string, object?> { { "0", "UpdatedTest" }, { "1", 1 } });
97+
db.ExecuteSQL("SELECT * FROM test WHERE id = ?", new Dictionary<string, object?> { { "0", 1 } });
98+
99+
// Test DELETE
100+
Console.WriteLine("\nTesting DELETE:");
101+
db.ExecuteSQL("DELETE FROM test WHERE id = ?", new Dictionary<string, object?> { { "0", 2 } });
102+
db.ExecuteSQL("SELECT * FROM test");
103+
104+
// Test JOIN
105+
Console.WriteLine("\nTesting JOIN:");
106+
db.ExecuteSQL("SELECT test.id, users.name FROM test JOIN users ON test.id = users.id");
107+
108+
// Test LEFT JOIN
109+
Console.WriteLine("\nTesting LEFT JOIN:");
110+
db.ExecuteSQL("SELECT test.id, users.name FROM test LEFT JOIN users ON test.id = users.id");
111+
112+
// Step 6: Readonly Connection Test
113+
Console.WriteLine("\n--- Readonly Connection Test ---");
114+
var dbReadonly = factory.Create(dbPath, masterPassword, true); // Readonly
115+
Console.WriteLine("Readonly database created");
116+
117+
// Try to insert in readonly (should fail)
118+
try
119+
{
120+
dbReadonly.ExecuteSQL("INSERT INTO test VALUES (?, ?, ?, ?, ?)", new Dictionary<string, object?> { { "0", 3 }, { "1", "Readonly Test" }, { "2", false }, { "3", new DateTime(2023, 1, 2) }, { "4", 20.0 } });
121+
}
122+
catch (Exception ex)
123+
{
124+
Console.WriteLine($"Insert in readonly failed: {ex.Message}");
125+
}
126+
127+
// Query in readonly (allows dirty reads)
128+
Console.WriteLine("Querying in readonly:");
129+
dbReadonly.ExecuteSQL("SELECT * FROM test");
130+
131+
Console.WriteLine("\nDemo completed successfully!");
109132
}
110133
catch (Exception ex)
111134
{
112-
Console.WriteLine($"Insert in readonly failed: {ex.Message}");
135+
Console.WriteLine($"An error occurred: {ex.Message}");
136+
}
137+
finally
138+
{
139+
// Ensure to flush and stop the logger
140+
Log.CloseAndFlush();
113141
}
114-
115-
// Query in readonly (allows dirty reads)
116-
Console.WriteLine("Querying in readonly:");
117-
dbReadonly.ExecuteSQL("SELECT * FROM test");
118-
119-
Console.WriteLine("\nDemo completed successfully!");
120142
}
121143
}
122144

SharpCoreDB.Demo/SharpCoreDB.Demo.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
<ItemGroup>
1212
<ProjectReference Include="..\SharpCoreDB\SharpCoreDB.csproj" />
1313
</ItemGroup>
14+
<ItemGroup>
15+
<PackageReference Include="Serilog" Version="4.0.0" />
16+
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
17+
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
18+
</ItemGroup>
1419

1520
</Project>
1621

SharpCoreDB.EntityFrameworkCore/Storage/SharpCoreDBConnection.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,41 @@ public override void Open()
111111
_state = ConnectionState.Open;
112112
}
113113

114+
/// <inheritdoc />
115+
public override async Task OpenAsync(CancellationToken cancellationToken = default)
116+
{
117+
if (_state == ConnectionState.Open)
118+
return;
119+
120+
if (string.IsNullOrWhiteSpace(_connectionStringBuilder.DataSource))
121+
throw new InvalidOperationException("Data source must be specified in connection string.");
122+
123+
// Check if pooling is enabled
124+
var usePooling = _connectionStringBuilder.Cache?.Equals("Shared", StringComparison.OrdinalIgnoreCase) == true;
125+
126+
if (usePooling)
127+
{
128+
_pool = _services.GetService<DatabasePool>();
129+
if (_pool == null)
130+
{
131+
// Create a new pool if not in DI
132+
_pool = new DatabasePool(_services, maxPoolSize: 10);
133+
}
134+
135+
_database = await Task.Run(() => _pool.GetDatabase(ConnectionString), cancellationToken);
136+
}
137+
else
138+
{
139+
var factory = _services.GetRequiredService<DatabaseFactory>();
140+
_database = await Task.Run(() => factory.Create(
141+
_connectionStringBuilder.DataSource,
142+
_connectionStringBuilder.Password ?? "default",
143+
_connectionStringBuilder.ReadOnly), cancellationToken);
144+
}
145+
146+
_state = ConnectionState.Open;
147+
}
148+
114149
/// <inheritdoc />
115150
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
116151
{

SharpCoreDB.Tests/AsyncTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,37 @@ public async Task ExecuteSQLAsync_WithConfig_UsesConfiguration()
119119
Assert.True(stats.Count >= 0); // Cache should be enabled
120120
}
121121

122+
[Fact]
123+
public void Prepare_And_ExecutePrepared_SelectWithParameter()
124+
{
125+
// Arrange
126+
var db = _factory.Create(_testDbPath, "testpass");
127+
db.ExecuteSQL("CREATE TABLE users (id INTEGER, name TEXT)");
128+
db.ExecuteSQL("INSERT INTO users VALUES (1, 'Alice')");
129+
db.ExecuteSQL("INSERT INTO users VALUES (2, 'Bob')");
130+
131+
// Act
132+
var stmt = db.Prepare("SELECT * FROM users WHERE id = ?");
133+
db.ExecutePrepared(stmt, new Dictionary<string, object?> { { "0", 1 } });
134+
135+
// Assert - no exception means success
136+
}
137+
138+
[Fact]
139+
public async Task ExecutePreparedAsync_InsertWithParameter()
140+
{
141+
// Arrange
142+
var db = _factory.Create(_testDbPath, "testpass");
143+
db.ExecuteSQL("CREATE TABLE prepared_test (id INTEGER, value TEXT)");
144+
145+
// Act
146+
var stmt = db.Prepare("INSERT INTO prepared_test VALUES (?, ?)");
147+
await db.ExecutePreparedAsync(stmt, new Dictionary<string, object?> { { "0", 1 }, { "1", "PreparedValue" } });
148+
149+
// Assert - verify insert worked
150+
db.ExecuteSQL("SELECT * FROM prepared_test"); // Should not throw
151+
}
152+
122153
public void Dispose()
123154
{
124155
try

0 commit comments

Comments
 (0)