├── .gitignore ├── .paket └── paket.bootstrapper.exe ├── README.md ├── SqlDoc.sln ├── SqlDoc ├── AssemblyInfo.fs ├── SqlDoc.fs ├── SqlDoc.fsproj └── paket.references ├── SqlDocCs ├── IDocumentSession.cs ├── Operation.cs ├── Properties │ └── AssemblyInfo.cs ├── QueryFor.cs ├── SqlDocCs.csproj ├── UnitOfWork.cs └── paket.references ├── Tests ├── 001-create.sql ├── App.config ├── Tests.RunningScripts.fs ├── Tests.WorkingWithDocuments.fs ├── Tests.fsproj └── paket.references ├── TestsCs ├── DocumentSessionAPITests.cs ├── Givn.cs ├── Properties │ └── AssemblyInfo.cs ├── TestsCs.csproj ├── WorkingWithDocumentsTests.cs └── paket.references ├── a.txt ├── b.txt ├── build.fsx ├── build.sh ├── paket.dependencies └── paket.lock /.gitignore: -------------------------------------------------------------------------------- 1 | paket.exe 2 | bin/ 3 | obj/ 4 | .vs/ 5 | packages/ 6 | 7 | .fake/ 8 | -------------------------------------------------------------------------------- /.paket/paket.bootstrapper.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liammclennan/SqlDoc/20d8d74532d881894a6e23d600a9433bd486c076/.paket/paket.bootstrapper.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SqlDoc 2 | =========== 3 | 4 | SqlDoc is a unit of work + document database on Postgresql (JSON), [Sql Server](https://github.com/liammclennan/SqlDoc/wiki/SQL-Server-Support) (XML) and [Sql Server 2016](http://withouttheloop.com/articles/2016-11-12-working-with-documents/) (JSON). There are [many reasons why Postgres makes a good document store](http://withouttheloop.com/articles/2014-09-30-postgresql-nosql/) including speed, stability, ecosystem, ACID transactions, mixing with relational data and joins. 5 | 6 | As your program runs, record a series of data updates (the unit of work). At the end of the unit of work persist all the changes in a transaction. Changes can be inserts, updates or deletes. PostgresDoc also provides a querying API. 7 | 8 | PostgresDoc is written in F# but provides APIs for F# and C#. The C# version simply translates to the F# API. 9 | 10 | Unit of Work API 11 | ---------------- 12 | 13 | ### CSharp 14 | 15 | The CSharp API uses a variation of the IDocumentSession API from RavenDB and Marten. 16 | 17 | public class DocumentSessionAPITests 18 | { 19 | [Fact] 20 | public void ICanAddADocumentAndReadItBack() 21 | { 22 | Giv.n(IAddADocument); 23 | Th.n(ICanReadItBack); 24 | } 25 | 26 | [Fact] 27 | public void ICanAddADocumentAndDeleteItAndItsGone() 28 | { 29 | Wh.n(IAddADocument) 30 | .And(IDeleteTheDocument); 31 | Th.n(TheDocumentIsGone); 32 | } 33 | 34 | [Fact] 35 | public void ICanAddADocumentAndUpdateItAndTheChangesPersist() 36 | { 37 | Wh.n(IAddADocument) 38 | .And(IUpdateTheDocument); 39 | Th.n(TheChangePersists); 40 | } 41 | 42 | private void IAddADocument() 43 | { 44 | _aDocument = new PersonCs 45 | { 46 | _id = Guid.NewGuid(), 47 | Name = "Docsesh", 48 | Age = 90, 49 | FavouriteThings = new[] { "Golf", "Statue of liberty" } 50 | }; 51 | _documentSession.Store(_aDocument._id, _aDocument); 52 | _documentSession.SaveChanges(); 53 | } 54 | 55 | private void ICanReadItBack() 56 | { 57 | var fresh = _documentSession.Load(_aDocument._id); 58 | Assert.True(_aDocument.Equals(fresh)); 59 | } 60 | 61 | private void IUpdateTheDocument() 62 | { 63 | _aDocument.Age += 1; 64 | _documentSession.Update(_aDocument._id, _aDocument); 65 | _documentSession.SaveChanges(); 66 | } 67 | 68 | private void TheChangePersists() 69 | { 70 | var fresh = _documentSession.Load(_aDocument._id); 71 | Assert.Equal(91, fresh.Age); 72 | } 73 | 74 | private void IDeleteTheDocument() 75 | { 76 | _documentSession.Delete(_aDocument._id, _aDocument); 77 | _documentSession.SaveChanges(); 78 | } 79 | 80 | private void TheDocumentIsGone() 81 | { 82 | var result = _documentSession.Query( 83 | "select data from PersonCs where Data.value('(/FsPickler/value/instance/idkBackingField)[1]', 'uniqueidentifier') = @id", 84 | new Dictionary { { "id", _aDocument._id } }); 85 | Assert.Empty(result); 86 | } 87 | 88 | private IDocumentSession _documentSession = 89 | new DocumentSession(SqlConnection.From(ConfigurationManager.AppSettings["ConnSql"])); 90 | private PersonCs _aDocument; 91 | } 92 | 93 | ### FSharp 94 | 95 | type Person = 96 | { _id: System.Guid; age: int; name: string } 97 | 98 | let store = { connString = "Server=127.0.0.1;Port=5432;User Id=*******;Password=*****;Database=testo;" } 99 | 100 | let julio = { _id = System.Guid.NewGuid(); age = 30; name = "Julio" } 101 | let timmy = { _id = System.Guid.NewGuid(); age = 3; name = "Timmy" } 102 | 103 | // newer operations are prepended 104 | let uow = [ 105 | delete timmy._id timmy; 106 | update julio._id { julio with age = 31 }; 107 | insert julio._id julio; 108 | insert timmy._id timmy; 109 | ] 110 | commit store uow 111 | 112 | #### Querying 113 | 114 | let peopleWhoAreThirty = 115 | [ "age", box (30) ] 116 | |> select store "select data from people where data->>'age' = :age" 117 | 118 | Expected Schema 119 | --------------- 120 | 121 | The database table should have the same name as the type, an `id` column matching the type used for identifiers, and a json or jsonb `data` column. The table name should be lowercase. 122 | 123 | In the example above I have used `Guid` (`uuid`) identifiers and a type called `Person` so: 124 | 125 | ### Postgres 126 | 127 | ``` 128 | create table "person" ( 129 | id uuid NOT NULL PRIMARY KEY, 130 | data json NOT NULL 131 | ); 132 | ``` 133 | 134 | ### Sql Server (xml) 135 | 136 | ``` 137 | CREATE TABLE [dbo].[person]( 138 | [Id] [uniqueidentifier] NOT NULL, 139 | [Data] [xml] NOT NULL, 140 | CONSTRAINT [PK_person] PRIMARY KEY CLUSTERED 141 | ( 142 | [Id] ASC 143 | ) 144 | ``` 145 | 146 | ### Sql Server (json) 147 | 148 | ``` 149 | CREATE TABLE [dbo].[person]( 150 | [Id] [uniqueidentifier] NOT NULL, 151 | [Data] [nvarchar](max) NOT NULL, 152 | CONSTRAINT [PK_person] PRIMARY KEY CLUSTERED 153 | ( 154 | [Id] ASC 155 | ) 156 | ``` 157 | 158 | Development Instructions 159 | ====================== 160 | 161 | 1. [Install Paket](https://fsprojects.github.io/Paket/installation.html) 162 | 1. Build 163 | 1. Create Postgres, old Sql Server and Sql Server >= 2016 databases matching the connection strings in `Tests/app.config`. 164 | 1. Run the tests 165 | -------------------------------------------------------------------------------- /SqlDoc.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SqlDoc", "SqlDoc\SqlDoc.fsproj", "{59985C6C-6981-451F-BD2D-1EF6768BAB02}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CB365F19-2C68-45C6-BAC7-A92262F581C2}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | build.fsx = build.fsx 12 | build.sh = build.sh 13 | paket.dependencies = paket.dependencies 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "Tests\Tests.fsproj", "{A1738C53-4AC7-4BE6-95A7-F28036336164}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlDocCs", "SqlDocCs\SqlDocCs.csproj", "{4C189715-53A0-45AD-AFB1-A704DF6BCB20}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsCs", "TestsCs\TestsCs.csproj", "{451F2A2B-BE7C-401E-8652-10E92C9C5CB4}" 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|Any CPU = Debug|Any CPU 26 | Release|Any CPU = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {59985C6C-6981-451F-BD2D-1EF6768BAB02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {59985C6C-6981-451F-BD2D-1EF6768BAB02}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {59985C6C-6981-451F-BD2D-1EF6768BAB02}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {59985C6C-6981-451F-BD2D-1EF6768BAB02}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {A1738C53-4AC7-4BE6-95A7-F28036336164}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {A1738C53-4AC7-4BE6-95A7-F28036336164}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {A1738C53-4AC7-4BE6-95A7-F28036336164}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {A1738C53-4AC7-4BE6-95A7-F28036336164}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {4C189715-53A0-45AD-AFB1-A704DF6BCB20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {4C189715-53A0-45AD-AFB1-A704DF6BCB20}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {4C189715-53A0-45AD-AFB1-A704DF6BCB20}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {4C189715-53A0-45AD-AFB1-A704DF6BCB20}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {451F2A2B-BE7C-401E-8652-10E92C9C5CB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {451F2A2B-BE7C-401E-8652-10E92C9C5CB4}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {451F2A2B-BE7C-401E-8652-10E92C9C5CB4}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {451F2A2B-BE7C-401E-8652-10E92C9C5CB4}.Release|Any CPU.Build.0 = Release|Any CPU 45 | EndGlobalSection 46 | GlobalSection(SolutionProperties) = preSolution 47 | HideSolutionNode = FALSE 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /SqlDoc/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | [] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | [] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [] 37 | [] 38 | [] 39 | 40 | do 41 | () -------------------------------------------------------------------------------- /SqlDoc/SqlDoc.fs: -------------------------------------------------------------------------------- 1 | module SqlDoc 2 | 3 | open Npgsql 4 | open System.Data.SqlClient 5 | open System.Data.Common 6 | open Newtonsoft.Json 7 | open System.IO 8 | open Nessos.FsPickler 9 | 10 | type Store = 11 | | SqlXmlStore of connString:string 12 | | PostgresStore of connString:string 13 | | SqlJsonStore of connString:string 14 | 15 | type KeyedValue<'a> = 'a * obj 16 | 17 | type Operation<'a> = 18 | | Insert of KeyedValue<'a> 19 | | Update of KeyedValue<'a> 20 | | Delete of KeyedValue<'a> 21 | 22 | type UnitOfWork<'a> = Operation<'a> list 23 | 24 | let xmlSerializer = FsPickler.CreateXmlSerializer(indent = true) 25 | 26 | let insert (key:'a) (value:'b) = 27 | Insert (key, box value) 28 | 29 | let update (key:'a) (value:'b) = 30 | Update (key, box value) 31 | 32 | let delete (key:'a) (value:'b) = 33 | Delete (key, box value) 34 | 35 | let private typeToName (t:System.Type) = 36 | t.Name.ToLowerInvariant() 37 | 38 | let tableName<'a> = 39 | typedefof<'a> |> typeToName 40 | 41 | let private objectToTableName o = 42 | o.GetType() |> typeToName 43 | 44 | let private getConnection = function 45 | | SqlXmlStore conn -> new SqlConnection(conn) :> DbConnection 46 | | PostgresStore conn -> new NpgsqlConnection(conn) :> DbConnection 47 | | SqlJsonStore conn -> new SqlConnection(conn) :> DbConnection 48 | 49 | let private getCommand (store:Store) pattern (conn:DbConnection) (transaction:DbTransaction) : DbCommand = 50 | match store with 51 | | SqlXmlStore cs -> new SqlCommand(pattern, conn :?> SqlConnection, transaction :?> SqlTransaction) :> DbCommand 52 | | SqlJsonStore cs -> new SqlCommand(pattern, conn :?> SqlConnection, transaction :?> SqlTransaction) :> DbCommand 53 | | PostgresStore cs -> new NpgsqlCommand(pattern, conn :?> NpgsqlConnection, transaction :?> NpgsqlTransaction) :> DbCommand 54 | 55 | let private getParameter (store:Store) conn k v = 56 | match store with 57 | | SqlXmlStore cs -> new SqlParameter(ParameterName = k, Value = v) :> DbParameter 58 | | SqlJsonStore cs -> new SqlParameter(ParameterName = k, Value = v) :> DbParameter 59 | | PostgresStore cs -> 60 | let p = new NpgsqlParameter(ParameterName = k, Value = v) 61 | if k = "data" then p.NpgsqlDbType <- NpgsqlTypes.NpgsqlDbType.Json 62 | p :> DbParameter 63 | 64 | let private serialize o = function 65 | | SqlXmlStore cs -> 66 | let w = new System.IO.StringWriter() 67 | xmlSerializer.Serialize(w, o) 68 | w.ToString() 69 | | PostgresStore cs -> 70 | JsonConvert.SerializeObject(o) 71 | | SqlJsonStore cs -> 72 | JsonConvert.SerializeObject(o) 73 | 74 | let private deserialize<'a> s = function 75 | | SqlXmlStore cs -> 76 | xmlSerializer.Deserialize(new StringReader(s)) :?> 'a 77 | | PostgresStore cs -> 78 | JsonConvert.DeserializeObject<'a>(s) 79 | | SqlJsonStore cs -> 80 | JsonConvert.DeserializeObject<'a>(s) 81 | 82 | let private parameterPrefix = function 83 | | SqlXmlStore _ -> "@" 84 | | SqlJsonStore _ -> "@" 85 | | PostgresStore _ -> ":" 86 | 87 | let commit (store:Store) (uow:UnitOfWork<'a>) = 88 | use conn = getConnection store 89 | conn.Open() 90 | use transaction = conn.BeginTransaction() 91 | 92 | let insertUpdate id o (p:string) = 93 | let pattern = System.String.Format(p, o |> objectToTableName, parameterPrefix store) 94 | let command = getCommand store pattern conn transaction 95 | command.Parameters.Add(getParameter store conn "id" id) |> ignore 96 | command.Parameters.Add(getParameter store conn "data" (serialize o store)) |> ignore 97 | command.ExecuteNonQuery() 98 | 99 | let insert (id:'a) (o:obj) = insertUpdate id o @"insert into ""{0}"" (id, data) values({1}id, {1}data)" 100 | let update (id:'a) (o:obj) = insertUpdate id o @"update ""{0}"" set data = {1}data where id = {1}id" 101 | 102 | let delete id o = 103 | let pattern = System.String.Format(@"delete from ""{0}"" where id = {1}id", o |> objectToTableName, parameterPrefix store) 104 | let command = getCommand store pattern conn transaction 105 | command.Parameters.Add(getParameter store conn "id" id) |> ignore 106 | command.ExecuteNonQuery() 107 | 108 | if List.isEmpty uow then 109 | () 110 | else 111 | try 112 | try 113 | for op in List.rev uow do 114 | match op with 115 | | Insert kv -> kv ||> insert 116 | | Update kv -> kv ||> update 117 | | Delete kv -> kv ||> delete 118 | |> ignore 119 | with 120 | | _ -> 121 | transaction.Rollback() 122 | reraise() 123 | transaction.Commit() 124 | finally 125 | conn.Close() 126 | () 127 | 128 | let select<'a> (store:Store) select (m:(string * 'c) list) : 'a array = 129 | let ps = Map.ofList m 130 | use conn = getConnection store 131 | conn.Open() 132 | use transaction = conn.BeginTransaction() 133 | use command = getCommand store select conn transaction 134 | let parameters = 135 | ps 136 | |> Map.map (fun k v -> getParameter store conn k v) 137 | |> Map.toArray 138 | |> Array.map (fun (k,v) -> v) 139 | command.Parameters.AddRange(parameters) 140 | try 141 | use dr = command.ExecuteReader() 142 | [| 143 | while dr.Read() do 144 | let data = dr.[0] :?> string 145 | yield deserialize<'a> data store 146 | |] 147 | finally 148 | conn.Close() 149 | 150 | let runScript (store:Store) (script:string) = 151 | use conn = getConnection store 152 | conn.Open() 153 | use transaction = conn.BeginTransaction() 154 | use command = getCommand store script conn transaction 155 | try 156 | try 157 | command.ExecuteNonQuery() |> ignore 158 | with 159 | | _ -> 160 | transaction.Rollback() 161 | reraise() 162 | transaction.Commit() 163 | finally 164 | conn.Close() 165 | () 166 | 167 | let createTable (store:Store) (tableName:string) = 168 | runScript store (System.String.Format(" 169 | IF (not EXISTS (SELECT * 170 | FROM INFORMATION_SCHEMA.TABLES 171 | WHERE TABLE_SCHEMA = 'dbo' 172 | AND TABLE_NAME = '{0}')) 173 | BEGIN 174 | CREATE TABLE [dbo].[{0}]( 175 | [Id] [uniqueidentifier] NOT NULL, 176 | [Data] [xml] NOT NULL, 177 | CONSTRAINT [PK_{0}] PRIMARY KEY CLUSTERED ( [Id] ASC ) 178 | ) 179 | END 180 | ", tableName)) -------------------------------------------------------------------------------- /SqlDoc/SqlDoc.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 59985c6c-6981-451f-bd2d-1ef6768bab02 9 | Library 10 | SqlDoc 11 | SqlDoc 12 | v4.5.2 13 | 4.4.0.0 14 | true 15 | SqlDoc 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\SqlDoc.XML 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\SqlDoc.XML 35 | 36 | 37 | 11 38 | 39 | 40 | 41 | 42 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 43 | 44 | 45 | 46 | 47 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | True 61 | 62 | 63 | 64 | 65 | 66 | 67 | 74 | 75 | 76 | 77 | 78 | ..\packages\FsPickler\lib\net35\FsPickler.dll 79 | True 80 | True 81 | 82 | 83 | True 84 | 85 | 86 | True 87 | 88 | 89 | 90 | 91 | 92 | 93 | ..\packages\FsPickler\lib\net40\FsPickler.dll 94 | True 95 | True 96 | 97 | 98 | True 99 | 100 | 101 | True 102 | 103 | 104 | 105 | 106 | 107 | 108 | ..\packages\FsPickler\lib\net45\FsPickler.dll 109 | True 110 | True 111 | 112 | 113 | True 114 | 115 | 116 | True 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | ..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll 126 | True 127 | True 128 | 129 | 130 | 131 | 132 | 133 | 134 | ..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll 135 | True 136 | True 137 | 138 | 139 | 140 | 141 | 142 | 143 | ..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll 144 | True 145 | True 146 | 147 | 148 | 149 | 150 | 151 | 152 | ..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll 153 | True 154 | True 155 | 156 | 157 | 158 | 159 | 160 | 161 | ..\packages\Newtonsoft.Json\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll 162 | True 163 | True 164 | 165 | 166 | 167 | 168 | 169 | 170 | ..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+dnxcore50\Newtonsoft.Json.dll 171 | True 172 | True 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | ..\packages\Npgsql\lib\net45\Npgsql.dll 182 | True 183 | True 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /SqlDoc/paket.references: -------------------------------------------------------------------------------- 1 | Npgsql 2 | Newtonsoft.Json 3 | FsPickler -------------------------------------------------------------------------------- /SqlDocCs/IDocumentSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SqlDocCs 6 | { 7 | public interface IQuerySession 8 | { 9 | TVal Load(TKey id) where TVal : class; 10 | IEnumerable Query(string sql, Dictionary parameters = null); 11 | } 12 | 13 | /// 14 | /// Interface for document session 15 | /// 16 | public interface IDocumentSession : IQuerySession 17 | { 18 | void Delete(TKey id, object datum); 19 | void SaveChanges(); 20 | void Store(TKey id, TVal entity) where TVal : class; 21 | void Update(TKey id, TVal entity) where TVal : class; 22 | } 23 | 24 | public class DocumentSession : IDocumentSession 25 | { 26 | private IConnection _connection; 27 | private Queue> _uow = new Queue>(); 28 | 29 | public DocumentSession(IConnection connection) 30 | { 31 | _connection = connection; 32 | } 33 | 34 | public void Delete(TKey id, object datum) 35 | { 36 | _uow.Enqueue(Operation.Delete(id, datum)); 37 | } 38 | 39 | public TVal Load(TKey id) where TVal : class 40 | { 41 | var result = QueryFor.For(_connection, 42 | string.Format("SELECT [Data] from {0} where Id = @id", SqlDoc.tableName()), 43 | new Dictionary { { "id", id } }); 44 | return result.First(); 45 | } 46 | 47 | public IEnumerable Query(string sql, Dictionary parameters = null) 48 | { 49 | return QueryFor.For(_connection, sql, parameters); 50 | } 51 | 52 | public void SaveChanges() 53 | { 54 | UnitOfWork.Commit(_connection, _uow); 55 | } 56 | 57 | public void Store(TKey id, TVal entity) where TVal : class 58 | { 59 | _uow.Enqueue(Operation.Insert(id, entity)); 60 | } 61 | 62 | public void Update(TKey id, TVal entity) where TVal : class 63 | { 64 | _uow.Enqueue(Operation.Update(id, entity)); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /SqlDocCs/Operation.cs: -------------------------------------------------------------------------------- 1 | using DesignByContract; 2 | 3 | namespace SqlDocCs 4 | { 5 | public enum Verb { 6 | Insert,Update,Delete 7 | } 8 | 9 | public interface IConnection 10 | { 11 | string String { get; } 12 | } 13 | 14 | public class SqlConnection : IConnection 15 | { 16 | public string String { get; private set; } 17 | 18 | public static SqlConnection From(string connString) 19 | { 20 | return new SqlConnection() { String = connString }; 21 | } 22 | } 23 | 24 | public class PostgresConnection : IConnection 25 | { 26 | public string String { get; private set; } 27 | 28 | public static PostgresConnection From(string connString) 29 | { 30 | return new PostgresConnection() { String = connString }; 31 | } 32 | } 33 | 34 | public class Operation 35 | { 36 | public TKey Id { get; private set; } 37 | public Verb Verb { get; private set; } 38 | public object Datum { get; private set; } 39 | 40 | public Operation(TKey id, Verb verb, object datum) 41 | { 42 | Dbc.Requires(datum != null); 43 | Id = id; 44 | Verb = verb; 45 | Datum = datum; 46 | } 47 | } 48 | 49 | public static class Operation 50 | { 51 | public static Operation Insert(TKey id, object datum) 52 | { 53 | return new Operation(id, Verb.Insert, datum); 54 | } 55 | 56 | public static Operation Update(TKey id, object datum) 57 | { 58 | return new Operation(id, Verb.Update, datum); 59 | } 60 | 61 | public static Operation Delete(TKey id, object datum) 62 | { 63 | return new Operation(id, Verb.Delete, datum); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SqlDocCs/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("SqlDocCs")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SqlDocCs")] 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("4c189715-53a0-45ad-afb1-a704df6bcb20")] 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 | -------------------------------------------------------------------------------- /SqlDocCs/QueryFor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SqlDocCs 6 | { 7 | public class QueryFor 8 | { 9 | public static T[] For(IConnection connection, string sql, Dictionary parameters = null) 10 | { 11 | var store = UnitOfWork.ConnectionToStore(connection); 12 | return For(store, sql, parameters); 13 | } 14 | 15 | private static T[] For(SqlDoc.Store store, string sql, Dictionary parameters = null) 16 | { 17 | parameters = parameters ?? new Dictionary(); 18 | return SqlDoc.select(store, sql, DictionaryToListOfTuples(parameters)); 19 | } 20 | 21 | private static Microsoft.FSharp.Collections.FSharpList> DictionaryToListOfTuples(Dictionary parameters) 22 | { 23 | if (parameters.Count == 0) return Microsoft.FSharp.Collections.FSharpList>.Empty; 24 | return new Microsoft.FSharp.Collections.FSharpList>( 25 | KvpToTuple(parameters.First()), 26 | DictionaryToListOfTuples(parameters.Skip(1).ToDictionary(k => k.Key, k => k.Value))); 27 | } 28 | 29 | private static Tuple KvpToTuple(KeyValuePair kvp) 30 | { 31 | return Tuple.Create(kvp.Key, kvp.Value); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SqlDocCs/SqlDocCs.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4C189715-53A0-45AD-AFB1-A704DF6BCB20} 8 | Library 9 | Properties 10 | SqlDocCs 11 | SqlDocCs 12 | v4.5.2 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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 60 | 61 | 62 | {59985c6c-6981-451f-bd2d-1ef6768bab02} 63 | SqlDoc 64 | 65 | 66 | 67 | 68 | ..\packages\DesignByContract\lib\DesignByContract.dll 69 | True 70 | True 71 | 72 | 73 | 74 | 75 | 76 | 77 | ..\packages\FSharp.Core\lib\net20\FSharp.Core.dll 78 | True 79 | True 80 | 81 | 82 | 83 | 84 | 85 | 86 | ..\packages\FSharp.Core\lib\net40\FSharp.Core.dll 87 | True 88 | True 89 | 90 | 91 | 92 | 93 | 94 | 95 | ..\packages\FSharp.Core\lib\portable-net45+monoandroid10+monotouch10+xamarinios10\FSharp.Core.dll 96 | True 97 | True 98 | 99 | 100 | 101 | 102 | 103 | 104 | ..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll 105 | True 106 | True 107 | 108 | 109 | 110 | 111 | 112 | 113 | ..\packages\FSharp.Core\lib\portable-net45+netcore45+wp8\FSharp.Core.dll 114 | True 115 | True 116 | 117 | 118 | 119 | 120 | 121 | 122 | ..\packages\FSharp.Core\lib\portable-net45+netcore45+wpa81+wp8\FSharp.Core.dll 123 | True 124 | True 125 | 126 | 127 | 128 | 129 | 130 | 131 | ..\packages\FSharp.Core\lib\portable-net45+sl5+netcore45\FSharp.Core.dll 132 | True 133 | True 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /SqlDocCs/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SqlDocCs 5 | { 6 | public class UnitOfWork 7 | { 8 | public static void Commit(IConnection connection, Queue> uow) 9 | { 10 | var store = ConnectionToStore(connection); 11 | SqlDoc.commit(store, QueueToFSharpList(uow)); 12 | } 13 | 14 | public static SqlDoc.Store ConnectionToStore(IConnection connection) 15 | { 16 | if (connection is PostgresConnection) 17 | { 18 | return SqlDoc.Store.NewPostgresStore(connection.String); 19 | } 20 | if (connection is SqlConnection) 21 | { 22 | return SqlDoc.Store.NewSqlXmlStore(connection.String); 23 | } 24 | throw new ArgumentException("Unknown IConnection type"); 25 | } 26 | 27 | private static Microsoft.FSharp.Collections.FSharpList> QueueToFSharpList(Queue> uow) 28 | { 29 | if (uow.Count == 0) 30 | { 31 | return Microsoft.FSharp.Collections.FSharpList>.Empty; 32 | } 33 | return new Microsoft.FSharp.Collections.FSharpList>( 34 | OperationToOperation(uow.Dequeue()), 35 | QueueToFSharpList(uow)); 36 | } 37 | 38 | private static SqlDoc.Operation OperationToOperation(Operation op) 39 | { 40 | switch (op.Verb) 41 | { 42 | case Verb.Insert: 43 | return SqlDoc.Operation.NewInsert(Tuple.Create(op.Id, op.Datum)); 44 | case Verb.Update: 45 | return SqlDoc.Operation.NewUpdate(Tuple.Create(op.Id, op.Datum)); 46 | case Verb.Delete: 47 | return SqlDoc.Operation.NewDelete(Tuple.Create(op.Id, op.Datum)); 48 | default: 49 | throw new Exception("What kind of verb is " + op.Verb + "?"); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SqlDocCs/paket.references: -------------------------------------------------------------------------------- 1 | DesignByContract 2 | FSharp.Core 3 | -------------------------------------------------------------------------------- /Tests/001-create.sql: -------------------------------------------------------------------------------- 1 | create table Person ( 2 | id uuid NOT NULL, 3 | data json NOT NULL 4 | ); 5 | -------------------------------------------------------------------------------- /Tests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Tests/Tests.RunningScripts.fs: -------------------------------------------------------------------------------- 1 | module Tests.RunningScripts 2 | open System.Configuration 3 | open Xunit 4 | open FsUnit.Xunit 5 | open SqlDoc 6 | 7 | let storeSql = SqlXmlStore ConfigurationManager.AppSettings.["ConnSqlXml"] 8 | 9 | [] 10 | let ``run ddl script`` () = 11 | let tableName = (System.Guid.NewGuid().ToString()) 12 | let script = sprintf """ 13 | CREATE TABLE [dbo].[%s]( 14 | [Id] [uniqueidentifier] NOT NULL, 15 | [Data] [xml] NOT NULL, 16 | CONSTRAINT [PK_%s] PRIMARY KEY CLUSTERED 17 | ( 18 | [Id] ASC 19 | )) 20 | """ tableName (System.Guid.NewGuid().ToString()) 21 | runScript storeSql script 22 | 23 | runScript storeSql (sprintf "drop table [%s]" tableName) 24 | () 25 | -------------------------------------------------------------------------------- /Tests/Tests.WorkingWithDocuments.fs: -------------------------------------------------------------------------------- 1 | module Tests.WorkingWithDocuments 2 | open System.Configuration 3 | open Xunit 4 | open FsUnit.Xunit 5 | open SqlDoc 6 | 7 | [] 8 | type Person = { _id: System.Guid; age: int; name: string } 9 | type CustomerName(firstName, middleInitial, lastName) = 10 | member this.FirstName = firstName 11 | member this.MiddleInitial = middleInitial 12 | member this.LastName = lastName 13 | 14 | let store = PostgresStore ConfigurationManager.AppSettings.["ConnString"] 15 | let storeSql = SqlXmlStore ConfigurationManager.AppSettings.["ConnSqlXml"] 16 | let storeSqlJson = SqlJsonStore ConfigurationManager.AppSettings.["ConnSqlJson"] 17 | 18 | [] 19 | let ``insert (sql)`` () = 20 | // insert 21 | let id = System.Guid.NewGuid() 22 | let o = { _id = id; age = 45; name = "Cecile" } 23 | commit storeSql [insert id o] 24 | 25 | [] 26 | let ``insert (sql json)`` () = 27 | // insert 28 | let id = System.Guid.NewGuid() 29 | let o = { _id = id; age = 45; name = "Cecile" } 30 | commit storeSqlJson [insert id o] 31 | 32 | [] 33 | let ``insert, read, update, delete a document (sql json)`` () = 34 | // insert 35 | let id = System.Guid.NewGuid() 36 | let o = { _id = id; age = 45; name = "Cecile" } 37 | commit storeSqlJson [insert id o] 38 | 39 | // read 40 | let read = 41 | [ "id", box id ] 42 | |> select storeSqlJson @"SELECT [Data] from Person 43 | where JSON_VALUE([Data], '$._id') = @id" 44 | o |> should equal read.[0] 45 | Array.length read |> should equal 1 46 | 47 | // update 48 | let updated = {o with age = 46 } 49 | commit storeSqlJson [update o._id updated] 50 | 51 | // read again :{P 52 | let readUpdated = 53 | ["id", box id] 54 | |> select storeSqlJson "SELECT [Data] from Person where Id = @id" 55 | updated |> should equal readUpdated.[0] 56 | Array.length readUpdated |> should equal 1 57 | 58 | // delete 59 | commit storeSqlJson [delete o._id o] 60 | let readDeleted = 61 | ["id", box id] 62 | |> select storeSqlJson "select [Data] from Person where Id = @id" 63 | Array.length readDeleted |> should equal 0 64 | 65 | 66 | [] 67 | let ``insert, read, update, delete a document (sql xml)`` () = 68 | // insert 69 | let id = System.Guid.NewGuid() 70 | let o = { _id = id; age = 45; name = "Cecile" } 71 | commit storeSql [insert id o] 72 | 73 | // read 74 | let read = 75 | [ "id", box id ] 76 | |> select storeSql @"SELECT [Data] from Person 77 | Where Data.value('(/FsPickler/value/instance/id)[1]', 'uniqueidentifier') = @id" 78 | o |> should equal read.[0] 79 | Array.length read |> should equal 1 80 | 81 | // update 82 | let updated = {o with age = 46 } 83 | commit storeSql [update o._id updated] 84 | 85 | // read again :{P 86 | let readUpdated = 87 | ["id", box id] 88 | |> select storeSql "SELECT [Data] from Person where Id = @id" 89 | updated |> should equal readUpdated.[0] 90 | Array.length readUpdated |> should equal 1 91 | 92 | // delete 93 | commit storeSql [delete o._id o] 94 | let readDeleted = 95 | ["id", box id] 96 | |> select storeSql "select [Data] from Person where Id = @id" 97 | Array.length readDeleted |> should equal 0 98 | 99 | [] 100 | let ``insert, read, update, delete a document`` () = 101 | // insert 102 | let id = System.Guid.NewGuid() 103 | let o = { _id = id; age = 45; name = "Cecile" } 104 | commit store [insert id o] 105 | 106 | // read 107 | let read = 108 | [ "id", id |> string |> box ] 109 | |> select store "select data from Person where (data->>'_id') = :id" 110 | o |> should equal read.[0] 111 | Array.length read |> should equal 1 112 | 113 | // update 114 | let updated = {o with age = 46 } 115 | commit store [update o._id updated] 116 | 117 | // read again :{P 118 | let readUpdated = 119 | ["id", string id |> box] 120 | |> select store "select data from Person where data->>'_id' = :id" 121 | updated |> should equal readUpdated.[0] 122 | Array.length readUpdated |> should equal 1 123 | 124 | // delete 125 | commit store [delete o._id o] 126 | let readDeleted = 127 | ["id", string id |> box] 128 | |> select store "select data from Person where data->>'_id' = :id" 129 | Array.length readDeleted |> should equal 0 130 | 131 | [] 132 | let ``commit a unit of work`` () = 133 | let julio = { _id = System.Guid.NewGuid(); age = 30; name = "Julio" } 134 | let timmy = { _id = System.Guid.NewGuid(); age = 3; name = "Timmy" } 135 | let uow = [ 136 | delete timmy._id timmy 137 | update julio._id { julio with age = 31 }; 138 | insert julio._id julio; 139 | insert timmy._id timmy; 140 | ] 141 | commit store uow 142 | () 143 | 144 | //[] 145 | //let ``check perf`` () = 146 | // let uow = [ 147 | // for i in [1..10000] do 148 | // let id = System.Guid.NewGuid() 149 | // yield insert id { _id = id ; age = i; name = "person" + i.ToString() } 150 | // ] 151 | // commit store uow 152 | // 153 | //[] 154 | //let ``check perf (sql)`` () = 155 | // let uow = [ 156 | // for i in [1..10000] do 157 | // let id = System.Guid.NewGuid() 158 | // yield insert id { _id = id ; age = i; name = "person" + i.ToString() } 159 | // ] 160 | // commit storeSql uow 161 | -------------------------------------------------------------------------------- /Tests/Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | a1738c53-4ac7-4be6-95a7-f28036336164 9 | Library 10 | Tests 11 | Tests 12 | v4.5.2 13 | 4.4.0.0 14 | true 15 | Tests 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\Tests.XML 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\Tests.XML 35 | 36 | 37 | 11 38 | 39 | 40 | 41 | 42 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 43 | 44 | 45 | 46 | 47 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | <__paket__xunit_core_props>portable-net45+win8+wp8+wpa81\xunit.core 56 | 57 | 58 | 59 | 60 | <__paket__xunit_core_props>win81\xunit.core 61 | 62 | 63 | 64 | 65 | <__paket__xunit_core_props>wpa81\xunit.core 66 | 67 | 68 | 69 | 70 | 77 | 78 | 79 | 80 | 81 | 82 | PreserveNewest 83 | 84 | 85 | 86 | 87 | 88 | 89 | True 90 | 91 | 92 | 93 | 94 | 95 | 96 | SqlDoc 97 | {59985c6c-6981-451f-bd2d-1ef6768bab02} 98 | True 99 | 100 | 101 | 102 | 103 | 104 | 105 | ..\packages\FsUnit\lib\net45\FsUnit.NUnit.dll 106 | True 107 | True 108 | 109 | 110 | ..\packages\FsUnit\lib\net45\NHamcrest.dll 111 | True 112 | True 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | ..\packages\FsUnit.xUnit\lib\net45\FsUnit.Xunit.dll 122 | True 123 | True 124 | 125 | 126 | ..\packages\FsUnit.xUnit\lib\net45\NHamcrest.dll 127 | True 128 | True 129 | 130 | 131 | 132 | 133 | 134 | 135 | ..\packages\NUnit\lib\nunit.framework.dll 136 | True 137 | True 138 | 139 | 140 | 141 | 142 | 143 | 144 | ..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll 145 | True 146 | True 147 | 148 | 149 | 150 | 151 | 152 | 153 | ..\packages\xunit.abstractions\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.abstractions.dll 154 | True 155 | True 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | ..\packages\xunit.assert\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll 165 | True 166 | True 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | ..\packages\xunit.extensibility.core\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll 176 | True 177 | True 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | ..\packages\xunit.extensibility.execution\lib\monoandroid\xunit.execution.dotnet.dll 187 | True 188 | True 189 | 190 | 191 | 192 | 193 | 194 | 195 | ..\packages\xunit.extensibility.execution\lib\monotouch\xunit.execution.dotnet.dll 196 | True 197 | True 198 | 199 | 200 | 201 | 202 | 203 | 204 | ..\packages\xunit.extensibility.execution\lib\net45\xunit.execution.desktop.dll 205 | True 206 | True 207 | 208 | 209 | 210 | 211 | 212 | 213 | ..\packages\xunit.extensibility.execution\lib\portable-net45+win8+wp8+wpa81\xunit.execution.dotnet.dll 214 | True 215 | True 216 | 217 | 218 | 219 | 220 | 221 | 222 | ..\packages\xunit.extensibility.execution\lib\win8\xunit.execution.dotnet.dll 223 | True 224 | True 225 | 226 | 227 | 228 | 229 | 230 | 231 | ..\packages\xunit.extensibility.execution\lib\wp8\xunit.execution.dotnet.dll 232 | True 233 | True 234 | 235 | 236 | 237 | 238 | 239 | 240 | ..\packages\xunit.extensibility.execution\lib\xamarinios\xunit.execution.dotnet.dll 241 | True 242 | True 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /Tests/paket.references: -------------------------------------------------------------------------------- 1 | FsUnit 2 | Xunit 3 | FsUnit.Xunit -------------------------------------------------------------------------------- /TestsCs/DocumentSessionAPITests.cs: -------------------------------------------------------------------------------- 1 | using Givn; 2 | using SqlDocCs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Configuration; 6 | using Xunit; 7 | 8 | namespace TestsCs 9 | { 10 | public class DocumentSessionAPITests 11 | { 12 | [Fact] 13 | public void ICanAddADocumentAndReadItBack() 14 | { 15 | Giv.n(IAddADocument); 16 | Th.n(ICanReadItBack); 17 | } 18 | 19 | [Fact] 20 | public void ICanAddADocumentAndDeleteItAndItsGone() 21 | { 22 | Wh.n(IAddADocument) 23 | .And(IDeleteTheDocument); 24 | Th.n(TheDocumentIsGone); 25 | } 26 | 27 | [Fact] 28 | public void ICanAddADocumentAndUpdateItAndTheChangesPersist() 29 | { 30 | Wh.n(IAddADocument) 31 | .And(IUpdateTheDocument); 32 | Th.n(TheChangePersists); 33 | } 34 | 35 | private void IAddADocument() 36 | { 37 | _aDocument = new PersonCs 38 | { 39 | _id = Guid.NewGuid(), 40 | Name = "Docsesh", 41 | Age = 90, 42 | FavouriteThings = new[] { "Golf", "Statue of liberty" } 43 | }; 44 | _documentSession.Store(_aDocument._id, _aDocument); 45 | _documentSession.SaveChanges(); 46 | } 47 | 48 | private void ICanReadItBack() 49 | { 50 | var fresh = _documentSession.Load(_aDocument._id); 51 | Assert.True(_aDocument.Equals(fresh)); 52 | } 53 | 54 | private void IUpdateTheDocument() 55 | { 56 | _aDocument.Age += 1; 57 | _documentSession.Update(_aDocument._id, _aDocument); 58 | _documentSession.SaveChanges(); 59 | } 60 | 61 | private void TheChangePersists() 62 | { 63 | var fresh = _documentSession.Load(_aDocument._id); 64 | Assert.Equal(91, fresh.Age); 65 | } 66 | 67 | private void IDeleteTheDocument() 68 | { 69 | _documentSession.Delete(_aDocument._id, _aDocument); 70 | _documentSession.SaveChanges(); 71 | } 72 | 73 | private void TheDocumentIsGone() 74 | { 75 | var result = _documentSession.Query( 76 | "select data from PersonCs where Data.value('(/FsPickler/value/instance/idkBackingField)[1]', 'uniqueidentifier') = @id", 77 | new Dictionary { { "id", _aDocument._id } }); 78 | Assert.Empty(result); 79 | } 80 | 81 | private IDocumentSession _documentSession = 82 | new DocumentSession(SqlConnection.From(ConfigurationManager.AppSettings["ConnSqlXml"])); 83 | private PersonCs _aDocument; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /TestsCs/Givn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Givn 4 | { 5 | public static class Giv 6 | { 7 | public static Ander n(Action action) 8 | { 9 | action(); 10 | return new Ander(); 11 | } 12 | } 13 | 14 | public class Ander 15 | { 16 | public Ander And(Action action) 17 | { 18 | action(); 19 | return this; 20 | } 21 | } 22 | 23 | public static class Wh 24 | { 25 | public static Ander n(Action action) 26 | { 27 | action(); 28 | return new Ander(); 29 | } 30 | } 31 | 32 | public static class Th 33 | { 34 | public static Ander n(Action action) 35 | { 36 | action(); 37 | return new Ander(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /TestsCs/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("TestsCs")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestsCs")] 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("451f2a2b-be7c-401e-8652-10e92c9c5cb4")] 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 | -------------------------------------------------------------------------------- /TestsCs/TestsCs.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {451F2A2B-BE7C-401E-8652-10E92C9C5CB4} 8 | Library 9 | Properties 10 | TestsCs 11 | TestsCs 12 | v4.5.2 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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | True 47 | 48 | 49 | 50 | 51 | 52 | 53 | {4c189715-53a0-45ad-afb1-a704df6bcb20} 54 | SqlDocCs 55 | 56 | 57 | 58 | 59 | App.config 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | <__paket__xunit_core_props>portable-net45+win8+wp8+wpa81\xunit.core 68 | 69 | 70 | 71 | 72 | <__paket__xunit_core_props>win81\xunit.core 73 | 74 | 75 | 76 | 77 | <__paket__xunit_core_props>wpa81\xunit.core 78 | 79 | 80 | 81 | 82 | 89 | 90 | 91 | 92 | 93 | ..\packages\FSharp.Core\lib\net20\FSharp.Core.dll 94 | True 95 | True 96 | 97 | 98 | 99 | 100 | 101 | 102 | ..\packages\FSharp.Core\lib\net40\FSharp.Core.dll 103 | True 104 | True 105 | 106 | 107 | 108 | 109 | 110 | 111 | ..\packages\FSharp.Core\lib\portable-net45+monoandroid10+monotouch10+xamarinios10\FSharp.Core.dll 112 | True 113 | True 114 | 115 | 116 | 117 | 118 | 119 | 120 | ..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll 121 | True 122 | True 123 | 124 | 125 | 126 | 127 | 128 | 129 | ..\packages\FSharp.Core\lib\portable-net45+netcore45+wp8\FSharp.Core.dll 130 | True 131 | True 132 | 133 | 134 | 135 | 136 | 137 | 138 | ..\packages\FSharp.Core\lib\portable-net45+netcore45+wpa81+wp8\FSharp.Core.dll 139 | True 140 | True 141 | 142 | 143 | 144 | 145 | 146 | 147 | ..\packages\FSharp.Core\lib\portable-net45+sl5+netcore45\FSharp.Core.dll 148 | True 149 | True 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ..\packages\FsUnit\lib\net45\FsUnit.NUnit.dll 159 | True 160 | True 161 | 162 | 163 | ..\packages\FsUnit\lib\net45\NHamcrest.dll 164 | True 165 | True 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | ..\packages\FsUnit.xUnit\lib\net45\FsUnit.Xunit.dll 175 | True 176 | True 177 | 178 | 179 | ..\packages\FsUnit.xUnit\lib\net45\NHamcrest.dll 180 | True 181 | True 182 | 183 | 184 | 185 | 186 | 187 | 188 | ..\packages\NUnit\lib\nunit.framework.dll 189 | True 190 | True 191 | 192 | 193 | 194 | 195 | 196 | 197 | ..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll 198 | True 199 | True 200 | 201 | 202 | 203 | 204 | 205 | 206 | ..\packages\xunit.abstractions\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.abstractions.dll 207 | True 208 | True 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | ..\packages\xunit.assert\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll 218 | True 219 | True 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | ..\packages\xunit.extensibility.core\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll 229 | True 230 | True 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | ..\packages\xunit.extensibility.execution\lib\monoandroid\xunit.execution.dotnet.dll 240 | True 241 | True 242 | 243 | 244 | 245 | 246 | 247 | 248 | ..\packages\xunit.extensibility.execution\lib\monotouch\xunit.execution.dotnet.dll 249 | True 250 | True 251 | 252 | 253 | 254 | 255 | 256 | 257 | ..\packages\xunit.extensibility.execution\lib\net45\xunit.execution.desktop.dll 258 | True 259 | True 260 | 261 | 262 | 263 | 264 | 265 | 266 | ..\packages\xunit.extensibility.execution\lib\portable-net45+win8+wp8+wpa81\xunit.execution.dotnet.dll 267 | True 268 | True 269 | 270 | 271 | 272 | 273 | 274 | 275 | ..\packages\xunit.extensibility.execution\lib\win8\xunit.execution.dotnet.dll 276 | True 277 | True 278 | 279 | 280 | 281 | 282 | 283 | 284 | ..\packages\xunit.extensibility.execution\lib\wp8\xunit.execution.dotnet.dll 285 | True 286 | True 287 | 288 | 289 | 290 | 291 | 292 | 293 | ..\packages\xunit.extensibility.execution\lib\xamarinios\xunit.execution.dotnet.dll 294 | True 295 | True 296 | 297 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /TestsCs/WorkingWithDocumentsTests.cs: -------------------------------------------------------------------------------- 1 | using Givn; 2 | using SqlDocCs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Configuration; 6 | using System.Linq; 7 | using Xunit; 8 | 9 | namespace TestsCs 10 | { 11 | [Serializable] 12 | public class PersonCs 13 | { 14 | public Guid _id { get; set; } 15 | public string Name { get; set; } 16 | public int Age { get; set; } 17 | public string[] FavouriteThings { get; set; } 18 | public override bool Equals(object obj) 19 | { 20 | var other = obj as PersonCs; 21 | return _id == other._id && Name == other.Name && Age == other.Age && FavouriteThings.Length == other.FavouriteThings.Length; // ignores FavouriteThings 22 | } 23 | } 24 | 25 | public class WorkingWithDocumentsTests 26 | { 27 | private Queue> _uow; 28 | private PersonCs _ernesto; 29 | private IConnection connection = SqlConnection.From(ConfigurationManager.AppSettings["ConnSqlXml"]); 30 | 31 | public WorkingWithDocumentsTests() 32 | { 33 | _uow = new Queue>(); 34 | } 35 | 36 | [Fact] 37 | public void UnitOfWork_JustInsert() 38 | { 39 | _ernesto = new PersonCs 40 | { 41 | _id = Guid.NewGuid(), 42 | Name = "Ernesto", 43 | Age = 31, 44 | FavouriteThings = new[] { "Pistachio Ice Cream", "Postgresql", "F#" } 45 | }; 46 | Giv.n(() => AnOperation(Operation.Insert(_ernesto._id, _ernesto))); 47 | Wh.n(TheUnitOfWorkIsCommitted); 48 | Th.n(TheDocumentWasInserted); 49 | } 50 | 51 | [Fact] 52 | public void CanQueryAll() { 53 | var e = QueryFor.For( 54 | connection, 55 | "select data from PersonCs"); 56 | Assert.NotNull(e); 57 | } 58 | 59 | private void AnOperation(Operation operation) 60 | { 61 | _uow.Enqueue(operation); 62 | } 63 | 64 | private void TheUnitOfWorkIsCommitted() 65 | { 66 | UnitOfWork.Commit(connection, _uow); 67 | } 68 | 69 | private void TheDocumentWasInserted() 70 | { 71 | var e = QueryFor.For( 72 | connection, 73 | "select data from PersonCs where Data.value('(/FsPickler/value/instance/idkBackingField)[1]', 'uniqueidentifier') = @id", 74 | new Dictionary { {"id", _ernesto._id} }); 75 | Assert.Equal(1, e.Length); 76 | Assert.Equal("Ernesto", e.First().Name); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /TestsCs/paket.references: -------------------------------------------------------------------------------- 1 | FsUnit 2 | Xunit 3 | FsUnit.Xunit 4 | Giv.n -------------------------------------------------------------------------------- /a.txt: -------------------------------------------------------------------------------- 1 | adsf 2 | sadfasd 3 | -------------------------------------------------------------------------------- /b.txt: -------------------------------------------------------------------------------- 1 | gdfg 2 | 3 | 3322 4 | sadf 5 | -------------------------------------------------------------------------------- /build.fsx: -------------------------------------------------------------------------------- 1 | #r @"packages/FAKE/tools/FakeLib.dll" 2 | open Fake 3 | open Fake.Testing 4 | open System.IO 5 | 6 | Target "Build" <| fun _ -> 7 | let projects = 8 | [ 9 | "SqlDoc/SqlDoc.fsproj" 10 | "Tests/Tests.fsproj" 11 | "SqlDocCs/SqlDocCs.csproj" 12 | "TestsCs/TestsCs.csproj" 13 | ] 14 | for projFile in projects do 15 | build (fun x -> 16 | { x with 17 | Properties = 18 | [ "Optimize", environVarOrDefault "Build.Optimize" "True" 19 | "DebugSymbols", environVarOrDefault "Build.DebugSymbols" "True" 20 | "Configuration", environVarOrDefault "Build.Configuration" "Release" ] 21 | Targets = 22 | [ "Rebuild" ] 23 | Verbosity = Some Quiet }) projFile 24 | 25 | Target "Default" (fun _ -> 26 | () 27 | ) 28 | 29 | Target "Test" (fun _ -> 30 | let testDir = "Tests/" 31 | !! (testDir + "bin/Release/Tests.dll") 32 | |> xUnit (fun p -> {p with HtmlOutputPath = testDir @@ "html" |> Some; ToolPath = "packages/xunit.runner.console/tools/xunit.console.exe";NUnitXmlOutputPath = Some testDir})) 33 | 34 | "Build" 35 | ==> "Test" 36 | 37 | // start build 38 | RunTargetOrDefault "Build" 39 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | .paket/paket.bootstrapper.exe 2 | .paket/paket install 3 | 4 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://nuget.org/api/v2 2 | 3 | nuget FAKE 4 | nuget Npgsql 5 | nuget Newtonsoft.Json 6 | nuget FsUnit 7 | nuget FsUnit.Xunit 8 | nuget Xunit 9 | nuget Xunit.Runner.Console 10 | nuget DesignByContract 11 | nuget FSharp.Core 12 | nuget Giv.n 13 | nuget FsPickler -------------------------------------------------------------------------------- /paket.lock: -------------------------------------------------------------------------------- 1 | NUGET 2 | remote: https://www.nuget.org/api/v2 3 | DesignByContract (1.0.1) 4 | FAKE (4.9.3) 5 | FSharp.Core (4.0.0.1) 6 | FsPickler (1.6.2) 7 | FsUnit (1.4) 8 | FSharp.Core (>= 3.1.2.5) 9 | NUnit (2.6.4) 10 | FsUnit.xUnit (1.4) 11 | FSharp.Core (>= 3.1.2.5) 12 | xunit (2.1) 13 | Giv.n (1.0) 14 | Newtonsoft.Json (7.0.1) 15 | Npgsql (3.0.3) 16 | NUnit (2.6.4) 17 | System.Collections (4.0.10) - framework: dnxcore50 18 | System.Diagnostics.Debug (>= 4.0) - framework: dnxcore50 19 | System.Resources.ResourceManager (>= 4.0) - framework: dnxcore50 20 | System.Runtime (>= 4.0) - framework: dnxcore50 21 | System.Runtime (>= 4.0.20) - framework: dnxcore50 22 | System.Runtime.Extensions (>= 4.0) - framework: dnxcore50 23 | System.Threading (>= 4.0) - framework: dnxcore50 24 | System.Diagnostics.Contracts (4.0) - framework: dnxcore50 25 | System.Runtime (>= 4.0) - framework: dnxcore50 26 | System.Diagnostics.Debug (4.0.10) - framework: dnxcore50 27 | System.Runtime (>= 4.0) - framework: dnxcore50 28 | System.Globalization (4.0.10) - framework: dnxcore50 29 | System.Runtime (>= 4.0) - framework: dnxcore50 30 | System.IO (4.0.10) - framework: dnxcore50 31 | System.Globalization (>= 4.0) - framework: dnxcore50 32 | System.Runtime (>= 4.0.20) - framework: dnxcore50 33 | System.Text.Encoding (>= 4.0) - framework: dnxcore50 34 | System.Text.Encoding (>= 4.0.10) - framework: dnxcore50 35 | System.Text.Encoding.Extensions (>= 4.0) - framework: dnxcore50 36 | System.Threading (>= 4.0) - framework: dnxcore50 37 | System.Threading.Tasks (>= 4.0) - framework: dnxcore50 38 | System.Linq (4.0) - framework: dnxcore50 39 | System.Collections (>= 4.0.10) - framework: dnxcore50 40 | System.Diagnostics.Debug (>= 4.0.10) - framework: dnxcore50 41 | System.Resources.ResourceManager (>= 4.0) - framework: dnxcore50 42 | System.Runtime (>= 4.0.20) - framework: dnxcore50 43 | System.Runtime.Extensions (>= 4.0.10) - framework: dnxcore50 44 | System.Linq.Expressions (4.0.10) - framework: dnxcore50 45 | System.Collections (>= 4.0) - framework: dnxcore50 46 | System.Diagnostics.Debug (>= 4.0) - framework: dnxcore50 47 | System.Globalization (>= 4.0) - framework: dnxcore50 48 | System.IO (>= 4.0) - framework: dnxcore50 49 | System.Linq (>= 4.0) - framework: dnxcore50 50 | System.ObjectModel (>= 4.0) - framework: dnxcore50 51 | System.Reflection (>= 4.0) - framework: dnxcore50 52 | System.Reflection.Emit (>= 4.0) - framework: dnxcore50 53 | System.Reflection.Extensions (>= 4.0) - framework: dnxcore50 54 | System.Reflection.Primitives (>= 4.0) - framework: dnxcore50 55 | System.Reflection.TypeExtensions (>= 4.0) - framework: dnxcore50 56 | System.Resources.ResourceManager (>= 4.0) - framework: dnxcore50 57 | System.Runtime (>= 4.0) - framework: dnxcore50 58 | System.Runtime (>= 4.0.20) - framework: dnxcore50 59 | System.Runtime.Extensions (>= 4.0) - framework: dnxcore50 60 | System.Threading (>= 4.0) - framework: dnxcore50 61 | System.ObjectModel (4.0.10) - framework: dnxcore50 62 | System.Collections (>= 4.0.10) - framework: dnxcore50 63 | System.Diagnostics.Debug (>= 4.0.10) - framework: dnxcore50 64 | System.Resources.ResourceManager (>= 4.0) - framework: dnxcore50 65 | System.Runtime (>= 4.0.20) - framework: dnxcore50 66 | System.Threading (>= 4.0.10) - framework: dnxcore50 67 | System.Private.Uri (4.0) - framework: dnxcore50 68 | System.Reflection (4.0.10) - framework: dnxcore50 69 | System.IO (>= 4.0) - framework: dnxcore50 70 | System.Reflection.Primitives (>= 4.0) - framework: dnxcore50 71 | System.Runtime (>= 4.0.20) - framework: dnxcore50 72 | System.Reflection.Emit (4.0) - framework: dnxcore50 73 | System.IO (>= 4.0) - framework: dnxcore50 74 | System.Reflection (>= 4.0) - framework: dnxcore50 75 | System.Reflection.Emit.ILGeneration (>= 4.0) - framework: dnxcore50 76 | System.Reflection.Primitives (>= 4.0) - framework: dnxcore50 77 | System.Runtime (>= 4.0) - framework: dnxcore50 78 | System.Reflection.Emit.ILGeneration (4.0) - framework: dnxcore50 79 | System.Reflection (>= 4.0) - framework: dnxcore50 80 | System.Reflection.Primitives (>= 4.0) - framework: dnxcore50 81 | System.Runtime (>= 4.0) - framework: dnxcore50 82 | System.Reflection.Extensions (4.0) - framework: dnxcore50 83 | System.Diagnostics.Debug (>= 4.0.10) - framework: dnxcore50 84 | System.Reflection (>= 4.0) - framework: dnxcore50 85 | System.Reflection (>= 4.0.10) - framework: dnxcore50 86 | System.Reflection.Primitives (>= 4.0) - framework: dnxcore50 87 | System.Reflection.TypeExtensions (>= 4.0) - framework: dnxcore50 88 | System.Resources.ResourceManager (>= 4.0) - framework: dnxcore50 89 | System.Runtime (>= 4.0) - framework: dnxcore50 90 | System.Runtime (>= 4.0.20) - framework: dnxcore50 91 | System.Runtime.Extensions (>= 4.0.10) - framework: dnxcore50 92 | System.Reflection.Primitives (4.0) - framework: dnxcore50 93 | System.Runtime (>= 4.0) - framework: dnxcore50 94 | System.Threading (>= 4.0) - framework: dnxcore50 95 | System.Reflection.TypeExtensions (4.0) - framework: dnxcore50 96 | System.Diagnostics.Contracts (>= 4.0) - framework: dnxcore50 97 | System.Diagnostics.Debug (>= 4.0.10) - framework: dnxcore50 98 | System.Linq (>= 4.0) - framework: dnxcore50 99 | System.Reflection (>= 4.0) - framework: dnxcore50 100 | System.Reflection (>= 4.0.10) - framework: dnxcore50 101 | System.Reflection.Primitives (>= 4.0) - framework: dnxcore50 102 | System.Resources.ResourceManager (>= 4.0) - framework: dnxcore50 103 | System.Runtime (>= 4.0) - framework: dnxcore50 104 | System.Runtime (>= 4.0.20) - framework: dnxcore50 105 | System.Runtime.Extensions (>= 4.0.10) - framework: dnxcore50 106 | System.Resources.ResourceManager (4.0) - framework: dnxcore50 107 | System.Globalization (>= 4.0) - framework: dnxcore50 108 | System.Reflection (>= 4.0) - framework: dnxcore50 109 | System.Reflection (>= 4.0.10) - framework: dnxcore50 110 | System.Runtime (>= 4.0) - framework: dnxcore50 111 | System.Runtime (>= 4.0.20) - framework: dnxcore50 112 | System.Runtime (4.0.20) - framework: dnxcore50 113 | System.Private.Uri (>= 4.0) - framework: dnxcore50 114 | System.Runtime.Extensions (4.0.10) - framework: dnxcore50 115 | System.Runtime (>= 4.0.20) - framework: dnxcore50 116 | System.Text.Encoding (4.0.10) - framework: dnxcore50 117 | System.Runtime (>= 4.0) - framework: dnxcore50 118 | System.Text.Encoding.Extensions (4.0.10) - framework: dnxcore50 119 | System.Runtime (>= 4.0) - framework: dnxcore50 120 | System.Text.Encoding (>= 4.0.10) - framework: dnxcore50 121 | System.Text.RegularExpressions (4.0.10) - framework: dnxcore50 122 | System.Collections (>= 4.0.10) - framework: dnxcore50 123 | System.Globalization (>= 4.0.10) - framework: dnxcore50 124 | System.Resources.ResourceManager (>= 4.0) - framework: dnxcore50 125 | System.Runtime (>= 4.0.20) - framework: dnxcore50 126 | System.Runtime.Extensions (>= 4.0.10) - framework: dnxcore50 127 | System.Threading (>= 4.0.10) - framework: dnxcore50 128 | System.Threading (4.0.10) - framework: dnxcore50 129 | System.Runtime (>= 4.0) - framework: dnxcore50 130 | System.Threading.Tasks (>= 4.0) - framework: dnxcore50 131 | System.Threading.Tasks (4.0.10) - framework: dnxcore50 132 | System.Runtime (>= 4.0) - framework: dnxcore50 133 | xunit (2.1) 134 | xunit.assert (2.1) 135 | xunit.core (2.1) 136 | xunit.abstractions (2.0) - framework: >= net45, dnx451, dnxcore50, monoandroid, monotouch, portable-net45+win80+wp80+wpa81, xamarinios, winv4.5, wpv8.1, wpv8.0 137 | xunit.assert (2.1) 138 | System.Collections (>= 4.0) - framework: dnxcore50 139 | System.Diagnostics.Debug (>= 4.0) - framework: dnxcore50 140 | System.Globalization (>= 4.0) - framework: dnxcore50 141 | System.Linq (>= 4.0) - framework: dnxcore50 142 | System.ObjectModel (>= 4.0) - framework: dnxcore50 143 | System.Reflection (>= 4.0) - framework: dnxcore50 144 | System.Reflection.Extensions (>= 4.0) - framework: dnxcore50 145 | System.Runtime (>= 4.0) - framework: dnxcore50 146 | System.Runtime.Extensions (>= 4.0) - framework: dnxcore50 147 | System.Text.RegularExpressions (>= 4.0) - framework: dnxcore50 148 | System.Threading.Tasks (>= 4.0) - framework: dnxcore50 149 | xunit.core (2.1) 150 | System.Collections (>= 4.0) - framework: dnxcore50 151 | System.Diagnostics.Debug (>= 4.0) - framework: dnxcore50 152 | System.Globalization (>= 4.0) - framework: dnxcore50 153 | System.Linq (>= 4.0) - framework: dnxcore50 154 | System.Reflection (>= 4.0) - framework: dnxcore50 155 | System.Reflection.Extensions (>= 4.0) - framework: dnxcore50 156 | System.Runtime (>= 4.0) - framework: dnxcore50 157 | System.Runtime.Extensions (>= 4.0) - framework: dnxcore50 158 | System.Threading.Tasks (>= 4.0) - framework: dnxcore50 159 | xunit.abstractions (>= 2.0) - framework: dnxcore50 160 | xunit.extensibility.core (2.1) - framework: >= net45, dnx451, dnxcore50, monoandroid, monotouch, portable-net45+win80+wp80+wpa81, xamarinios, winv4.5, wpv8.0, wpv8.1 161 | xunit.extensibility.execution (2.1) - framework: >= net45, dnx451, dnxcore50, monoandroid, monotouch, portable-net45+win80+wp80+wpa81, xamarinios, winv4.5, wpv8.0, wpv8.1 162 | xunit.extensibility.core (2.1) - framework: >= net45, dnx451, dnxcore50, monoandroid, monotouch, portable-net45+win80+wp80+wpa81, xamarinios, winv4.5, wpv8.1, wpv8.0 163 | xunit.abstractions (2.0) 164 | xunit.extensibility.execution (2.1) - framework: >= net45, dnx451, dnxcore50, monoandroid, monotouch, portable-net45+win80+wp80+wpa81, xamarinios, winv4.5, wpv8.1, wpv8.0 165 | System.Collections (>= 4.0) - framework: dnxcore50 166 | System.Diagnostics.Debug (>= 4.0) - framework: dnxcore50 167 | System.Globalization (>= 4.0) - framework: dnxcore50 168 | System.IO (>= 4.0) - framework: dnxcore50 169 | System.Linq (>= 4.0) - framework: dnxcore50 170 | System.Linq.Expressions (>= 4.0) - framework: dnxcore50 171 | System.Reflection (>= 4.0) - framework: dnxcore50 172 | System.Reflection.Extensions (>= 4.0) - framework: dnxcore50 173 | System.Runtime (>= 4.0) - framework: dnxcore50 174 | System.Runtime.Extensions (>= 4.0) - framework: dnxcore50 175 | System.Text.Encoding (>= 4.0) - framework: dnxcore50 176 | System.Threading (>= 4.0) - framework: dnxcore50 177 | System.Threading.Tasks (>= 4.0) - framework: dnxcore50 178 | xunit.abstractions (>= 2.0) - framework: dnxcore50 179 | xunit.extensibility.core (2.1) - framework: >= net45, dnx451, dnxcore50, monoandroid, monotouch, xamarinios, winv4.5, wpv8.0, wpv8.1 180 | xunit.runner.console (2.1) 181 | --------------------------------------------------------------------------------