Using results from Neo4j .Net driver - c#

I am struggling to cast the INode results from the native Neo4j .Net driver to my own object.
public class User
{
public String UUID { get; set; }
public String firstname { get; set; }
}
My node as returned in Neo4j desktop browser:
{
"UUID": "e0bc991a-1018-4edb-83df-771b8cee4c88",
"firstname": "John"
}
C# code using Neo4jClient (this works):
var users = client.Cypher
.Match("(p:Person)")
.Return(p => p.As<User>())
.Results.ToList();
C# code using native Neo4j .Net driver (fails):
var users = new List<User>();
using (var session = _driver.Session())
{
session.ReadTransaction(tx =>
{
var result = tx.Run("MATCH (p:Person) RETURN p").ToList();
users = result.Select(p => p[0].As<User>()).ToList();
});
}
My error message is:
System.InvalidCastException: Unable to cast object of type Neo4j.Driver.Internal.Node to type sandbox_neotesting.Models.User.
Any pointers on how I can fix this? I was hoping to find a solution where I could directly cast the result to my internal model, not having to set each property specifically.
Thanks! :)

Very quiet here, but found a working solution. I simply serialize the node's properties, then deserialize into a User object. Feel free to suggest improvements!
var statementText = "MATCH (p:Person) RETURN p";
var users = new List<User>();
using (var session = _driver.Session())
{
session.ReadTransaction(tx =>
{
var result = tx.Run(statementText);
foreach(var record in result)
{
var nodeProps = JsonConvert.SerializeObject(record[0].As<INode>().Properties);
users.Add(JsonConvert.DeserializeObject<User>(nodeProps));
}
});
}

Related

SQLite EF6 Async API throws InvalidCastException

