Skip to content

Commit 7c2e482

Browse files
committed
refactored sql storage
1 parent 12f4fdb commit 7c2e482

File tree

12 files changed

+297
-314
lines changed

12 files changed

+297
-314
lines changed

docs/storage/sql-storage/index.md

Lines changed: 47 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,67 +5,57 @@ layout: submenu
55
## {{page.title}}
66

77

8-
By default, journal files and snapshots are written to the file system. Storage is configurable and supports writing the journal to a relational database taking advantage of existing infrastructure and operations. Also, the journal can be queried/manipulated using regular SQL.
8+
Storage is configurable. By default, journal files and snapshots are written to the file system. Using Sql Storage, The journal can be stored in a relational database, allowing you to take advantage of existing infrastructure and operations. Also, the journal can be queried/manipulated using regular SQL.
99

10-
The first step is to set the storage type:
10+
Sql Storage uses the DbProviderFactories of NET.
11+
The built-in providers are MsSqlProvider and OleDbProvider.
12+
13+
Sql storage is flexible, you can supply custom statements for initializing, reading and writing entries.
14+
15+
The default table has these columns:
16+
17+
Name | Type | Description
18+
---- | ---- | -----
19+
Id | ulong | The unique sequential id of the journal entry
20+
Created | DateTime | When the command was executed
21+
Type | String | The type name of the command executed
22+
Data | Binary or string | The serialized command
23+
24+
To enable sql storage set the storage type for journaling to Sql:
1125

1226
```csharp
1327
var config = new EngineConfiguration();
1428
config.JournalStorage = StorageType.Sql;
29+
var engine = Engine.For<MyDb>(config);
30+
```
31+
32+
The default settings assume a connection string entry in the application configuration file named 'origo':
33+
34+
```xml
35+
<connectionString name="origo"
36+
connectionString="Data Source=.;Initial Catalog=freedb;Integrated Security=True"
37+
providerName="System.Data.SqlClient"/>
38+
```
39+
40+
The providerName must be one the following supported providers or a custom provider. See Custom Providers below.
41+
42+
* System.Data.SqlClient
43+
* System.Data.OleDbClient
44+
* System.Data.OdbcClient
45+
46+
Here are the default settings, which can be assigned new values. The `ConnectionString` property can be assigned either a connection string name in the application configuration file or an actual connection string.
47+
48+
```csharp
49+
config.SqlSettings.TableName = "OrigoJournal";
50+
config.ConnectionString = "origo";
51+
config.SqlSettings.ProviderName = "System.Data.SqlClient";
52+
var engine = Engine.For<MyDb>(config);
53+
```
54+
55+
## Register a custom provider
56+
Providers derive from SqlProvider and supply the vendor specific sql statements for reading and writing the journal. Custom providers need to be registered using the name and a constructor function taking a SqlSettings as input:
57+
```csharp
58+
SqlProvider.Register("MyProviderName", settings => new MyProvider(settings));
1559
```
1660

17-
## The happy path
18-
19-
1. Set the JournalStorage to StorageType.Sql
20-
2. S
21-
22-
## Relevant types
23-
* SqlSettings
24-
* SqlProvider
25-
* MsSqlProvider
26-
* SqlStorage
27-
28-
The provider creates a single table named CommandJournal with the following columns:
29-
30-
Name | Description
31-
-----|------------
32-
Id | The unique sequential id of the command
33-
CommandName | The type name of the command executed
34-
Created | The point in time when the command was executed
35-
Entry | The serialized `JournalEntry<Command>` object
36-
37-
## Supported SQL databases
38-
Release 0.1.0 was tested on Sql Server 2008 R2 developer edition and should work with 2005, 2008, 2012 and SqlCE.
39-
40-
## Configuring SqlStorage
41-
1. Add OrigoDb.Modules.SqlStorage to your project. Grab it on the downloads page or using nuget.
42-
2. Add a connectionstring to your app config file pointing to an existing database
43-
{% highlight xml %}
44-
<connectionStrings>
45-
<add name="connectionName"
46-
connectionString="Data Source=.;Initial Catalog=freedb;Integrated Security=True"
47-
providerName="System.Data.SqlClient" />
48-
</connectionStrings>
49-
{% endhighlight %}
50-
51-
3. Pass an instance of `SqlEngineConfiguration` when creating or loading your database
52-
53-
{% highlight csharp %}
54-
var config = new SqlEngineConfiguration("connectionName");
55-
config.SnapshotLocation = @"c:\\temp";
56-
var engine = Engine.LoadOrCreate<MyModel>(config);
57-
{% endhighlight %}
58-
59-
Alternatively, you can set `Location` to a connection string directly. If so, you must also set the `LocationType` and `ProviderName` properties:
60-
61-
{% highlight csharp %}
62-
var config = new SqlEngineConfiguration();
63-
config.Location = "Data Source=.;Initial Catalog=freedb;Integrated Security=True";
64-
config.LocationType = LocationType.ConnectionString;
65-
config.ProviderName = "System.Data.SqlClient";
66-
config.SnapshotLocation = @"c:\\temp";
67-
var engine = Engine.LoadOrCreate<MyModel>(config);
68-
{% endhighlight %}
69-
70-
## Converting existing journal
71-
Use the StorageUtility to copy an existing journal from file to sql or sql to file.
61+
Additionally, the provider name must be recognized by DbProviderFactories, see MSDN documentation.

