diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Neo4j.Driver.Tests.csproj b/Neo4j.Driver/Neo4j.Driver.Tests/Neo4j.Driver.Tests.csproj
index da9980b5e..d800e715b 100644
--- a/Neo4j.Driver/Neo4j.Driver.Tests/Neo4j.Driver.Tests.csproj
+++ b/Neo4j.Driver/Neo4j.Driver.Tests/Neo4j.Driver.Tests.csproj
@@ -114,6 +114,8 @@
+
+
diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Result/ResultReaderBuilderTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Result/ResultReaderBuilderTests.cs
new file mode 100644
index 000000000..0fd950899
--- /dev/null
+++ b/Neo4j.Driver/Neo4j.Driver.Tests/Result/ResultReaderBuilderTests.cs
@@ -0,0 +1,153 @@
+// Copyright (c) 2002-2017 "Neo Technology,"
+// Network Engine for Objects in Lund AB [http://neotechnology.com]
+//
+// This file is part of Neo4j.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Neo4j.Driver.Internal.Result;
+using Neo4j.Driver.V1;
+using Xunit;
+
+namespace Neo4j.Driver.Tests
+{
+ public class ResultReaderBuilderTests
+ {
+ private static ResultReaderBuilder GenerateBuilder(IDictionary meta = null)
+ {
+ var builder = new ResultReaderBuilder();
+ builder.CollectFields(meta ?? new Dictionary { { "fields", new List { "x" } } });
+ return builder;
+ }
+
+ private static Task AssertGetExpectResults(IStatementResultReader reader, int numberExpected, List exspectedRecordsValues = null)
+ {
+ int count = 0;
+ var t = Task.Factory.StartNew(async () =>
+ {
+ // ReSharper disable once LoopCanBeConvertedToQuery
+ while (await reader.ReadAsync())
+ {
+ if (exspectedRecordsValues != null)
+ {
+ reader.Current().Values.First().Value.Should().Be(exspectedRecordsValues[count]);
+ }
+
+ count++;
+ }
+
+ count.Should().Be(numberExpected);
+ });
+ return t;
+ }
+
+ public class CollectRecordMethod
+ {
+ [Fact]
+ public void ShouldStreamResults()
+ {
+ var builder = GenerateBuilder();
+ var i = 0;
+ builder.SetReceiveOneFunc(() =>
+ {
+ if (i++ >= 3)
+ {
+ builder.CollectSummary(null);
+ }
+ else
+ {
+ builder.CollectRecord(new object[] {123 + i});
+ }
+
+ return Task.CompletedTask;
+ });
+ var result = builder.PreBuild();
+
+ var t = AssertGetExpectResults(result, 3, new List {124, 125, 126});
+
+ t.Wait();
+ }
+
+ [Fact]
+ public void ShouldReturnNoResultsWhenNoneRecieved()
+ {
+ var builder = GenerateBuilder();
+ builder.SetReceiveOneFunc(() =>
+ {
+ builder.CollectSummary(null);
+
+ return Task.CompletedTask;
+ });
+ var result = builder.PreBuild();
+
+ var t = AssertGetExpectResults(result, 0);
+
+ t.Wait();
+ }
+
+ [Fact]
+ public void ShouldReturnQueuedResultsWithExspectedValue()
+ {
+ var builder = GenerateBuilder();
+ List recordValues = new List
+ {
+ 1,
+ "Hello",
+ false,
+ 10
+ };
+ for (int i = 0; i < recordValues.Count; i++)
+ {
+ builder.CollectRecord(new[] { recordValues[i] });
+ }
+ builder.CollectSummary(null);
+
+ var result = builder.PreBuild();
+
+ var task = AssertGetExpectResults(result, recordValues.Count, recordValues);
+ task.Wait();
+ }
+ }
+
+ public class InvalidateResultMethod
+ {
+ [Fact]
+ public void ShouldStopStreamingWhenResultIsInvalid()
+ {
+ var builder = GenerateBuilder();
+ var i = 0;
+ builder.SetReceiveOneFunc(() =>
+ {
+ if (i++ >= 3)
+ {
+ builder.DoneFailure();
+ }
+ else
+ {
+ builder.CollectRecord(new object[] {123 + i});
+ }
+
+ return Task.CompletedTask;
+ });
+ var result = builder.PreBuild();
+
+ var t = AssertGetExpectResults(result, 3, new List { 124, 125, 126 });
+ t.Wait();
+ }
+ }
+ }
+}
diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultReaderTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultReaderTests.cs
new file mode 100644
index 000000000..ee449b71b
--- /dev/null
+++ b/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultReaderTests.cs
@@ -0,0 +1,443 @@
+// Copyright (c) 2002-2017 "Neo Technology,"
+// Network Engine for Objects in Lund AB [http://neotechnology.com]
+//
+// This file is part of Neo4j.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Xunit;
+using Xunit.Abstractions;
+using Neo4j.Driver.Internal.Result;
+using Neo4j.Driver.V1;
+using Record = Neo4j.Driver.Internal.Result.Record;
+
+namespace Neo4j.Driver.Tests
+{
+ public class StatementResultReaderTests
+ {
+
+ private static Task NextRecordFromEnum(IEnumerator resultEnum)
+ {
+ if (resultEnum.MoveNext())
+ {
+ return Task.FromResult(resultEnum.Current);
+ }
+ else
+ {
+ return Task.FromResult((IRecord)null);
+ }
+ }
+
+ private static class ResultReaderCreator
+ {
+ public static StatementResultReader CreateResultReader(int keySize, int recordSize = 1,
+ Func> getSummaryFunc = null)
+ {
+ var keys = RecordCreator.CreateKeys(keySize);
+ var records = RecordCreator.CreateRecords(recordSize, keys);
+ var recordsEnum = records.GetEnumerator();
+
+ return new StatementResultReader(keys, () => NextRecordFromEnum(recordsEnum), getSummaryFunc);
+ }
+
+ }
+
+ public class Constructor
+ {
+ [Fact]
+ public void ShouldThrowArgumentNullExceptionIfRecordsIsNull()
+ {
+ var ex = Xunit.Record.Exception(() => new StatementResult(new List{"test"}, null));
+ ex.Should().NotBeNull();
+ ex.Should().BeOfType();
+ }
+
+ [Fact]
+ public void ShouldThrowArgumentNullExceptionIfKeysIsNull()
+ {
+ var ex = Xunit.Record.Exception(() => new StatementResult(null, new ListBasedRecordSet(new List())));
+ ex.Should().NotBeNull();
+ ex.Should().BeOfType();
+ }
+
+ [Fact]
+ public void ShouldSetKeysProperlyIfKeysNotNull()
+ {
+ var result = new StatementResult(new List{"test"}, new ListBasedRecordSet(new List()));
+ result.Keys.Should().HaveCount(1);
+ result.Keys.Should().Contain("test");
+ }
+ }
+
+ public class Keys
+ {
+
+ [Fact]
+ public void KeysShouldReturnTheSameGivenInConstructor()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1);
+
+ result.Keys.Count.Should().Be(1);
+ result.Keys.Should().Contain("key0");
+ }
+
+ }
+
+ public class ConsumeAsyncMethod
+ {
+ // INFO: Rewritten because StatementResult no longers takes IPeekingEnumerator in constructor
+ [Fact]
+ public async void ShouldConsumeAllRecords()
+ {
+ var result = ResultReaderCreator.CreateResultReader(0, 3);
+ await result.ConsumeAsync();
+ var rec = await result.PeekAsync();
+ rec.Should().BeNull();
+ var read = await result.ReadAsync();
+ read.Should().BeFalse();
+ }
+
+ [Fact]
+ public async void ShouldConsumeSummaryCorrectly()
+ {
+ int getSummaryCalled = 0;
+ var result = ResultReaderCreator.CreateResultReader(1, 0, () => { getSummaryCalled++; return Task.FromResult((IResultSummary)new FakeSummary()); });
+
+
+ await result.ConsumeAsync();
+ getSummaryCalled.Should().Be(1);
+
+ // the same if we call it multiple times
+ await result.ConsumeAsync();
+ getSummaryCalled.Should().Be(1);
+ }
+
+ [Fact]
+ public async void ShouldThrowNoExceptionWhenCallingMultipleTimes()
+ {
+
+ var result = ResultReaderCreator.CreateResultReader(1);
+
+ await result.ConsumeAsync();
+ var ex = await Xunit.Record.ExceptionAsync(() => result.ConsumeAsync());
+ ex.Should().BeNull();
+ }
+
+ [Fact]
+ public async void ShouldConsumeRecordCorrectly()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1, 3);
+
+ await result.ConsumeAsync();
+
+ var read = await result.ReadAsync();
+ read.Should().BeFalse();
+ result.Current().Should().BeNull();
+ }
+ }
+
+ public class StreamingRecords
+ {
+ private readonly ITestOutputHelper _output;
+
+ private class TestRecordYielder
+ {
+ private readonly IList _records = new List();
+ private readonly int _total = 0;
+
+ private readonly ITestOutputHelper _output;
+ public static List Keys => new List{"Test", "Keys"};
+
+ public TestRecordYielder(int count, int total, ITestOutputHelper output)
+ {
+ Add(count);
+ _total = total;
+ _output = output;
+ }
+
+ public void AddNew(int count)
+ {
+ Add(count);
+ }
+
+ private void Add(int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ _records.Add(new Record(Keys, new object[] { "Test", 123 }));
+ }
+ }
+
+ public IEnumerable Records
+ {
+ get
+ {
+ int i = 0;
+ while (i < _total)
+ {
+ while (i == _records.Count)
+ {
+ _output.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} -> Waiting for more Records");
+ Thread.Sleep(50);
+ }
+
+ yield return _records[i];
+ i++;
+ }
+ }
+ }
+
+ public IEnumerable RecordsWithAutoLoad
+ {
+ get
+ {
+ int i = 0;
+ while (i < _total)
+ {
+ while (i == _records.Count)
+ {
+ _output.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} -> Waiting for more Records");
+ Thread.Sleep(500);
+ AddNew(1);
+ _output.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} -> Record arrived");
+ }
+
+ yield return _records[i];
+ i++;
+ }
+ }
+ }
+ }
+
+ public StreamingRecords(ITestOutputHelper output)
+ {
+ _output = output;
+ }
+
+
+
+ [Fact]
+ public async void ShouldReturnRecords()
+ {
+ var recordYielder = new TestRecordYielder(5, 10, _output);
+ var recordYielderEnum = recordYielder.RecordsWithAutoLoad.GetEnumerator();
+ var cursor = new StatementResultReader( TestRecordYielder.Keys, () => NextRecordFromEnum(recordYielderEnum));
+ var records = new List();
+ while (await cursor.ReadAsync())
+ {
+ records.Add(cursor.Current());
+ }
+
+ records.Count.Should().Be(10);
+ }
+
+ [Fact]
+ public async void ShouldWaitForAllRecordsToArrive()
+ {
+ var recordYielder = new TestRecordYielder(5, 10, _output);
+ var recordYielderEnum = recordYielder.Records.GetEnumerator();
+
+ int count = 0;
+ var cursor = new StatementResultReader(TestRecordYielder.Keys, () => NextRecordFromEnum(recordYielderEnum));
+ var t = Task.Factory.StartNew(async () =>
+ {
+ // ReSharper disable once LoopCanBeConvertedToQuery
+ while (await cursor.ReadAsync())
+ {
+ count++;
+ }
+
+ count.Should().Be(10);
+ });
+
+ while (count < 5)
+ {
+ Thread.Sleep(10);
+ }
+
+ recordYielder.AddNew(5);
+
+ await t;
+ }
+
+ [Fact]
+ public async void ShouldReturnRecordsImmediatelyWhenReady()
+ {
+ var recordYielder = new TestRecordYielder(5, 10, _output);
+ var recordYielderEnum = recordYielder.Records.GetEnumerator();
+ var result = new StatementResultReader(TestRecordYielder.Keys, () => NextRecordFromEnum(recordYielderEnum));
+ var records = new List();
+ var count = 5;
+ while (count > 0 && await result.ReadAsync())
+ {
+ records.Add(result.Current());
+ count--;
+ }
+ records.Count.Should().Be(5);
+ }
+ }
+
+ public class ResultNavigation
+ {
+ [Fact]
+ public async void ShouldGetTheFirstRecordAndMoveToNextPosition()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1, 3);
+ var read = await result.ReadAsync();
+ read.Should().BeTrue();
+ var record = result.Current();
+ record[0].ValueAs().Should().Be("record0:key0");
+
+ read = await result.ReadAsync();
+ read.Should().BeTrue();
+ record = result.Current();
+ record[0].ValueAs().Should().Be("record1:key0");
+ }
+
+ //[Fact]
+ //public void ShouldAlwaysAdvanceRecordPosition()
+ //{
+ // var result = ResultReaderCreator.CreateResultReader(1, 3);
+ // var enumerable = result.Take(1);
+ // var records = result.Take(2).ToList();
+
+ // records[0][0].ValueAs().Should().Be("record0:key0");
+ // records[1][0].ValueAs().Should().Be("record1:key0");
+
+ // records = enumerable.ToList();
+ // records[0][0].ValueAs().Should().Be("record2:key0");
+ //}
+ }
+
+ public class SummaryAsyncMethod
+ {
+ [Fact]
+ public async void ShouldCallGetSummaryWhenGetSummaryIsNotNull()
+ {
+ bool getSummaryCalled = false;
+ var result = ResultReaderCreator.CreateResultReader(1, 0, () => { getSummaryCalled = true; return Task.FromResult((IResultSummary)null); });
+
+ // ReSharper disable once UnusedVariable
+ var summary = await result.SummaryAsync();
+
+ getSummaryCalled.Should().BeTrue();
+ }
+
+ [Fact]
+ public async void ShouldReturnNullWhenGetSummaryIsNull()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1, 0);
+ var summary = await result.SummaryAsync();
+
+ summary.Should().BeNull();
+ }
+
+ [Fact]
+ public async void ShouldReturnExistingSummaryWhenSummaryHasBeenRetrieved()
+ {
+ int getSummaryCalled = 0;
+ var result = ResultReaderCreator.CreateResultReader(1, 0, () => { getSummaryCalled++; return Task.FromResult((IResultSummary)new FakeSummary()); });
+
+ // ReSharper disable once NotAccessedVariable
+ var summary = await result.SummaryAsync();
+ // ReSharper disable once RedundantAssignment
+ summary = await result.SummaryAsync();
+ getSummaryCalled.Should().Be(1);
+ }
+ }
+
+ public class PeekAsyncMethod
+ {
+ [Fact]
+ public async void ShouldReturnNextRecordWithoutMovingCurrentRecord()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1);
+ var record = await result.PeekAsync();
+ record.Should().NotBeNull();
+
+ result.Current().Should().BeNull();
+ }
+
+ [Fact]
+ public async void ShouldReturnNullJustBeforeAtEnd()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1);
+ var read = await result.ReadAsync();
+ read.Should().BeTrue();
+ var record = await result.PeekAsync();
+ record.Should().BeNull();
+ }
+
+ [Fact]
+ public async void ShouldReturnNullIfAtEnd()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1);
+ var read = await result.ReadAsync();
+ read.Should().BeTrue();
+ read = await result.ReadAsync();
+ read.Should().BeFalse();
+ var record = await result.PeekAsync();
+ record.Should().BeNull();
+ }
+
+ [Fact]
+ public async void ShouldReturnSameRecordIfPeekedTwice()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1);
+ var peeked1 = await result.PeekAsync();
+ peeked1.Should().NotBeNull();
+ var peeked2 = await result.PeekAsync();
+ peeked2.Should().NotBeNull();
+ peeked2.Should().Be(peeked1);
+ }
+ }
+
+ public class ReadAsyncMethod
+ {
+
+ [Fact]
+ public async void ReadAsyncAndCurrentWillReturnPeekedAfterPeek()
+ {
+ var result = ResultReaderCreator.CreateResultReader(1);
+ var peeked = await result.PeekAsync();
+ peeked.Should().NotBeNull();
+ var read = await result.ReadAsync();
+ read.Should().BeTrue();
+ var record = result.Current();
+ record.Should().NotBeNull();
+ record.Should().Be(peeked);
+ }
+
+ }
+
+ private class FakeSummary : IResultSummary
+ {
+ public Statement Statement { get; }
+ public ICounters Counters { get; }
+ public StatementType StatementType { get; }
+ public bool HasPlan { get; }
+ public bool HasProfile { get; }
+ public IPlan Plan { get; }
+ public IProfiledPlan Profile { get; }
+ public IList Notifications { get; }
+ public TimeSpan ResultAvailableAfter { get; }
+ public TimeSpan ResultConsumedAfter { get; }
+ public IServerInfo Server { get; }
+ }
+ }
+}
diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/SessionTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/SessionTests.cs
index f4a5fbebb..9fb6f236f 100644
--- a/Neo4j.Driver/Neo4j.Driver.Tests/SessionTests.cs
+++ b/Neo4j.Driver/Neo4j.Driver.Tests/SessionTests.cs
@@ -67,6 +67,32 @@ public void ResultBuilderShouldObtainServerInfoFromConnection()
}
}
+ public class RunAsyncMethod
+ {
+ [Fact]
+ public async void ShouldSendOnRun()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ await session.RunAsync("lalalal");
+
+ mockConn.Verify(x => x.Run("lalalal", new Dictionary(), It.IsAny(), true), Times.Once);
+ mockConn.Verify(x => x.SendAsync());
+ }
+
+ [Fact]
+ public async void ResultBuilderShouldObtainServerInfoFromConnection()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ await session.RunAsync("lalalal");
+
+ mockConn.Verify(x => x.Server, Times.Once);
+ }
+ }
+
public class BeginTransactionMethod
{
[Fact]
@@ -216,6 +242,126 @@ public void ShouldDisposeConnectionOnNewBeginTxIfBeginTxFailed()
}
}
+ public class BeginTransactionAsyncMethod
+ {
+ [Fact]
+ public async void ShouldNotAllowNewTxWhileOneIsRunning()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ await session.BeginTransactionAsync();
+ var error = await Record.ExceptionAsync(() => session.BeginTransactionAsync());
+ error.Should().BeOfType();
+ }
+
+ [Fact]
+ public async void ShouldBeAbleToOpenTxAfterPreviousIsClosed()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ var tx = await session.BeginTransactionAsync();
+ await tx.RollbackAsync();
+ tx = await session.BeginTransactionAsync();
+ }
+
+ [Fact]
+ public async void ShouldNotBeAbleToUseSessionWhileOngoingTransaction()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ var tx = await session.BeginTransactionAsync();
+
+ var error = await Record.ExceptionAsync(() => session.RunAsync("lalal"));
+ error.Should().BeOfType();
+ }
+
+ [Fact]
+ public async void ShouldBeAbleToUseSessionAgainWhenTransactionIsClosed()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ var tx = await session.BeginTransactionAsync();
+ await tx.RollbackAsync();
+
+ await session.RunAsync("lalal");
+ }
+
+ [Fact]
+ public async void ShouldClosePreviousRunConnectionWhenRunMoreStatements()
+ {
+ var mockConn = new Mock();
+ var session = NewSession(mockConn.Object);
+ await session.RunAsync("lalal");
+
+ await session.RunAsync("bibib");
+ mockConn.Verify(c => c.CloseAsync(), Times.Once);
+ }
+
+ [Fact]
+ public async void ShouldClosePreviousRunConnectionWhenRunMoreTransactions()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(false);
+ var session = NewSession(mockConn.Object);
+ await session.RunAsync("lala");
+
+ await session.BeginTransactionAsync();
+ mockConn.Verify(c => c.CloseAsync(), Times.Once);
+ }
+
+ [Fact]
+ public async void ShouldDisposeConnectionOnRunIfBeginTxFailed()
+ {
+ // Given
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ mockConn.Setup(x => x.Run("BEGIN", null, null, true))
+ .Throws(new IOException("Triggered an error when beginTx"));
+ var session = NewSession(mockConn.Object);
+ var exc = await Record.ExceptionAsync(() => session.BeginTransactionAsync());
+ exc.Should().BeOfType();
+
+ // When
+ await session.RunAsync("lala");
+
+ // Then
+ mockConn.Verify(x => x.CloseAsync(), Times.Once);
+ }
+
+ [Fact]
+ public async void ShouldDisposeConnectionOnNewBeginTxIfBeginTxFailed()
+ {
+ // Given
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var calls = 0;
+ mockConn.Setup(x => x.Run("BEGIN", null, null, true))
+ .Callback(() =>
+ {
+ // only throw exception on the first beginTx call
+ calls++;
+ if (calls == 1)
+ {
+ throw new IOException("Triggered an error when beginTx");
+ }
+ });
+ var session = NewSession(mockConn.Object);
+ var exc = await Record.ExceptionAsync(() => session.BeginTransactionAsync());
+ exc.Should().BeOfType();
+
+ // When
+ await session.BeginTransactionAsync();
+
+ // Then
+ mockConn.Verify(x => x.CloseAsync(), Times.Once);
+ }
+ }
+
+
public class DisposeMethod
{
[Fact]
@@ -275,6 +421,51 @@ public void ShouldThrowExceptionWhenDisposingSessionMoreThanOnce()
}
}
+ public class DisposeMethodOnAsync
+ {
+ [Fact]
+ public async void ShouldDisposeConnectionIfBeginTxFailed()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ mockConn.Setup(x => x.Run("BEGIN", null, null, true))
+ .Throws(new IOException("Triggered an error when beginTx"));
+ var session = NewSession(mockConn.Object);
+ var error = await Record.ExceptionAsync(() => session.BeginTransactionAsync());
+ error.Should().BeOfType();
+ session.Dispose();
+
+ mockConn.Verify(x => x.Close(), Times.Once);
+ }
+
+ [Fact]
+ public async void ShouldDisposeTxOnDispose()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ var tx = await session.BeginTransactionAsync();
+ session.Dispose();
+
+ mockConn.Verify(x => x.Run("ROLLBACK", null, null, false), Times.Once);
+ mockConn.Verify(x => x.Close(), Times.Once);
+ }
+
+ [Fact]
+ public async void ShouldDisposeConnectinOnDispose()
+ {
+ var mockConn = new Mock();
+ mockConn.Setup(x => x.IsOpen).Returns(true);
+ var session = NewSession(mockConn.Object);
+ await session.RunAsync("lalal");
+ session.Dispose();
+
+ mockConn.Verify(x => x.Sync(), Times.Once);
+ mockConn.Verify(x => x.Close(), Times.Once);
+ }
+
+ }
+
private class TestConnectionProvider : IConnectionProvider
{
private IConnection Connection { get; set; }
@@ -297,7 +488,7 @@ public IConnection Acquire(AccessMode mode)
public Task AcquireAsync(AccessMode mode)
{
- throw new NotSupportedException();
+ return Task.FromResult(Connection);
}
}
}
diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/TransactionTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/TransactionTests.cs
index d5d7f4b66..a9d1a9757 100644
--- a/Neo4j.Driver/Neo4j.Driver.Tests/TransactionTests.cs
+++ b/Neo4j.Driver/Neo4j.Driver.Tests/TransactionTests.cs
@@ -173,6 +173,66 @@ public void ResultBuilderShouldObtainServerInfoFromConnection()
}
}
+ public class RunAsyncMethod
+ {
+ [Fact]
+ public async void ShouldRunPullAllSyncRun()
+ {
+ var mockConn = new Mock();
+ var tx = new Transaction(mockConn.Object);
+
+ await tx.RunAsync("lalala");
+
+ mockConn.Verify(x => x.Run("lalala", new Dictionary(), It.IsAny(), true), Times.Once);
+ mockConn.Verify(x => x.SendAsync(), Times.Once);
+ }
+
+ [Fact]
+ public async void ShouldThrowExceptionIfPreviousTxFailed()
+ {
+ var mockConn = new Mock();
+ var tx = new Transaction(mockConn.Object);
+
+ try
+ {
+ mockConn.Setup(x => x.Run(It.IsAny(), new Dictionary(), It.IsAny(), true))
+ .Throws();
+ await tx.RunAsync("lalala");
+ }
+ catch (Neo4jException)
+ {
+ // Fine, the state is set to failed now.
+ }
+
+ var error = await Xunit.Record.ExceptionAsync(() => tx.RunAsync("ttt"));
+ error.Should().BeOfType();
+ }
+
+ [Fact]
+ public async void ShouldThrowExceptionIfFailedToRunAndFetchResult()
+ {
+ var mockConn = new Mock();
+ var tx = new Transaction(mockConn.Object);
+
+ mockConn.Setup(x => x.Run(It.IsAny(), new Dictionary(), It.IsAny(), true))
+ .Throws();
+
+ var error = await Xunit.Record.ExceptionAsync(() => tx.RunAsync("ttt"));
+ error.Should().BeOfType();
+ }
+
+ [Fact]
+ public async void ResultBuilderShouldObtainServerInfoFromConnection()
+ {
+ var mockConn = new Mock();
+ var tx = new Transaction(mockConn.Object);
+
+ await tx.RunAsync("lalala");
+
+ mockConn.Verify(x => x.Server, Times.Once);
+ }
+ }
+
public class DisposeMethod
{
[Fact]
diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/BuilderBase.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/BuilderBase.cs
new file mode 100644
index 000000000..036f1df12
--- /dev/null
+++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/BuilderBase.cs
@@ -0,0 +1,253 @@
+// Copyright (c) 2002-2017 "Neo Technology,"
+// Network Engine for Objects in Lund AB [http://neotechnology.com]
+//
+// This file is part of Neo4j.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+using System.Collections.Generic;
+using System.Linq;
+using static Neo4j.Driver.V1.StatementType;
+using System;
+using Neo4j.Driver.V1;
+
+namespace Neo4j.Driver.Internal.Result
+{
+ internal abstract class BuilderBase : IMessageResponseCollector
+ {
+ protected readonly List _keys = new List();
+ protected readonly SummaryBuilder _summaryBuilder;
+ protected readonly IResultResourceHandler _resourceHandler;
+
+ protected BuilderBase(Statement statement, IServerInfo server, IResultResourceHandler resourceHandler = null)
+ {
+ _summaryBuilder = new SummaryBuilder(statement, server);
+ _resourceHandler = resourceHandler;
+ }
+
+ public abstract void CollectRecord(object[] fields);
+
+ public void CollectFields(IDictionary meta)
+ {
+ if (meta == null)
+ {
+ return;
+ }
+ CollectKeys(meta, "fields");
+ CollectResultAvailableAfter(meta, "result_available_after");
+ }
+
+ public void CollectBookmark(IDictionary meta)
+ {
+ throw new NotSupportedException(
+ $"Should not get a bookmark on a result. bookmark = {meta[Bookmark.BookmarkKey].As()}");
+ }
+
+ public virtual void CollectSummary(IDictionary meta)
+ {
+ if (meta == null)
+ {
+ return;
+ }
+ CollectType(meta, "type");
+ CollectCounters(meta, "stats");
+ CollectPlan(meta, "plan");
+ CollectProfile(meta, "profile");
+ CollectNotifications(meta, "notifications");
+ CollectResultConsumedAfter(meta, "result_consumed_after");
+ }
+
+ public abstract void DoneSuccess();
+
+ public abstract void DoneFailure();
+
+ public abstract void DoneIgnored();
+
+ private void CollectKeys(IDictionary meta, string name)
+ {
+ if (!meta.ContainsKey(name))
+ {
+ return;
+ }
+
+ if (meta.ContainsKey(name))
+ {
+ foreach (var key in meta[name].As>())
+ {
+ _keys.Add(key);
+ }
+ }
+ }
+
+ private void CollectType(IDictionary meta, string name)
+ {
+ if (!meta.ContainsKey(name))
+ {
+ return;
+ }
+ var type = meta[name] as string;
+ _summaryBuilder.StatementType = FromCode(type);
+ }
+
+ private void CollectCounters(IDictionary meta, string name)
+ {
+ if (!meta.ContainsKey(name))
+ {
+ return;
+ }
+ var stats = meta[name] as IDictionary;
+
+ _summaryBuilder.Counters = new Counters(
+ CountersValue(stats, "nodes-created"),
+ CountersValue(stats, "nodes-deleted"),
+ CountersValue(stats, "relationships-created"),
+ CountersValue(stats, "relationships-deleted"),
+ CountersValue(stats, "properties-set"),
+ CountersValue(stats, "labels-added"),
+ CountersValue(stats, "labels-removed"),
+ CountersValue(stats, "indexes-added"),
+ CountersValue(stats, "indexes-removed"),
+ CountersValue(stats, "constraints-added"),
+ CountersValue(stats, "constraints-removed"));
+ }
+
+ private void CollectPlan(IDictionary meta, string name)
+ {
+ if (meta == null || !meta.ContainsKey(name))
+ {
+ return;
+ }
+ var planDictionary = meta[name] as IDictionary;
+ _summaryBuilder.Plan = CollectPlan(planDictionary);
+ }
+
+
+ private static IPlan CollectPlan(IDictionary planDictionary)
+ {
+ if (planDictionary == null || planDictionary.Count == 0)
+ {
+ return null;
+ }
+
+ var operationType = planDictionary.GetMandatoryValue("operatorType");
+ var args = planDictionary.GetValue("args", new Dictionary());
+ var identifiers = planDictionary.GetValue("identifiers", new List()).Cast();
+ var children = planDictionary.GetValue("children", new List());
+
+ var childPlans = children
+ .Select(child => child as IDictionary)
+ .Select(CollectPlan)
+ .Where(childPlan => childPlan != null)
+ .ToList();
+ return new Plan(operationType, args, identifiers.ToList(), childPlans);
+ }
+
+ private static IProfiledPlan CollectProfile(IDictionary profileDictionary)
+ {
+ if (profileDictionary == null || profileDictionary.Count == 0)
+ {
+ return null;
+ }
+ var operationType = profileDictionary.GetMandatoryValue("operatorType");
+ var args = profileDictionary.GetValue("args", new Dictionary());
+ var identifiers = profileDictionary.GetValue("identifiers", new List()).Cast();
+ var dbHits = profileDictionary.GetMandatoryValue("dbHits");
+ var rows = profileDictionary.GetMandatoryValue("rows");
+ var children = profileDictionary.GetValue("children", new List());
+
+ var childPlans = children
+ .Select(child => child as IDictionary)
+ .Select(CollectProfile)
+ .Where(childProfile => childProfile != null)
+ .ToList();
+ return new ProfiledPlan(operationType, args, identifiers.ToList(), childPlans, dbHits, rows);
+ }
+
+
+ private void CollectProfile(IDictionary meta, string name)
+ {
+ if (!meta.ContainsKey(name))
+ {
+ return;
+ }
+ var profiledPlan = meta[name] as IDictionary;
+ _summaryBuilder.Profile = CollectProfile(profiledPlan);
+ }
+
+ private void CollectNotifications(IDictionary meta, string name)
+ {
+ if (!meta.ContainsKey(name))
+ {
+ return;
+ }
+ var list = (meta[name] as List).Cast>();
+ var notifications = new List();
+ foreach (var value in list)
+ {
+ var code = value.GetValue("code", string.Empty);
+ var title = value.GetValue("title", string.Empty);
+ var description = value.GetValue("description", string.Empty);
+
+ var posValue = value.GetValue("position", new Dictionary());
+
+ var position = new InputPosition(
+ (int)posValue.GetValue("offset", 0L),
+ (int)posValue.GetValue("line", 0L),
+ (int)posValue.GetValue("column", 0L));
+ var severity = value.GetValue("severity", string.Empty);
+ notifications.Add(new Notification(code, title, description, position, severity));
+ }
+ _summaryBuilder.Notifications = notifications;
+ }
+
+ private void CollectResultAvailableAfter(IDictionary meta, string name)
+ {
+ if (!meta.ContainsKey(name))
+ {
+ return;
+ }
+ _summaryBuilder.ResultAvailableAfter = meta[name].As();
+ }
+
+ private void CollectResultConsumedAfter(IDictionary meta, string name)
+ {
+ if (!meta.ContainsKey(name))
+ {
+ return;
+ }
+ _summaryBuilder.ResultConsumedAfter = meta[name].As();
+ }
+
+ private static int CountersValue(IDictionary counters, string name)
+ {
+ return (int)counters.GetValue(name, 0L);
+ }
+
+ private static StatementType FromCode(string type)
+ {
+ switch (type.ToLowerInvariant())
+ {
+ case "r":
+ return ReadOnly;
+ case "rw":
+ return ReadWrite;
+ case "w":
+ return WriteOnly;
+ case "s":
+ return SchemaWrite;
+ default:
+ throw new ClientException("Unknown statement type: `" + type + "`.");
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs
index 5281f17e2..f40e3cfa7 100644
--- a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs
+++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs
@@ -22,26 +22,20 @@
namespace Neo4j.Driver.Internal.Result
{
- internal class ResultBuilder : IMessageResponseCollector
+ internal class ResultBuilder : BuilderBase
{
- private readonly List _keys = new List();
- private readonly SummaryBuilder _summaryBuilder;
-
private Action _receiveOneAction;
private readonly Queue _records = new Queue();
private bool _hasMoreRecords = true;
- private readonly IResultResourceHandler _resourceHandler;
-
public ResultBuilder() : this(null, null, null, null, null)
{
}
public ResultBuilder(Statement statement, Action receiveOneAction, IServerInfo server, IResultResourceHandler resourceHandler = null)
+ : base(statement, server, resourceHandler)
{
- _summaryBuilder = new SummaryBuilder(statement, server);
- _resourceHandler = resourceHandler;
SetReceiveOneAction(receiveOneAction);
}
@@ -102,55 +96,30 @@ internal void SetReceiveOneAction(Action receiveOneAction)
};
}
- public void CollectRecord(object[] fields)
+ public override void CollectRecord(object[] fields)
{
var record = new Record(_keys, fields);
_records.Enqueue(record);
}
-
- public void CollectFields(IDictionary meta)
- {
- if (meta == null)
- {
- return;
- }
- CollectKeys(meta, "fields");
- CollectResultAvailableAfter(meta, "result_available_after");
- }
-
- public void CollectBookmark(IDictionary meta)
- {
- throw new NotSupportedException(
- $"Should not get a bookmark on a result. bookmark = {meta[Bookmark.BookmarkKey].As()}");
- }
-
- public void CollectSummary(IDictionary meta)
+
+ public override void CollectSummary(IDictionary meta)
{
NoMoreRecords();
- if (meta == null)
- {
- return;
- }
- CollectType(meta, "type");
- CollectCounters(meta, "stats");
- CollectPlan(meta, "plan");
- CollectProfile(meta, "profile");
- CollectNotifications(meta, "notifications");
- CollectResultConsumedAfter(meta, "result_consumed_after");
+ base.CollectSummary(meta);
}
- public void DoneSuccess()
+ public override void DoneSuccess()
{
// do nothing
}
- public void DoneFailure()
+ public override void DoneFailure()
{
NoMoreRecords();// an error received, so the result is broken
}
- public void DoneIgnored()
+ public override void DoneIgnored()
{
NoMoreRecords();// the result is ignored
}
@@ -159,183 +128,6 @@ private void NoMoreRecords()
{
_hasMoreRecords = false;
}
-
- private void CollectKeys(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
-
- if (meta.ContainsKey(name))
- {
- foreach (var key in meta[name].As>())
- {
- _keys.Add(key);
- }
- }
- }
-
- private void CollectType(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var type = meta[name] as string;
- _summaryBuilder.StatementType = FromCode(type);
- }
-
- private void CollectCounters(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var stats = meta[name] as IDictionary;
-
- _summaryBuilder.Counters = new Counters(
- CountersValue(stats, "nodes-created"),
- CountersValue(stats, "nodes-deleted"),
- CountersValue(stats, "relationships-created"),
- CountersValue(stats, "relationships-deleted"),
- CountersValue(stats, "properties-set"),
- CountersValue(stats, "labels-added"),
- CountersValue(stats, "labels-removed"),
- CountersValue(stats, "indexes-added"),
- CountersValue(stats, "indexes-removed"),
- CountersValue(stats, "constraints-added"),
- CountersValue(stats, "constraints-removed"));
- }
-
- private void CollectPlan(IDictionary meta, string name)
- {
- if (meta == null || !meta.ContainsKey(name))
- {
- return;
- }
- var planDictionary = meta[name] as IDictionary;
- _summaryBuilder.Plan = CollectPlan(planDictionary);
- }
-
-
- private static IPlan CollectPlan(IDictionary planDictionary)
- {
- if (planDictionary == null || planDictionary.Count == 0)
- {
- return null;
- }
-
- var operationType = planDictionary.GetMandatoryValue("operatorType");
- var args = planDictionary.GetValue("args", new Dictionary());
- var identifiers = planDictionary.GetValue("identifiers", new List()).Cast();
- var children = planDictionary.GetValue("children", new List());
-
- var childPlans = children
- .Select(child => child as IDictionary)
- .Select(CollectPlan)
- .Where(childPlan => childPlan != null)
- .ToList();
- return new Plan(operationType, args, identifiers.ToList(), childPlans);
- }
-
- private static IProfiledPlan CollectProfile(IDictionary profileDictionary)
- {
- if (profileDictionary == null || profileDictionary.Count == 0)
- {
- return null;
- }
- var operationType = profileDictionary.GetMandatoryValue("operatorType");
- var args = profileDictionary.GetValue("args", new Dictionary());
- var identifiers = profileDictionary.GetValue("identifiers", new List()).Cast();
- var dbHits = profileDictionary.GetMandatoryValue("dbHits");
- var rows = profileDictionary.GetMandatoryValue("rows");
- var children = profileDictionary.GetValue("children", new List());
-
- var childPlans = children
- .Select(child => child as IDictionary)
- .Select(CollectProfile)
- .Where(childProfile => childProfile != null)
- .ToList();
- return new ProfiledPlan(operationType, args, identifiers.ToList(), childPlans, dbHits, rows);
- }
-
-
- private void CollectProfile(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var profiledPlan = meta[name] as IDictionary;
- _summaryBuilder.Profile = CollectProfile(profiledPlan);
- }
-
- private void CollectNotifications(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var list = (meta[name] as List).Cast>();
- var notifications = new List();
- foreach (var value in list)
- {
- var code = value.GetValue("code", string.Empty);
- var title = value.GetValue("title", string.Empty);
- var description = value.GetValue("description", string.Empty);
-
- var posValue = value.GetValue("position", new Dictionary());
-
- var position = new InputPosition(
- (int)posValue.GetValue("offset", 0L),
- (int)posValue.GetValue("line", 0L),
- (int)posValue.GetValue("column", 0L));
- var severity = value.GetValue("severity", string.Empty);
- notifications.Add(new Notification(code, title, description, position, severity));
- }
- _summaryBuilder.Notifications = notifications;
- }
-
- private void CollectResultAvailableAfter(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- _summaryBuilder.ResultAvailableAfter = meta[name].As();
- }
-
- private void CollectResultConsumedAfter(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- _summaryBuilder.ResultConsumedAfter = meta[name].As();
- }
-
- private static int CountersValue(IDictionary counters, string name)
- {
- return (int)counters.GetValue(name, 0L);
- }
-
- private static StatementType FromCode(string type)
- {
- switch (type.ToLowerInvariant())
- {
- case "r":
- return ReadOnly;
- case "rw":
- return ReadWrite;
- case "w":
- return WriteOnly;
- case "s":
- return SchemaWrite;
- default:
- throw new ClientException("Unknown statement type: `" + type + "`.");
- }
- }
-
+
}
}
\ No newline at end of file
diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultReaderBuilder.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultReaderBuilder.cs
index bb6662006..ba25e9173 100644
--- a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultReaderBuilder.cs
+++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultReaderBuilder.cs
@@ -23,22 +23,19 @@
namespace Neo4j.Driver.Internal.Result
{
- internal class ResultReaderBuilder : IMessageResponseCollector
+ internal class ResultReaderBuilder : BuilderBase
{
- private readonly List _keys = new List();
- private readonly SummaryBuilder _summaryBuilder;
-
private Func _receiveOneFunc;
private readonly Queue _records = new Queue();
private bool _hasMoreRecords = true;
-
- private readonly IResultResourceHandler _resourceHandler;
+ public ResultReaderBuilder() : this(null, null, null, null, null)
+ {
+ }
public ResultReaderBuilder(Statement statement, Func receiveOneFunc, IServerInfo server, IResultResourceHandler resourceHandler = null)
+ : base(statement, server, resourceHandler)
{
- _summaryBuilder = new SummaryBuilder(statement, server);
- _resourceHandler = resourceHandler;
SetReceiveOneFunc(receiveOneFunc);
}
@@ -50,7 +47,7 @@ public ResultReaderBuilder(Statement statement, Func receiveOneFunc, IServ
public IStatementResultReader PreBuild()
{
- return new StatementResultReader(_keys, NextRecord, SummaryAsync);
+ return new StatementResultReader(_keys, NextRecordAsync, SummaryAsync);
}
///
@@ -72,7 +69,7 @@ private async Task SummaryAsync()
/// Return next record in the record stream if any, otherwise return null
///
/// Next record in the record stream if any, otherwise return null
- private async Task NextRecord()
+ private async Task NextRecordAsync()
{
if (_records.Count > 0)
{
@@ -85,7 +82,7 @@ private async Task NextRecord()
return _records.Count > 0 ? _records.Dequeue() : null;
}
- private void SetReceiveOneFunc(Func receiveOneAction)
+ internal void SetReceiveOneFunc(Func receiveOneAction)
{
_receiveOneFunc = async () =>
{
@@ -99,55 +96,30 @@ private void SetReceiveOneFunc(Func receiveOneAction)
};
}
- public void CollectRecord(object[] fields)
+ public override void CollectRecord(object[] fields)
{
var record = new Record(_keys, fields);
_records.Enqueue(record);
}
- public void CollectFields(IDictionary meta)
- {
- if (meta == null)
- {
- return;
- }
- CollectKeys(meta, "fields");
- CollectResultAvailableAfter(meta, "result_available_after");
- }
-
- public void CollectBookmark(IDictionary meta)
- {
- throw new NotSupportedException(
- $"Should not get a bookmark on a result. bookmark = {meta[Bookmark.BookmarkKey].As()}");
- }
-
- public void CollectSummary(IDictionary meta)
+ public override void CollectSummary(IDictionary meta)
{
NoMoreRecords();
- if (meta == null)
- {
- return;
- }
- CollectType(meta, "type");
- CollectCounters(meta, "stats");
- CollectPlan(meta, "plan");
- CollectProfile(meta, "profile");
- CollectNotifications(meta, "notifications");
- CollectResultConsumedAfter(meta, "result_consumed_after");
+ base.CollectSummary(meta);
}
- public void DoneSuccess()
+ public override void DoneSuccess()
{
// do nothing
}
- public void DoneFailure()
+ public override void DoneFailure()
{
NoMoreRecords();// an error received, so the result is broken
}
- public void DoneIgnored()
+ public override void DoneIgnored()
{
NoMoreRecords();// the result is ignored
}
@@ -156,183 +128,6 @@ private void NoMoreRecords()
{
_hasMoreRecords = false;
}
-
- private void CollectKeys(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
-
- if (meta.ContainsKey(name))
- {
- foreach (var key in meta[name].As>())
- {
- _keys.Add(key);
- }
- }
- }
-
- private void CollectType(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var type = meta[name] as string;
- _summaryBuilder.StatementType = FromCode(type);
- }
-
- private void CollectCounters(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var stats = meta[name] as IDictionary;
-
- _summaryBuilder.Counters = new Counters(
- CountersValue(stats, "nodes-created"),
- CountersValue(stats, "nodes-deleted"),
- CountersValue(stats, "relationships-created"),
- CountersValue(stats, "relationships-deleted"),
- CountersValue(stats, "properties-set"),
- CountersValue(stats, "labels-added"),
- CountersValue(stats, "labels-removed"),
- CountersValue(stats, "indexes-added"),
- CountersValue(stats, "indexes-removed"),
- CountersValue(stats, "constraints-added"),
- CountersValue(stats, "constraints-removed"));
- }
-
- private void CollectPlan(IDictionary meta, string name)
- {
- if (meta == null || !meta.ContainsKey(name))
- {
- return;
- }
- var planDictionary = meta[name] as IDictionary;
- _summaryBuilder.Plan = CollectPlan(planDictionary);
- }
-
-
- private static IPlan CollectPlan(IDictionary planDictionary)
- {
- if (planDictionary == null || planDictionary.Count == 0)
- {
- return null;
- }
-
- var operationType = planDictionary.GetMandatoryValue("operatorType");
- var args = planDictionary.GetValue("args", new Dictionary());
- var identifiers = planDictionary.GetValue("identifiers", new List()).Cast();
- var children = planDictionary.GetValue("children", new List());
-
- var childPlans = children
- .Select(child => child as IDictionary)
- .Select(CollectPlan)
- .Where(childPlan => childPlan != null)
- .ToList();
- return new Plan(operationType, args, identifiers.ToList(), childPlans);
- }
-
- private static IProfiledPlan CollectProfile(IDictionary profileDictionary)
- {
- if (profileDictionary == null || profileDictionary.Count == 0)
- {
- return null;
- }
- var operationType = profileDictionary.GetMandatoryValue("operatorType");
- var args = profileDictionary.GetValue("args", new Dictionary());
- var identifiers = profileDictionary.GetValue("identifiers", new List()).Cast();
- var dbHits = profileDictionary.GetMandatoryValue("dbHits");
- var rows = profileDictionary.GetMandatoryValue("rows");
- var children = profileDictionary.GetValue("children", new List());
-
- var childPlans = children
- .Select(child => child as IDictionary)
- .Select(CollectProfile)
- .Where(childProfile => childProfile != null)
- .ToList();
- return new ProfiledPlan(operationType, args, identifiers.ToList(), childPlans, dbHits, rows);
- }
-
-
- private void CollectProfile(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var profiledPlan = meta[name] as IDictionary;
- _summaryBuilder.Profile = CollectProfile(profiledPlan);
- }
-
- private void CollectNotifications(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- var list = (meta[name] as List).Cast>();
- var notifications = new List();
- foreach (var value in list)
- {
- var code = value.GetValue("code", string.Empty);
- var title = value.GetValue("title", string.Empty);
- var description = value.GetValue("description", string.Empty);
-
- var posValue = value.GetValue("position", new Dictionary());
-
- var position = new InputPosition(
- (int)posValue.GetValue("offset", 0L),
- (int)posValue.GetValue("line", 0L),
- (int)posValue.GetValue("column", 0L));
- var severity = value.GetValue("severity", string.Empty);
- notifications.Add(new Notification(code, title, description, position, severity));
- }
- _summaryBuilder.Notifications = notifications;
- }
-
- private void CollectResultAvailableAfter(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- _summaryBuilder.ResultAvailableAfter = meta[name].As();
- }
-
- private void CollectResultConsumedAfter(IDictionary meta, string name)
- {
- if (!meta.ContainsKey(name))
- {
- return;
- }
- _summaryBuilder.ResultConsumedAfter = meta[name].As();
- }
-
- private static int CountersValue(IDictionary counters, string name)
- {
- return (int)counters.GetValue(name, 0L);
- }
-
- private static StatementType FromCode(string type)
- {
- switch (type.ToLowerInvariant())
- {
- case "r":
- return ReadOnly;
- case "rw":
- return ReadWrite;
- case "w":
- return WriteOnly;
- case "s":
- return SchemaWrite;
- default:
- throw new ClientException("Unknown statement type: `" + type + "`.");
- }
- }
-
+
}
}
\ No newline at end of file
diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResultReader.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResultReader.cs
index cd9e0a2e7..3bb01aff1 100644
--- a/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResultReader.cs
+++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResultReader.cs
@@ -32,9 +32,16 @@ public StatementResultReader(List keys, Func> nextRecordFu
public Task SummaryAsync()
{
- if (_summary == null && _summaryFunc != null)
+ if (_summary == null)
{
- _summary = _summaryFunc();
+ if (_summaryFunc != null)
+ {
+ _summary = _summaryFunc();
+ }
+ else
+ {
+ _summary = Task.FromResult((IResultSummary)null);
+ }
}
return _summary;
diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs
index e4a4fa975..36f8f4267 100644
--- a/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs
+++ b/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs
@@ -15,7 +15,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
-using System.Collections.Generic;
using System.Threading.Tasks;
using Neo4j.Driver.Internal.Connector;
using Neo4j.Driver.Internal.Result;