I'm starting to play with neo4jclient, and although I have found the wiki pages which show pulling out nodes etc. I'm a little confused how to take a slighlty more complex structure from the graphDB and reconstruct it into my POCO objects.
As an example, say I have the following graph:
And I have the following classes:
public class Person
{
public string name { get; set; }
public List<Sport> watches { get; set; }
public List<Sport> plays { get; set; }
}
public class Sport
{
public string name { get; set; }
public GoverningBody governingBody { get; set; }
}
public class GoverningBody
{
public string name { get; set; }
}
Could somebody give me the c# code I would need to use to pull out "David", along with the sports he plays and the governing body for that sport. The end goal would be that the Person, Sport(s) and GoverningBody objects would all be populated so that I can use them as normal within the C# code.
Thanks
David
This is a very quick solution - you can create (in effect) an anonymous type in the With statement that you can parse into a result, for example, with the addition of a SportAndGovern class:
public class SportAndGovern
{
public Sport Sport { get; set; }
public GoverningBody Govern { get; set; }
}
You can execute this Cypher (I've not used parameterised stuff, you should) to get a person with a list of the sports they play - you do end up with duplicated Governing Bodies coming back, i.e. one for each Sport the person watches.
var query = Client.Cypher
.Match("(p:Person {Name:'David'})")
.OptionalMatch("(p)-[:PLAYS]->(s:Sport)<-[:GOVERNS]-(g:GoverningBody)")
.With("p, Collect(Distinct {Sport: s, Govern: g}) as sportAndGovern")
.Return((p, sportAndGovern) => new
{
Person = p.As<Person>(),
SportAndGovern = Return.As<IEnumerable<SportAndGovern>>("sportAndGovern")
});
This code should get you started
var userQuery = client.Cypher
.Match("(n:Person { name: 'David'})-[:PLAYS]->(s:Sport)")
.Return((n, s) => new
{
Peep = n.As<Person>(),
Sports = s.CollectAsDistinct<Sport>()
})
.Results
.FirstOrDefault();
var david = userQuery.Peep;
david.plays = userQuery.Sports.ToList();
SO looking at this in a little detail there are some points to note.
Firstly, client refers to an instance of Neo4jClient and assumes that you have previously initialised this.
Secondly, the query assumes that you only have one Person node where the name property has a value of "David".
The Return clause projects the query results into an Anonymous type. It uses the CollectAsDistinct method to return an IEnumerable<Sport> collection. This translates into COLLECT(distinct s) in Cypher to collect the Sport nodes.
Finally, it then users the anonymous type to build up a Person object to return.
Related
I have a class called Business, a class called Tag and a class called BusinessTagLink.
Both Business and Tag have the following property:
public virtual IList<BusinessTagLink>? BusinessTagLinks { get; set; }
and every business is linked to one or more tags via the BusinessTagLink table.
My BusinessTagLink class looks like this:
public class BusinessTagLink : BaseEntity
{
public int BusinessTagLinkId { get; set; }
public int BusinessId { get; set; }
public int TagId { get; set; }
public virtual Business Business { get; set; }
public virtual Tag Tag { get; set; }
}
I am trying to build a filter that will allow a user to filter down the list of businesses by interacting with a list of tags. I'm currently thinking they'll click on the tag and the ID of that tag will then get added to an int array, which will be passed to a filter method along with the full list of potential businesses. Something like:
public static IQueryable<Business> Filter(this IQueryable<Business> businesses, int[] tagIds) { }
What I'm really struggling with is how to use these parameters to return a list of filtered businesses. I think the LINQ query will make use of .Contains() and/or .Any() but I'm clueless beyond that.
return businesses.Where(tagIds.Contains(x => x.BusinessTagLinks.Select(y => y.TagId)))
return businesses.Where(x => x.BusinessTagLinks.Any(y => y.TagId == [no idea how to target array here]));
Any advice would be greatly appreciated.
return businesses.Where(x => x.BusinessTagLinks.Any(y => tagIdArray.Contains(y.TagId)));
This is basically saying give me the businesses where any of the business tag links has a tagId that exists in the array you're providing.
I do project by asp.net core mvc. when I do copy of the model and change its values, the values of the original model is changing too because it made copy of the model by reference, so the value place in data is same.
I need way that I can do copy for values of the model doesn't connect with original model.
Your question could have many answers depending on the encapsulation of the object you are copying. I will assume you are operating on a low level entity object rather than an object that is supposed to encapsulate it. If this assumption is incorrect and it is a higher level object that encapsulates entity operations I will gently remind you of good programming practices: Martin Fowler - TellDontAsk.
For the answer I will use the class below to illustrate:
public class Student
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
I assume what is happening is something similar to the following:
Student john = new Student();
Student jane = john;
jane.FirstName = "Jane"; // now john.FirstName == "Jane"
What you are going to need to do is clone the object to a new object instance. There are various ways to do that.
Option #1:
// Create a new entity object manually assigning each value
// from the first object to the value in the new object.
var clonedStudent = new Student
{
Id = john.Id, // Copies value not reference
LastName = john.LastName, // string is immutable this OK
FirstName = john.FirstName, // string is immutable this OK
// DateTime is a struct I think so it should pass value
EnrollmentDate = john.EnrollmentDate // Verify my assumption
};
Option #2:
// Make Student class partial and extend it with clone method.
// This is helpful for generated entities not using the code-first approach.
public partial class Student
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
public partial class Student
{
public Student Clone()
{
return new Student
{
Id = Id, // Copies value not reference
LastName = LastName, // string is immutable this OK
FirstName = FirstName, // string is immutable this OK
// DateTime is a struct I think so it should pass value
EnrollmentDate = EnrollmentDate, // Verify my assumption
};
}
}
To use it you would write:
Student clonedStudent = john.Clone();
Option #3: You could use a NuGet package that does the cloning for you. There are various ones that do that. A quick google search pulled up this one for me. DeepCloner
If you are copying objects from one type to another you might want to use AutoMapper.
NOTE: Also, based on your question a good knowledge of how entity framework handles changes might be useful.
Tracking vs. No-Tracking Queries
Hopefully that helps.
Happy coding!!!
option 1 use AutoMapper
option 2 create Copy using Reflection
public class PropertyCopier<TParent, TChild> where TParent : class
where TChild : class
{
public static void Copy(TParent parent, TChild child)
{
var parentProperties = parent.GetType().GetProperties();
var childProperties = child.GetType().GetProperties();
foreach (var parentProperty in parentProperties)
{
foreach (var childProperty in childProperties)
{
if (parentProperty.Name == childProperty.Name && parentProperty.PropertyType == childProperty.PropertyType)
{
childProperty.SetValue(child, parentProperty.GetValue(parent));
break;
}
}
}
}
}
I am trying out using Neo4j in .Net with Neo4jClient. I am trying to find the best way to populate the following concrete C# classes:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class PersonData
{
public Person Person { get; set; }
public List<Relation> Relations { get; set; }
public List<string> Labels { get; set; }
}
public class Relation
{
public Person Relative {get; set;}
public string Relationship { get; set; }
}
I currently have the following basic graph model:
(p:Person{ id: 1, name: 'Fred', age: 42})-[r:PARENT_OF]->(c:Person{ id: 2, name: 'Sarah', age: 8})
Also with other relationship types, e.g. MARRIED_TO.
I currently have the following query, and I want to get a particular person node, and it's relations (i.e. the related person nodes and a string of what the relationship type is, which could be the relationship type or a value of the relationship), populating PersonData. I can currently easily populate Person, but I do not know how I can populate Relations.
var data = client.Cypher
.Match("(p:Person)-[r*1..1]->(per:Person)")
.Where((Person p) => p.Id == 3)
.Return((p, per) => new PersonData
{
Person = p.As<Person>()
})
.Results;
Is this population of PersonData something I will have to do outside of the query, or can it be done in the return statement?
I also have the added problem that this query is returning the node with id 3 twice, and I do not know why.
Many thanks.
This works with your classes - as long as you change from List<Person> to IEnumerable
var query = gc.Cypher.Match("(p:Person {Id:2})")
.OptionalMatch("(p)-[r]->(p2:Person)")
.With("p, {Relationship: type(r), Relative: p2} as relations")
.Return((p, relations) => new PersonData
{
Person = p.As<Person>(),
Relations = relations.CollectAs<Relation>()
});
I am using entity framework and linq and I am trying to return an array of parents without using a for loop. Basically here is an example like the one I am facing (I can't give the real example but this is the same thing). There is a person and when I retrieve that person, I want to get an array of that person's ancestors (Male only). So I would return and array [father, grandfather, great-grandfather,...] until the "Father" property is null, or in other words we don't know who the person's father is. What would be the best way to do this with Entity Framework?
Here's an example:
class Person()
{
public string Name { get; set; }
public Guid FatherId { get; set; }
public virtual Person Father { get; set; }
}
class PersonDto()
{
public string Name { get; set; }
public IEnumerable<PersonDto> Ancestors { get; set; }
}
How I would do this with a for loop is:
Person person = Context.People.Find(personId);
PersonDto personDto = person.ToDto();
Person father = person.Father;
while (father != null)
{
personDto.Ancestors.Add(father.ToDto());
father = father.Father;
}
Assuming that there is a foreign key on your person table that references the father (within the same table), and that you are using SQL Server for persistence, you would probably be best off using a stored procedure containing a common table expression to do this.
http://blogs.msdn.com/b/simonince/archive/2007/10/17/hierarchies-with-common-table-expressions.aspx
If you then use EF to run the SP, and set the import as returning a set of "Person", you should also find (as an added bonus) that the parents are actually set on the objects.
I am trying to select custom type from my domain object set and noticed very strange EF behavior. I can figure out that exactly and why that's happening at all. So I have:
public class Person
{
public IList<Device> Devices { get; set; }
}
public class Device
{
public DeviceConfiguration DeviceConfiguration { get; set; }
}
public class DeviceConfiguration
{
public IList<DeviceFeature> DeviceFeatures { get; set; }
}
public class DeviceFeature
{
public DeviceFeatureType DeviceFeatureType { get; set; }
}
And I want to select a custom type from Persons set:
var personDTOs = persons.Select(x =>
new PersonDTO
{
Devices = x.Devices,
DeviceFeatures = x.Devices.SelectMany(y => y.DeviceConfiguration.DeviceFeatures )
});
Problem there is that DeviceFeatures populated with some random entries even when that Person doesn't have ANY Devices
Any thoughts why that is happening?
Update 1:
One significant thing that I forgot to mention is that personDTOs after are sorted and paged (OrderBy, Skip, Take, ToList) and these "random" DeviceFeatures included only after that. But if I select problematic entry before these action I can see everything is as expected. There something wrong with delayed execution and/or selecting custom type, but I cant understand what exactly and what is proper way of doing this kind of selects