src/OrigoDB.Core.UnitTests/SqlStorageTest.cs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Configuration;
3+
using System.Data;
4+
using System.Data.Common;
35
using System.Diagnostics;
46
using System.IO;
57
using System.Runtime.Serialization.Formatters.Binary;
@@ -13,23 +15,40 @@ namespace OrigoDB.Core.Test
1315
[TestFixture]
1416
public class SqlStorageTest
1517
{
18+
19+
[Test]
20+
public void DisplayProviders()
21+
{
22+
var table = DbProviderFactories.GetFactoryClasses();
23+
foreach (DataRow row in table.Rows)
24+
{
25+
foreach (DataColumn column in table.Columns)
26+
{
27+
Console.WriteLine(column.ColumnName + ":" + row[column]);
28+
}
29+
Console.WriteLine("-------------------------------------------------");
30+
}
31+
}
32+
1633
[Test, Ignore]
17-
public void MsSqlProviderSmokeTest()
34+
public void MsSqlCommandStoreWriteReadEntries()
1835
{
19-
var settings = new SqlSettings();
36+
var config = new EngineConfiguration();
37+
var settings = config.SqlSettings;
2038
settings.ConnectionString = "Data Source=.;Initial Catalog=fish;Integrated Security=True";
2139
settings.ProviderName = "System.Data.SqlClient";
22-
var provider = SqlProvider.Create(settings);
23-
provider.Initialize();
40+
settings.TableName = "[test-" + Guid.NewGuid() + "]";
41+
var commandStore = new SqlCommandStore(config);
42+
commandStore.Initialize();
2443
var formatter = new BinaryFormatter();
25-
var writer = new SqlJournalWriter(formatter, provider);
44+
var writer = new SqlJournalWriter(formatter, commandStore);
2645
writer.Write(JournalEntry.Create(1UL, DateTime.Now, new ModelCreated(typeof (TestModel))));
2746
writer.Write(JournalEntry.Create(2UL, DateTime.Now.AddSeconds(1), new AppendNumberCommand(42)));
2847
writer.Write(JournalEntry.Create(3UL, DateTime.Now.AddSeconds(2), new AppendNumberCommand(64)));
2948

30-
foreach (var entry in provider.ReadJournalEntries(1, bytes => formatter.FromByteArray<object>(bytes)))
49+
foreach (var entry in commandStore.GetJournalEntriesFrom(1))
3150
{
32-
Trace.WriteLine(entry.GetItem());
51+
Trace.WriteLine(entry);
3352
}
3453
writer.Dispose();
3554
}
@@ -39,7 +58,7 @@ public void MsSqlProviderIntegrationTest()
3958
var config = new EngineConfiguration();
4059
config.JournalStorage = StorageType.Sql;
4160
config.SqlSettings.ConnectionString = "Data Source=.;Initial Catalog=fish;Integrated Security=True;";
42-
config.SqlSettings.ProviderName = MsSqlProvider.Name;
61+
config.SqlSettings.ProviderName = "System.Data.SqlClient";
4362
var engine = Engine.For<TestModel>(config);
4463
int initial = engine.Execute(new DelegateQuery<TestModel, int>(m => m.CommandsExecuted));
4564
engine.Execute(new TestCommandWithoutResult());

src/OrigoDB.Core/Configuration/SqlSettings.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using System.Configuration;
2+
using OrigoDB.Core.Storage.Sql;
23

34
namespace OrigoDB.Core
45
{
56

67
/// <summary>
78
/// Configuration settings when using a backing sql store.
8-
/// Exposed via EngineConfiguration.SqlSettings property.
9+
/// Exposed via EngineConfiguration.Sql property.
910
/// </summary>
1011
public class SqlSettings
1112
{
@@ -31,6 +32,11 @@ public class SqlSettings
3132
/// </summary>
3233
public string ConnectionString { get; set; }
3334

35+
/// <summary>
36+
/// Custom SQL Statements, leave null to use the default, provider specific statements
37+
/// </summary>
38+
public SqlStatements Statements { get; set; }
39+
3440
/// <summary>
3541
/// Lookup ConnectionStringSetting in application configuration file using ConnectionString as key.
3642
/// If it exists, assign ConnectionString and ProviderName properties.

src/OrigoDB.Core/OrigoDB.Core.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,11 @@
127127
<Compile Include="Storage\Rollover\MaxBytesRolloverStrategy.cs" />
128128
<Compile Include="Storage\Rollover\MaxEntriesRolloverStrategy.cs" />
129129
<Compile Include="Storage\Rollover\ScheduledRolloverStrategy.cs" />
130+
<Compile Include="Storage\Sql\OleDbStatements.cs" />
130131
<Compile Include="Storage\Sql\SqlCommandStore.cs" />
131132
<Compile Include="Storage\Sql\SqlJournalWriter.cs" />
132-
<Compile Include="Storage\Sql\MsSqlProvider.cs" />
133-
<Compile Include="Storage\Sql\SqlProvider.cs" />
133+
<Compile Include="Storage\Sql\MsSqlStatements.cs" />
134+
<Compile Include="Storage\Sql\SqlStatements.cs" />
134135
<Compile Include="Utilities\Utils.Converters.cs" />
135136
<Compile Include="Storage\Journaling\JournalAppender.cs" />
136137
<Compile Include="Storage\Journaling\ModelCreated.cs" />

src/OrigoDB.Core/Storage/Journaling/JournalEntry.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ internal static JournalEntry Create(ulong id, DateTime created, object item)
5858
if (item is Command) return new JournalEntry<Command>(id, (Command) item, created);
5959
throw new ArgumentOutOfRangeException("unrecognized journal entry item :" + item);
6060
}
61+
62+
public override string ToString()
63+
{
64+
return String.Format("[{0} - {1} - {2}", Id, Created, GetItem().GetType());
65+
}
6166
}
6267

6368

src/OrigoDB.Core/Storage/Sql/MsSqlProvider.cs

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Text;
2+
3+
namespace OrigoDB.Core.Storage.Sql
4+
{
5+
public class MsSqlStatements : SqlStatements
6+
{
7+
public MsSqlStatements()
8+
{
9+
ReadEntries = "SELECT Id, Created, Data FROM {0} WHERE Id >= @id ORDER BY Id";
10+
InitStore = BuildInitStatement();
11+
AppendEntry = "INSERT INTO {0} VALUES (@Id, @Created, @Type, @Data);";
12+
}
13+
14+
private string BuildInitStatement()
15+
{
16+
var sb = new StringBuilder();
17+
sb.Append("IF NOT EXISTS ( SELECT * FROM sys.tables WHERE name = '{0}')\n");
18+
sb.Append("CREATE TABLE {0}\n");
19+
sb.AppendLine("(");
20+
sb.AppendLine(" Id bigint not null primary key,");
21+
sb.AppendLine(" Created datetime not null,");
22+
sb.AppendLine(" Type varchar(1024) not null,");
23+
sb.AppendLine(" Data varbinary(max) not null);");
24+
return sb.ToString();
25+
}
26+
}
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Text;
2+
3+
namespace OrigoDB.Core.Storage.Sql
4+
{
5+
public class OleDbStatements : SqlStatements
6+
{
7+
public OleDbStatements()
8+
{
9+
ReadEntries = "SELECT Id, Created, Data FROM {0} WHERE Id >= ? ORDER BY ID";
10+
InitStore = BuildInitStatement();
11+
AppendEntry = "INSERT INTO {0} VALUES (?,?,?,?);";
12+
}
13+
private string BuildInitStatement()
14+
{
15+
var sb = new StringBuilder();
16+
sb.Append("IF NOT EXISTS ( SELECT * FROM INFORMATION.TABLES WHERE TABLE_NAME = '{0}')\n");
17+
sb.Append("CREATE TABLE {0}\n");
18+
sb.AppendLine("(");
19+
sb.AppendLine(" Id bigint not null primary key,");
20+
sb.AppendLine(" Created datetime not null,");
21+
sb.AppendLine(" Type varchar(1024) not null,");
22+
sb.AppendLine(" Data varbinary(max) not null);");
23+
return sb.ToString();
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)