diff --git a/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/SessionIT.cs b/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/SessionIT.cs index 824314b9c..e8b819c18 100644 --- a/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/SessionIT.cs +++ b/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/SessionIT.cs @@ -198,5 +198,180 @@ public void ShouldNotThrowExceptionWhenDisposeSessionAfterDriver() driver.Dispose(); session.Dispose(); } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterRun() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var result = session.Run("RETURN 1 As X"); + result.Keys.Should().HaveCount(1); + result.Keys.Should().Contain("X"); + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterRunAndResultConsumption() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var result = session.Run("RETURN 1 As X"); + result.Keys.Should().HaveCount(1); + result.Keys.Should().Contain("X"); + result.Consume(); + result.Keys.Should().HaveCount(1); + result.Keys.Should().Contain("X"); + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRun() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var result1 = session.Run("RETURN 1 As X"); + var result2 = session.Run("RETURN 1 As Y"); + + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRunAndResultConsumption() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var result1 = session.Run("RETURN 1 As X"); + var result2 = session.Run("RETURN 1 As Y"); + + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + + result1.Consume(); + result2.Consume(); + + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRunNoOrder() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var result1 = session.Run("RETURN 1 As X"); + var result2 = session.Run("RETURN 1 As Y"); + + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRunAndResultConsumptionNoOrder() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var result1 = session.Run("RETURN 1 As X"); + var result2 = session.Run("RETURN 1 As Y"); + + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + + result2.Consume(); + result1.Consume(); + + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + } + } + } + + + [RequireServerFact] + public async void KeysShouldBeAvailableJustAfterRunAsync() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var cursor = await session.RunAsync("RETURN 1 As X"); + cursor.Keys.Should().HaveCount(1); + cursor.Keys.Should().Contain("X"); + } + } + } + + [RequireServerFact] + public async void KeysShouldBeAvailableJustAfterConsecutiveRunAsync() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var cursor1 = await session.RunAsync("RETURN 1 As X"); + var cursor2 = await session.RunAsync("RETURN 1 As Y"); + + cursor1.Keys.Should().HaveCount(1); + cursor1.Keys.Should().Contain("X"); + + cursor2.Keys.Should().HaveCount(1); + cursor2.Keys.Should().Contain("Y"); + } + } + } + + [RequireServerFact] + public async void KeysShouldBeAvailableJustAfterConsecutiveRunAsyncWithConsumptionInBetween() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + var cursor1 = await session.RunAsync("RETURN 1 As X"); + var cursor2 = await session.RunAsync("RETURN 1 As Y"); + + await cursor1.ConsumeAsync(); + + cursor1.Keys.Should().HaveCount(1); + cursor1.Keys.Should().Contain("X"); + + cursor2.Keys.Should().HaveCount(1); + cursor2.Keys.Should().Contain("Y"); + } + } + } + } } diff --git a/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/TransactionIT.cs b/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/TransactionIT.cs index 72028d962..cbe65c63f 100644 --- a/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/TransactionIT.cs +++ b/Neo4j.Driver/Neo4j.Driver.IntegrationTests/DirectDriver/TransactionIT.cs @@ -126,5 +126,207 @@ public void ShouldNotCommitIfError() matchResult.Should().Be(0); } } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterRun() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = session.BeginTransaction()) + { + var result = txc.Run("RETURN 1 As X"); + result.Keys.Should().HaveCount(1); + result.Keys.Should().Contain("X"); + } + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterRunAndResultConsumption() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = session.BeginTransaction()) + { + var result = txc.Run("RETURN 1 As X"); + result.Keys.Should().HaveCount(1); + result.Keys.Should().Contain("X"); + result.Consume(); + result.Keys.Should().HaveCount(1); + result.Keys.Should().Contain("X"); + } + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRun() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = session.BeginTransaction()) + { + var result1 = txc.Run("RETURN 1 As X"); + var result2 = txc.Run("RETURN 1 As Y"); + + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + } + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRunAndResultConsumption() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = session.BeginTransaction()) + { + var result1 = txc.Run("RETURN 1 As X"); + var result2 = txc.Run("RETURN 1 As Y"); + + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + + result1.Consume(); + result2.Consume(); + + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + } + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRunNoOrder() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = session.BeginTransaction()) + { + var result1 = txc.Run("RETURN 1 As X"); + var result2 = txc.Run("RETURN 1 As Y"); + + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + } + } + } + } + + [RequireServerFact] + public void KeysShouldBeAvailableAfterConsecutiveRunAndResultConsumptionNoOrder() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = session.BeginTransaction()) + { + var result1 = txc.Run("RETURN 1 As X"); + var result2 = txc.Run("RETURN 1 As Y"); + + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + + result2.Consume(); + result1.Consume(); + + result2.Keys.Should().HaveCount(1); + result2.Keys.Should().Contain("Y"); + result1.Keys.Should().HaveCount(1); + result1.Keys.Should().Contain("X"); + } + } + } + } + + [RequireServerFact] + public async void KeysShouldBeAvailableJustAfterRunAsync() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = await session.BeginTransactionAsync()) + { + var cursor = await txc.RunAsync("RETURN 1 As X"); + cursor.Keys.Should().HaveCount(1); + cursor.Keys.Should().Contain("X"); + } + } + } + } + + [RequireServerFact] + public async void KeysShouldBeAvailableJustAfterConsecutiveRunAsync() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = await session.BeginTransactionAsync()) + { + var cursor1 = await txc.RunAsync("RETURN 1 As X"); + var cursor2 = await txc.RunAsync("RETURN 1 As Y"); + + cursor1.Keys.Should().HaveCount(1); + cursor1.Keys.Should().Contain("X"); + + cursor2.Keys.Should().HaveCount(1); + cursor2.Keys.Should().Contain("Y"); + } + } + } + } + + [RequireServerFact] + public async void KeysShouldBeAvailableJustAfterConsecutiveRunAsyncWithConsumptionInBetween() + { + using (var driver = GraphDatabase.Driver(ServerEndPoint, AuthToken)) + { + using (var session = driver.Session()) + { + using (var txc = await session.BeginTransactionAsync()) + { + var cursor1 = await txc.RunAsync("RETURN 1 As X"); + var cursor2 = await txc.RunAsync("RETURN 1 As Y"); + + await cursor1.ConsumeAsync(); + + cursor1.Keys.Should().HaveCount(1); + cursor1.Keys.Should().Contain("X"); + + cursor2.Keys.Should().HaveCount(1); + cursor2.Keys.Should().Contain("Y"); + } + } + } + } + + } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketClientTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketClientTests.cs index 82188c05f..74a82d84b 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketClientTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketClientTests.cs @@ -83,7 +83,7 @@ public async Task ShouldSendMessagesAsExpected() var messageHandler = new MessageResponseHandler(); messageHandler.EnqueueMessage(new InitMessage(DefaultUserAgent, new Dictionary())); - var rb = new ResultBuilder(); + var rb = new ResultBuilder(null, () => { }, null, null); messageHandler.EnqueueMessage(messages[0], rb); messageHandler.EnqueueMessage(messages[1], rb); @@ -115,7 +115,7 @@ public async Task ShouldCreateExceptionWhenErrorReceivedFromDatabase() var messages = new IRequestMessage[] {new RunMessage("This will cause a syntax error")}; var messageHandler = new MessageResponseHandler(); messageHandler.EnqueueMessage(new InitMessage(DefaultUserAgent, new Dictionary())); - messageHandler.EnqueueMessage(messages[0], new ResultBuilder()); + messageHandler.EnqueueMessage(messages[0], new ResultBuilder(null, () => { }, null, null)); harness.SetupReadStream("00 00 00 01" + "00 03 b1 70 a0 00 00" + @@ -152,8 +152,8 @@ public async Task ShouldIgnorePullAllWhenErrorHappenedDuringRun() var messageHandler = new TestResponseHandler(); messageHandler.EnqueueMessage(new InitMessage(DefaultUserAgent, new Dictionary())); - messageHandler.EnqueueMessage(messages[0], new ResultBuilder()); - messageHandler.EnqueueMessage(messages[1], new ResultBuilder()); + messageHandler.EnqueueMessage(messages[0], new ResultBuilder(null, () => { }, null, null)); + messageHandler.EnqueueMessage(messages[1], new ResultBuilder(null, () => { }, null, null)); harness.SetupReadStream("00 00 00 01" + "00 03 b1 70 a0 00 00" + diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketConnectionTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketConnectionTests.cs index df84bbb5e..20fcb0c8a 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketConnectionTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Connector/SocketConnectionTests.cs @@ -172,7 +172,7 @@ public void ShouldEnqueueRunMessageAndDiscardAllMessage() var con = NewSocketConnection(); // When - con.Run("a statement", null, new ResultBuilder(), false); + con.Run("a statement", null, new ResultBuilder(null, () => { }, null, null), false); // Then con.Messages.Count.Should().Be(2); // Run + DiscardAll @@ -186,7 +186,7 @@ public void ShouldEnqueueResultBuilderOnResponseHandler() var mockResponseHandler = new Mock(); var con = NewSocketConnection(handler:mockResponseHandler.Object); - var rb = new ResultBuilder(); + var rb = new ResultBuilder(null, () => { }, null, null); con.Run("statement", null, rb, false); mockResponseHandler.Verify(h => h.EnqueueMessage(It.IsAny(), rb), Times.Once); @@ -200,7 +200,7 @@ public void ShouldEnqueueRunMessageAndPullAllMessage() var con = NewSocketConnection(); // When - con.Run("a statement", null, new ResultBuilder(), true); + con.Run("a statement", null, new ResultBuilder(null, () => { }, null, null), true); // Then con.Messages.Count.Should().Be(2); // Run + PullAll @@ -214,7 +214,7 @@ public void ShouldEnqueueResultBuildersOnResponseHandler() var mockResponseHandler = new Mock(); var con = NewSocketConnection(handler: mockResponseHandler.Object); - var rb = new ResultBuilder(); + var rb = new ResultBuilder(null, () => { }, null, null); con.Run("statement", null, rb, true); mockResponseHandler.Verify(h => h.EnqueueMessage(It.IsAny(), rb), Times.Once); diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Connector/TcpSocketClientTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Connector/TcpSocketClientTests.cs index 35601fbe0..f7fd2bdab 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Connector/TcpSocketClientTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Connector/TcpSocketClientTests.cs @@ -63,23 +63,14 @@ public class ConnectSocketAsyncMethod [Fact] public async Task ShouldThrowExceptionIfConnectionTimedOut() { - var client = new TcpSocketClient( - new SocketSettings{ConnectionTimeout = TimeSpan.FromSeconds(0)}); + var client = new TcpSocketClientWithDisposeDetection( + new SocketSettings { ConnectionTimeout = TimeSpan.FromSeconds(0) }); var exception = await Record.ExceptionAsync( ()=>client.ConnectSocketAsync(IPAddress.Parse("127.0.0.1"), 9999)); exception.Should().BeOfType(); exception.Message.Should().Be("Failed to connect to server 127.0.0.1:9999 within 0ms."); - } - [Fact] - public async Task ShouldCloseTcpClientIfTimedOut() - { - var client = new TcpSocketClientWithDisposeDetection( - new SocketSettings{ConnectionTimeout = TimeSpan.FromSeconds(0)}); - var exception = await Record.ExceptionAsync( - ()=>client.ConnectSocketAsync(IPAddress.Parse("127.0.0.1"), 9999)); - exception.Should().BeOfType(); client.DisposeCalled.Should().BeTrue(); } diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Result/ResultBuilderTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Result/ResultBuilderTests.cs index 042aaba64..05f378c48 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Result/ResultBuilderTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Result/ResultBuilderTests.cs @@ -28,7 +28,7 @@ public class ResultBuilderTests { private static ResultBuilder GenerateBuilder(IDictionary meta = null) { - var builder = new ResultBuilder(); + var builder = new ResultBuilder(null, () => { }, null, null); builder.CollectFields(meta ?? new Dictionary { { "fields", new List { "x" } } }); return builder; } @@ -142,7 +142,7 @@ public class CollectFieldsMethod [Fact] public void ShouldPassDefaultKeysToResultIfNoKeySet() { - var builder = new ResultBuilder(); + var builder = new ResultBuilder(null, () => { }, null, null); var result = builder.PreBuild(); result.Keys.Should().BeEmpty(); @@ -151,7 +151,7 @@ public void ShouldPassDefaultKeysToResultIfNoKeySet() [Fact] public void ShouldDoNothingWhenMetaIsNull() { - var builder = new ResultBuilder(); + var builder = new ResultBuilder(null, () => { }, null, null); builder.CollectFields(null); var result = builder.PreBuild(); @@ -161,7 +161,7 @@ public void ShouldDoNothingWhenMetaIsNull() [Fact] public void ShouldDoNothingWhenMetaDoesNotContainFields() { - var builder = new ResultBuilder(); + var builder = new ResultBuilder(null, () => { }, null, null); var meta = new Dictionary { {"something", "here" } @@ -178,7 +178,7 @@ public void ShouldCollectKeys() IDictionary meta = new Dictionary { {"fields", new List {"fieldKey1", "fieldKey2", "fieldKey3"} },{"type", "r" } }; - var builder = new ResultBuilder(); + var builder = new ResultBuilder(null, () => { }, null, null); builder.CollectFields(meta); var result = builder.PreBuild(); diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultCursorTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultCursorTests.cs index 5bb8b4ccb..ab0547aef 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultCursorTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultCursorTests.cs @@ -62,7 +62,7 @@ public class Constructor [Fact] public void ShouldThrowArgumentNullExceptionIfRecordsIsNull() { - var ex = Xunit.Record.Exception(() => new StatementResult(new List{"test"}, null)); + var ex = Xunit.Record.Exception(() => new StatementResult(() => new List{"test"}, null)); ex.Should().NotBeNull(); ex.Should().BeOfType(); } @@ -78,7 +78,7 @@ public void ShouldThrowArgumentNullExceptionIfKeysIsNull() [Fact] public void ShouldSetKeysProperlyIfKeysNotNull() { - var result = new StatementResult(new List{"test"}, new ListBasedRecordSet(new List())); + var result = new StatementResult(() => new List{"test"}, new ListBasedRecordSet(new List())); result.Keys.Should().HaveCount(1); result.Keys.Should().Contain("test"); } diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultTests.cs index b292d36f4..6970b3863 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Result/StatementResultTests.cs @@ -38,7 +38,7 @@ private static class ResultCreator var keys = RecordCreator.CreateKeys(keySize); var records = RecordCreator.CreateRecords(recordSize, keys); - return new StatementResult(keys, new ListBasedRecordSet(records), getSummaryFunc); + return new StatementResult(() => keys, new ListBasedRecordSet(records), getSummaryFunc); } } @@ -47,7 +47,7 @@ public class Constructor [Fact] public void ShouldThrowArgumentNullExceptionIfRecordsIsNull() { - var ex = Xunit.Record.Exception(() => new StatementResult(new List{"test"}, null)); + var ex = Xunit.Record.Exception(() => new StatementResult(() => new List{"test"}, null)); ex.Should().NotBeNull(); ex.Should().BeOfType(); } @@ -63,7 +63,7 @@ public void ShouldThrowArgumentNullExceptionIfKeysIsNull() [Fact] public void ShouldSetKeysProperlyIfKeysNotNull() { - var result = new StatementResult(new List{"test"}, new ListBasedRecordSet(new List())); + var result = new StatementResult(() => new List{"test"}, new ListBasedRecordSet(new List())); result.Keys.Should().HaveCount(1); result.Keys.Should().Contain("test"); } @@ -229,7 +229,7 @@ public StreamingRecords(ITestOutputHelper output) public void ShouldReturnRecords() { var recordYielder = new TestRecordYielder(5, 10, _output); - var cursor = new StatementResult( TestRecordYielder.Keys, new FuncBasedRecordSet(() => recordYielder.RecordsWithAutoLoad)); + var cursor = new StatementResult(() => TestRecordYielder.Keys, new FuncBasedRecordSet(() => recordYielder.RecordsWithAutoLoad)); var records = cursor.ToList(); records.Count.Should().Be(10); } @@ -240,7 +240,7 @@ public void ShouldWaitForAllRecordsToArrive() var recordYielder = new TestRecordYielder(5, 10, _output); int count = 0; - var cursor = new StatementResult(TestRecordYielder.Keys, new FuncBasedRecordSet(() => recordYielder.Records)); + var cursor = new StatementResult(() => TestRecordYielder.Keys, new FuncBasedRecordSet(() => recordYielder.Records)); var t = Task.Factory.StartNew(() => { // ReSharper disable once LoopCanBeConvertedToQuery @@ -264,7 +264,7 @@ public void ShouldWaitForAllRecordsToArrive() public void ShouldReturnRecordsImmediatelyWhenReady() { var recordYielder = new TestRecordYielder(5, 10, _output); - var result = new StatementResult(TestRecordYielder.Keys, new FuncBasedRecordSet(() => recordYielder.Records)); + var result = new StatementResult(() => TestRecordYielder.Keys, new FuncBasedRecordSet(() => recordYielder.Records)); var temp = result.Take(5); var records = temp.ToList(); records.Count.Should().Be(5); diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs index 9fc256a6a..26905c0de 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilder.cs @@ -28,10 +28,6 @@ internal class ResultBuilder : ResultBuilderBase private readonly Queue _records = new Queue(); private bool _hasMoreRecords = true; - public ResultBuilder() : this(null, null, null, null, null) - { - } - public ResultBuilder(Statement statement, Action receiveOneAction, IServerInfo server, IResultResourceHandler resourceHandler = null) : base(statement, server) { @@ -44,10 +40,10 @@ public ResultBuilder(Statement statement, Action receiveOneAction, IServerInfo s : this(new Statement(statement, parameters), receiveOneAction, server, resourceHandler) { } - + public StatementResult PreBuild() { - return new StatementResult(Keys, new RecordSet(NextRecord), Summary); + return new StatementResult(() => Keys, new RecordSet(NextRecord), Summary); } /// @@ -96,6 +92,11 @@ internal void SetReceiveOneAction(Action receiveOneAction) }; } + protected override void EnsureStatementProcessed() + { + _receiveOneAction(); + } + protected override void EnqueueRecord(Record record) { _records.Enqueue(record); diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilderBase.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilderBase.cs index 9a14bfece..27cdfff55 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilderBase.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultBuilderBase.cs @@ -22,7 +22,8 @@ namespace Neo4j.Driver.Internal.Result { internal abstract class ResultBuilderBase : IMessageResponseCollector { - protected List Keys { get; } = new List(); + private bool _statementProcessed = false; + protected List _keys = new List(); protected SummaryCollector SummaryCollector { get; } protected ResultBuilderBase(Statement statement, IServerInfo server) @@ -30,13 +31,27 @@ protected ResultBuilderBase(Statement statement, IServerInfo server) SummaryCollector = new SummaryCollector(statement, server); } + protected List Keys + { + get + { + if (!_statementProcessed) + { + EnsureStatementProcessed(); + } + + return _keys; + } + } + public void CollectFields(IDictionary meta) { if (meta == null) { return; } - CollectKeys(meta, "fields", Keys); + + CollectKeys(meta, "fields", _keys); SummaryCollector.CollectWithFields(meta); } @@ -48,7 +63,7 @@ public void CollectBookmark(IDictionary meta) public void CollectRecord(object[] fields) { - var record = new Record(Keys, fields); + var record = new Record(_keys, fields); EnqueueRecord(record); } @@ -65,18 +80,22 @@ public void CollectSummary(IDictionary meta) public void DoneSuccess() { // do nothing + _statementProcessed = true; } public void DoneFailure() { NoMoreRecords();// an error received, so the result is broken + _statementProcessed = true; } public void DoneIgnored() { NoMoreRecords();// the result is ignored + _statementProcessed = true; } + protected abstract void EnsureStatementProcessed(); protected abstract void NoMoreRecords(); protected abstract void EnqueueRecord(Record record); diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultCursorBuilder.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultCursorBuilder.cs index 9059d1f2e..1faf60917 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultCursorBuilder.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/ResultCursorBuilder.cs @@ -105,5 +105,11 @@ protected override void NoMoreRecords() { _hasMoreRecords = false; } + + protected override void EnsureStatementProcessed() + { + + } + } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResult.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResult.cs index b47b192b7..bbf08a53b 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResult.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Result/StatementResult.cs @@ -26,13 +26,13 @@ namespace Neo4j.Driver.Internal.Result /// internal class StatementResult : IStatementResult { - private readonly List _keys; + private readonly Func> _keys; private readonly Func _getSummary; private readonly IRecordSet _recordSet; private IResultSummary _summary; - public StatementResult(List keys, IRecordSet recordSet, Func getSummary = null) + public StatementResult(Func> keys, IRecordSet recordSet, Func getSummary = null) { Throw.ArgumentNullException.IfNull(keys, nameof(keys)); Throw.ArgumentNullException.IfNull(recordSet, nameof(recordSet)); @@ -42,7 +42,7 @@ public StatementResult(List keys, IRecordSet recordSet, Func Keys => _keys; + public IReadOnlyList Keys => _keys(); public IResultSummary Summary { diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs index 34b49b815..377b34989 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Session.cs @@ -82,6 +82,9 @@ public override Task RunAsync(Statement statement) await _connection.SendAsync().ConfigureAwait(false); + // Wait for SUCCESS/FAILURE message from the server + await _connection.ReceiveOneAsync().ConfigureAwait(false); + return resultBuilder.PreBuild(); }); } diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Transaction.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Transaction.cs index d5d5a2ac4..b5bb67e7f 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Transaction.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Transaction.cs @@ -208,6 +208,7 @@ public override IStatementResult Run(Statement statement) _connection.Server); _connection.Run(statement.Text, statement.Parameters, resultBuilder); _connection.Send(); + return resultBuilder.PreBuild(); }); } @@ -222,6 +223,10 @@ public override Task RunAsync(Statement statement) _connection.Server); _connection.Run(statement.Text, statement.Parameters, resultBuilder); await _connection.SendAsync().ConfigureAwait(false); + + // Wait for SUCCESS/FAILURE message from the server + await _connection.ReceiveOneAsync().ConfigureAwait(false); + return resultBuilder.PreBuild(); }); }