I'm new to entity framework and try to use it with SQLite. I've a working setup if I don't use the async API.
In my simplified szenario are only two tables containing a one-to-many relationship. DB setup and insertion of values works fine. Query is a problem.
Here is the simplified code:
var connectionStringBuilder = new SQLiteConnectionStringBuilder { DataSource = dbFile, ForeignKeys = true };
using (var connection = new SQLiteConnection { ConnectionString = connectionStringBuilder.ConnectionString })
{
await connection.OpenAsync();
// query data
using (var context = new TestContext(connection, contextOwnsConnection: false))
{
var xQuery = context.Xs.Include(item => item.Ys);
var syncX = xQuery.First(); // works fine
var asyncX = await xQuery.FirstAsync(); // throws InvalidCastException
}
}
Why does the call to FirstAsync() throws an exception while First() doesn't?
The same happens using SingleAsync() and ToListAsync(). Currently I'm of the opinion that this happens on all async calls.
Removing the Include(...) clause from query will fix the problem but forces a second database query on access to the property.
To avoid hints like "you're calling First and FirstAsync on the same query object...": NO. The problem still occurs if I only call ...Async() without calling a synchronous method first. That's just for clearification.
I'm using a WinForms application .Net 4.7.1, EF6 (by adding System.Data.SQLite v1.0.108 via Nuget).
The complete code to reproduce the problem:
private async void OnClick(object sender, EventArgs e)
{
var dbFile = "test.sqlite";
if (File.Exists(dbFile)) File.Delete(dbFile);
SQLiteConnection.CreateFile(dbFile);
var connectionStringBuilder = new SQLiteConnectionStringBuilder { DataSource = dbFile, ForeignKeys = true };
using (var connection = new SQLiteConnection { ConnectionString = connectionStringBuilder.ConnectionString })
{
await connection.OpenAsync();
// setup database
using (var context = new TestContext(connection, contextOwnsConnection: false))
{
await context.Database.ExecuteSqlCommandAsync("CREATE TABLE X (Id VARCHAR2 PRIMARY KEY);");
await context.Database.ExecuteSqlCommandAsync("CREATE TABLE Y (Id VARCHAR2 PRIMARY KEY, XId VARCHAR2 NOT NULL, FOREIGN KEY (XId) REFERENCES X(Id));");
var x0 = new X { Id = "X0" };
var y0 = new Y { Id = "Y0", XId = x0.Id }; // Currently I don't know why using the navigation property 'X = x0' isn't working but the XId will work.
var y1 = new Y { Id = "Y1", XId = x0.Id }; // Currently I don't know why using the navigation property 'X = x0' isn't working but the XId will work.
x0.Ys.Add(y0);
x0.Ys.Add(y1);
context.Xs.Add(x0);
context.Ys.Add(y0); // not needed but for safety :-)
context.Ys.Add(y1); // not needed but for safety :-)
await context.SaveChangesAsync();
}
// query data
using (var context = new TestContext(connection, contextOwnsConnection: false))
{
var xQuery = context.Xs.Include(item => item.Ys);
var syncX = xQuery.First(); // works fine
var asyncX = await xQuery.FirstAsync(); // throws InvalidCastException
}
}
}
Using the following model classes:
public class TestContext : DbContext
{
public TestContext(DbConnection existingConnection, bool contextOwnsConnection = true) :
base(existingConnection, contextOwnsConnection)
{
}
public DbSet<X> Xs { get; set; }
public DbSet<Y> Ys { get; set; }
}
[Table("X")]
public class X
{
public X()
{
// ReSharper disable once VirtualMemberCallInConstructor
this.Ys = new HashSet<Y>();
}
[Column("Id")]
[Key, Required]
public string Id { get; set; }
public virtual ICollection<Y> Ys { get; set; }
}
[Table("Y")]
public class Y
{
[Column("Id")]
[Key, Required]
public string Id { get; set; }
[Column("XId")]
[Required]
public string XId { get; set; }
[ForeignKey("XId")]
public virtual X X { get; set; }
}
I'm sorry, I can't write comments.
Your problem is very interesting, the first thought I had was that the types do not match, because I remembered exactly what FirstAsync takes IQueryable, and if I do include something the type of request is IIncludeableQueryable, but I tested my guess and looked at the implementation through dot-peek, alas, it work in both cases, and by itself IIncludeableQueryableis inherited from IQueryable
var testQuery = DbContext.Practices.Include(x => x.Facility);
var testQuery2 = DbContext.Practices.Select(x => x);
var asyncPracticeCorrectType = await testQuery2.FirstAsync();
var asyncPractice = await testQuery.FirstAsync();
i got entity Practice in both ways:(
I think your problem in area SQLite, because your code look correctly
I tried entity framework core instead of entity framework 6 which works perfectly according to that problem.
In my option it's a bug in entitiy framework 6 or in the sqlite provider.

MongoDB Text Search with projection

Using MongoDB with C# and driver 2.0, I am trying to do the following:
Text search
Sort the hits by text search score
Project BigClass to SmallClass
Here is a (simplified version of) the classes:
class BigClass
{
[BsonIgnoreIfDefault]
public ObjectId _id { get; set; }
public string Guid { get; set; }
public string Title { get; set; }
public DateTime CreationTime { get; set; }
// lots of other stuff
[BsonIgnoreIfNull]
public double? TextMatchScore { get; set; } // Temporary place for the text match score, for sorting
}
class SmallClass
{
[BsonIgnoreIfDefault]
public ObjectId _id { get; set; }
public string Title { get; set; }
[BsonIgnoreIfNull]
public double? TextMatchScore { get; set; } // Temporary place for the text match score, for sorting
}
If I do a text search, it is pretty straightforward:
var F = Builders<BigClass>.Filter.Text("text I am looking for");
var Result = MongoDriver.Find(F).ToListAsync().Result;
If I want to sort by the score of the text search, it's a bit more messy (and very POORLY documented):
var F = Builders<BigClass>.Filter.Text("text I am looking for");
var P = Builders<BigClass>.Projection.MetaTextScore("TextMatchScore");
var S = Builders<BigClass>.Sort.MetaTextScore("TextMatchScore");
var Result = MongoDriver.Find(F).Project<BigClass>.Sort(S).ToListAsync().Result;
Essentially it requires me to add a field in the class (TextMatchScore) to hold the result.
If I want to get the data, without sorting and project it to SmallClass, it is straightforward:
var F = Builders<BigClass>.Filter.Text("text I am looking for");
var P = Builders<BigClass>.Projection.Include(_ => _.id).Include(_ => _.Title);
var Result = MongoDriver.Find(F).Project<SmallClass>(P).ToListAsync().Result;
Now if "I want it all", that's where problem arises:
var F = Builders<BigClass>.Filter.Text("text I am looking for");
var P = Builders<BigClass>.Projection.MetaTextScore("TextMatchScore").Include(_ => _.id).Include(_ => _.Title).Include(_ => _.TextMatchScore);
var S = Builders<BigClass>.Sort.MetaTextScore("TextMatchScore");
var Result = MongoDriver.Find(F).Project<SmallClass>.Sort(S).ToListAsync().Result;
I get an exception:
Message = "QueryFailure flag was true (response was { \"$err\" : \"Can't canonicalize query: BadValue must have $meta projection for all $meta sort keys\", \"code\" : 17287 })."
As expected, the error is not documented anywhere as the Mongo guys expect users to self-document everything.
If I make the projection to 'BigClass', there is no problem, the code runs and just fills in the right fields.
If you google that text with C#, the posts you find are mine when I was trying to figure out the text search, which is also poorly documented.
So when we combine projection, text search and sorting, there doesn't seem to be any example anywhere and I just can't get it to work.
Does anyone know the reason for that problem?
This works for me:
var client = new MongoClient();
var db = client.GetDatabase("test");
var col = db.GetCollection<BigClass>("big");
await db.DropCollectionAsync(col.CollectionNamespace.CollectionName);
await col.Indexes.CreateOneAsync(Builders<BigClass>.IndexKeys.Text(x => x.Title));
await col.InsertManyAsync(new[]
{
new BigClass { Title = "One Jumped Over The Moon" },
new BigClass { Title = "Two went Jumping Over The Sun" }
});
var filter = Builders<BigClass>.Filter.Text("Jump Over");
// don't need to Include(x => x.TextMatchScore) because it's already been included with MetaTextScore.
var projection = Builders<BigClass>.Projection.MetaTextScore("TextMatchScore").Include(x => x._id).Include(x => x.Title);
var sort = Builders<BigClass>.Sort.MetaTextScore("TextMatchScore");
var result = await col.Find(filter).Project<SmallClass>(projection).Sort(sort).ToListAsync();
I removed the include of the TextMatchScore. It still comes back, because it was included by the MetaTextScore("TextMatchScore").
Documentation is a work in progress. We tackle the major use cases first as those hit the most people. This use case isn't that common and hasn't been documented. We certainly accept pull requests, both for code and documentation. Also, feel free to file a documentation ticket at jira.mongodb.org under the CSHARP project.
Solution which works in MongoDB.Driver 2.x is as follows. What is important is to not do Include in Projection, as it will erase default one, (or remember to add proper projection)
Query:
{
"find":"SoceCollection",
"filter":{
"$text":{
"$search":"some text to search"
}
},
"sort":{
"TextScore":{
"$meta":"textScore"
}
},
"projection":{
"TextScore":{
"$meta":"textScore"
},
"_id":0,
"CreatedDate":0
},
"limit":20,
"collation":{
"locale":"en",
"strength":1
} ...
CODE
var sort = Builders<BigModel>.Sort.MetaTextScore(nameof(LightModel.TextScore));
var projection = Builders<BigModel>.Projection
.MetaTextScore(nameof(LightModel.TextScore))
.Exclude(x => x.Id)
.Exclude(x => x.CreatedDate);
return await Collection()
.Find(filter, new FindOptions { Collation = new Collation("en", strength: CollationStrength.Primary) })
.Project<LightModel>(projection)
.Sort(sort)
.Limit(20)
.ToListAsync();

Using JArray for serializing data from database in WebAPI (C#)

I want to achieve the following JSON data:
[
{
"name":"Deutschland",
"code":"de"
},
{
"name":"Frankreich",
"code":"fr"
},
{
"name":"Japan",
"code":"jpn"
}
]
Currently I'm getting this result of JSON data:
{
"groups":[
{
"name":"Deutschland",
"code":"de"
},
{
"name":"Frankreich",
"code":"fr"
},
{
"name":"Japan",
"code":"jpn"
}
]
}
Here is the code of the Controller:
public dynamic GetGroups()
{
JObject o = JObject.FromObject(new
{
groups = from g in db.QR_Groups
select new
{
name = g.name,
code = g.code
}
});
return o;
/*Here I've tried to get JSON data as array without the Property "groups"*/
//JArray a = new JArray(
// from g in db.QR_Groups
// select new JValue(g));
//return a;
}
Can anyone tell me how to retrieve the JSON data as per the first JSON example above?
And is the type "dynamic" good practice for the method?
First of all there is no need to do serialization manually. ASP.Net WebApi MediaFormatters are going to take care of it based on the Request Content-Type. So Create a class as shown below.
public class Group
{
public string name { get; set; }
public string code { get; set; }
}
Then your Web API endpoint should be -
[HttpGet]
public HttpResponseMessage GetCountries()
{
List<Group> groups = (from g in db.QR_Groups
select new Group
{
name = g.name,
code = g.code
}).ToList();
return Request.CreateResponse(HttpStatusCode.OK, groups);
}
And when I make a Fiddler request, I was able to get the output which you are interested -
Try this one:
var json = JsonConvert.SerializeObject(from g in db.QR_Groups
select new
{
name = g.name,
code = g.code
});
And is the type "dynamic" good practice for the method?
no, it's not best practise. Better one is to create new class

C# Facebook SDK dynamic data conversion

I got an Object from Facebook SDK
var responsePages = (JsonObject)FBClient_.Get(new { ids =
[123123123, 123123123, 12312213, etc]});
This query returns data (see image)
Now how do I convert this into a list of objects?
I have tried following but it does not work
var pe = (from dynamic page
in (IList<object>)(object)responsePages.Values
orderby page.name ascending
select new FBPage
{
Id = Convert.ToString(page.id),
}
).ToList();
So that failed, would appreciate any help because this dynamic stuff drives me seriously mad.
Thanks
You don't need to cast if you are using dynamic.
var fb = new FacebookClient();
dynamic result = fb.Get(new { ids = new[] { "...", "..." } });
var pages = new List<FBPage>();
foreach (var page in result.Values)
{
var fbPage = new FBPage {
Id = page.id,
Name = page.name
};
pages.Add(fbPage);
}
or if you want to use linq. (extension methods are not supported for dynamic, so you will need to do some casting, JsonObject => IDictionary<string, dyanmic>, JsonArray => IList<dynamic>)
var pages = ((IDictionary<string, dynamic>)result)
.Select(kpv => kpv.Value)
.Select(p => new FBPage { id = p.id, name = p.name })
.ToList();
Or you strongly typed classes.
var fbPages = fb.Get<IDictionary<string, FBPage>>(new {
ids = new[] { "...", "..." }
}).Values;
public class FBPage
{
public string id { get; set; }
public string name { get; set; }
}
'responsePages.Values' is an ICollection<JsonValue>
JsonValues can be serialized to Json strings, which can then be made the play things of a good Json deserializer such as Json.Net.
Using a simple loop this might look something like:
List<dynamic> dynamicList = new List<dynamic>();
List<FBPage> pe = new List<FBPage>();
foreach(var page in responsePages.Values)
{
//ToString is overridden in JsonValue to provide a string in Json format
string pageAsJsonString = page.ToString();
//Deserialize (parse) to a dynamic object using Json.Net's JObject.Parse static method
dynamic parsedPage = JObject.Parse(pageAsJsonString);
dynamicList.Add(parsedPage);
//Alternatively, if you have an appropriate object handy, deserialize directly:
FBPage deserializedPage = JsonConvert.DeserializeObject<FBPage>(pageAsJsonString);
pe.Add(deserializedPage);
}
foreach(dynamic page in dynamicList)
{
Console.WriteLine(page.name);
}
foreach(FBPage page in pe)
{
Console.WriteLine(page.Id);
}

Linq using StartsWith always empty

I have a simple List with dummy data as follows:
List<Organisation> list = new List<Organisation>();
list.Add(new Organisation() { LogoUrl = "/images/logos/Blade.png", OrganisationId = 1, OrganisationName = "Blade" });
list.Add(new Organisation() { LogoUrl = "/images/logos/Torn.png", OrganisationId = 2, OrganisationName = "Torn" });
When I run the linq query:
var results = from org in OrganisationsController.GetDummyList()
where org.OrganisationName.StartsWith(searchString)
select org;
It always returns an Empty result. In this case the searchString is specified by the user and the example would be "Tor".
Using different variations like 'where org.OrganisationName == searchString' where the search string is Torn works. But StartsWith never works.
Any ideas where I'm going wrong?
EDIT:
From Jon's code I changed my code to look as follows:
public JsonResult Search(string searchString)
{
//create json result object
JsonResult data = new JsonResult();
var list = OrganisationsController.GetDummyList();
//query the list
var results = from org in list
where org.OrganisationName.ToLower().Contains(searchString.ToLower())
select org;
if (results.Any())
{
System.Diagnostics.Debug.Write("found");
}
//setup the data
data.Data = results;
//return the data
return Json(data, JsonRequestBehavior.AllowGet);
}
Note: I changed the StartsWith to Contains, but both are giving me similary problems.
One of my organisations is called 'Absa'. Here's the really strange thing when I fire up the app for the first time putting in 'bsa' returns nothing, I then enter 'Absa' and it returns a good result. Then I entered 'bsa' again just to double check and it returned Absa which it didn't in the first test. Why would the result not work at first then work later?
Thanks,
Jacques
Unable to reproduce. It works fine for me:
using System;
using System.Collections.Generic;
using System.Linq;
class Organisation
{
public string LogoUrl { get; set; }
// Removed redundant Organisation prefixes
public int Id { get; set; }
public string Name { get; set; }
}
class Test
{
static void Main()
{
// Used collection initializer for sanity
var list = new List<Organisation>
{
new Organisation { LogoUrl = "Blade.png", Id = 1, Name = "Blade" },
new Organisation { LogoUrl = "Torn.png", Id = 2, Name = "Torn" },
};
string searchString = "Tor";
var query = from org in list
where org.Name.StartsWith(searchString)
select org;
// Nicer version:
// var query = list.Where(org => org.Name.StartsWith(searchString));
Console.WriteLine(query.Count()); // 1
}
}
Work out the difference between your code and my code to find out what's wrong.
In particular, you've shown code using List<T>, which means LINQ to Objects. If your real code uses LINQ to SQL or Entity Framework, that could easily affect things.

Categories