├── .gitignore
├── .nuget
└── packages.config
├── .travis.yml
├── Elephanet.Console
├── App.config
├── Elephanet.Console.csproj
├── Elephanet.Console.sln
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── packages.config
├── Elephanet.Tests
├── App.config
├── CorrectEscapingTests.cs
├── DirtySpeedTests.cs
├── DocumentStoreBaseFixture.cs
├── Elephanet.Tests.csproj
├── Entities
│ ├── AnotherEntity.cs
│ ├── AnotherEntityWithDateTime.cs
│ ├── BaseEntity.cs
│ ├── Bike.cs
│ ├── Car.cs
│ ├── EntityForCorrectEscapingTests.cs
│ ├── EntityForGetByIdTests.cs
│ ├── EntityForLinqOrderByTests.cs
│ ├── EntityForSchemaConventionsTest.cs
│ ├── EntityForSessionAndStoreTests.cs
│ ├── EntityForWhereLinqTests.cs
│ ├── EntityWithDateTime.cs
│ └── SomeTestClassThatNoOneWillEverUse.cs
├── GetAllTests.cs
├── GetByIdTests.cs
├── JsonConverterTests.cs
├── LinqOrderByTests.cs
├── LinqTests.cs
├── Properties
│ └── AssemblyInfo.cs
├── SessionAndStoreTests.cs
├── StoreConventionsTests.cs
├── StoreInfoTests.cs
├── TableInfoTests.cs
├── TestStore.cs
└── packages.config
├── Elephanet.sln
├── Elephanet
├── App.config
├── ConcurrentHashSet.cs
├── Conventions
│ └── EntityNotFoundBehavior.cs
├── DocumentSession.cs
├── Elephanet.csproj
├── Elephanet.nuspec
├── EntityException.cs
├── EntityNotFoundException.cs
├── Expressions
│ ├── ExpressionEvaluator.cs
│ ├── JsonbExpression.cs
│ ├── JsonbExpressionType.cs
│ ├── JsonbExpressionVisitor.cs
│ ├── JsonbTable.cs
│ ├── SelectExpression.cs
│ ├── SqlExpression.cs
│ └── SqlExpressionType.cs
├── Extensions
│ └── QueryExtension.cs
├── IDocumentSession.cs
├── IStore.cs
├── IStoreConventions.cs
├── IStoreInfo.cs
├── ITableInfo.cs
├── IdentityFactory.cs
├── JsonbQueryProvider.cs
├── NuGet.exe
├── Properties
│ └── AssemblyInfo.cs
├── QueryTranslator.cs
├── Queryable.cs
├── Serialization
│ ├── IJsonConverter.cs
│ └── JilJsonConverter.cs
├── Sql.cs
├── Store.cs
├── StoreConventions.cs
├── StoreInfo.cs
├── StringHelpers.cs
├── TableInfo.cs
├── TypeHelper.cs
└── packages.config
├── LICENSE
├── create_store.bat
├── create_store.sql
├── deploy.sh
├── readme.md
└── release-notes.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | [Rr]eleases/
14 | x64/
15 | x86/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | *_i.c
38 | *_p.c
39 | *_i.h
40 | *.ilk
41 | *.meta
42 | *.obj
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.tlb
50 | *.tli
51 | *.tlh
52 | *.tmp
53 | *.tmp_proj
54 | *.log
55 | *.vspscc
56 | *.vssscc
57 | .builds
58 | *.pidb
59 | *.svclog
60 | *.scc
61 |
62 | # Chutzpah Test files
63 | _Chutzpah*
64 |
65 | # Visual C++ cache files
66 | ipch/
67 | *.aps
68 | *.ncb
69 | *.opensdf
70 | *.sdf
71 | *.cachefile
72 |
73 | # Visual Studio profiler
74 | *.psess
75 | *.vsp
76 | *.vspx
77 |
78 | # TFS 2012 Local Workspace
79 | $tf/
80 |
81 | # Guidance Automation Toolkit
82 | *.gpState
83 |
84 | # ReSharper is a .NET coding add-in
85 | _ReSharper*/
86 | *.[Rr]e[Ss]harper
87 | *.DotSettings.user
88 |
89 | # JustCode is a .NET coding addin-in
90 | .JustCode
91 |
92 | # TeamCity is a build add-in
93 | _TeamCity*
94 |
95 | # DotCover is a Code Coverage Tool
96 | *.dotCover
97 |
98 | # NCrunch
99 | _NCrunch_*
100 | .*crunch*.local.xml
101 |
102 | # MightyMoose
103 | *.mm.*
104 | AutoTest.Net/
105 |
106 | # Web workbench (sass)
107 | .sass-cache/
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.[Pp]ublish.xml
127 | *.azurePubxml
128 | # TODO: Comment the next line if you want to checkin your web deploy settings
129 | # but database connection strings (with potential passwords) will be unencrypted
130 | *.pubxml
131 | *.publishproj
132 |
133 | # NuGet Packages
134 | *.nupkg
135 | # The packages folder can be ignored because of Package Restore
136 | **/packages/*
137 | # except build/, which is used as an MSBuild target.
138 | !**/packages/build/
139 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
140 | #!**/packages/repositories.config
141 |
142 | # Windows Azure Build Output
143 | csx/
144 | *.build.csdef
145 |
146 | # Windows Store app package directory
147 | AppPackages/
148 |
149 | # Others
150 | sql/
151 | *.Cache
152 | ClientBin/
153 | [Ss]tyle[Cc]op.*
154 | ~$*
155 | *~
156 | *.dbmdl
157 | *.dbproj.schemaview
158 | *.pfx
159 | *.publishsettings
160 | node_modules/
161 |
162 | # RIA/Silverlight projects
163 | Generated_Code/
164 |
165 | # Backup & report files from converting an old project file
166 | # to a newer Visual Studio version. Backup files are not needed,
167 | # because we have git ;-)
168 | _UpgradeReport_Files/
169 | Backup*/
170 | UpgradeLog*.XML
171 | UpgradeLog*.htm
172 |
173 | # SQL Server files
174 | *.mdf
175 | *.ldf
176 |
177 | # Business Intelligence projects
178 | *.rdl.data
179 | *.bim.layout
180 | *.bim_*.settings
181 |
182 | # Microsoft Fakes
183 | FakesAssemblies/
--------------------------------------------------------------------------------
/.nuget/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: csharp
2 | mono:
3 | - alpha
4 | env: VERSION=0.3.10
5 | solution: Elephanet.sln
6 | addons:
7 | postgresql: "9.4"
8 |
9 | before_script:
10 | - psql -c "CREATE USER store_user with PASSWORD 'my super secret password';" -U postgres
11 | - psql -c "CREATE DATABASE store;" -U postgres
12 | - psql -c "GRANT ALL PRIVILEGES ON DATABASE store to store_user;" -U postgres
13 |
14 | script:
15 | - xbuild /p:Configuration=Release Elephanet.sln
16 | - mono ./packages/xunit.runner.console.2.0.0/tools/xunit.console.exe ./Elephanet.Tests/bin/Release/Elephanet.Tests.dll -noshadow
17 | - chmod 755 ./packages/ILRepack.1.25.0/tools/ILRepack.exe
18 | - ./packages/ILRepack.1.25.0/tools/ILRepack.exe Elephanet/bin/Release/Elephanet.dll Elephanet/bin/Release/Sigil.dll Elephanet/bin/Release/Jil.dll Elephanet/bin/Release/Mono.Security.dll Elephanet/bin/Release/Npgsql.dll --internalize --out:Elephanet/Elephanet.dll
19 |
20 | deploy:
21 | provider: script
22 | skip_cleanup: true
23 | script:
24 | - cd Elephanet && nuget pack Elephanet.nuspec -Version $VERSION -IncludeReferencedProjects -Prop Configuration=Release && nuget push *.nupkg $NUGET_API_KEY -verbosity detailed
25 | on:
26 | branch: master
27 |
--------------------------------------------------------------------------------
/Elephanet.Console/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Elephanet.Console/Elephanet.Console.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}
8 | Exe
9 | Properties
10 | Elephanet.Console
11 | Elephanet.Console
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | ..\packages\AutoFixture.3.20.1\lib\net40\Ploeh.AutoFixture.dll
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | {1156667a-85f7-493c-bf33-f22284134559}
57 | Elephanet.Tests
58 |
59 |
60 | {f6f93a0a-0eb1-47a5-9089-db6f56369f98}
61 | Elephanet
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
89 |
--------------------------------------------------------------------------------
/Elephanet.Console/Elephanet.Console.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | # SharpDevelop 5.0
5 | VisualStudioVersion = 12.0.20827.3
6 | MinimumVisualStudioVersion = 10.0.40219.1
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elephanet.Console", "Elephanet.Console.csproj", "{7B67E4BB-55E4-4976-AA8E-72448CDE9E69}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Release|Any CPU.Build.0 = Release|Any CPU
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/Elephanet.Console/Program.cs:
--------------------------------------------------------------------------------
1 | using Elephanet.Tests;
2 | using Elephanet.Tests.Entities;
3 | using Ploeh.AutoFixture;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System;
7 | using System.Diagnostics;
8 |
9 | namespace Elephanet.Benchmark
10 | {
11 | class Program
12 | {
13 | static void Main(string[] args)
14 | {
15 | const int records = 10000;
16 | var watch = new Stopwatch();
17 | Console.WriteLine("Creating {0} records", records);
18 | DocumentStore store = new DocumentStore("Server=127.0.0.1;Port=5432;User id=store_user;password=my super secret password;database=store;");
19 | var cars = GenerateCars(records);
20 | Console.WriteLine("Generated {0} records", records);
21 | Console.WriteLine("Saving {0} records to database", records);
22 |
23 | watch.Start();
24 | using (var session = store.OpenSession())
25 | {
26 | foreach(var car in cars)
27 | {
28 | session.Store(car);
29 | }
30 | session.SaveChanges();
31 | }
32 | watch.Stop();
33 | var rate = records / watch.Elapsed.TotalSeconds;
34 |
35 | Console.WriteLine("Saved {0} records to datatbase in {1}s @ {2} p/s rate", records, watch.Elapsed.TotalSeconds, rate);
36 | Console.WriteLine("Press any key to do query test....");
37 | Console.ReadLine();
38 |
39 | List fords;
40 | using (var session = store.OpenSession())
41 | {
42 | watch.Reset();
43 | watch.Start();
44 | fords = session.Query().Where(c => c.Make == "Ford").ToList();
45 | watch.Stop();
46 | }
47 | foreach (var ford in fords)
48 | {
49 | Console.WriteLine("Id: {0}, Make: {1}", ford.Id, ford.Make);
50 | }
51 | Console.WriteLine("Query took {0}ms", watch.Elapsed.TotalMilliseconds);
52 |
53 | List holdens;
54 | using (var session = store.OpenSession())
55 | {
56 | watch.Reset();
57 | watch.Start();
58 | holdens = session.Query().Where(c => c.Make == "Holden").ToList();
59 | watch.Stop();
60 | }
61 | foreach (var holden in holdens)
62 | {
63 | Console.WriteLine("Id: {0}, Make: {1}", holden.Id, holden.Make);
64 | }
65 | Console.WriteLine("Query took {0}ms", watch.Elapsed.TotalMilliseconds);
66 |
67 | //cleanup
68 | using (var session = store.OpenSession())
69 | {
70 | session.DeleteAll();
71 | }
72 | Console.ReadLine();
73 | }
74 |
75 | public static List GenerateCars(int numberOfCars)
76 | {
77 | var dummyCars = new Fixture().Build()
78 | .With(x => x.Make, "Subaru")
79 | .CreateMany(numberOfCars).ToList();
80 | //spice it up
81 | dummyCars[100].Make = "Ford";
82 | dummyCars[1000].Make = "Ford";
83 | dummyCars[2948].Make = "Ford";
84 | dummyCars[256].Make = "Holden";
85 | dummyCars[269].Make = "Holden";
86 | dummyCars[452].Make = "Holden";
87 |
88 | return dummyCars.ToList();
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Elephanet.Console/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Elephanet.Console")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Elephanet.Console")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("0d6a9984-40b3-47f6-a67f-1a792c97de8c")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Elephanet.Console/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Elephanet.Tests/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Elephanet.Tests/CorrectEscapingTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Elephanet.Tests.Entities;
3 | using Xunit;
4 | using Shouldly;
5 |
6 | namespace Elephanet.Tests
7 | {
8 | public class CorrectEscapingTests : IClassFixture, IDisposable
9 | {
10 | private readonly TestStore _store;
11 | private EntityForCorrectEscapingTests _entity;
12 |
13 | public CorrectEscapingTests(DocumentStoreBaseFixture data)
14 | {
15 | _store = data.TestStore;
16 | }
17 |
18 | [Fact]
19 | public void SingleQuotes_ShouldBe_EscapedWhenSaving()
20 | {
21 | _entity = new EntityForCorrectEscapingTests() {Id = Guid.NewGuid(), PropertyOne = "Kia", PropertyTwo = "Cee'd"};
22 |
23 |
24 | //save the car
25 | using (var session = _store.OpenSession())
26 | {
27 | session.Store(_entity);
28 | session.SaveChanges();
29 | }
30 |
31 | //retrieve and check the make and model are correct
32 | using (var session = _store.OpenSession())
33 | {
34 | var savedCar = session.GetById(_entity.Id);
35 | savedCar.PropertyTwo.ShouldBe("Cee'd");
36 | savedCar.PropertyOne.ShouldBe("Kia");
37 | }
38 | }
39 |
40 | public void Dispose()
41 | {
42 | using (var session = _store.OpenSession())
43 | {
44 | session.Delete(_entity.Id);
45 | session.SaveChanges();
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Elephanet.Tests/DirtySpeedTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Elephanet.Tests.Entities;
6 | using Ploeh.AutoFixture;
7 | using System.Diagnostics;
8 | using Xunit;
9 |
10 | namespace Elephanet.Tests
11 | {
12 | public class DirtySpeedTests : IDisposable
13 | {
14 | readonly DocumentStore _store;
15 | const int WriteCount = 50000;
16 | readonly Stopwatch _watch;
17 |
18 | public DirtySpeedTests()
19 | {
20 | _store = new TestStore();
21 | _watch = new Stopwatch();
22 | }
23 |
24 |
25 | public List GenerateCars(int numberOfCars)
26 | {
27 | var dummyCars = new Fixture().Build()
28 | .With(x => x.Make, "Subaru")
29 | .CreateMany(numberOfCars);
30 | return dummyCars.ToList();
31 | }
32 |
33 | [Fact(Skip="Uncomment for a speed test")]
34 | public void InsertSpeedTest()
35 | {
36 | var cars = GenerateCars(WriteCount);
37 | _watch.Reset();
38 | _watch.Start();
39 | using (var session = _store.OpenSession())
40 | {
41 | foreach (Car car in cars)
42 | {
43 | session.Store(car);
44 | }
45 |
46 | session.SaveChanges();
47 | }
48 | _watch.Stop();
49 | var rate = WriteCount / _watch.Elapsed.TotalSeconds;
50 | Console.WriteLine("{0} writes p/s", rate);
51 | Console.WriteLine("{0} writes in {1} seconds", WriteCount, _watch.Elapsed.TotalSeconds);
52 | }
53 |
54 | [Fact(Skip="Uncomment for a speed test")]
55 | public void LinqWhereQuerySpeedTest()
56 | {
57 | const int seedNumber = 20000; //that should be enough to see if we are hitting the index nicely
58 | var cars = GenerateCars(seedNumber);
59 |
60 | //replace with a few other makes in there
61 | cars[1000].Make = "Ford";
62 | cars[1201].Make = "Ford";
63 | cars[2005].Make = "Ford";
64 |
65 | //save to the database
66 | using (var session = _store.OpenSession())
67 | {
68 | foreach (Car car in cars)
69 | {
70 | session.Store(car);
71 | }
72 |
73 | session.SaveChanges();
74 | }
75 |
76 | using (var session = _store.OpenSession())
77 | {
78 | _watch.Start();
79 | var query = session.Query().Where(c => c.Make == "Ford");
80 | //force the query
81 | var fords = query.ToList();
82 | _watch.Stop();
83 | }
84 |
85 | Console.WriteLine("{0} ms for reading 3 records from {1} total (initial query)", _watch.Elapsed.TotalMilliseconds, seedNumber);
86 |
87 | }
88 |
89 | public void Dispose()
90 | {
91 | using (var session = _store.OpenSession())
92 | {
93 | session.DeleteAll();
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Elephanet.Tests/DocumentStoreBaseFixture.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet.Tests
4 | {
5 | ///
6 | /// Use this as an IClassFixture to inject the TestStore into other test fixtures
7 | ///
8 | public class DocumentStoreBaseFixture : IDisposable
9 | {
10 | public DocumentStoreBaseFixture()
11 | {
12 | TestStore = new TestStore();
13 | }
14 |
15 | public TestStore TestStore { get; private set; }
16 |
17 | public void Dispose()
18 | {
19 | TestStore.Destroy();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Elephanet.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Debug
8 | AnyCPU
9 | {1156667A-85F7-493C-BF33-F22284134559}
10 | Library
11 | Properties
12 | Elephanet.Tests
13 | Elephanet.Tests
14 | v4.5
15 | 512
16 |
17 |
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 | ..\packages\NSubstitute.1.7.2.0\lib\NET45\NSubstitute.dll
39 |
40 |
41 | ..\packages\AutoFixture.3.20.1\lib\net40\Ploeh.AutoFixture.dll
42 |
43 |
44 | ..\packages\Shouldly.2.1.1\lib\net40\Shouldly.dll
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll
55 | True
56 |
57 |
58 | ..\packages\xunit.assert.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll
59 | True
60 |
61 |
62 | ..\packages\xunit.extensibility.core.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll
63 | True
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | {f6f93a0a-0eb1-47a5-9089-db6f56369f98}
105 | Elephanet
106 |
107 |
108 |
109 |
110 |
111 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
112 |
113 |
114 |
115 |
116 |
123 |
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/AnotherEntity.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class AnotherEntity : BaseEntity
4 | {
5 | public string Description { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/AnotherEntityWithDateTime.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class AnotherEntityWithDateTime : BaseEntity
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/BaseEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet.Tests.Entities
4 | {
5 | public class BaseEntity
6 | {
7 | public Guid Id { get; set; }
8 | public string PropertyOne { get; set; }
9 | public string PropertyTwo { get; set; }
10 | public string PropertyThree { get; set; }
11 | public string PropertyFour { get; set; }
12 | public DateTime CreatedAt { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/Bike.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet.Tests.Entities
4 | {
5 | public class Bike
6 | {
7 | public Guid Id { get; set; }
8 | public double WheelSize { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/Car.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet.Tests.Entities
4 | {
5 | public class Car
6 | {
7 | public Guid Id { get; set; }
8 | public string Make { get; set; }
9 | public string Model { get; set; }
10 | public string ImageUrl { get; set; }
11 | public string NumberPlate { get; set; }
12 | public DateTime CreatedAt { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/EntityForCorrectEscapingTests.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class EntityForCorrectEscapingTests : BaseEntity
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/EntityForGetByIdTests.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class EntityForGetByIdTests : BaseEntity
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/EntityForLinqOrderByTests.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class EntityForLinqOrderByTests : BaseEntity
4 | {
5 | }
6 |
7 |
8 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/EntityForSchemaConventionsTest.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class EntityForSchemaConventionsTest : BaseEntity
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/EntityForSessionAndStoreTests.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class EntityForSessionAndStoreTests : BaseEntity
4 | {
5 |
6 | }
7 |
8 | public class SecondEntityForSessionAndStoreTest : EntityForSessionAndStoreTests
9 | {
10 | public int WheelSize { get; set; }
11 | }
12 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/EntityForWhereLinqTests.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Tests.Entities
2 | {
3 | public class EntityForWhereLinqTests : BaseEntity
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/EntityWithDateTime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet.Tests.Entities
4 | {
5 | public class EntityWithDateTime
6 | {
7 | public DateTime CreatedAt { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/Entities/SomeTestClassThatNoOneWillEverUse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet.Tests.Entities
4 | {
5 | public class SomeTestClassThatNoOneWillEverUse
6 | {
7 | public Guid Id { get; set; }
8 | public string Something { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/Elephanet.Tests/GetAllTests.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Elephanet.Tests.Entities;
3 | using Shouldly;
4 | using Xunit;
5 |
6 | namespace Elephanet.Tests
7 | {
8 | public class GetAllTests : IClassFixture
9 | {
10 | private readonly TestStore _store;
11 |
12 | public GetAllTests(DocumentStoreBaseFixture data)
13 | {
14 | _store = data.TestStore;
15 | }
16 |
17 | [Fact]
18 | public void GetAllCreatesTheTableIfItDoesNotExist()
19 | {
20 | using (var session = _store.OpenSession())
21 | {
22 | var things = session.GetAll().ToList();
23 | things.Count.ShouldBe(0);
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Elephanet.Tests/GetByIdTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Elephanet.Tests.Entities;
4 | using Ploeh.AutoFixture;
5 | using Shouldly;
6 | using Xunit;
7 | using System;
8 |
9 | namespace Elephanet.Tests
10 | {
11 | public class GetByIdTests : IClassFixture, IDisposable
12 | {
13 | private readonly TestStore _store;
14 | private readonly List _dummyCars;
15 |
16 | public GetByIdTests(DocumentStoreBaseFixture data)
17 | {
18 | _store = data.TestStore;
19 | _dummyCars = new Fixture().CreateMany().ToList();
20 | using (var session = _store.OpenSession())
21 | {
22 | foreach (var car in _dummyCars)
23 | {
24 | session.Store(car);
25 | }
26 |
27 | session.SaveChanges();
28 | }
29 | }
30 |
31 | [Fact]
32 | public void GetById_Should_NotByNull()
33 | {
34 | using (var session = _store.OpenSession())
35 | {
36 | var first = session.GetById(_dummyCars[0].Id);
37 | first.ShouldNotBe(null);
38 | }
39 | }
40 |
41 | [Fact]
42 | public void GetByIds_Should_NotByNull()
43 | {
44 | using (var session = _store.OpenSession())
45 | {
46 | var cars = session.GetByIds(_dummyCars.Select(c => c.Id).ToList()).ToList();
47 | cars[0].Id.ShouldBe(_dummyCars[0].Id);
48 | cars[2].Id.ShouldBe(_dummyCars[2].Id);
49 | }
50 | }
51 |
52 | [Fact]
53 | public void GetById_Should_Throw_EntityNotFoundException()
54 | {
55 | using (var session = _store.OpenSession())
56 | {
57 | Should.Throw(() =>
58 | {
59 | session.GetById(Guid.NewGuid());
60 | });
61 | }
62 | }
63 |
64 | [Fact]
65 | public void GetById_Should_Return_Null_When_ReturnNull_Convention_Set()
66 | {
67 | var store = TestStore.CreateStoreWithEntityNotFoundBehaviorReturnNull();
68 | using (var session = store.OpenSession())
69 | {
70 | session.GetById(Guid.NewGuid())
71 | .ShouldBe(null);
72 | }
73 | }
74 |
75 | [Fact]
76 | public void GetAll_ShouldGetAll()
77 | {
78 | using (var session = _store.OpenSession())
79 | {
80 | var cars = session.GetAll().ToList();
81 | cars.Count.ShouldBe(3);
82 | }
83 | }
84 |
85 | public void Dispose()
86 | {
87 | using (var session = _store.OpenSession())
88 | {
89 | session.DeleteAll();
90 | }
91 | }
92 |
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Elephanet.Tests/JsonConverterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Elephanet.Serialization;
3 | using Elephanet.Tests.Entities;
4 | using Shouldly;
5 | using Xunit;
6 |
7 | namespace Elephanet.Tests
8 | {
9 | public class JsonConverterTests
10 | {
11 | private readonly JilJsonConverter _converter;
12 | private Guid _id;
13 | private readonly string _description;
14 | private readonly AnotherEntity _entityToSerialize;
15 |
16 | public JsonConverterTests()
17 | {
18 | _converter = new JilJsonConverter();
19 | _id = Guid.NewGuid();
20 | _description = "xxx";
21 | _entityToSerialize = new AnotherEntity {Id = _id,Description = _description};
22 | }
23 |
24 | [Fact]
25 | public void JsonConverter_GivenAnInheritedObject_ShouldSerialize()
26 | {
27 | string json = _converter.Serialize(_entityToSerialize);
28 | json.ShouldContain(_id.ToString());
29 | json.ShouldContain(_description);
30 | }
31 |
32 | [Fact]
33 | public void JsonConverter_GivenAnInheritedObject_ShouldDeserialize()
34 | {
35 | string json = _converter.Serialize(_entityToSerialize);
36 | var entity = _converter.Deserialize(json);
37 | entity.Id.ShouldBe(_id);
38 | entity.Description.ShouldBe(_description);
39 | }
40 |
41 | [Fact]
42 | public void JsonConverter_GivenAnEntityWithDateTime_ShouldSerializeAndDeserialize()
43 | {
44 | var entity = new EntityWithDateTime();
45 | var now = DateTime.UtcNow;
46 | entity.CreatedAt = now;
47 | string json = _converter.Serialize(entity);
48 |
49 | var deSerializedEntity = _converter.Deserialize(json);
50 | deSerializedEntity.CreatedAt.ToString().ShouldBe(now.ToString());
51 | }
52 |
53 | [Fact]
54 | public void JsonConverter_GivenAnInheritedEntityWithDateTime_ShouldSerializeAndDeserialize()
55 | {
56 | var entity = new AnotherEntityWithDateTime();
57 | var now = DateTime.UtcNow;
58 | entity.CreatedAt = now;
59 | string json = _converter.Serialize(entity);
60 |
61 | var deSerializedEntity = _converter.Deserialize(json);
62 | deSerializedEntity.CreatedAt.ToString().ShouldBe(now.ToString());
63 | }
64 |
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Elephanet.Tests/LinqOrderByTests.cs:
--------------------------------------------------------------------------------
1 | using Elephanet.Tests.Entities;
2 | using Ploeh.AutoFixture;
3 | using Xunit;
4 | using Shouldly;
5 | using System.Linq;
6 | using System;
7 |
8 | namespace Elephanet.Tests
9 | {
10 | public class LinqOrderByTests : IClassFixture, IDisposable
11 | {
12 | private readonly TestStore _store;
13 |
14 | public LinqOrderByTests(DocumentStoreBaseFixture data)
15 | {
16 | _store = data.TestStore;
17 |
18 | var carA = new Fixture().Build()
19 | .With(x => x.PropertyOne, "Mazda")
20 | .With(y => y.PropertyTwo, "A")
21 | .Create();
22 |
23 | var carB = new Fixture().Build()
24 | .With(x => x.PropertyOne, "Mazda")
25 | .With(y => y.PropertyTwo, "B")
26 | .Create();
27 |
28 | using (var session = _store.OpenSession())
29 | {
30 | session.Store(carA);
31 | session.Store(carB);
32 | session.SaveChanges();
33 | }
34 | }
35 |
36 | [Fact]
37 | public void IQueryable_Should_ImplementOrderBy()
38 | {
39 | using (var session = _store.OpenSession())
40 | {
41 | var cars = session.Query().Where(c => c.PropertyOne == "Mazda").OrderBy(o => o.PropertyTwo).ToList();
42 | cars[0].PropertyTwo.ShouldBe("A");
43 | cars[1].PropertyTwo.ShouldBe("B");
44 | }
45 | }
46 |
47 | [Fact]
48 | public void IQueryable_Should_ImplementOrderByDescending()
49 | {
50 | using (var session = _store.OpenSession())
51 | {
52 | var cars = session.Query().Where(c => c.PropertyOne == "Mazda").OrderByDescending(o => o.PropertyTwo).ToList();
53 | cars[0].PropertyTwo.ShouldBe("B");
54 | cars[1].PropertyTwo.ShouldBe("A");
55 | }
56 | }
57 |
58 | public void Dispose()
59 | {
60 | using (var session = _store.OpenSession())
61 | {
62 | session.DeleteAll();
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Elephanet.Tests/LinqTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Elephanet.Tests.Entities;
5 | using Xunit;
6 | using Shouldly;
7 | using Ploeh.AutoFixture;
8 |
9 |
10 | namespace Elephanet.Tests
11 | {
12 | public class LinqTests : IClassFixture, IDisposable
13 | {
14 | private readonly IDocumentStore _store;
15 |
16 | public LinqTests(DocumentStoreBaseFixture data)
17 | {
18 | _store = data.TestStore;
19 | CreateDummyCar();
20 | }
21 |
22 | public void CreateDummyCar()
23 | {
24 |
25 | var dummyEntity = new Fixture().Build()
26 | .With(x => x.PropertyOne, "Subaru")
27 | .CreateMany();
28 |
29 | var lowercasePropertyOneEntity = new Fixture().Build()
30 | .With(x => x.PropertyOne, "SAAB")
31 | .CreateMany();
32 |
33 | using (var session = _store.OpenSession())
34 | {
35 | foreach (var entity in dummyEntity)
36 | {
37 | session.Store(entity);
38 | }
39 |
40 | foreach (var entity in lowercasePropertyOneEntity)
41 | {
42 | session.Store(entity);
43 | }
44 | session.SaveChanges();
45 | }
46 |
47 | }
48 |
49 | [Fact]
50 | public void WherePredicate_Should_Build()
51 | {
52 | using (var session = _store.OpenSession())
53 | {
54 | var results = session.Query().Where(x => x.PropertyOne == "Subaru").ToList();
55 | results.ShouldNotBeEmpty();
56 | }
57 | }
58 |
59 | [Fact]
60 | public void WhereExpression_Should_ReturnWhereQuery()
61 | {
62 | using (var session = _store.OpenSession())
63 | {
64 | var results = session.Query().Where(c => c.PropertyOne == "Subaru");
65 | var car = results.ToList();
66 | car.Count.ShouldBe(3);
67 | car.ShouldBeOfType>();
68 | car.ForEach(c => c.PropertyOne.ShouldBe("Subaru"));
69 |
70 | }
71 | }
72 |
73 | [Fact]
74 | public void WhereExpression_Should_HandleExpressionSubtrees()
75 | {
76 | const string propertyOne = "Subaru";
77 | using (var session = _store.OpenSession())
78 | {
79 | var results = session.Query().Where(c => c.PropertyOne == propertyOne);
80 | var car = results.ToList();
81 | car.Count.ShouldBe(3);
82 | car.ShouldBeOfType>();
83 | car.ForEach(c => c.PropertyOne.ShouldBe("Subaru"));
84 | }
85 | }
86 |
87 | [Fact]
88 | public void WhereExpression_Should_HandleExtensionMethodsInSubtrees()
89 | {
90 | using (var session = _store.OpenSession())
91 | {
92 | var results = session.Query().Where(c => c.PropertyOne == "saab".ToUpper());
93 | var car = results.ToList();
94 | car.Count.ShouldBe(3);
95 | car.ShouldBeOfType>();
96 | car.ForEach(c => c.PropertyOne.ShouldBe("SAAB"));
97 | }
98 |
99 | }
100 |
101 | [Fact]
102 | public void IQueryable_Should_ImplementTakeMethod()
103 | {
104 | using (var session = _store.OpenSession())
105 | {
106 | var results = session.Query().Where(c => c.PropertyOne == "SAAB").Take(2);
107 | var car = results.ToList();
108 | car.Count.ShouldBe(2);
109 | car.ShouldBeOfType>();
110 | }
111 | }
112 |
113 | [Fact]
114 | public void IQueryable_Should_ImplementSkipMethod()
115 | {
116 | using (var session = _store.OpenSession())
117 | {
118 | var results = session.Query().Where(c => c.PropertyOne == "SAAB").Skip(2);
119 | var car = results.ToList();
120 | car.Count.ShouldBe(1);
121 | car.ShouldBeOfType>();
122 | }
123 |
124 | }
125 |
126 | public void Dispose()
127 | {
128 | using (var session = _store.OpenSession())
129 | {
130 | session.DeleteAll();
131 | }
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/Elephanet.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Elephanet.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Elephanet.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("88924a5d-1ddc-461a-bcb3-643e9c75ede2")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Elephanet.Tests/SessionAndStoreTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Elephanet.Tests.Entities;
4 | using Ploeh.AutoFixture;
5 | using Shouldly;
6 | using Xunit;
7 |
8 | namespace Elephanet.Tests
9 | {
10 | public class SessionAndStoreTests : IClassFixture, IDisposable
11 | {
12 | private readonly TestStore _store;
13 |
14 | public SessionAndStoreTests(DocumentStoreBaseFixture data)
15 | {
16 | _store = data.TestStore;
17 | }
18 |
19 | [Fact]
20 | public void WillPass()
21 | {
22 | true.ShouldBe(true);
23 | }
24 |
25 | [Fact]
26 | public void NewStore_ConnectionStringShouldBe_SameAsCtor()
27 | {
28 | _store.ConnectionString.ShouldBe("Server=127.0.0.1;Port=5432;User id=store_user;password=my super secret password;database=store;");
29 | }
30 |
31 | [Fact]
32 | public void Store_Should_OpenSession()
33 | {
34 | using (var session = _store.OpenSession())
35 | {
36 | //not really doing anything
37 | }
38 | }
39 |
40 | [Fact]
41 | public void SessionStore_Should_NotThrow()
42 | {
43 | using (var session = _store.OpenSession())
44 | {
45 | var car = new EntityForSessionAndStoreTests
46 | {
47 | Id = Guid.NewGuid(),
48 | PropertyOne = "Subaru",
49 | PropertyTwo = "Impreza",
50 | PropertyThree = "http://www.carsfotodb.com/uploads/subaru/subaru-impreza/subaru-impreza-08.jpg",
51 | PropertyFour = "NRM1003"
52 | };
53 |
54 | Should.NotThrow(() => session.Store(car));
55 | }
56 |
57 | }
58 |
59 | [Fact]
60 | public void SessionSaveChanges_Should_SaveChanges()
61 | {
62 | using (var session = _store.OpenSession())
63 | {
64 | var car = new EntityForSessionAndStoreTests
65 | {
66 | Id = Guid.NewGuid(),
67 | PropertyOne = "Subaru",
68 | PropertyTwo = "Impreza",
69 | PropertyThree = "http://www.carsfotodb.com/uploads/subaru/subaru-impreza/subaru-impreza-08.jpg",
70 | PropertyFour = "NRM1003"
71 | };
72 |
73 | session.Store(car);
74 | session.SaveChanges();
75 | }
76 |
77 | }
78 |
79 | [Fact]
80 | public void StoringAnEnitityAndFetchingWithinSession_Should_ReturnEntity()
81 | {
82 |
83 | var dummyCar = new Fixture().Create();
84 |
85 | using (var session = _store.OpenSession())
86 | {
87 | session.Store(dummyCar);
88 | var result = session.GetById(dummyCar.Id);
89 | result.Id.ShouldBe(dummyCar.Id);
90 | result.PropertyOne.ShouldBe(dummyCar.PropertyOne);
91 | result.PropertyTwo.ShouldBe(dummyCar.PropertyTwo);
92 | result.PropertyThree.ShouldBe(dummyCar.PropertyThree);
93 | result.PropertyFour.ShouldBe(dummyCar.PropertyFour);
94 |
95 | }
96 | }
97 |
98 | [Fact]
99 | public void NewStoreWithNoCtors_Should_SetConventions()
100 | {
101 | _store.StoreInfo.ShouldNotBe(null);
102 | _store.Conventions.ShouldNotBe(null);
103 | }
104 |
105 | [Fact]
106 | public void SaveSessionThenLoading_Should_ReturnEntity()
107 | {
108 |
109 | var dummyCar = new Fixture().Create();
110 |
111 | using (var session = _store.OpenSession())
112 | {
113 | session.Store(dummyCar);
114 | session.SaveChanges();
115 | }
116 |
117 | using (var session = _store.OpenSession())
118 | {
119 | var car = session.GetById(dummyCar.Id);
120 | car.Id.ShouldBe(dummyCar.Id);
121 | car.PropertyOne.ShouldBe(dummyCar.PropertyOne);
122 | car.PropertyTwo.ShouldBe(dummyCar.PropertyTwo);
123 | car.PropertyThree.ShouldBe(dummyCar.PropertyThree);
124 | car.PropertyFour.ShouldBe(dummyCar.PropertyFour);
125 | }
126 | }
127 |
128 | [Fact]
129 | public void SaveChangesWithMultipleDifferentClasses_Should_Save()
130 | {
131 |
132 | var firstDummyEntity = new Fixture().Create();
133 | var secondDummyEntity = new Fixture().Create();
134 |
135 | using (var session = _store.OpenSession())
136 | {
137 | session.Store(firstDummyEntity);
138 | session.Store(secondDummyEntity);
139 | session.SaveChanges();
140 | }
141 |
142 | using (var session = _store.OpenSession())
143 | {
144 | session.GetById(secondDummyEntity.Id).ShouldNotBe(null);
145 | session.GetById(firstDummyEntity.Id).ShouldNotBe(null);
146 | }
147 | }
148 |
149 |
150 | [Fact]
151 | public void SessionCanDeleteById()
152 | {
153 |
154 | var dummyCars = new Fixture().Build()
155 | .With(x => x.PropertyOne, "Subaru")
156 | .CreateMany().ToList();
157 | //setup
158 | using (var session = _store.OpenSession())
159 | {
160 | foreach (var car in dummyCars)
161 | {
162 | session.Store(car);
163 | }
164 | session.SaveChanges();
165 | }
166 | //delete
167 | using (var session = _store.OpenSession())
168 | {
169 | foreach (var car in dummyCars)
170 | {
171 | session.Delete(car.Id);
172 | session.SaveChanges();
173 | }
174 | }
175 | //check
176 | using (var session = _store.OpenSession())
177 | {
178 | var records = session.Query().Where(c => c.PropertyOne == "Subaru").ToList();
179 | records.Count.ShouldBe(0);
180 | }
181 | }
182 |
183 | [Fact]
184 | public void SaveChangesWithUpdates_Should_Persist()
185 | {
186 |
187 | var firstDummyEntity = new Fixture().Create();
188 | var secondDummyEntity = new Fixture().Create();
189 |
190 | using (var session = _store.OpenSession())
191 | {
192 | session.Store(firstDummyEntity);
193 | session.Store(secondDummyEntity);
194 | session.SaveChanges();
195 | }
196 |
197 | using (var session = _store.OpenSession())
198 | {
199 | var retrievedBike = session.GetById(secondDummyEntity.Id);
200 | var retrievedCar = session.GetById(firstDummyEntity.Id);
201 |
202 | retrievedBike.WheelSize = 900;
203 | retrievedCar.PropertyOne = "Lada";
204 | session.Store(retrievedCar);
205 | session.Store(retrievedBike);
206 | session.SaveChanges();
207 | }
208 |
209 | using (var session = _store.OpenSession())
210 | {
211 | var alteredBike = session.GetById(secondDummyEntity.Id);
212 | var alteredCar = session.GetById(firstDummyEntity.Id);
213 |
214 | alteredBike.WheelSize.ShouldBe(900);
215 | alteredCar.PropertyOne.ShouldBe("Lada");
216 |
217 | }
218 | }
219 |
220 | [Fact]
221 | public void SaveChangesWithUpdates_Should_BeQueryable()
222 | {
223 | var dummyCars = new Fixture().Build()
224 | .With(x => x.PropertyOne, "Subaru")
225 | .CreateMany(100).ToList();
226 | using (var session = _store.OpenSession())
227 | {
228 | foreach (var car in dummyCars)
229 | {
230 | session.Store(car);
231 | session.SaveChanges();
232 | }
233 |
234 | }
235 |
236 | using (var session = _store.OpenSession())
237 | {
238 | //retrieve a couple of cars
239 | var car1ToAlter = session.GetById(dummyCars[15].Id);
240 | var car2ToAlter = session.GetById(dummyCars[85].Id);
241 | car1ToAlter.PropertyOne = "Ford";
242 | car2ToAlter.PropertyOne = "Ford";
243 | session.Store(car1ToAlter);
244 | session.Store(car2ToAlter);
245 | session.SaveChanges();
246 | }
247 |
248 | using (var session = _store.OpenSession())
249 | {
250 | var cars = session.Query().Where(c => c.PropertyOne == "Ford").ToList();
251 | cars.Count.ShouldBe(2);
252 | }
253 | }
254 |
255 | public void Dispose()
256 | {
257 | using (var session = _store.OpenSession())
258 | {
259 | session.DeleteAll();
260 | session.DeleteAll();
261 | }
262 | }
263 | }
264 | }
265 |
266 |
--------------------------------------------------------------------------------
/Elephanet.Tests/StoreConventionsTests.cs:
--------------------------------------------------------------------------------
1 | using Elephanet.Conventions;
2 | using Elephanet.Serialization;
3 | using Shouldly;
4 | using Xunit;
5 |
6 | namespace Elephanet.Tests
7 | {
8 | public class StoreConventionsTests
9 | {
10 | [Fact]
11 | public void Ctor_Sets_JsonConverter_And_TableInfo()
12 | {
13 | var jsonConverter = new JilJsonConverter();
14 | var info = new TableInfo();
15 |
16 | var convention = new StoreConventions(jsonConverter, info);
17 |
18 | convention.JsonConverter.ShouldBeSameAs(jsonConverter);
19 | convention.TableInfo.ShouldBeSameAs(info);
20 | }
21 |
22 | [Fact]
23 | public void Ctor_Providing_IJsonConverter_Sets_JsonConverter_And_TableInfo_To_Default()
24 | {
25 | var jsonConverter = new JilJsonConverter();
26 |
27 | var conventions = new StoreConventions(jsonConverter);
28 |
29 | conventions.JsonConverter.ShouldBeSameAs(jsonConverter);
30 | conventions.TableInfo.ShouldBeOfType();
31 | }
32 |
33 | [Fact]
34 | public void EntityNotFoundBehavior_Defaults_To_Throw()
35 | {
36 | new StoreConventions()
37 | .EntityNotFoundBehavior.ShouldBe(EntityNotFoundBehavior.Throw);
38 | }
39 |
40 | [Fact]
41 | public void SetEntityNotFoundBehavior_Sets__EntityNotFoundBehavior()
42 | {
43 | var conventions = new StoreConventions();
44 |
45 | conventions.SetEntityNotFoundBehavior(EntityNotFoundBehavior.ReturnNull);
46 |
47 | conventions.EntityNotFoundBehavior.ShouldBe(EntityNotFoundBehavior.ReturnNull);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Elephanet.Tests/StoreInfoTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Elephanet;
5 | using Xunit;
6 | using Shouldly;
7 |
8 | namespace Elephanet.Tests
9 | {
10 | public class StoreInfoTests
11 | {
12 | [Fact]
13 | public void StoreInfo_GivenANewTable_Should_AddItToItsTableNamesList()
14 | {
15 | StoreInfo storeInfo = new StoreInfo();
16 | storeInfo.Add("someting or rather");
17 |
18 | storeInfo.Tables.ShouldContain("someting or rather");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Elephanet.Tests/TableInfoTests.cs:
--------------------------------------------------------------------------------
1 | using Elephanet.Tests.Entities;
2 | using Xunit;
3 | using Shouldly;
4 |
5 | namespace Elephanet.Tests
6 | {
7 | public class ConventionTests
8 | {
9 | [Fact]
10 | public void TableInfo_GivenNoSchema_ShouldReturnSchemaOfPublic()
11 | {
12 | TableInfo tableInfo = new TableInfo();
13 | tableInfo.Schema.ShouldBe("public");
14 | }
15 |
16 | [Fact]
17 | public void TableInfo_GivenASchema_ShouldReturnThatSchema()
18 | {
19 | TableInfo tableInfo = new TableInfo("aschema");
20 | tableInfo.Schema.ShouldBe("aschema");
21 | }
22 |
23 | [Fact]
24 | public void TableInfo_GivenAType_ShouldReturn_TheCorrectTableNameWithoutSchema()
25 | {
26 | TableInfo tableInfo = new TableInfo("aschema");
27 | tableInfo.TableNameWithoutSchema(typeof(EntityForSchemaConventionsTest)).ShouldBe("elephanet_tests_entities_entityforschemaconventionstest");
28 |
29 | }
30 |
31 | [Fact]
32 | public void TableInfo_GivenAType_ShouldReturn_TheCorrectTableNameWithSchema()
33 | {
34 | TableInfo tableInfo = new TableInfo("aschema");
35 | tableInfo.TableNameWithSchema(typeof(EntityForSchemaConventionsTest)).ShouldBe("aschema.elephanet_tests_entities_entityforschemaconventionstest");
36 |
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Elephanet.Tests/TestStore.cs:
--------------------------------------------------------------------------------
1 |
2 | using Elephanet.Conventions;
3 |
4 | namespace Elephanet.Tests
5 | {
6 | public class TestStore : DocumentStore
7 | {
8 | private const string TestDbConnectionString = "Server=127.0.0.1;Port=5432;User id=store_user;password=my super secret password;database=store;";
9 |
10 | public TestStore() : base(TestDbConnectionString, new StoreConventions())
11 | { }
12 |
13 | public TestStore(IStoreConventions storeConventions) : base(TestDbConnectionString, storeConventions)
14 | {
15 | }
16 |
17 | ///
18 | /// Factory method for creating a configured TestStore
19 | ///
20 | ///
21 | public static TestStore CreateStoreWithEntityNotFoundBehaviorReturnNull()
22 | {
23 | var conventions = new StoreConventions();
24 | conventions.SetEntityNotFoundBehavior(EntityNotFoundBehavior.ReturnNull);
25 | return new TestStore(conventions);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Elephanet.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Elephanet.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elephanet", "Elephanet\Elephanet.csproj", "{F6F93A0A-0EB1-47A5-9089-DB6F56369F98}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elephanet.Tests", "Elephanet.Tests\Elephanet.Tests.csproj", "{1156667A-85F7-493C-BF33-F22284134559}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{A70354CF-567C-4D90-A635-8AC825CFA7EA}"
11 | ProjectSection(SolutionItems) = preProject
12 | .nuget\packages.config = .nuget\packages.config
13 | EndProjectSection
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{64298DF8-AB03-405A-9530-C036710DDBC2}"
16 | ProjectSection(SolutionItems) = preProject
17 | Performance1.psess = Performance1.psess
18 | EndProjectSection
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elephanet.Console", "Elephanet.Console\Elephanet.Console.csproj", "{7B67E4BB-55E4-4976-AA8E-72448CDE9E69}"
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {F6F93A0A-0EB1-47A5-9089-DB6F56369F98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {F6F93A0A-0EB1-47A5-9089-DB6F56369F98}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {F6F93A0A-0EB1-47A5-9089-DB6F56369F98}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {F6F93A0A-0EB1-47A5-9089-DB6F56369F98}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {1156667A-85F7-493C-BF33-F22284134559}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {1156667A-85F7-493C-BF33-F22284134559}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {1156667A-85F7-493C-BF33-F22284134559}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {1156667A-85F7-493C-BF33-F22284134559}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {7B67E4BB-55E4-4976-AA8E-72448CDE9E69}.Release|Any CPU.Build.0 = Release|Any CPU
40 | EndGlobalSection
41 | GlobalSection(SolutionProperties) = preSolution
42 | HideSolutionNode = FALSE
43 | EndGlobalSection
44 | EndGlobal
45 |
--------------------------------------------------------------------------------
/Elephanet/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Elephanet/ConcurrentHashSet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Runtime.Serialization;
6 | using System.Threading;
7 |
8 | namespace Elephanet
9 | {
10 | ///
11 | /// Concurrent HashSet implementaion from http://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework
12 | /// as .NET framework doesnt have one.
13 | ///
14 | [DebuggerDisplay("Count = {Count}")]
15 | [Serializable]
16 | public class ConcurrentHashSet : ICollection, ISet, ISerializable, IDeserializationCallback
17 | {
18 | private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
19 |
20 | private readonly HashSet _hashSet = new HashSet();
21 |
22 | public ConcurrentHashSet()
23 | {
24 | }
25 |
26 | public ConcurrentHashSet(IEqualityComparer comparer)
27 | {
28 | _hashSet = new HashSet(comparer);
29 | }
30 |
31 | public ConcurrentHashSet(IEnumerable collection)
32 | {
33 | _hashSet = new HashSet(collection);
34 | }
35 |
36 | public ConcurrentHashSet(IEnumerable collection, IEqualityComparer comparer)
37 | {
38 | _hashSet = new HashSet(collection, comparer);
39 | }
40 |
41 | protected ConcurrentHashSet(SerializationInfo info, StreamingContext context)
42 | {
43 | _hashSet = new HashSet();
44 |
45 | // not sure about this one really...
46 | var iSerializable = _hashSet as ISerializable;
47 | iSerializable.GetObjectData(info, context);
48 | }
49 |
50 | #region Dispose
51 |
52 | public void Dispose()
53 | {
54 | Dispose(true);
55 | GC.SuppressFinalize(this);
56 | }
57 |
58 | protected virtual void Dispose(bool disposing)
59 | {
60 | if (disposing)
61 | if (_lock != null)
62 | _lock.Dispose();
63 | }
64 |
65 | public IEnumerator GetEnumerator()
66 | {
67 | return _hashSet.GetEnumerator();
68 | }
69 |
70 | ~ConcurrentHashSet()
71 | {
72 | Dispose(false);
73 | }
74 |
75 | public void OnDeserialization(object sender)
76 | {
77 | _hashSet.OnDeserialization(sender);
78 | }
79 |
80 | public void GetObjectData(SerializationInfo info, StreamingContext context)
81 | {
82 | _hashSet.GetObjectData(info, context);
83 | }
84 |
85 | IEnumerator IEnumerable.GetEnumerator()
86 | {
87 | return GetEnumerator();
88 | }
89 |
90 | #endregion
91 |
92 | public void Add(T item)
93 | {
94 | _lock.EnterWriteLock();
95 | try
96 | {
97 | _hashSet.Add(item);
98 | }
99 | finally
100 | {
101 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
102 | }
103 | }
104 |
105 | public void UnionWith(IEnumerable other)
106 | {
107 | _lock.EnterWriteLock();
108 | _lock.EnterReadLock();
109 | try
110 | {
111 | _hashSet.UnionWith(other);
112 | }
113 | finally
114 | {
115 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
116 | if (_lock.IsReadLockHeld) _lock.ExitReadLock();
117 | }
118 | }
119 |
120 | public void IntersectWith(IEnumerable other)
121 | {
122 | _lock.EnterWriteLock();
123 | _lock.EnterReadLock();
124 | try
125 | {
126 | _hashSet.IntersectWith(other);
127 | }
128 | finally
129 | {
130 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
131 | if (_lock.IsReadLockHeld) _lock.ExitReadLock();
132 | }
133 | }
134 |
135 | public void ExceptWith(IEnumerable other)
136 | {
137 | _lock.EnterWriteLock();
138 | _lock.EnterReadLock();
139 | try
140 | {
141 | _hashSet.ExceptWith(other);
142 | }
143 | finally
144 | {
145 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
146 | if (_lock.IsReadLockHeld) _lock.ExitReadLock();
147 | }
148 | }
149 |
150 | public void SymmetricExceptWith(IEnumerable other)
151 | {
152 | _lock.EnterWriteLock();
153 | try
154 | {
155 | _hashSet.SymmetricExceptWith(other);
156 | }
157 | finally
158 | {
159 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
160 | }
161 | }
162 |
163 | public bool IsSubsetOf(IEnumerable other)
164 | {
165 | _lock.EnterWriteLock();
166 | try
167 | {
168 | return _hashSet.IsSubsetOf(other);
169 | }
170 | finally
171 | {
172 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
173 | }
174 | }
175 |
176 | public bool IsSupersetOf(IEnumerable other)
177 | {
178 | _lock.EnterWriteLock();
179 | try
180 | {
181 | return _hashSet.IsSupersetOf(other);
182 | }
183 | finally
184 | {
185 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
186 | }
187 | }
188 |
189 | public bool IsProperSupersetOf(IEnumerable other)
190 | {
191 | _lock.EnterWriteLock();
192 | try
193 | {
194 | return _hashSet.IsProperSupersetOf(other);
195 | }
196 | finally
197 | {
198 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
199 | }
200 | }
201 |
202 | public bool IsProperSubsetOf(IEnumerable other)
203 | {
204 | _lock.EnterWriteLock();
205 | try
206 | {
207 | return _hashSet.IsProperSubsetOf(other);
208 | }
209 | finally
210 | {
211 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
212 | }
213 | }
214 |
215 | public bool Overlaps(IEnumerable other)
216 | {
217 | _lock.EnterWriteLock();
218 | try
219 | {
220 | return _hashSet.Overlaps(other);
221 | }
222 | finally
223 | {
224 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
225 | }
226 | }
227 |
228 | public bool SetEquals(IEnumerable other)
229 | {
230 | _lock.EnterWriteLock();
231 | try
232 | {
233 | return _hashSet.SetEquals(other);
234 | }
235 | finally
236 | {
237 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
238 | }
239 | }
240 |
241 | bool ISet.Add(T item)
242 | {
243 | _lock.EnterWriteLock();
244 | try
245 | {
246 | return _hashSet.Add(item);
247 | }
248 | finally
249 | {
250 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
251 | }
252 | }
253 |
254 | public void Clear()
255 | {
256 | _lock.EnterWriteLock();
257 | try
258 | {
259 | _hashSet.Clear();
260 | }
261 | finally
262 | {
263 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
264 | }
265 | }
266 |
267 | public bool Contains(T item)
268 | {
269 | _lock.EnterWriteLock();
270 | try
271 | {
272 | return _hashSet.Contains(item);
273 | }
274 | finally
275 | {
276 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
277 | }
278 | }
279 |
280 | public void CopyTo(T[] array, int arrayIndex)
281 | {
282 | _lock.EnterWriteLock();
283 | try
284 | {
285 | _hashSet.CopyTo(array, arrayIndex);
286 | }
287 | finally
288 | {
289 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
290 | }
291 | }
292 |
293 | public bool Remove(T item)
294 | {
295 | _lock.EnterWriteLock();
296 | try
297 | {
298 | return _hashSet.Remove(item);
299 | }
300 | finally
301 | {
302 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
303 | }
304 | }
305 |
306 | public int Count
307 | {
308 | get
309 | {
310 | _lock.EnterWriteLock();
311 | try
312 | {
313 | return _hashSet.Count;
314 | }
315 | finally
316 | {
317 | if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
318 | }
319 |
320 | }
321 | }
322 |
323 | public bool IsReadOnly
324 | {
325 | get { return false; }
326 | }
327 | }
328 |
329 | }
330 |
--------------------------------------------------------------------------------
/Elephanet/Conventions/EntityNotFoundBehavior.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Elephanet.Conventions
8 | {
9 | ///
10 | /// Enum to control the behavior of IDocumentSession.GetById
11 | ///
12 | public enum EntityNotFoundBehavior
13 | {
14 | ///
15 | /// When Entity is not found by Id, throw an EntityNotFoundException. Default behavior.
16 | ///
17 | Throw = 0,
18 |
19 | ///
20 | /// When Entity is not found by Id, return null
21 | ///
22 | ReturnNull = 1
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Elephanet/DocumentSession.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Npgsql;
4 | using System.Linq;
5 | using System.Data;
6 | using Elephanet.Serialization;
7 | using System.Text;
8 | using Elephanet.Conventions;
9 | using Elephanet.Helpers;
10 |
11 |
12 | /*
13 | *implement fts on all string columns with a query like the following...
14 | *
15 | * select body from elephanet_demo_toilet
16 | where lower(cast(body as text)) like '%sandy%point%victoria%'
17 | and similarity(lower(cast(body as text)), '%sandy%point%victoria%') > 0
18 | order by similarity(lower(cast(body as text)), '%sandy%point%victoria%') desc
19 | limit 20;
20 | *
21 | *
22 | * needs an index like
23 | *
24 | * CREATE INDEX toilet_search_idx ON elephanet_demo_toilet USING gin (lower(cast(body as text)) gin_trgm_ops)
25 | drop index toilet_search_idx;
26 | */
27 |
28 | namespace Elephanet
29 | {
30 | public class DocumentSession : IDocumentSession
31 | {
32 | private readonly IDocumentStore _documentStore;
33 | private NpgsqlConnection _conn;
34 | protected readonly Dictionary _entities = new Dictionary();
35 | readonly IJsonConverter _jsonConverter;
36 | private JsonbQueryProvider _queryProvider;
37 | private ITableInfo _tableInfo;
38 |
39 |
40 | public DocumentSession(IDocumentStore documentStore)
41 | {
42 | _documentStore = documentStore;
43 | _tableInfo = _documentStore.Conventions.TableInfo;
44 | _conn = new NpgsqlConnection(documentStore.ConnectionString);
45 | _conn.Open();
46 | _jsonConverter = documentStore.Conventions.JsonConverter;
47 | _queryProvider = new JsonbQueryProvider(_conn, _jsonConverter, _tableInfo);
48 | }
49 |
50 | public void Delete(Guid id)
51 | {
52 | GetOrCreateTable(typeof(T));
53 | using (var command = _conn.CreateCommand())
54 | {
55 | command.CommandType = CommandType.Text;
56 | command.CommandText = String.Format(@"Delete FROM {0} WHERE id = :id;", _tableInfo.TableNameWithSchema(typeof(T)));
57 | command.Parameters.AddWithValue(":id", id);
58 | command.ExecuteNonQuery();
59 | }
60 | }
61 |
62 | public void DeleteAll()
63 | {
64 | GetOrCreateTable(typeof(T));
65 | using (var command = _conn.CreateCommand())
66 | {
67 | command.CommandType = CommandType.Text;
68 | command.CommandText = String.Format(@"DELETE FROM {0};", _tableInfo.TableNameWithSchema(typeof(T)));
69 | command.ExecuteNonQuery();
70 | }
71 |
72 | }
73 |
74 | public T LoadInternal(Guid id)
75 | {
76 |
77 | GetOrCreateTable(typeof(T));
78 | using (var command = _conn.CreateCommand())
79 | {
80 | command.CommandType = CommandType.Text;
81 | command.CommandText = String.Format(@"SELECT body FROM {0} WHERE id = :id LIMIT 1;", _tableInfo.TableNameWithSchema(typeof(T)));
82 |
83 | command.Parameters.AddWithValue(":id", id);
84 |
85 | using (var reader = command.ExecuteReader())
86 | {
87 | if (reader.Read())
88 | {
89 | return _jsonConverter.Deserialize(reader.GetString(0));
90 | }
91 |
92 | return default(T);
93 | }
94 | }
95 | }
96 |
97 | public IJsonbQueryable Query()
98 | {
99 | IJsonbQueryable query = new JsonbQueryable(new JsonbQueryProvider(_conn, _jsonConverter, _tableInfo));
100 | return query;
101 | }
102 |
103 | public void SaveChanges()
104 | {
105 | //save the cache out to the db
106 | SaveInternal();
107 | }
108 |
109 | HashSet> MatchEntityToFinalTableAndTemporaryTable(Dictionary entities)
110 | {
111 | HashSet> typeToTableMap = new HashSet>();
112 |
113 | var types = entities.Values.Select(v => v.GetType()).Distinct();
114 | foreach (Type type in types)
115 | {
116 | typeToTableMap.Add(new Tuple(type, _tableInfo.TableNameWithSchema(type), Guid.NewGuid().ToString()));
117 | }
118 |
119 | return typeToTableMap;
120 | }
121 |
122 | void SaveInternal()
123 | {
124 | StringBuilder sb = new StringBuilder();
125 |
126 | HashSet> matches = MatchEntityToFinalTableAndTemporaryTable(_entities);
127 |
128 |
129 | foreach (var item in _entities)
130 | {
131 | //make sure we have tables for all types
132 | GetOrCreateTable(item.Value.GetType());
133 | }
134 |
135 | sb.Append("BEGIN;");
136 | foreach (var match in matches)
137 | {
138 | sb.Append(string.Format("CREATE TEMPORARY TABLE \"{0}\" (id uuid, body jsonb);", match.Item3));
139 | }
140 |
141 | foreach (var item in _entities)
142 | {
143 | sb.Append(string.Format("INSERT INTO \"{0}\" (id, body) VALUES ('{1}', '{2}');", matches.Where(c => c.Item1 == item.Value.GetType()).Select(j => j.Item3).First(), item.Key, _jsonConverter.Serialize(item.Value).EscapeQuotes()));
144 | }
145 |
146 | foreach (var match in matches)
147 | {
148 | sb.Append(string.Format("LOCK TABLE {0} IN EXCLUSIVE MODE;", match.Item2));
149 | sb.Append(string.Format("UPDATE {0} SET body = tmp.body from \"{1}\" tmp where tmp.id = {0}.id;", match.Item2, match.Item3));
150 | sb.Append(string.Format("INSERT INTO {0} SELECT tmp.id, tmp.body from \"{1}\" tmp LEFT OUTER JOIN {0} ON ({0}.id = tmp.id) where {0}.id IS NULL;", match.Item2, match.Item3));
151 | }
152 |
153 |
154 | sb.Append("COMMIT;");
155 |
156 | using (var command = _conn.CreateCommand())
157 | {
158 | command.CommandTimeout = 60;
159 | command.CommandType = CommandType.Text;
160 | command.CommandText = sb.ToString();
161 | command.ExecuteNonQuery();
162 | }
163 |
164 | _entities.Clear();
165 | }
166 |
167 | public void Store(T entity)
168 | {
169 | var id = IdentityFactory.SetEntityId(entity);
170 | _entities[id] = entity;
171 | }
172 |
173 | private bool IndexDoesNotExist(Type type)
174 | {
175 | using (var command = _conn.CreateCommand())
176 | {
177 | command.CommandType = CommandType.Text;
178 | command.CommandText = string.Format(@"select count(*)
179 | from pg_indexes
180 | where schemaname = '{0}'
181 | and tablename = '{1}'
182 | and indexname = 'idx_{1}_body';", _tableInfo.Schema, _tableInfo.TableNameWithoutSchema(type));
183 | var indexCount = (Int64)command.ExecuteScalar();
184 | return indexCount == 0;
185 | }
186 |
187 | }
188 | private void CreateIndex(Type type)
189 | {
190 | if (IndexDoesNotExist(type))
191 | {
192 | using (var command = _conn.CreateCommand())
193 | {
194 | command.CommandType = CommandType.Text;
195 | command.CommandText = string.Format(@"CREATE INDEX idx_{0}_body ON {0} USING gin (body);", _tableInfo.TableNameWithoutSchema(type));
196 | command.ExecuteNonQuery();
197 | }
198 | }
199 | }
200 |
201 |
202 | private void GetOrCreateTable(Type type)
203 | {
204 | if (!_documentStore.StoreInfo.Tables.Contains(_tableInfo.TableNameWithSchema(type)))
205 | {
206 | _documentStore.StoreInfo.Tables.Add(_tableInfo.TableNameWithSchema(type));
207 | try
208 | {
209 | using (var command = _conn.CreateCommand())
210 | {
211 | command.CommandType = CommandType.Text;
212 | command.CommandText = String.Format(@"
213 | CREATE TABLE IF NOT EXISTS {0}
214 | (
215 | id uuid NOT NULL,
216 | body jsonb NOT NULL,
217 | created timestamp without time zone NOT NULL DEFAULT (now() at time zone 'utc'),
218 | CONSTRAINT pk_{1} PRIMARY KEY (id)
219 | );", _tableInfo.TableNameWithSchema(type), _tableInfo.TableNameWithoutSchema(type));
220 | command.ExecuteNonQuery();
221 | }
222 | }
223 | catch (Exception exception)
224 | {
225 | throw new Exception(String.Format("Could not create table {0}; see the inner exception for more information.", _tableInfo.TableNameWithSchema(type)), exception);
226 | }
227 | try
228 | {
229 | CreateIndex(type);
230 | }
231 | catch (Exception exception)
232 | {
233 | throw new Exception(String.Format("Could not create index on table {0}; see the inner exception for more information.", _tableInfo.TableNameWithSchema(type)), exception);
234 | }
235 | }
236 | }
237 |
238 | public void Dispose()
239 | {
240 | _conn.Close();
241 | }
242 |
243 |
244 | public void Delete(T entity)
245 | {
246 | throw new NotImplementedException();
247 | }
248 |
249 | public T GetById(Guid id)
250 | {
251 | GetOrCreateTable(typeof(T));
252 | //hit the db first, so we get most up-to-date
253 | var entity = LoadInternal(id);
254 | //try the cache just incase hasn't been saved to db yet, but is in session
255 | if ((entity == null) && _entities.ContainsKey(id))
256 | entity = (T)_entities[id];
257 |
258 | if (entity == null)
259 | {
260 | if (_documentStore.Conventions.EntityNotFoundBehavior == EntityNotFoundBehavior.ReturnNull)
261 | {
262 | return default(T);
263 | }
264 |
265 | throw new EntityNotFoundException(id, typeof (T));
266 | }
267 | return entity;
268 | }
269 |
270 | public IEnumerable GetByIds(IEnumerable ids)
271 | {
272 |
273 | GetOrCreateTable(typeof(T));
274 | using (var command = _conn.CreateCommand())
275 | {
276 | command.CommandType = CommandType.Text;
277 | command.CommandText = String.Format(@"SELECT body FROM {0} WHERE id in ({1});", _tableInfo.TableNameWithSchema(typeof(T)), JoinAndCommaSeperateAndSurroundWithSingleQuotes(ids));
278 | Console.WriteLine(command.CommandText);
279 |
280 | List entities = new List();
281 |
282 | using (var reader = command.ExecuteReader())
283 | {
284 | while (reader.Read())
285 | {
286 | T entity = _jsonConverter.Deserialize(reader.GetString(0));
287 | entities.Add(entity);
288 | }
289 | }
290 | return entities;
291 | }
292 | }
293 |
294 | private string JoinAndCommaSeperateAndSurroundWithSingleQuotes(IEnumerable ids)
295 | {
296 | return string.Join(",", ids.Select(n => n.ToString().SurroundWithSingleQuote()).ToArray());
297 | }
298 |
299 | public IEnumerable GetAll()
300 | {
301 | GetOrCreateTable(typeof(T));
302 | using (var command = _conn.CreateCommand())
303 | {
304 | command.CommandType = CommandType.Text;
305 | command.CommandText = String.Format(@"SELECT body FROM {0};", _tableInfo.TableNameWithSchema(typeof(T)));
306 | Console.WriteLine(command.CommandText);
307 |
308 | List entities = new List();
309 |
310 | using (var reader = command.ExecuteReader())
311 | {
312 | while (reader.Read())
313 | {
314 | T entity = _jsonConverter.Deserialize(reader.GetString(0));
315 | entities.Add(entity);
316 | }
317 | }
318 | return entities;
319 | }
320 | }
321 |
322 |
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/Elephanet/Elephanet.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {F6F93A0A-0EB1-47A5-9089-DB6F56369F98}
8 | Library
9 | Properties
10 | Elephanet
11 | Elephanet
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\Jil.2.8.1\lib\net45\Jil.dll
35 |
36 |
37 | ..\packages\Npgsql.2.2.4.3\lib\net45\Mono.Security.dll
38 |
39 |
40 | ..\packages\Npgsql.2.2.4.3\lib\net45\Npgsql.dll
41 |
42 |
43 | ..\packages\Sigil.4.4.0\lib\net45\Sigil.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
101 |
--------------------------------------------------------------------------------
/Elephanet/Elephanet.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Elephanet
5 | $version$
6 | Elephanet
7 | James Kelly
8 | James Kelly
9 | https://github.com/YoloDev/elephanet/blob/master/LICENCE
10 | https://github.com/YoloDev/elephanet
11 | false
12 | A .NET api for PostgreSQLs jsonb that is easy to use
13 | postgresql jsonb npgsql
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Elephanet/EntityException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 | namespace Elephanet
4 | {
5 | public class EntityException : Exception
6 | {
7 | public EntityException()
8 | : this(String.Empty, null) { }
9 |
10 | public EntityException(string message)
11 | : this(message, null) { }
12 |
13 | public EntityException(string message, Exception innerException)
14 | : base(message, innerException) { }
15 |
16 | public EntityException(SerializationInfo info, StreamingContext context)
17 | : base(info, context) { }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Elephanet/EntityNotFoundException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet
4 | {
5 | public class EntityNotFoundException : Exception
6 | {
7 | public EntityNotFoundException(Guid id, Type type)
8 | : base(string.Format("Entity of type {0} with id {1} could not be found.", type, id))
9 | {
10 |
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/Elephanet/Expressions/ExpressionEvaluator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 |
6 |
7 | namespace Elephanet
8 | {
9 | // http://blogs.msdn.com/b/mattwar/archive/2007/08/01/linq-building-an-iqueryable-provider-part-iii.aspx
10 |
11 | public static class ExpressionEvaluator
12 | {
13 | public static Expression EvaluateSubtrees(Expression expression, Func canBeEvaluated)
14 | {
15 | return new SubtreeEvaluator(canBeEvaluated).Evaluate(expression);
16 | }
17 |
18 | public static Expression EvaluateSubtrees(Expression expression)
19 | {
20 | return new SubtreeEvaluator(CanBeEvaluatedLocally).Evaluate(expression);
21 | }
22 |
23 | private static bool CanBeEvaluatedLocally(Expression expression)
24 | {
25 | return expression.NodeType != ExpressionType.Parameter;
26 | }
27 |
28 | private sealed class SubtreeEvaluator : ExpressionVisitor
29 | {
30 | private readonly SubtreeNominator _nominator;
31 | private HashSet _candidates;
32 |
33 | internal SubtreeEvaluator(Func canBeEvaluated)
34 | {
35 | _nominator = new SubtreeNominator(canBeEvaluated);
36 | }
37 |
38 | internal Expression Evaluate(Expression node)
39 | {
40 | _candidates = _nominator.NominateSubtrees(node);
41 |
42 | return Visit(node);
43 | }
44 |
45 | public override Expression Visit(Expression node)
46 | {
47 | if (node == null)
48 | {
49 | return node;
50 | }
51 | else if (_candidates.Contains(node))
52 | {
53 | return TryEvaluateCandidate(node);
54 | }
55 | else
56 | {
57 | return base.Visit(node);
58 | }
59 | }
60 |
61 | private static Expression TryEvaluateCandidate(Expression node)
62 | {
63 | return node.NodeType == ExpressionType.Constant ? node : EvaluateCandidate(node);
64 | }
65 |
66 | private static Expression EvaluateCandidate(Expression node)
67 | {
68 | var lambda = Expression.Lambda(node);
69 |
70 | var function = lambda.Compile();
71 |
72 | return Expression.Constant(function.DynamicInvoke(null), node.Type);
73 | }
74 | }
75 |
76 | private sealed class SubtreeNominator : ExpressionVisitor
77 | {
78 | private readonly Func _canBeEvaluated;
79 | private HashSet _candidates;
80 | private bool _cannotBeEvaluated;
81 |
82 | internal SubtreeNominator(Func canBeEvaluated)
83 | {
84 | _canBeEvaluated = canBeEvaluated;
85 | }
86 |
87 | internal HashSet NominateSubtrees(Expression node)
88 | {
89 | _candidates = new HashSet();
90 |
91 | Visit(node);
92 |
93 | return _candidates;
94 | }
95 |
96 | public override Expression Visit(Expression node)
97 | {
98 | if (node != null)
99 | {
100 | TryNominateSubtree(node);
101 | }
102 |
103 | return node;
104 | }
105 |
106 | private void TryNominateSubtree(Expression node)
107 | {
108 | var priorCannotBeEvaluated = _cannotBeEvaluated;
109 |
110 | _cannotBeEvaluated = false;
111 |
112 | base.Visit(node);
113 |
114 | if (!_cannotBeEvaluated)
115 | {
116 | if (_canBeEvaluated(node))
117 | {
118 | _candidates.Add(node);
119 | }
120 | else
121 | {
122 | _cannotBeEvaluated = true;
123 | }
124 | }
125 |
126 | _cannotBeEvaluated |= priorCannotBeEvaluated;
127 | }
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Elephanet/Expressions/JsonbExpression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 |
6 | namespace Elephanet.Expressions
7 | {
8 |
9 |
10 |
11 | public class JsonbExpression : Expression
12 | {
13 | Expression _jsonbTable;
14 | List _jsonbPaths;
15 |
16 | public JsonbExpression(ExpressionType expressionType, Type type)
17 | : base((ExpressionType)expressionType, type)
18 | {
19 |
20 | }
21 | }
22 |
23 | public class JsonbTableExpression: JsonbExpression
24 | {
25 | private string _name;
26 | public JsonbTableExpression(Type type)
27 | : base((ExpressionType)JsonbExpressionType.JsonbTable, type)
28 | {
29 | _name = string.Format("{0}_{1}", type.Namespace, type.Name);
30 | }
31 |
32 | public string Name { get { return _name; } }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Elephanet/Expressions/JsonbExpressionType.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet.Expressions
2 | {
3 | internal enum JsonbExpressionType
4 | {
5 | JsonbTable = 1000,
6 | JsonbPath,
7 | JsonbProperty
8 |
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Elephanet/Expressions/JsonbExpressionVisitor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using Npgsql;
6 | using System.Data;
7 |
8 | namespace Elephanet.Expressions
9 | {
10 | public class JsonbExpressionVisitor : ExpressionVisitor
11 | {
12 | private NpgsqlCommand _command;
13 |
14 | public JsonbExpressionVisitor()
15 | {
16 | _command = new NpgsqlCommand();
17 | }
18 |
19 | public NpgsqlCommand Command { get { return _command; } }
20 |
21 | protected override Expression VisitExtension(Expression node)
22 | {
23 | var jsonbNode = node as JsonbExpression;
24 | return jsonbNode == null ? node : VisitJsonb(jsonbNode);
25 | }
26 |
27 | protected virtual Expression VisitSql(SqlExpression node)
28 | {
29 | _command = node.Query.Command;
30 | _command.CommandType = CommandType.Text;
31 | return node;
32 | }
33 |
34 | protected virtual Expression VisitJsonb(JsonbExpression node)
35 | {
36 | // switch (node.JsonbType)
37 | // {
38 | // case JsonbType.Type1:
39 | // return VisitJsonbType1((JsonbType1)node);
40 | // case JsonbType.Type2:
41 | // return VisitJsonbType2((JsonbType2)node);
42 | // default:
43 | return node;
44 | // }
45 | }
46 |
47 | // protected virtual Expression VisitJsonbType1(JsonbType1 node)
48 | // {
49 | // Visit node parts
50 | // }
51 |
52 | // protected virtual Expression VisitJsonbType2(JsonbType2 node)
53 | // {
54 | // Visit node parts
55 | // }
56 |
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Elephanet/Expressions/JsonbTable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 |
6 | namespace Elephanet
7 | {
8 | public class JsonbTable
9 | {
10 | private string _name;
11 | public JsonbTable(Type type, Expression expression)
12 | {
13 | _name = string.Format("@0_@1", type.Namespace, type.Name);
14 | }
15 |
16 | public string Name { get { return _name; } }
17 | }
18 |
19 | public class JsonbPath
20 | {
21 | private string _name;
22 | public JsonbPath(string name, Expression expression)
23 | {
24 | _name = name;
25 | }
26 |
27 | public string Name { get { return _name; } }
28 |
29 | }
30 |
31 | public class JsonbValue
32 | {
33 | private string _value;
34 | private Expression _expression;
35 | public JsonbValue(string value, Expression expression)
36 | {
37 | _value = value;
38 | _expression = expression;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Elephanet/Expressions/SelectExpression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 |
6 | namespace Elephanet.Expressions
7 | {
8 | public class SelectExpression : Expression
9 | {
10 | private Expression _from;
11 | private Expression _where;
12 | public SelectExpression(Type type, Expression from, Expression where)
13 | {
14 | _from = from;
15 | _where = where;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Elephanet/Expressions/SqlExpression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using Npgsql;
6 |
7 | namespace Elephanet.Expressions
8 | {
9 | public class SqlExpression : Expression
10 | {
11 | private Sql _query;
12 |
13 | public SqlExpression(Sql query) : base() {
14 | _query = query;
15 | }
16 |
17 | public Sql Query { get { return _query; } }
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Elephanet/Expressions/SqlExpressionType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Elephanet.Expressions
8 | {
9 | internal enum SqlExpressionType
10 | {
11 | SqlQuery
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Elephanet/Extensions/QueryExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Reflection;
6 | using Elephanet.Expressions;
7 | using Npgsql;
8 |
9 | namespace Elephanet
10 | {
11 |
12 | public static class StringExtension
13 | {
14 | public static string ReplaceDotWithUnderscore(this string text)
15 | {
16 | text = text.Replace(".", "_");
17 | return text;
18 | }
19 |
20 | public static string SurroundWith(this string text, string ends)
21 | {
22 | return ends + text + ends;
23 | }
24 |
25 | public static string SurroundWithSingleQuote(this string text)
26 | {
27 | return SurroundWith(text, "'");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Elephanet/IDocumentSession.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Elephanet
5 | {
6 | public interface IDocumentSession : IDisposable
7 | {
8 | void Delete(Guid id);
9 | void Delete(T entity);
10 | T GetById(Guid id);
11 | IEnumerable GetByIds(IEnumerable ids);
12 | IEnumerable GetAll();
13 | IJsonbQueryable Query();
14 | void SaveChanges();
15 | void Store(T entity);
16 | void DeleteAll();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Elephanet/IStore.cs:
--------------------------------------------------------------------------------
1 | namespace Elephanet
2 | {
3 | public interface IDocumentStore
4 | {
5 | IDocumentSession OpenSession();
6 | string ConnectionString { get; }
7 | IStoreConventions Conventions { get; }
8 | IStoreInfo StoreInfo { get; }
9 | void Empty();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Elephanet/IStoreConventions.cs:
--------------------------------------------------------------------------------
1 | using Elephanet.Conventions;
2 | using Elephanet.Serialization;
3 | namespace Elephanet
4 | {
5 | public interface IStoreConventions
6 | {
7 | IJsonConverter JsonConverter { get; }
8 | ITableInfo TableInfo { get; }
9 | EntityNotFoundBehavior EntityNotFoundBehavior { get; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Elephanet/IStoreInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Elephanet
5 | {
6 | public interface IStoreInfo
7 | {
8 | ///
9 | /// Using custom ConcurrentHashSet. This may be a micro optimisation over using ConcurrentDictionary / ConcurrentList
10 | /// which should be switched out at the first sign of issues.
11 | ///
12 | string Name { get; }
13 | ConcurrentHashSet Tables { get; }
14 | void Add(string tableName);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Elephanet/ITableInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet
4 | {
5 | ///
6 | /// Provides the flexibility to inject and control table naming behavior
7 | ///
8 | public interface ITableInfo
9 | {
10 | ///
11 | /// Return the table name for the given Type with its Schema
12 | ///
13 | /// The Type mapped to to the table
14 | /// Table name
15 | string TableNameWithSchema(Type type);
16 |
17 | ///
18 | /// Return the table name for the given Type without its Schema
19 | ///
20 | /// The Type mapped to the table
21 | /// Table name
22 | string TableNameWithoutSchema(Type type);
23 |
24 | ///
25 | /// The PostgreSql schema that all tables will be created in
26 | ///
27 | string Schema { get; }
28 | }
29 | }
--------------------------------------------------------------------------------
/Elephanet/IdentityFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 |
7 | namespace Elephanet
8 | {
9 | internal static class IdentityFactory
10 | {
11 | private static readonly ConcurrentDictionary _keyCache =
12 | new ConcurrentDictionary();
13 |
14 | public static Guid SetEntityId(object value)
15 | {
16 | PropertyInfo propertyInfo;
17 | if (TryGetIdProperty(value, out propertyInfo) == false)
18 | {
19 | throw new EntityException("Entity does not have an ID property of type Guid.");
20 | }
21 |
22 | try
23 | {
24 | var id = (Guid) propertyInfo.GetValue(value, null);
25 | if (id != Guid.Empty)
26 | {
27 | return id;
28 | }
29 |
30 | id = Guid.NewGuid();
31 |
32 | propertyInfo.SetValue(value, id, null);
33 | return id;
34 | }
35 | catch (Exception exception)
36 | {
37 | throw new EntityException("Could not get or set the ID property of the entity.", exception);
38 | }
39 | }
40 |
41 | public static Guid GetEntityId(object value)
42 | {
43 | PropertyInfo propertyInfo;
44 | if (TryGetIdProperty(value, out propertyInfo) == false)
45 | {
46 | throw new EntityException("Entity does not have an ID property of type Guid.");
47 | }
48 |
49 | try
50 | {
51 | return (Guid) propertyInfo.GetValue(value, null);
52 | }
53 | catch (Exception exception)
54 | {
55 | throw new EntityException("Could not get the ID property of the entity.", exception);
56 | }
57 | }
58 |
59 | private static bool TryGetIdProperty(object value, out PropertyInfo propertyInfo)
60 | {
61 | var type = value.GetType();
62 |
63 | propertyInfo = _keyCache.GetOrAdd(type,
64 | typeofValue => typeofValue.GetProperties(BindingFlags.Public | BindingFlags.Instance)
65 | .Where(x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase))
66 | .Where(x => x.PropertyType == typeof (Guid))
67 | .FirstOrDefault(x => x.CanRead && x.CanWrite));
68 |
69 | return (propertyInfo != null);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Elephanet/JsonbQueryProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Reflection;
7 | using Npgsql;
8 | using System.Data;
9 | using System.Text;
10 |
11 | using Elephanet.Expressions;
12 | using Elephanet.Serialization;
13 |
14 | namespace Elephanet
15 | {
16 | public interface IJsonbQueryProvider : IQueryProvider
17 | {
18 | NpgsqlConnection Connection {get;}
19 | IJsonConverter JsonConverter { get; }
20 | }
21 |
22 | public class JsonbQueryProvider : IJsonbQueryProvider
23 | {
24 | private readonly NpgsqlConnection _conn;
25 | private readonly IJsonConverter _jsonConverter;
26 | private QueryTranslator _translator;
27 |
28 | public JsonbQueryProvider(NpgsqlConnection connection, IJsonConverter jsonConverter, ITableInfo tableInfo)
29 | {
30 | _conn = connection;
31 | _jsonConverter = jsonConverter;
32 | _translator = new QueryTranslator(tableInfo);
33 | }
34 |
35 | public NpgsqlConnection Connection { get { return _conn; } }
36 | public IJsonConverter JsonConverter { get { return _jsonConverter; } }
37 |
38 | public object Execute(Expression expression)
39 | {
40 | string sql = _translator.Translate(expression);
41 | using (var command = new NpgsqlCommand(sql,_conn))
42 | {
43 | Type elementType = TypeSystem.GetElementType(expression.Type);
44 | Type listType = typeof(List<>).MakeGenericType(elementType);
45 | var list = (IList)Activator.CreateInstance(listType);
46 |
47 | using (var reader = command.ExecuteReader())
48 | {
49 | while (reader.Read())
50 | {
51 | object entity = _jsonConverter.Deserialize(reader.GetString(0), elementType);
52 | list.Add(entity);
53 | }
54 | }
55 |
56 | return list;
57 |
58 | }
59 |
60 | }
61 |
62 | T IQueryProvider.Execute(Expression expression)
63 | {
64 | return (T)Execute(expression);
65 | }
66 |
67 | object IQueryProvider.Execute(Expression expression)
68 | {
69 | return Execute(expression);
70 | }
71 |
72 |
73 | IQueryable IQueryProvider.CreateQuery(Expression expression)
74 | {
75 | return new JsonbQueryable(this, expression);
76 | }
77 |
78 | IQueryable IQueryProvider.CreateQuery(Expression expression)
79 | {
80 | Type elementType = TypeSystem.GetElementType(expression.Type);
81 | try
82 | {
83 | return (IJsonbQueryable)Activator.CreateInstance(typeof(JsonbQueryable<>).MakeGenericType(elementType), new object[] { this, expression });
84 | }
85 | catch (TargetInvocationException tie)
86 | {
87 | throw tie.InnerException;
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Elephanet/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YoloDev/elephanet/3816548ceb7342391b8611d3da34330089452936/Elephanet/NuGet.exe
--------------------------------------------------------------------------------
/Elephanet/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Elephanet")]
9 | [assembly: AssemblyDescription("A .NET api to PostgreSQL's jsonb that's easy to use")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("James Kelly")]
12 | [assembly: AssemblyProduct("Elephanet")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("65b6d608-9e5f-4eed-b765-ad6191d65bcb")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.1.2")]
36 | [assembly: AssemblyFileVersion("0.1.2")]
37 |
--------------------------------------------------------------------------------
/Elephanet/QueryTranslator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Reflection;
6 | using System.Text;
7 |
8 | namespace Elephanet
9 | {
10 |
11 |
12 | public class QueryTranslator : ExpressionVisitor
13 | {
14 | const string columnName = "body";
15 | private StringBuilder _sb;
16 | private ITableInfo _tableInfo;
17 |
18 | private Expression _where;
19 | private StringBuilder _limit;
20 | private StringBuilder _offset;
21 | private StringBuilder _orderBy;
22 |
23 | public QueryTranslator(ITableInfo tableInfo)
24 | {
25 | _sb = new StringBuilder();
26 | _limit = new StringBuilder();
27 | _offset = new StringBuilder();
28 | _orderBy = new StringBuilder();
29 | _tableInfo = tableInfo;
30 | }
31 |
32 | public string Translate(Expression expression)
33 | {
34 | var inlined = ExpressionEvaluator.EvaluateSubtrees(expression);
35 | Visit(inlined);
36 |
37 | _sb.Append(_orderBy);
38 | _sb.Append(_limit);
39 | _sb.Append(_offset);
40 | _sb.Append(";");
41 | Console.WriteLine(_sb.ToString());
42 | return _sb.ToString();
43 | }
44 |
45 | private static Expression StripQuotes(Expression e)
46 | {
47 | while (e.NodeType == ExpressionType.Quote)
48 | {
49 | e = ((UnaryExpression)e).Operand;
50 | }
51 | return e;
52 | }
53 |
54 | protected override Expression VisitMethodCall(MethodCallExpression node)
55 | {
56 | if (node.Method.DeclaringType == typeof(Queryable))
57 | {
58 | switch (node.Method.Name)
59 | {
60 | case "Where":
61 | {
62 | Type elementType = TypeSystem.GetElementType(node.Type);
63 | _sb.Append(string.Format("select {0} from {1} where {0} ", columnName, _tableInfo.TableNameWithSchema(elementType)));
64 | //Visit(node.Arguments[0]);
65 | LambdaExpression lambda = (LambdaExpression)StripQuotes(node.Arguments[1]);
66 | Visit(lambda.Body);
67 | return node;
68 | }
69 | case "Take":
70 | {
71 | _limit.Append(" limit ");
72 | VisitLimit((ConstantExpression)node.Arguments[1]);
73 | VisitMethodCall((MethodCallExpression)node.Arguments[0]);
74 | return node;
75 | }
76 | case "Skip":
77 | {
78 | _offset.Append(" offset ");
79 | VisitOffset((ConstantExpression)node.Arguments[1]);
80 | VisitMethodCall((MethodCallExpression)node.Arguments[0]);
81 | return node;
82 |
83 | }
84 | case "OrderBy":
85 | {
86 | Type elementType = TypeSystem.GetElementType(node.Type);
87 | LambdaExpression lambda = (LambdaExpression)StripQuotes(node.Arguments[1]);
88 | VisitOrderBy((MemberExpression)lambda.Body);
89 | VisitMethodCall((MethodCallExpression)node.Arguments[0]);
90 | return node;
91 | }
92 | case "OrderByDescending":
93 | {
94 | Type elementType = TypeSystem.GetElementType(node.Type);
95 | LambdaExpression lambda = (LambdaExpression)StripQuotes(node.Arguments[1]);
96 | VisitOrderByDesc((MemberExpression)lambda.Body);
97 | VisitMethodCall((MethodCallExpression)node.Arguments[0]);
98 | return node;
99 |
100 | }
101 |
102 |
103 | }
104 | }
105 | throw new NotSupportedException(string.Format("The method '{0}' is not supported", node.Method.Name));
106 | }
107 |
108 | private Expression VisitOrderByDesc(MemberExpression node)
109 | {
110 | if (node.Expression != null && node.Expression.NodeType == ExpressionType.Parameter)
111 | {
112 | _orderBy.Append(string.Format(" order by body->>'{0}' desc", node.Member.Name));
113 | return node;
114 | }
115 |
116 | throw new NotSupportedException(string.Format("The member '{0}' is not supported from the OrderByDescending operator", node.Member.Name));
117 | }
118 |
119 | protected override Expression VisitBinary(BinaryExpression node)
120 | {
121 | switch (node.NodeType) {
122 | case ExpressionType.Equal:
123 | _sb.Append("@>");
124 | break;
125 | default:
126 | throw new NotSupportedException(string.Format("The operator '{0}' is not yet supported", node.NodeType));
127 | }
128 | //wrap up values in json
129 | _sb.Append("'{");
130 | Visit(node.Left);
131 | _sb.Append(":");
132 | Visit(node.Right);
133 | _sb.Append("}'");
134 |
135 | return node;
136 | }
137 |
138 | protected override Expression VisitConstant(ConstantExpression node)
139 | {
140 | _sb.Append(string.Format("\"{0}\"",node.Value));
141 | return node;
142 | }
143 |
144 | private Expression VisitLimit(ConstantExpression node)
145 | {
146 | _limit.Append(string.Format("{0}",node.Value));
147 | return node;
148 | }
149 |
150 |
151 | private Expression VisitOffset(ConstantExpression node)
152 | {
153 | _offset.Append(string.Format("{0}", node.Value));
154 | return node;
155 | }
156 |
157 | private Expression VisitOrderBy(MemberExpression node)
158 | {
159 | if (node.Expression != null && node.Expression.NodeType == ExpressionType.Parameter)
160 | {
161 | _orderBy.Append(string.Format(" order by body->>'{0}'", node.Member.Name));
162 | return node;
163 | }
164 |
165 | throw new NotSupportedException(string.Format("The member '{0}' is not supported", node.Member.Name));
166 |
167 | }
168 |
169 | protected override Expression VisitMember(MemberExpression node)
170 | {
171 | if (node.Expression != null && node.Expression.NodeType == ExpressionType.Parameter)
172 | {
173 | _sb.Append(string.Format("\"{0}\"", node.Member.Name));
174 | return node;
175 | }
176 |
177 |
178 |
179 | throw new NotSupportedException(string.Format("The member '{0}' is not supported", node.Member.Name));
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/Elephanet/Queryable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 |
7 |
8 | namespace Elephanet
9 | {
10 |
11 | public interface IJsonbQueryable: IOrderedQueryable
12 | {
13 | }
14 |
15 | public interface IJsonbQueryable: IOrderedQueryable
16 | {
17 | }
18 |
19 | public class JsonbQueryable : IJsonbQueryable
20 | {
21 | IJsonbQueryProvider _provider;
22 | Expression _expression;
23 |
24 | public JsonbQueryable(IJsonbQueryProvider provider) {
25 | if (provider == null) {
26 | throw new ArgumentNullException("provider");
27 | }
28 | _provider = provider;
29 | _expression = Expression.Constant(this);
30 | }
31 |
32 | public JsonbQueryable(IJsonbQueryProvider provider, Expression expression) {
33 | if (provider == null) {
34 | throw new ArgumentNullException("provider");
35 | }
36 | if (expression == null) {
37 | throw new ArgumentNullException("expression");
38 | }
39 | if (!typeof(IQueryable).IsAssignableFrom(expression.Type)) {
40 | throw new ArgumentOutOfRangeException("expression");
41 | }
42 | _provider = provider;
43 | _expression = expression;
44 | }
45 |
46 | Expression IQueryable.Expression {
47 | get { return _expression; }
48 | }
49 |
50 | Type IQueryable.ElementType {
51 | get { return typeof(T); }
52 | }
53 |
54 | public IEnumerator GetEnumerator() {
55 | return ((IEnumerable)_provider.Execute(_expression)).GetEnumerator();
56 | }
57 |
58 | IEnumerator IEnumerable.GetEnumerator() {
59 | return ((IEnumerable)_provider.Execute(_expression)).GetEnumerator();
60 | }
61 |
62 | IQueryProvider IQueryable.Provider
63 | {
64 | get { return (IJsonbQueryProvider)_provider; }
65 | }
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/Elephanet/Serialization/IJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet.Serialization
4 | {
5 | public interface IJsonConverter
6 | {
7 | string Serialize(T entity);
8 | T Deserialize(string json);
9 | object Deserialize(string json);
10 | object Deserialize(string json, Type type);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Elephanet/Serialization/JilJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using Jil;
2 |
3 | namespace Elephanet.Serialization
4 | {
5 | public class JilJsonConverter : IJsonConverter
6 | {
7 | private readonly Options _options;
8 | public JilJsonConverter()
9 | {
10 | _options = new Options(includeInherited: true, dateFormat:DateTimeFormat.ISO8601);
11 | }
12 |
13 | public string Serialize(T entity)
14 | {
15 |
16 | return JSON.Serialize(entity,_options);
17 | }
18 |
19 | public T Deserialize(string json)
20 | {
21 | return JSON.Deserialize(json,_options);
22 | }
23 |
24 | public object Deserialize(string json)
25 | {
26 | return JSON.DeserializeDynamic(json, _options);
27 | }
28 |
29 | public object Deserialize(string json, System.Type type)
30 | {
31 | return JSON.Deserialize(json, type,_options);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Elephanet/Sql.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Npgsql;
5 | using System.Text;
6 | using System.Text.RegularExpressions;
7 | using System.Globalization;
8 | using System.Threading;
9 |
10 | namespace Elephanet
11 | {
12 |
13 | public class Sql
14 | {
15 | private NpgsqlCommand _command;
16 | public Sql(string query, object[] parameters)
17 | {
18 | _command = new NpgsqlCommand();
19 |
20 | _command.CommandText = String.Format(@"SELECT body FROM public.{0}_{1} WHERE body @> {2}", typeof(T).Namespace.ReplaceDotWithUnderscore(), typeof(T).Name, query);
21 | foreach (var entry in MatchParameters(query, parameters))
22 | {
23 | string json = ConvertKeyValueToJson(entry);
24 | _command.Parameters.Add(new NpgsqlParameter(entry.Key,json));
25 | }
26 | }
27 |
28 | private string ConvertKeyValueToJson(KeyValuePair entry)
29 | {
30 | CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;
31 | TextInfo textInfo = cultureInfo.TextInfo;
32 | return string.Format("{{\"{0}\":\"{1}\"}}", textInfo.ToTitleCase(entry.Key.Substring(1)), entry.Value);
33 | }
34 |
35 | private Dictionary MatchParameters(string query, object[] parameters)
36 | {
37 | var matches = new Dictionary();
38 | int counter = 0;
39 | foreach (Match match in Regex.Matches(query,(@"(? MatchParameters(string query, object[] parameters)
67 | {
68 | var matches = new Dictionary();
69 | int counter = 0;
70 | foreach (Match match in Regex.Matches(query,(@"(? _tableNames;
15 |
16 | public DocumentStore(string connectionString)
17 | {
18 | _connectionString = connectionString;
19 | _conventions = new StoreConventions();
20 | _storeInfo = new StoreInfo();;
21 | _tableNames = new List();
22 | }
23 |
24 | public DocumentStore(string connectionString, IStoreConventions conventions)
25 | {
26 | _connectionString = connectionString;
27 | _conventions = conventions;
28 | _storeInfo = new StoreInfo();
29 | }
30 |
31 | public DocumentStore(string connectionString, IStoreConventions conventions, IStoreInfo storeInfo)
32 | {
33 | _connectionString = connectionString;
34 | _conventions = conventions;
35 | _storeInfo = storeInfo;
36 | }
37 |
38 | public DocumentStore(string connectionString, IStoreInfo storeInfo)
39 | {
40 | _connectionString = connectionString;
41 | _conventions = new StoreConventions();
42 | _storeInfo = storeInfo;
43 | }
44 |
45 | public IStoreConventions Conventions { get { return _conventions; }}
46 |
47 | public List TableNames { get {return _tableNames;} }
48 |
49 | public IDocumentSession OpenSession()
50 | {
51 | return new DocumentSession(this);
52 | }
53 |
54 | public string ConnectionString
55 | {
56 | get { return _connectionString; }
57 | }
58 |
59 | public IStoreInfo StoreInfo { get {return _storeInfo;}}
60 |
61 |
62 | public void Destroy()
63 | {
64 | var connection = new NpgsqlConnection(_connectionString);
65 | try
66 | {
67 | connection.Open();
68 | foreach (var tablename in StoreInfo.Tables)
69 | {
70 | using (var command = connection.CreateCommand())
71 | {
72 | command.CommandType = CommandType.Text;
73 | command.CommandText = String.Format(@"drop table {0};", tablename);
74 | command.ExecuteNonQuery();
75 | }
76 | }
77 | }
78 |
79 | catch (NpgsqlException exception)
80 | {
81 | throw new Exception(String.Format("Could not drop table {0}; see the inner exception for more information.", _storeInfo.Name), exception);
82 | }
83 |
84 | finally
85 | {
86 | connection.Dispose();
87 | }
88 | }
89 |
90 | public void Empty()
91 | {
92 | var connection = new NpgsqlConnection(_connectionString);
93 | try
94 | {
95 | connection.Open();
96 | foreach (var tablename in StoreInfo.Tables)
97 | {
98 | using (var command = connection.CreateCommand())
99 | {
100 | command.CommandType = CommandType.Text;
101 | command.CommandText = String.Format(@"delete from {0};", tablename);
102 | command.ExecuteNonQuery();
103 | }
104 | }
105 | }
106 |
107 | catch (NpgsqlException exception)
108 | {
109 | throw new Exception(String.Format("Could not delete all from table {0}; see the inner exception for more information.", _storeInfo.Name), exception);
110 | }
111 |
112 | finally
113 | {
114 | connection.Dispose();
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Elephanet/StoreConventions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Elephanet;
3 | using Elephanet.Conventions;
4 | using Elephanet.Serialization;
5 |
6 | namespace Elephanet
7 | {
8 | public class StoreConventions : IStoreConventions
9 | {
10 | IJsonConverter _jsonConverter;
11 | private ITableInfo _tableInfo;
12 | private EntityNotFoundBehavior _entityNotFoundBehavior = EntityNotFoundBehavior.Throw;
13 |
14 | public StoreConventions()
15 | {
16 | _jsonConverter = new JilJsonConverter();
17 | _tableInfo = new TableInfo();
18 | }
19 |
20 | public StoreConventions(IJsonConverter jsonConverter)
21 | {
22 | _jsonConverter = jsonConverter;
23 | _tableInfo = new TableInfo();
24 | }
25 |
26 | public StoreConventions(IJsonConverter jsonConverter, ITableInfo tableInfo)
27 | {
28 | _jsonConverter = jsonConverter;
29 | _tableInfo = tableInfo;
30 | }
31 |
32 | public IJsonConverter JsonConverter
33 | {
34 | get { return _jsonConverter; }
35 | }
36 |
37 | public ITableInfo TableInfo
38 | {
39 | get { return _tableInfo; }
40 | }
41 |
42 | public EntityNotFoundBehavior EntityNotFoundBehavior { get {return _entityNotFoundBehavior;} }
43 |
44 | ///
45 | /// Behavior of DocumentSession.GetById when the Entity is not found.
46 | ///
47 | ///
48 | public void SetEntityNotFoundBehavior(EntityNotFoundBehavior behavior)
49 | {
50 | _entityNotFoundBehavior = behavior;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Elephanet/StoreInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Elephanet
5 | {
6 | public class StoreInfo : IStoreInfo
7 | {
8 | private string _name;
9 | readonly ConcurrentHashSet _tableNames;
10 | public StoreInfo()
11 | {
12 | _name = "store";
13 | _tableNames = new ConcurrentHashSet();
14 | }
15 |
16 | public ConcurrentHashSet Tables { get { return _tableNames; } }
17 |
18 | public void Add(string tableName)
19 | {
20 | _tableNames.Add(tableName);
21 | }
22 |
23 | public StoreInfo(string storeName)
24 | {
25 | _name = storeName;
26 | }
27 |
28 | public string Name { get { return _name; } }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Elephanet/StringHelpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 |
6 | namespace Elephanet.Helpers
7 | {
8 | public static class StringHelpers
9 | {
10 | public static string EscapeQuotes(this string text)
11 | {
12 | return text.Replace("'", "''");
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Elephanet/TableInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Elephanet
4 | {
5 | public class TableInfo : ITableInfo
6 | {
7 | private string _schema;
8 |
9 | public TableInfo()
10 | {
11 | _schema = "public";
12 | }
13 |
14 | public TableInfo(string schema)
15 | {
16 | _schema = schema;
17 | }
18 |
19 | public string TableNameWithSchema(Type type)
20 | {
21 | return String.Format("{0}.{1}_{2}", _schema, type.Namespace.ReplaceDotWithUnderscore().ToLower(), type.Name.ToLower());
22 | }
23 |
24 | public string TableNameWithoutSchema(Type type)
25 | {
26 | return String.Format("{0}_{1}", type.Namespace.ReplaceDotWithUnderscore().ToLower(), type.Name.ToLower());
27 | }
28 |
29 | public string Schema
30 | {
31 | get
32 | {
33 | return _schema;
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Elephanet/TypeHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Elephanet
8 | {
9 | internal static class TypeSystem
10 | {
11 | internal static Type GetElementType(Type seqType)
12 | {
13 | Type ienum = FindIEnumerable(seqType);
14 | if (ienum == null) return seqType;
15 | return ienum.GetGenericArguments()[0];
16 | }
17 | private static Type FindIEnumerable(Type seqType)
18 | {
19 | if (seqType == null || seqType == typeof(string))
20 | return null;
21 | if (seqType.IsArray)
22 | return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
23 | if (seqType.IsGenericType)
24 | {
25 | foreach (Type arg in seqType.GetGenericArguments())
26 | {
27 | Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
28 | if (ienum.IsAssignableFrom(seqType))
29 | {
30 | return ienum;
31 | }
32 | }
33 | }
34 | Type[] ifaces = seqType.GetInterfaces();
35 | if (ifaces != null && ifaces.Length > 0)
36 | {
37 | foreach (Type iface in ifaces)
38 | {
39 | Type ienum = FindIEnumerable(iface);
40 | if (ienum != null) return ienum;
41 | }
42 | }
43 | if (seqType.BaseType != null && seqType.BaseType != typeof(object))
44 | {
45 | return FindIEnumerable(seqType.BaseType);
46 | }
47 | return null;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Elephanet/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 James Kelly
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/create_store.bat:
--------------------------------------------------------------------------------
1 | "C:\Program Files\PostgreSQL\9.4\bin\psql.exe" -f create_store.sql -U postgres
2 |
--------------------------------------------------------------------------------
/create_store.sql:
--------------------------------------------------------------------------------
1 | CREATE USER store_user with PASSWORD 'my super secret password';
2 | CREATE DATABASE store;
3 | GRANT ALL PRIVILEGES ON DATABASE store to store_user;
4 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | cd Elephanet && nuget pack Elephanet.nuspec -Version $VERSION -IncludeReferencedProjects -Prop Configuration=Release && nuget push *.nupkg $NUGET_API_KEY -verbosity detailed
2 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ##Please Note##
2 |
3 | In the interest in not wasting peoples time, if you are considering using elephanet, please take a look at http://jasperfx.github.io/marten/ as it's far more complete that this project, with a far more active
4 | contributer base, yet it follows many of the same goals.
5 |
6 | [](https://travis-ci.org/YoloDev/elephanet)
7 | ##Elephanet - A .NET document database built on PostgreSQL.##
8 |
9 | ###With an api thats easy to use###
10 |
11 | A document db api backed by Postgresql.
12 |
13 | Heavily influenced by the RavenDb .NET client, this libary provides a simple api to allow easy use of postgres as a document store, taking advantage of Postgresql 9.4 and its new jsonb indexing, allowing for fast querying of native json objects.
14 |
15 | ###Quick Start
16 |
17 | For Windows
18 |
19 | 1. Install Postgresql > 9.4 (available at http://www.postgresql.org/download/windows/). When you do so, pay particular attention to your postgres user password (you will need this in the next step)
20 | 2. Clone (or fork) this repository
21 | 3. Alter create_store.sql file and replace "my super secret password" with your own password
22 | 4. Run create_store.bat from within a cmd prompt.
23 |
24 | For Ubuntu (and likely other Debian based distros)
25 |
26 | 1. Install Postgresql via apt-get. Make sure it is greater than version 9.4
27 | 2. Install Mono (http://www.mono-project.com/docs/getting-started/install/linux/)
28 | 3. Clone (or fork) this repository
29 | 4. Alter create_store.sql file and replace "my super secret password" with your own password
30 | 5. run `psql -f create_store.sql -U postgres`
31 |
32 | ###Got Questions?
33 |
34 | - https://jabbr.net/#/rooms/elephanet (chat)
35 | - https://groups.google.com/d/forum/elephanet (forum)
36 |
37 |
38 |
39 | ###Example Code
40 |
41 | ```
42 | using System;
43 |
44 | public class Car
45 | {
46 | public Guid Id {get;set;}
47 | public string Make {get;set;}
48 | public string Model {get;set;}
49 | public string ImageUrl {get;set;}
50 | public string NumberPlate {get;set;}
51 | }
52 | ```
53 |
54 | ```
55 | //create the datastore
56 | DocumentStore store = new DocumentStore("Server=127.0.0.1;Port=5432;User Id=store_user;password=my super secret password;database=store;");
57 |
58 |
59 | //create the object
60 | var myAudi = new Car {
61 | Id = Guid.NewGuid(),
62 | Make = "Audi",
63 | Model = "A8",
64 | ImageUrl = "http://some_image_url",
65 | NumberPlate = "ABC029"
66 | };
67 |
68 | //save the object to the document store
69 | using (var session = store.OpenSession())
70 | {
71 | session.Store(myAudi);
72 | session.SaveChanges();
73 | }
74 |
75 | //get the same car back out of the document store
76 | using (var session = store.OpenSession())
77 | {
78 | var car = session.GetById(myAudi.Id);
79 | }
80 |
81 |
82 | //create a couple of other cars
83 | var myFord = new Car {
84 | Id = Guid.NewGuid(),
85 | Make = "Ford",
86 | Model = "Mustang",
87 | ImageUrl = "http://some_image_url",
88 | NumberPlate = "XYZ999"
89 | };
90 |
91 | var myOldAudi {
92 | Id = Guid.NewGuid(),
93 | Make = "Audi",
94 | Model = "A5",
95 | ImageUrl = "http://some_image_url",
96 | NumberPlate = "ABC002"
97 | };
98 |
99 | //store these other cars
100 | using (var session = store.OpenSession())
101 | {
102 | session.Store(myOldAudi);
103 | session.Store(myFord);
104 | session.SaveChanges();
105 | }
106 |
107 | //update existing object
108 | using (var session = store.OpenSession())
109 | {
110 | var audi = session.GetById(myOldAudi.Id);
111 | audi.Image_Url = "http://some_new_url";
112 |
113 | session.Store(audi);
114 | session.SaveChanges();
115 | }
116 |
117 | //query by make
118 | using (var session = store.OpenSession())
119 | {
120 | var audis = session.Query().Where(c => c.Make == "Audi").ToList();
121 | }
122 |
123 | //delete
124 | using (var session = store.OpenSession())
125 | {
126 | session.Delete(myOldAudi.Id);
127 | }
128 |
129 | //delete all of a particular type
130 | using (var session = store.OpenSession())
131 | {
132 | session.DeleteAll();
133 | }
134 |
135 | ```
136 |
137 | ###Currently implemented###
138 |
139 | * You can ```session.Store(T entity)```
140 | * You can ```session.SaveChanges();```
141 | * You can ```session.GetById(Guid id)```
142 | * You can ```session.GetByIds(IEnumerable ids)```
143 | * You can ```session.Delete(Guid id)```
144 | * You can ```session.GetAll();```
145 | * You can ```session.DeleteAll();```
146 | * You can ```session.Query(x => x.SomeAttribute == "some value").ToList();```
147 | * You can ```session.Query(x => x.SomeAttribute == "some value").Take(10).Skip(5);```
148 | * You can ```session.Query(x => x.SomeAttribute == "some value").OrderBy(c => c.SomeOtherAttibute);```
149 | * You can ```session.Query(x => x.SomeAttribute == "some value").OrderByDescending(c => c.SomeOtherAttibute);```
150 |
151 | ###Things of note:
152 |
153 | * You can implement your own custom json serialization (Jil is internalised by default)
154 | * Store() is a unit of work stored in memory. SaveChanges() flushes the in memory values to the database
155 |
--------------------------------------------------------------------------------
/release-notes.md:
--------------------------------------------------------------------------------
1 | ###v0.3.10 - 2015-09-15
2 |
3 | ####Improvements
4 | - tidy up IdentityFactory
5 |
6 | ###v0.3.9 - 2015-09-14
7 |
8 | ####Improvements
9 | - Improvements for thread safety, including running tests in parallel and general test tidyup
10 |
11 | ###v0.3.8 - 2015-09-09
12 |
13 | ####Improvements
14 | - GetById behavior (throw vs. returnnull) is now configurable via StoreConventions
15 |
16 | ###v0.3.7 - 2015-08-31
17 |
18 | ####Improvements
19 | - Modify the serializer to serialize dates to ISO8601 by default fixing https://github.com/YoloDev/elephanet/issues/19
20 |
21 | ###v0.3.6 - 2015-08-27
22 |
23 | ####Improvements
24 | - Modify the serlializer to serialize inherit properties
25 |
26 | ###v0.3.5 - 2015-08-24
27 |
28 | ####Improvements
29 | - Update time to timestamp (incorrectly implemented in past release)
30 |
31 | ###v0.3.4 - 2015-08-24
32 |
33 | ####Improvements
34 | - Create time by default at UTC, picked up by Frank Wise (https://github.com/fwise)
35 |
36 | ###v0.3.3 - 2015-08-24
37 |
38 | ####Improvements
39 | - Extract ITableInfo and inject into StoreConventions to allow overriding of table naming conventions with thanks to Frank Wise (https://github.com/fwise)
40 |
41 | ###v0.3.2 - 2015-08-23
42 | ####New Features
43 | - Add sql script for easy creation of data store database
44 | - Add bat file for running on windows to run sql script previously mentioned
45 |
46 | ####Improvements
47 | - Updated readme
48 |
49 | ###v0.3.1 - 2015-04-22
50 | ####New Features
51 | - Add support for .OrderBy() and .OrderByDescending()
52 | - Add support for .Skip() and .Take()
53 | - Add release-notes.md
54 |
55 | ####Improvements
56 | - Updated readme
57 |
--------------------------------------------------------------------------------