Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
12-18-2019 07:59 AM
We are using Neo4j.Driver and the code looks like this:
using (var session = driver.Session(AccessMode.Write))
{
var result = [...].WriteTransaction(tx => tx.Run[...]
var value1 = result.First()["value1"].As<string>();
var value2 = result.First()["value2"].As<string>();
[...]
}
On my machine this works fine. On my coworker's machine, result is suddenly empty after the first "First()" call.
Any ideas? Maybe it's a problem with C# in general?
EDIT: We have determined that this problem only occurs when using Visual Studio 2019 16.4.2, the working machine has 16.4.1! After upgrading to 16.4.2 the same error happens!
EDIT: This was with neo4jDriver v.1.7.2
Solved! Go to Solution.
02-04-2020 04:10 AM
OK, so this is how the IStatementResult
is intended to work.
What's important to note is that whilst var result
is an IEnumerable
the underlying structure isn't an Array
or List<T>
but a stream of data.
When you call First()
you iterate the IEnumerable
and pull the first item from the stream, the next time you call First()
you get the first item from the stream - but in this case, because the stream has already yielded it's first item, you're getting the second.
Woah! Hard to read much?!?!
Imagine you have an array [1,2,3,4,5]
on a server somewhere, and this is streamed to you.
You call arr.First()
- this goes to the server and pulls down 1
. At this point, the server now has:
[2,3,4,5]
You then call arr.First()
- and the server now sends you 2
as - from it's point of view that is the first item.
I'm not sure if that's clearer or not - let me know if not.
Anyhews - if you want First()
to always work - you would need to do ToList()
, so for example:
void Main()
{
var driver = GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "neo"));
using (var session = driver.Session(AccessMode.Write))
{
Console.WriteLine("Just IEnumerable");
var result = session.ReadTransaction(s => s.Run("MATCH (m:Movie) RETURN m.title AS title, m.tagline AS tagline"));
WriteToScreen(result);
Console.WriteLine();
Console.WriteLine("Using ToList()");
var result2 = session.ReadTransaction(s => s.Run("MATCH (m:Movie) RETURN m.title AS title, m.tagline AS tagline"));
WriteToScreen(result2.ToList());
}
}
public static void WriteToScreen(IEnumerable<IRecord> result)
{
var value1 = result.First()["title"].As<string>();
var value2 = result.First()["tagline"].As<string>();
value1.Dump();
value2.Dump();
}
outputs:
Just IEnumerable
The Matrix
Free your mind
Using ToList()
The Matrix
Welcome to the Real World
The ToList
version doing what you're hoping for.
Now. The way you probably want to approach this - to avoid pulling everything from the DB in one go, is to do:
using (var session = driver.Session(AccessMode.Write))
{
var result = [...].WriteTransaction(tx => tx.Run[...]
var first = result.First();
var value1 = first["value1"].As<string>();
var value2 = first["value2"].As<string>();
[...]
}
Lastly - as to why changing VS made a difference, I can't explain - this should always work like this - are you 100% sure the code wasn't different?
All the best
Chris
02-04-2020 04:10 AM
OK, so this is how the IStatementResult
is intended to work.
What's important to note is that whilst var result
is an IEnumerable
the underlying structure isn't an Array
or List<T>
but a stream of data.
When you call First()
you iterate the IEnumerable
and pull the first item from the stream, the next time you call First()
you get the first item from the stream - but in this case, because the stream has already yielded it's first item, you're getting the second.
Woah! Hard to read much?!?!
Imagine you have an array [1,2,3,4,5]
on a server somewhere, and this is streamed to you.
You call arr.First()
- this goes to the server and pulls down 1
. At this point, the server now has:
[2,3,4,5]
You then call arr.First()
- and the server now sends you 2
as - from it's point of view that is the first item.
I'm not sure if that's clearer or not - let me know if not.
Anyhews - if you want First()
to always work - you would need to do ToList()
, so for example:
void Main()
{
var driver = GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "neo"));
using (var session = driver.Session(AccessMode.Write))
{
Console.WriteLine("Just IEnumerable");
var result = session.ReadTransaction(s => s.Run("MATCH (m:Movie) RETURN m.title AS title, m.tagline AS tagline"));
WriteToScreen(result);
Console.WriteLine();
Console.WriteLine("Using ToList()");
var result2 = session.ReadTransaction(s => s.Run("MATCH (m:Movie) RETURN m.title AS title, m.tagline AS tagline"));
WriteToScreen(result2.ToList());
}
}
public static void WriteToScreen(IEnumerable<IRecord> result)
{
var value1 = result.First()["title"].As<string>();
var value2 = result.First()["tagline"].As<string>();
value1.Dump();
value2.Dump();
}
outputs:
Just IEnumerable
The Matrix
Free your mind
Using ToList()
The Matrix
Welcome to the Real World
The ToList
version doing what you're hoping for.
Now. The way you probably want to approach this - to avoid pulling everything from the DB in one go, is to do:
using (var session = driver.Session(AccessMode.Write))
{
var result = [...].WriteTransaction(tx => tx.Run[...]
var first = result.First();
var value1 = first["value1"].As<string>();
var value2 = first["value2"].As<string>();
[...]
}
Lastly - as to why changing VS made a difference, I can't explain - this should always work like this - are you 100% sure the code wasn't different?
All the best
Chris
All the sessions of the conference are now available online