Returning neo4j results to a C# object - c#

I have a query that returns a list of object with properties. Consider a C# object with like this structure:
public class Neo4jResult {
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
The query returns a column called "mycollection" and I can store the results to something like this:
public async Task<IEnumerable<Neo4jResult>> MyNeo4jQuery() {
var cypher = client.Cypher
.Match(matchQuery)
.WithParams(myParams);
cypher =
cypher.ReturnDistinct<Neo4jResult>("mycollection")
.OrderBy("toLower(object.Prop1)");
var query = (IOrderedCypherFluentQuery<Neo4jResult>)cypher;
return await query.ResultsAsync;
}
This code works well. However, I had to get the count of this record as another property so my query now returns two columns - "mycollection", and "totalRecords". To facilitate this, I created a new object that reflects this:
public class Neo4jResultNew {
public int TotalRecords { get; set; }
public IEnumerable<Neo4jResult> Results { get; set; }
}
I have then changed my neo4j query to something like:
public async Task<IEnumerable<Neo4jResult>> MyComplexNeo4jQuery() {
var cypher = client.Cypher
.Match(matchQuery)
.WithParams(myParams);
cypher =
cypher.Return<Neo4jResultNew>( (mycollection, totalRecords) => {
{
Results = mycollection.As<IEnumerable<Neo4jResult>>(),
TotalRecords = totalRecords.As<int>()
});
var query = (IOrderedCypherFluentQuery<Neo4jResultNew>)cypher;
return await query.ResultsAsync;
}
The error returned by neo4j is: "Neo4j returned a valid response, however Neo4jClient was unable to deserialize into the object structure you supplied". I just followed the examples online but there's something missing in my projection maybe?

I think the problem is your:
Results = mycollection.As<IEnumerable<Neo4jResult>>(),
Bit. What you're actually wanting is a COLLECT so something like:
Results = mycollection.CollectAs<Neo4jResult>()
mycollection isn't actually an IEnumerable - and you can see it if you run the query in the browser - you don't put it here, so this is a 'rough' version.
If you executed:
MATCH (m:Movie)
RETURN m.title, count(m)
You'll get:
Title1, 1
Title2, 1
Title3, 1
etc
If you execute:
MATCH (m:Movie)
RETURN COLLECT(m.title), count(m)
You'll get:
[title1, title2, title3], 3
for example.

Related

Using Contains() in a Realm query

Let's say we have a realm results taken with
RealmDb.All<Entry>();
Then I want to do some search over those results using not yet supported techniques, like StartsWith on a function return or on a property which is not mapped in realm etc, so I get a subset
IEnumerable<Entry> subset = bgHaystack;
var results = subset.Where(entry => entry.Content.ToLower().StartsWith(needle));
To get somehow these as part of RealmResults, I extract the entry ids like this:
List<int> Ids = new List<int>();
foreach (Entry entry in entries)
{
Ids.Add(entry.Id);
}
return Ids;
and finally I want to return a subset of RealmResults (not IEnumerable) of only those Entries that contain those ids, how can I do that? IDE says the Contains method is not supported.
Can I use some kind of predicate or a comparer for that?
Entry is my model class
using System.ComponentModel.DataAnnotations.Schema;
using Realms;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System;
namespace Data.Models
{
[Table("entry")]
public class Entry : RealmObject
{
public class EntryType
{
public const byte Word = 1;
public const byte Phrase = 2;
public const byte Text = 3;
};
[Key]
[PrimaryKey]
[Column("entry_id")]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("source_id")]
public int SourceId { get; set; }
[Indexed]
[Column("type")]
public byte Type { get; set; }
[Column("rate")]
public int Rate { get; set; }
[Column("created_at")]
public string CreatedAt { get; set; }
[Column("updated_at")]
public string UpdatedAt { get; set; }
[NotMapped]
public Phrase Phrase { get; set; }
[NotMapped]
public Word Word { get; set; }
[NotMapped]
public Text Text { get; set; }
[NotMapped]
public IList<Translation> Translations { get; }
[NotMapped]
public string Content
{
get {
switch (Type)
{
case EntryType.Phrase:
return Phrase?.Content;
case EntryType.Word:
return Word?.Content;
case EntryType.Text:
return Text?.Content;
}
return "";
}
}
}
}
According to the documentation, Realm .NET supports LINQ, so that's promising. In your specific example, you indicate that StartsWith isn't supported, but I see that on the above page, specifically here.
Now, your example makes clear that Entry is a RealmObject, so it's not clear where you'd possibly get a RealmResult from (nor does their documentation on that page mention a RealmResult). Specifically, the home page indicates that you're really only going to ever work with Realm, RealmObject and Transaction, so I'm going to just assume that you meant that you'll need a resulting RealmObject per their examples.
The way you presently have your data object set up, you're rather stuck calling it like you are (though if I could make a recommendation to simplify it a little bit:
var entries = RealmDb.All<Entry>().ToList();
var results = entries.Where(entry => entry.Content.ToLower().StartsWith(needle));
var ids = results.Select(a => a.Id).ToList();
Now, your big issue with just combining the filter predicate in line 2 with the end of line 1: Content itself is marked with a [NotMapped] attribute. Per the documentation again:
As a general rule, you can only create predicates with conditions that
rely on data in Realm. Imagine a class
class Person : RealmObject
{
// Persisted properties
public string FirstName { get; set; }
public string LastName { get; set; }
// Non-persisted property
public string FullName => FirstName + " " + LastName;
}
Given this class, you can create queries with conditions that apply to
the FirstName and LastName properties but not to the FullName
property. Likewise, properties with the [Ignored] attribute cannot be
used.
Because you're using [NotMapped], I've got to believe that's going to behave similarly to [Ignored] and further, because it's just a computed value, it's not something that Realm is going to be able to process as part of the query - it simply doesn't know it because you didn't map it to the information Realm is storing. Rather, you'll have to compute the Content property when you've actually got the instances of your Entry objects to enumerate through.
Similarly, I expect you'll have issues pulling values from Phrase, Word and Text since they're also not mapped, and thus not stored in the record within Realm (unless you're populating those in code you didn't post before executing your Where filter).
As such, you might instead consider storing separate records as a PhraseEntry, WordEntry, and TextEntry so you can indeed perform exactly that filter and execute it on Realm. What if you instead used the following?
public class Entry : RealmObject
{
[Key]
[PrimaryKey]
[Column("entry_id")]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("source_id")]
public int SourceId { get; set; }
[Column("rate")]
public int Rate { get; set; }
[Column("created_at")]
public string CreatedAt { get; set; }
[Column("updated_at")]
public string UpdatedAt { get; set; }
[Column("content")]
public string Content { get; set; }
[NotMapped]
public IList<Translation> Translations { get; }
}
[Table("wordEntry")]
public class WordEntry : Entry
{
}
[Table("phraseEntry")]
public class PhraseEntry : Entry
{
}
[Table("textEntry")]
public class TextEntry : Entry
{
}
And now, you can offload the filtering to Realm:
var wordEntries = RealmDb.All<WordEntry>.Where(entry =>
entry.Content.StartsWith(needle, StringComparison.OrdinalIgnoreCase)).ToList();
var phraseEntries = RealmDb.All<PhraseEntry>.Where(entry => entry.Content.StartsWith(needle, StringComparison.OrdinalIgnoreCase)).ToList();
var textEntries = RealmDb.All<TextEntry>.Where(entry => entry.Content.StartsWith(needle, StringComparison.OrdinalIgnoreCase)).ToList();
var entries = new List<Entry>();
entries.AddRange(wordEntries);
entries.AddRange(phraseEntries);
entries.AddRange(textEntries);
var ids = entries.Select(entry => entry.Id).ToList();
It's not quite as brief as storing it all in one table, but I'm not immediately seeing any Realm documentation that indicates support for executing the same query against multiple tables simultaneously, so at least this would allow you to leave the filtering to the database and work against a more limited subset of values locally.
Finally, so we have all that and I missed your final question up top. You indicate that you want to return a subset of your entries based on some collection of ids you create. In the logic you provide, you're retrieving all the Id properties in all your results, so there's really no further subset to pull.
That said, let's assume you have a separate list of ids that for whatever complicated reason, you were only able to derive after retrieving the list of Entry types from above (themselves all PhraseEntry, WordEntry or TextEntry objects).
At this point, since you've already pulled all the values from Realm and have them locally, just execute another Where statement against them. Because a List implements IEnumerable, you can thus execute the LINQ locally without any of the Realm restrictions:
var myLimitedIdSet = new List<int>()
{
10, 15, 20, 25 //Really complicated logic to narrow these down locally
};
var resultingEntries = entries.Where(entry => myLimitedIdSet.Contains(entry.Id)).ToList();
And you're set. You'll have only those entries that match the IDs listed in myLimitedIdSet.
Edit to address comment
You see this error because of the detail provided at the top of this page in the documentation. Specifically (and adapting to your code):
The first statement gives you a new instance of Entry of a class that implements IQueryable... This is standard LINQ implementation - you get an object representing the query. The query doesn't do anything until you made a further call that needs to iterate or count the results.
Your error is then derived by taking the result from RealmDb.All<Entry>() and trying to cast it to an IEnumerable<Entry> to operate against it as though you have local data. Until you call ToList() onRealmDb.All` you simply have a LINQ representation of what the call will be, not the data itself. As such, when you further refine your results with a Where statement, you're actually adding that to a narrowed version of the IQueryable statement, which will also fail because you lack the appropriate mapping in the Realm dataset.
To skip the optimization I provided above, the following should resolve your issue here:
var bgHaystack = realm.All<Entry>().ToList(); //Now you have local data
var results = bgHaystack.Where(entry => entry.Content.ToLower().StartsWith(needle));
Unfortunately, given your provided code, I don't expect that you'll see any matches here unless needle is an empty string. Not only is your Content property not part of the Realm data and you thus cannot filter on it within Realm, but neither are your Phrase, Word or Text properties mapped either. As a result, you will only ever see an empty string when getting your Content value.
You can further refine the results variable above to yield only those instances with a provided ID as you see fit with normal LINQ (as again, you'll have pulled the data from Realm in the first line).
var limitedIds = new List<int>{10, 20, 30};
var resultsLimitedById = results.Select(a => limitedIds.Contains(a.Id)).ToList();
I've updated my examples above to reflect the use of ToList() in the appropriate places as well.

Convert EF6 to EF core with the ObjectResult

I have some code I am trying to convert. I don't have these ObjectResult and ObjectContext anymore
This is what I did have:
public virtual ObjectResult<string> GetTransData(string iN_MEM_ID)
{
var iN_MEM_IDParameter = iN_MEM_ID != null ?
new ObjectParameter("IN_MEM_ID", iN_MEM_ID) :
new ObjectParameter("IN_MEM_ID", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<string>("GetTransData", iN_MEM_IDParameter);
}
Since I need a List to be returned from a caller ( it is sent back as json data )
This is what I am trying to build
public virtual List<string> GetTransData(string iN_MEM_ID)
{
var iN_MEM_IDParameter = iN_MEM_ID != null ?
new SqlParameter("IN_MEM_ID", iN_MEM_ID) :
new SqlParameter("IN_MEM_ID", typeof(string));
Clinical_CaseTrakker_Context clinical = new Clinical_CaseTrakker_Context();
List<string> offLine = clinical.string.FromSql("EXECUTE CT.GetTransData {0}", iN_MEM_IDParameter);
return offLine;
}
Notice that I am stuck with clinical.string i can't do that , but I am not sure how to take dbcontext instance and run FromSql to execute sql and return to List
In EF Core, it is not possible to use the FromSql method to return a subset of properties (a projection) directly from the database.
You are required to define a some model and a DbSet for that class
public class Foo
{
public string Bar { get; set; }
}
then declare in your context
public DbSet<Foo> Foos { get; set; }
and use it like:
using (var context = new Clinical_CaseTrakker_Context())
{
var offLine = context.Foos
.FromSql($"EXECUTE CT.GetTransData {iN_MEM_IDParameter}")
.Select(x => x.Bar)
.ToList();
return offLine;
}
Your Context needs a virtual DbSet<string> ResultStrings { get; set; } that you can call upon and put the result in. (This does not work, see this post or Roman Marusyk comment below)
EDIT: Your Context needs a virtual DbSet<ResultEntity> ResultEntities { get; set; } that you can call upon and put the result in.
Then you can do return clinical.ResultEntities.FromSql("EXECUTE CT.GetTransData {0}", iN_MEM_IDParameter").toList() to fill the set.
Considering the ResultEntity has an Id and a value property, you can do ResultEntities.Select(e => e.value).toList() to extract a list of strings from the set.

Task/async Lambda on sub list that will call another method

I have a requirement that wants me to replace a value (StatusName) that is a field (ChildItems) in a list that is a field in another list (ParentItems). I haven't worked a lot with Async / Await so I'm sure I'm not doing this correctly
Here is an example of what I'm trying:
public class parentItem {
public int Id {get;set;}
public string Name {get;set;}
public string Description {get;set;}
public List<childItem> ChildItems {get;set;}
}
public class childItem {
public int Id {get;set;}
public string Name {get;set;}
public string StatusId {get;set;}
public string StatusName {get;set;}
}
// Populate parent in list.
List<parentItem> parents = new List<parentItem>();
//populate....
We have a data call that will populate these elements in an async await method. I was hoping to do a simple lambda expression that will call another method that will change the value of the status
Kind of like this:
// lambda I was hoping to try
parents = parents.Select((par) => {
par.ChildItems = par.ChildItems.Select((sub) => {
sub.StatusName = await ChangeChildStatusName(sub.StatusId);
}).ToList();
return par;
});
public async Task<string> ChangeChildStatusName(int statusId) {
// code.
}
In this method (ChangeChildStatusName) there is a database call getting other information that is needed. I'm pretty sure there is a different approach for something like this, but so far I haven't been able to find a solution that fits this scenario.
The key here is to pay attention to your types. The result of your inner Select (after you add a return sub;) is a List<Task<childItem>>, which you can use with await Task.WhenAll(..) to (asynchronously) wait for them all to complete. Similarly, your parent tasks can use the same technique:
var parentTasks = parents.Select(async (par) => {
var childTasks = par.ChildItems.Select(async (sub) => {
sub.StatusName = await ChangeChildStatusName(sub.StatusId);
return sub;
});
par.ChildItems = (await Task.WhenAll(childTasks)).ToList();
return par;
});
parents = (await Task.WhenAll(parentTasks)).ToList();
However, in your particular case (accessing a database), you can probably only do one database call at a time anyway, so you should replace your nested Selects with nested foreachs:
foreach (var par in parents)
foreach (var sub in par.ChildItems)
sub.StatusName = await ChangeChildStatusName(sub.StatusId);

Pass linq query result to viewmodel

In my MVC web application, I have linq query that feth the record from database, I want to display that record on view using viewmodel. I have tried with following code.
[HttpGet]
public ActionResult CreatePDF()
{
RentalAgreementEntities db = new RentalAgreementEntities();
String strSession1 = "39726-10275-6027589725",strStatus = "Y",strUserType = "L";
var q = (from um in db.User_Master
join ut in db.UserType_Master on um.US_SerialNo.ToString() equals ut.UT_UserNo
join pu in db.PropertyUser_Master on ut.UT_SerialNo.ToString() equals pu.PU_UserNo
join pr in db.Property_Master on pu.PU_PropertyNo equals pr.PR_SerialNo.ToString()
where pr.PR_MakerID == strSession1
&& ut.UT_Status == strStatus
&& ut.UT_UserType == strUserType
select new
{
um.US_FirstName,
um.US_LastName
}
).AsEnumerable().Select(um => new User_Master {
US_FirstName = um.US_FirstName.ToString(),
US_LastName=um.US_LastName
}).ToList();
var myviewmodel=new viewmodelPDF()
{
lsusermaster=q.ToList();
}
return View("pdfgenerationvw",myviewmodel);
}
I also created viemodel to manage all model's for to display on a view (Here, Just one model access code).
public class viewmodelPDF
{
public List<User_Master> lsusermaster { get; set; }
}
My model class, for which I am going to fetch record from database.
public partial class User_Master
{
public string US_FirstName { get; set; }
public string US_LastName { get; set; }
public int US_SerialNo { get; set; }
}
//Other Models
Now my problem is that, In my action code , when I am trying to assign query result to the lsusermaster of viewmodel then it gives compiler error as belows.
I don't know, why this compile error is thrown, How can I assign query result to viemodel property?
Try this:
var myviewmodel=new viewmodelPDF()
{
lsusermaster=q.ToList()
};
When you are using an object initializer in C#, you can't use ; between the properties, you use it at the end of the initializer
So just remove the ; (or use a ,, as suggested), and move it to the end of the initializer block
var myviewmodel=new viewmodelPDF()
{
lsusermaster=q.ToList()
};
Using a , works even if there are no more properties... it "looks" bad, but it makes easier to add new properties should you ever need them... if the code is final, I'd not use it, but that's personal preference

How to have a unique C# code with different Entities EF having the same columns

Good morning everybody,
I am doing a AngularJS project using ASP.NET-MVC, C#, EF and an SQL Express DB.
I have an HTML page calling some AngularJS functions calling some functions on MyController.cs.
In MyController.cs I have quite a lot functions using EF.
In my DB, I have hundred of tables with the same columns.
And I want to have the same HTML page for each table, so executing the same functions with different names
For example when I go to the link http://..../Index/TABLE1, MyController.cs would look like :
public ActionResult getCaptions()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = 500000000;
var data =
_db
.TABLE1
.OrderBy(i => i.CodeId)
.ToArray();
return Content(serializer.Serialize(data).ToString(), "application/json");
}
and when I go to the link http://..../Index/TABLE2, MyController.cs would look like :
public ActionResult getCaptions()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = 500000000;
var data =
_db
.TABLE2
.OrderBy(i => i.CodeId)
.ToArray();
return Content(serializer.Serialize(data).ToString(), "application/json");
}
I have thought about a solution like this :
Declare a global variable on the controller tableName
Modify the Index ActionResult
public ActionResult Index(string id)
{
tableName = id;
return View();
}
Now I am stuck ...
Any help is needed, thanks.
EDIT : If you downvote, can you at least explain why ? Thanks
I see you #aBennouna may have already given up on this question, but I took an interest in the question and decided it needed a solution. It may not be 100% what you asked for since here you can't take in a table name as a string parameter.
First if all the tables have same columns, you can inherit them from a same base:
public class TableBase
{
[Key]
public int CodeId { get; set; }
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public int Prop3 { get; set; }
}
Using this base we can define our tables:
public class Table1 : TableBase
{
}
public class Table2 : TableBase
{
}
// etc...
This enables us to write a generic GetCaptions method like #CodeCaster suggested (thanks for the nudge in right direction):
public ActionResult GetCaptions<T>() where T : TableBase
{
var set = db.Set<T>();
// get all objects to array
var list = set.OrderBy(i => i.CodeId).ToList();
// serialize and return result
// OR get single object and a value
var item = set.FirstOrDefault();
var propertyValue = item.Prop1;
}
Usage:
GetCaptions<Table1>();
GetCaptions<Table2>();

Categories