Skip to content

Commit af74ef9

Browse files
Closes #18
1 parent 456e1b3 commit af74ef9

File tree

11 files changed

+1296
-14
lines changed

11 files changed

+1296
-14
lines changed

DotNetToolkit.Repository.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
4-
VisualStudioVersion = 15.0.27130.2003
4+
VisualStudioVersion = 15.0.27004.2006
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DD273D5E-6D6C-41FA-A0C8-646CC53C4DC3}"
77
EndProject
@@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetToolkit.Repository.In
1919
EndProject
2020
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetToolkit.Repository.EntityFrameworkCore", "src\DotNetToolkit.Repository.EntityFrameworkCore\DotNetToolkit.Repository.EntityFrameworkCore.csproj", "{0A9D9AA4-F01C-470F-AD5B-5A81EA7A398D}"
2121
EndProject
22+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetToolkit.Repository.Json", "src\DotNetToolkit.Repository.Json\DotNetToolkit.Repository.Json.csproj", "{B71EA207-390A-4AB0-BFC8-44BA124FC35B}"
23+
EndProject
2224
Global
2325
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2426
Debug|Any CPU = Debug|Any CPU
@@ -49,6 +51,10 @@ Global
4951
{0A9D9AA4-F01C-470F-AD5B-5A81EA7A398D}.Debug|Any CPU.Build.0 = Debug|Any CPU
5052
{0A9D9AA4-F01C-470F-AD5B-5A81EA7A398D}.Release|Any CPU.ActiveCfg = Release|Any CPU
5153
{0A9D9AA4-F01C-470F-AD5B-5A81EA7A398D}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{B71EA207-390A-4AB0-BFC8-44BA124FC35B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{B71EA207-390A-4AB0-BFC8-44BA124FC35B}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{B71EA207-390A-4AB0-BFC8-44BA124FC35B}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{B71EA207-390A-4AB0-BFC8-44BA124FC35B}.Release|Any CPU.Build.0 = Release|Any CPU
5258
EndGlobalSection
5359
GlobalSection(SolutionProperties) = preSolution
5460
HideSolutionNode = FALSE
@@ -60,6 +66,7 @@ Global
6066
{2F99B66F-A347-4E8D-8ACA-5A34246A1FA6} = {DAB3DD1E-AD99-46C9-AC42-07E2F03D5A06}
6167
{715D2F11-3AAF-476E-9A6A-DCA6DEBD377E} = {DD273D5E-6D6C-41FA-A0C8-646CC53C4DC3}
6268
{0A9D9AA4-F01C-470F-AD5B-5A81EA7A398D} = {DD273D5E-6D6C-41FA-A0C8-646CC53C4DC3}
69+
{B71EA207-390A-4AB0-BFC8-44BA124FC35B} = {DD273D5E-6D6C-41FA-A0C8-646CC53C4DC3}
6370
EndGlobalSection
6471
GlobalSection(ExtensibilityGlobals) = postSolution
6572
SolutionGuid = {96973E0C-81D1-42DE-9F78-7103241B4E07}

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ after_build:
4848
- dotnet pack .\src\DotNetToolkit.Repository.InMemory\DotNetToolkit.Repository.InMemory.csproj --configuration Release
4949
- dotnet pack .\src\DotNetToolkit.Repository.EntityFramework\DotNetToolkit.Repository.EntityFramework.csproj --configuration Release
5050
- dotnet pack .\src\DotNetToolkit.Repository.EntityFrameworkCore\DotNetToolkit.Repository.EntityFrameworkCore.csproj --configuration Release
51+
- dotnet pack .\src\DotNetToolkit.Repository.Json\DotNetToolkit.Repository.Json.csproj --configuration Release
5152

5253
#---------------------------------#
5354
# tests configuration #

src/DotNetToolkit.Repository.InMemory/InMemoryRepositoryBase.cs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,20 @@ public abstract class InMemoryRepositoryBase<TEntity, TKey> : RepositoryBase<TEn
1919
private const string DefaultDatabaseName = "DotNetToolkit.Repository.InMemory";
2020

2121
private static readonly object _syncRoot = new object();
22-
private readonly string _name;
2322
private ConcurrentDictionary<TKey, EntitySet<TEntity, TKey>> _context;
2423
private bool _disposed;
2524

2625
#endregion
2726

27+
#region Properties
28+
29+
/// <summary>
30+
/// Gets or sets the name of the database.
31+
/// </summary>
32+
protected string DatabaseName { get; set; }
33+
34+
#endregion
35+
2836
#region Constructors
2937

3038
/// <summary>
@@ -33,14 +41,23 @@ public abstract class InMemoryRepositoryBase<TEntity, TKey> : RepositoryBase<TEn
3341
/// <param name="databaseName">The name of the in-memory database. This allows the scope of the in-memory database to be controlled independently of the context.</param>
3442
protected InMemoryRepositoryBase(string databaseName = null)
3543
{
36-
_name = string.IsNullOrEmpty(databaseName) ? DefaultDatabaseName : databaseName;
44+
DatabaseName = string.IsNullOrEmpty(databaseName) ? DefaultDatabaseName : databaseName;
3745
_context = new ConcurrentDictionary<TKey, EntitySet<TEntity, TKey>>();
3846
}
3947

4048
#endregion
4149

4250
#region Protected Methods
4351

52+
/// <summary>
53+
/// Ensures the in-memory store is completely deleted.
54+
/// </summary>
55+
public void EnsureDeleted()
56+
{
57+
_context.Clear();
58+
InMemoryCache<TEntity, TKey>.Instance.GetContext(DatabaseName).Clear();
59+
}
60+
4461
/// <summary>
4562
/// Releases unmanaged and - optionally - managed resources.
4663
/// </summary>
@@ -175,7 +192,7 @@ protected override void SaveChanges()
175192
{
176193
lock (_syncRoot)
177194
{
178-
var context = InMemoryCache<TEntity, TKey>.Instance.GetContext(_name);
195+
var context = InMemoryCache<TEntity, TKey>.Instance.GetContext(DatabaseName);
179196

180197
foreach (var entitySet in _context.Select(y => y.Value))
181198
{
@@ -205,7 +222,7 @@ protected override void SaveChanges()
205222
}
206223
else
207224
{
208-
context[key] = new EntitySet<TEntity, TKey>(DeepCopy(entitySet.Entity), key, EntityState.Unchanged);
225+
context[key] = DeepCopy(entitySet.Entity);
209226
}
210227
}
211228

@@ -219,9 +236,9 @@ protected override void SaveChanges()
219236
protected override IQueryable<TEntity> GetQuery(IFetchStrategy<TEntity> fetchStrategy = null)
220237
{
221238
return InMemoryCache<TEntity, TKey>.Instance
222-
.GetContext(_name)
239+
.GetContext(DatabaseName)
223240
.AsQueryable()
224-
.Select(y => y.Value.Entity);
241+
.Select(y => y.Value);
225242
}
226243

227244
/// <summary>
@@ -230,10 +247,10 @@ protected override IQueryable<TEntity> GetQuery(IFetchStrategy<TEntity> fetchStr
230247
protected override TEntity GetEntity(TKey key, IFetchStrategy<TEntity> fetchStrategy)
231248
{
232249
InMemoryCache<TEntity, TKey>.Instance
233-
.GetContext(_name)
234-
.TryGetValue(key, out EntitySet<TEntity, TKey> entitySet);
250+
.GetContext(DatabaseName)
251+
.TryGetValue(key, out TEntity entity);
235252

236-
return entitySet?.Entity;
253+
return entity;
237254
}
238255

239256
#endregion
@@ -311,7 +328,7 @@ private class InMemoryCache<TEntity, TKey> where TEntity : class
311328

312329
private static volatile InMemoryCache<TEntity, TKey> _instance;
313330
private static readonly object _syncRoot = new object();
314-
private readonly ConcurrentDictionary<string, SortedDictionary<TKey, EntitySet<TEntity, TKey>>> _storage;
331+
private readonly ConcurrentDictionary<string, SortedDictionary<TKey, TEntity>> _storage;
315332

316333
#endregion
317334

@@ -322,7 +339,7 @@ private class InMemoryCache<TEntity, TKey> where TEntity : class
322339
/// </summary>
323340
private InMemoryCache()
324341
{
325-
_storage = new ConcurrentDictionary<string, SortedDictionary<TKey, EntitySet<TEntity, TKey>>>();
342+
_storage = new ConcurrentDictionary<string, SortedDictionary<TKey, TEntity>>();
326343
}
327344

328345
#endregion
@@ -358,11 +375,11 @@ public static InMemoryCache<TEntity, TKey> Instance
358375
/// </summary>
359376
/// <param name="name">The database name.</param>
360377
/// <returns>The scoped database context by the specified database name.</returns>
361-
public SortedDictionary<TKey, EntitySet<TEntity, TKey>> GetContext(string name)
378+
public SortedDictionary<TKey, TEntity> GetContext(string name)
362379
{
363380
if (!_storage.ContainsKey(name))
364381
{
365-
_storage[name] = new SortedDictionary<TKey, EntitySet<TEntity, TKey>>();
382+
_storage[name] = new SortedDictionary<TKey, TEntity>();
366383
}
367384

368385
return _storage[name];
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="..\..\build\common.props" />
4+
5+
<PropertyGroup>
6+
<TargetFrameworks>net451;netstandard1.3</TargetFrameworks>
7+
<AssemblyName>DotNetToolkit.Repository.Json</AssemblyName>
8+
<RootNamespace>DotNetToolkit.Repository.Json</RootNamespace>
9+
<Description>A toolkit that provides a lightweight starter kit for using the Repository pattern for the JSON framework for .NET.</Description>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\DotNetToolkit.Repository.InMemory\DotNetToolkit.Repository.InMemory.csproj" />
18+
<ProjectReference Include="..\DotNetToolkit.Repository\DotNetToolkit.Repository.csproj" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<Compile Update="Properties\Resources.Designer.cs">
23+
<DesignTime>True</DesignTime>
24+
<AutoGen>True</AutoGen>
25+
<DependentUpon>Resources.resx</DependentUpon>
26+
</Compile>
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<EmbeddedResource Update="Properties\Resources.resx">
31+
<Generator>ResXFileCodeGenerator</Generator>
32+
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
33+
</EmbeddedResource>
34+
</ItemGroup>
35+
36+
</Project>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace DotNetToolkit.Repository.Json
2+
{
3+
/// <summary>
4+
/// Represents a repository for storing entities as an json formatted file.
5+
/// </summary>
6+
public class JsonRepository<TEntity, TKey> : JsonRepositoryBase<TEntity, TKey> where TEntity : class
7+
{
8+
#region Constructors
9+
10+
/// <summary>
11+
/// Initializes a new instance of the <see cref="JsonRepository{TEntity,TKey}"/> class.
12+
/// </summary>
13+
/// <param name="filePath">The file path.</param>
14+
public JsonRepository(string filePath = null) : base(filePath)
15+
{
16+
}
17+
18+
#endregion
19+
}
20+
21+
/// <summary>
22+
/// Represents a repository for storing entities as an json formatted file with a default primary key value of type integer (for testing purposes).
23+
/// </summary>
24+
public class JsonRepository<TEntity> : JsonRepositoryBase<TEntity, int>, IRepository<TEntity> where TEntity : class
25+
{
26+
#region Constructors
27+
28+
/// <summary>
29+
/// Initializes a new instance of the <see cref="JsonRepository{TEntity}"/> class.
30+
/// </summary>
31+
/// <param name="filePath">The file path.</param>
32+
public JsonRepository(string filePath = null) : base(filePath)
33+
{
34+
}
35+
36+
#endregion
37+
}
38+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
namespace DotNetToolkit.Repository.Json
2+
{
3+
using InMemory;
4+
using Newtonsoft.Json;
5+
using Newtonsoft.Json.Serialization;
6+
using Properties;
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Globalization;
10+
using System.IO;
11+
12+
/// <summary>
13+
/// Represents a repository for storing entities as an json formatted file.
14+
/// </summary>
15+
public abstract class JsonRepositoryBase<TEntity, TKey> : InMemoryRepositoryBase<TEntity, TKey> where TEntity : class
16+
{
17+
#region Fields
18+
19+
private const string FileExtension = ".json";
20+
21+
#endregion
22+
23+
#region Constructors
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="JsonRepositoryBase{TEntity, TKey}"/> class.
27+
/// </summary>
28+
/// <param name="filePath">The file path.</param>
29+
protected JsonRepositoryBase(string filePath)
30+
{
31+
if (string.IsNullOrEmpty(filePath))
32+
throw new ArgumentNullException(nameof(filePath));
33+
34+
// Ensures we have a valid file
35+
var fileName = filePath;
36+
37+
if (Directory.Exists(filePath))
38+
{
39+
if (!fileName.EndsWith(@"\"))
40+
fileName += @"\";
41+
42+
fileName += $"{GetType().Name}{FileExtension}";
43+
}
44+
else
45+
{
46+
if (string.IsNullOrEmpty(Path.GetExtension(fileName)))
47+
fileName += FileExtension;
48+
49+
if (!Path.GetExtension(fileName).Equals(FileExtension))
50+
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidFileExtension, fileName, FileExtension));
51+
52+
if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) < 0)
53+
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidFilePath, fileName));
54+
}
55+
56+
DatabaseName = fileName;
57+
58+
// Creates the file if does not exist
59+
if (!File.Exists(DatabaseName))
60+
{
61+
File.Create(DatabaseName).Dispose();
62+
}
63+
// Otherwise, try to get the data from the file
64+
else
65+
{
66+
// Adds the data from the file into memory
67+
using (var stream = new FileStream(DatabaseName, FileMode.Open, FileAccess.Read))
68+
using (var reader = new StreamReader(stream))
69+
{
70+
var serializer = new JsonSerializer();
71+
var entities = (List<TEntity>)serializer.Deserialize(reader, typeof(List<TEntity>));
72+
73+
EnsureDeleted();
74+
75+
entities.ForEach(AddItem);
76+
77+
base.SaveChanges();
78+
}
79+
}
80+
}
81+
82+
#endregion
83+
84+
#region Overrides of InMemoryRepositoryBase<TEntity,TKey>
85+
86+
/// <summary>
87+
/// A protected overridable method for saving changes made in the current unit of work in the repository.
88+
/// </summary>
89+
protected override void SaveChanges()
90+
{
91+
using (var stream = new FileStream(DatabaseName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Delete))
92+
{
93+
// Saves the data into memory
94+
base.SaveChanges();
95+
96+
// Puts from memory into the file
97+
using (var writer = new StreamWriter(stream))
98+
{
99+
var entities = GetQuery();
100+
var serializer = new JsonSerializer
101+
{
102+
Formatting = Formatting.Indented,
103+
ContractResolver = new CamelCasePropertyNamesContractResolver(),
104+
TypeNameHandling = TypeNameHandling.All
105+
};
106+
107+
serializer.Serialize(writer, entities);
108+
}
109+
}
110+
}
111+
112+
#endregion
113+
}
114+
}

0 commit comments

Comments
 (0)