Skip to content

Commit 1f53e87

Browse files
committed
Initial commit of BookStack.
0 parents  commit 1f53e87

File tree

11 files changed

+630
-0
lines changed

11 files changed

+630
-0
lines changed

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
*.deb
2+
*.exe
3+
*.dll
4+
*.mdb
5+
*.pidb
6+
*.pdb
7+
*.log
8+
*.mlpd
9+
*.userprefs
10+
*.resources
11+
*~
12+
13+
bin/
14+
obj/
15+
test-results/
16+
.directory
17+
.svn
18+

BookStack.sln

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 11.00
3+
# Visual Studio 2010
4+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookStack", "BookStack\BookStack.csproj", "{71D28F04-3CC3-483C-84AB-975EA5B1F574}"
5+
EndProject
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{2426CCC2-D3E6-4C5A-A05B-6F550EBEAB00}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{2426CCC2-D3E6-4C5A-A05B-6F550EBEAB00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{2426CCC2-D3E6-4C5A-A05B-6F550EBEAB00}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{2426CCC2-D3E6-4C5A-A05B-6F550EBEAB00}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{2426CCC2-D3E6-4C5A-A05B-6F550EBEAB00}.Release|Any CPU.Build.0 = Release|Any CPU
18+
{71D28F04-3CC3-483C-84AB-975EA5B1F574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19+
{71D28F04-3CC3-483C-84AB-975EA5B1F574}.Debug|Any CPU.Build.0 = Debug|Any CPU
20+
{71D28F04-3CC3-483C-84AB-975EA5B1F574}.Release|Any CPU.ActiveCfg = Release|Any CPU
21+
{71D28F04-3CC3-483C-84AB-975EA5B1F574}.Release|Any CPU.Build.0 = Release|Any CPU
22+
EndGlobalSection
23+
GlobalSection(MonoDevelopProperties) = preSolution
24+
StartupItem = BookStack\BookStack.csproj
25+
outputpath = BookStack\bin\
26+
EndGlobalSection
27+
EndGlobal

BookStack/BookStack.cs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright (c) 2013, Emergent Design Ltd. All rights reserved. Use of this source code
2+
// is governed by a BSD-style licence that can be found in the LICENSE file.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Threading.Tasks;
8+
using System.Collections.Generic;
9+
10+
using BookSleeve;
11+
using ServiceStack.Text;
12+
13+
14+
namespace BookStack
15+
{
16+
public static class BookStackExtensions
17+
{
18+
/// <summary>
19+
/// Generates an incremental ID for a given type. Each type is assigned their own incrementor in redis using the key "id:typename".
20+
/// This is the only synchronous method in BookStack since it is assumed that a new ID is required for immediate assignment.
21+
/// </summary>
22+
/// <returns> the new ID</returns>
23+
public static long NextId<T>(this RedisConnection redis, int db)
24+
{
25+
return redis.Wait(redis.Strings.Increment(db, string.Format("id:{0}", typeof(T).Name.ToLower())));
26+
}
27+
28+
29+
/// <summary>
30+
/// Stores the given entity to redis. It is assumed that the entity has a public property named "Id" which is used to form
31+
/// the redis key. An exception will occur if an ID property does not exist.
32+
/// The object will be stored under the key "urn:typename:id" and if the key already exists it will be overwritten with the new
33+
/// serialized object instance.
34+
/// The ID of the object is also added to a set under the key "ids:typename" and is used for the fast retrieval of all entities of
35+
/// a given type.
36+
/// </summary>
37+
/// <returns> the same entity instance that was passed in</returns>
38+
public static Task<T> Store<T>(this RedisConnection redis, int db, T entity)
39+
{
40+
object id = entity.GetType().GetProperty("Id").GetGetMethod().Invoke(entity, new object[0]);
41+
42+
return redis.Store(db, id, entity);
43+
}
44+
45+
46+
/// <summary>
47+
/// Stores the given entity to redis. The ID must be specified as this is used to form the redis key. The object will be stored
48+
/// under the key "urn:typename:id" and if the key already exists it will be overwritten with the new serialized object instance.
49+
/// The ID of the object is also added to a set under the key "ids:typename" and is used for the fast retrieval of all entities of
50+
/// a given type.
51+
/// </summary>
52+
/// <returns> the same entity instance that was passed in</returns>
53+
public static Task<T> Store<T>(this RedisConnection redis, int db, object id, T entity)
54+
{
55+
string urn = string.Format("urn:{0}:{1}", typeof(T).Name.ToLower(), id);
56+
var tasks = new Task [] {
57+
redis.Strings.Set(db, urn, entity.ToJson()),
58+
redis.Sets.Add(db, string.Format("ids:{0}", typeof(T).Name.ToLower()), id.ToString())
59+
};
60+
61+
return Task.Factory.ContinueWhenAll(tasks, t => entity);
62+
}
63+
64+
65+
/// <summary>
66+
/// Stores a collection of entities to redis. Each item must have a public property named "Id" which is used to form the redis keys,
67+
/// otherwise an exception will occur. Each entity is stored in the same way as the Store method (except that this is performed as a
68+
/// batch process).
69+
/// </summary>
70+
/// <returns> the number of new entries only, existing entries will simply be updated</returns>
71+
public static Task<long> StoreAll<T>(this RedisConnection redis, int db, IEnumerable<T> entities)
72+
{
73+
return redis.StoreAll(db, entities.ToDictionary(e => e.GetType().GetProperty("Id").GetGetMethod().Invoke(e, new object[0]), e => e));
74+
}
75+
76+
77+
/// <summary>
78+
/// Stores a collection of entities to redis. The items are expected as a dictionary where each key is the ID for the corresponding
79+
/// entity instance. Each entity is stored in the same way as the Store method (except that this is performed as a batch process).
80+
/// </summary>
81+
/// <returns> the number of new entries only, existing entries will simply be updated</returns>
82+
public static Task<long> StoreAll<T>(this RedisConnection redis, int db, Dictionary<object, T> entities)
83+
{
84+
string urn = string.Format("urn:{0}", typeof(T).Name.ToLower());
85+
var tasks = new Task [] {
86+
redis.Strings.Set(db, entities.ToDictionary(i => string.Format("{0}:{1}", urn, i.Key), i => i.Value.ToJson())),
87+
redis.Sets.Add(db, string.Format("ids:{0}", typeof(T).Name.ToLower()), entities.Keys.Select(k => k.ToString()).ToArray())
88+
};
89+
90+
return Task.Factory.ContinueWhenAll(tasks, t => (tasks[1] as Task<long>).Result);
91+
}
92+
93+
94+
/// <summary>
95+
/// Retrieves an entity from redis by ID. If the ID does not exist then null is returned.
96+
/// </summary>
97+
/// <returns> the deserialized entity instance or null if the ID cannot be found</returns>
98+
public static Task<T> Get<T>(this RedisConnection redis, int db, object id)
99+
{
100+
string urn = string.Format("urn:{0}:{1}", typeof(T).Name.ToLower(), id);
101+
var task = redis.Strings.GetString(db, urn);
102+
103+
return task.ContinueWith(t => JsonSerializer.DeserializeFromString<T>(t.Result));
104+
}
105+
106+
107+
/// <summary>
108+
/// Gets all entities of the given type from redis. The returned collection will be empty
109+
/// if no entities of that type exist.
110+
/// </summary>
111+
/// <returns> a collection of entities</returns>
112+
public static Task<IEnumerable<T>> GetAll<T>(this RedisConnection redis, int db)
113+
{
114+
string urn = string.Format("urn:{0}", typeof(T).Name.ToLower());
115+
var task = redis.Sets.GetAllString(db, string.Format("ids:{0}", typeof(T).Name.ToLower()));
116+
117+
return task.ContinueWith(ids => {
118+
var items = ids.Result.Length > 0
119+
? redis.Wait(redis.Strings.GetString(db, ids.Result.Select(i => string.Format("{0}:{1}", urn, i)).ToArray()))
120+
: new string[0];
121+
122+
return items.Select(i => JsonSerializer.DeserializeFromString<T>(i));
123+
});
124+
}
125+
126+
127+
/// <summary>
128+
/// Deletes the specified entity from redis.
129+
/// </summary>
130+
/// <returns> true if successful or false if the entity cannot be found</returns>
131+
/// <param name="redis">Redis.</param>
132+
/// <param name="db">Db.</param>
133+
/// <param name="id">Identifier.</param>
134+
/// <typeparam name="T">The 1st type parameter.</typeparam>
135+
public static Task<bool> Delete<T>(this RedisConnection redis, int db, object id)
136+
{
137+
string urn = string.Format("urn:{0}:{1}", typeof(T).Name.ToLower(), id);
138+
var tasks = new Task<bool> [] {
139+
redis.Keys.Remove(db, urn),
140+
redis.Sets.Remove(db, string.Format("ids:{0}", typeof(T).Name.ToLower()), id.ToString())
141+
};
142+
143+
return Task.Factory.ContinueWhenAll(tasks, t => tasks[0].Result && tasks[1].Result);
144+
}
145+
146+
147+
/// <summary>
148+
/// Deletes all entities of the given type from redis.
149+
/// </summary>
150+
/// <returns> the number of entities that were deleted</returns>
151+
public static Task<long> DeleteAll<T>(this RedisConnection redis, int db)
152+
{
153+
var task = redis.Sets.GetAllString(db, string.Format("ids:{0}", typeof(T).Name.ToLower()));
154+
155+
return task.ContinueWith(ids => redis.Wait(redis.DeleteAll<T>(db, task.Result)));
156+
}
157+
158+
159+
/// <summary>
160+
/// Deletes a number of entities from redis at once.
161+
/// </summary>
162+
/// <returns> the number of entities that were deleted</returns>
163+
public static Task<long> DeleteAll<T>(this RedisConnection redis, int db, string [] ids)
164+
{
165+
string urn = string.Format("urn:{0}", typeof(T).Name.ToLower());
166+
167+
if (ids.Count() > 0)
168+
{
169+
var tasks = new Task [] {
170+
redis.Keys.Remove(db, ids.Select(i => string.Format("{0}:{1}", urn, i)).ToArray()),
171+
redis.Sets.Remove(db, string.Format("ids:{0}", typeof(T).Name.ToLower()), ids.Select(i => i.ToString()).ToArray())
172+
};
173+
174+
return Task.Factory.ContinueWhenAll(tasks, t => (tasks[1] as Task<long>).Result);
175+
}
176+
177+
// This bit can be replaced with Task.FromResult when using .NET 4.5
178+
var result = new TaskCompletionSource<long>();
179+
result.SetResult(0);
180+
181+
return result.Task;
182+
}
183+
}
184+
}

BookStack/BookStack.csproj

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6+
<ProductVersion>10.0.0</ProductVersion>
7+
<SchemaVersion>2.0</SchemaVersion>
8+
<ProjectGuid>{71D28F04-3CC3-483C-84AB-975EA5B1F574}</ProjectGuid>
9+
<OutputType>Library</OutputType>
10+
<RootNamespace>BookStack</RootNamespace>
11+
<AssemblyName>BookStack</AssemblyName>
12+
</PropertyGroup>
13+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
14+
<DebugSymbols>True</DebugSymbols>
15+
<DebugType>full</DebugType>
16+
<Optimize>False</Optimize>
17+
<OutputPath>bin\Debug</OutputPath>
18+
<DefineConstants>DEBUG;</DefineConstants>
19+
<ErrorReport>prompt</ErrorReport>
20+
<WarningLevel>4</WarningLevel>
21+
<ConsolePause>False</ConsolePause>
22+
</PropertyGroup>
23+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
24+
<DebugType>none</DebugType>
25+
<Optimize>True</Optimize>
26+
<OutputPath>bin\Release</OutputPath>
27+
<ErrorReport>prompt</ErrorReport>
28+
<WarningLevel>4</WarningLevel>
29+
<ConsolePause>False</ConsolePause>
30+
</PropertyGroup>
31+
<ItemGroup>
32+
<Reference Include="System" />
33+
<Reference Include="BookSleeve">
34+
<HintPath>..\References\BookSleeve.dll</HintPath>
35+
</Reference>
36+
<Reference Include="ServiceStack.Text">
37+
<HintPath>..\References\ServiceStack.Text.dll</HintPath>
38+
</Reference>
39+
<Reference Include="System.Core" />
40+
</ItemGroup>
41+
<ItemGroup>
42+
<Compile Include="BookStack.cs" />
43+
</ItemGroup>
44+
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
45+
</Project>

LICENCE

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright (c) 2013, Emergent Design Ltd
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification,
5+
are permitted provided that the following conditions are met:
6+
7+
* Redistributions of source code must retain the above copyright notice, this list
8+
of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above copyright notice, this
10+
list of conditions and the following disclaimer in the documentation and/or other
11+
materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
14+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
16+
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
18+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
20+
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
21+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22+
23+
24+
BookSleeve is licensed separately, refer to http://code.google.com/p/booksleeve
25+
26+
ServiceStack.Text is licensed separately, refer to https://github.com/ServiceStack/ServiceStack.Text

README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
BookStack
2+
=========
3+
4+
BookStack is a small set of functions that extend the power of [BookSleeve](http://code.google.com/p/booksleeve)
5+
with the object serialisation capabilities of [ServiceStack.Text](http://github.com/ServiceStack/ServiceStack.Text).
6+
7+
ServiceStack does actually contain a very nice [library](http://github.com/ServiceStack/ServiceStack.Redis) for
8+
accessing redis via a [typed client](http://github.com/ServiceStack/ServiceStack.Redis/wiki/IRedisTypedClient) but
9+
if you have an existing project that uses BookSleeve or wish to leverage its async and thread-safe API then BookStack can be used to provide simple typed access.
10+
11+
12+
How it works
13+
------------
14+
15+
When storing objects, BookStack follows a very similar process to ServiceStack.Redis (although there are slight
16+
differences so they are not compatible with each other).
17+
18+
* Incremental IDs are stored at "id:typename".
19+
* Lists of entity IDs are stored in sets at "ids:typename".
20+
* The serialised entities themselves are stored at "urn:typename:id".
21+
22+
All typenames are converted to lower-case.
23+
24+
25+
Installation
26+
------------
27+
28+
You can either reference the tiny library or include the single source file within your solution. In either
29+
case the extension functions become available via the standard RedisConnection.
30+
31+
32+
Examples
33+
--------
34+
35+
The examples assume an existing RedisConnection instance named "redis" and the entity model defined below.
36+
37+
38+
### The example entity model
39+
40+
```csharp
41+
class Entity
42+
{
43+
public long Id { get; set; }
44+
public string Name { get; set; }
45+
}
46+
```
47+
48+
49+
### Generate an ID
50+
51+
```csharp
52+
var entity = new Entity { Id = redis.NextId<Entity>(0) };
53+
```
54+
55+
56+
### Store an entity
57+
58+
```csharp
59+
var entity = new Entity { Id = redis.NextId<Entity>(0), Name = "BookStack" };
60+
61+
redis.Store(0, entity);
62+
```
63+
64+
65+
### Retrieve an entity
66+
67+
```csharp
68+
var entity = redis.Wait(redis.Get<Entity>(0, id));
69+
// or if using C# 5.0
70+
var entity = await redis.Get<Entity>(0, id);
71+
```
72+
73+
74+
### Get all entities of a given type
75+
76+
```csharp
77+
var entities = redis.Wait(redis.GetAll<Entity>(0));
78+
```
79+
80+
81+
### Delete an entity
82+
83+
```csharp
84+
redis.Delete<Entity>(0, id);
85+
```

References/BookSleeve.dll

79 KB
Binary file not shown.

References/ServiceStack.Text.dll

165 KB
Binary file not shown.

0 commit comments

Comments
 (0)