Info from xml to c# with irregular xml - c#

I need to get info from an XML-file in C#.
Here is some fragments from the XML.
<Surface id="su-62" surfaceType="InteriorWall">
<Name>S-3-7-I-W-62</Name>
<AdjacentSpaceId spaceIdRef="sp-3-TUIN">
</AdjacentSpaceId>
<AdjacentSpaceId spaceIdRef="sp-7-huizen">
</AdjacentSpaceId>
<CADObjectId>Basic Wall: _omgevingsmuur [184610]</CADObjectId>
</Surface>
...
<Surface id="su-63" surfaceType="ExteriorWall">
<Name>N-4-E-W-63</Name>
<AdjacentSpaceId spaceIdRef="sp-4-onthaal">
</AdjacentSpaceId>
<Opening id="su-63-op-1" openingType="NonSlidingDoor">
</Opening>
<CADObjectId>Basic Wall: _detentiemuur [193431]</CADObjectId>
</Surface>
...
<Surface id="su-282" surfaceType="Shade">
<Name>X-S-282</Name>
<CADObjectId>Basic Roof: Generic - 400mm [190976]</CADObjectId>
</Surface>
As you see there are some surfaces that don't have all the info others have.
I have to know which surfaces are adjacent to which space and if there is an opening or not.
(The ultimate goal is to make an 2d array where you can see which SPACE is adjacent to which SPACE and another array to see if the have a joined opening.)

Assuming you have .NET 3.5 or greater, than this is a good use for LINQ2XML.
You can write a query that will grab the associated areas an identify which space are adjacent to each other.
// Load your XML File into an XDocument object
var xDoc = XDocument.Load(xmlPath);
// this is your query, in the end result with my a Dictionary with the Surface
// Id attribute as the key and the AdjacentSpaceId as the value
var result = (from x in xDoc.Descendants("AdjacentSpaceId")
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
}).GroupBy(sp => sp.SurfaceId)
.ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
If you are unfamiliar with any of the LINQ varients, I will attempt to explain the query in a bit more detail. The local field containing your query results is just called results. The var type is another .NET 3.5 addition that tells the compiler to identify the type by the right side of the expression. In this case, result will be a Dictionary<String, IEnumerable<String>>.
In the first line of the query:
from x in xDoc.Descendants("AdjacentSpaceId")
You are basically telling LINQ to iterate through all nodes in the XML called AdjacentSpaceId. It doesn't matter where these nodes are located or what the parent nodes are called. The Descendants() method in LINQ can be very powerful for this reason as it means you don't need to know the exact XML Path to a particular node to select it, you just need the name (see my note at the end)
The next lines of the query are defining exactly what you want to return from the query:
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
})
All LINQ queries using this syntax must have a select statement (there is also a method syntax that can be used instead of or in addition too that doesn't need a select necessarily, but this syntax is easier to learn in my opinion).
In the select clause, you are basically defining a new Anonymous Type that has 3 properties, SurfaceId, SurfaceName, and AdjacentSpace. I'll explain one of the assignments and it should let you understand all of them:
SurfaceId = (String)x.Parent.Attribute("id")
In this line, x refers to the initial line of the LINQ query where you were iterating all of your AdjacentSpaceId nodes, so x is of type XElement.
The Parent property of x is going to select the parent node, in this case Surface. (NOTE: If your root node in your XML happens to be called AdjacentSpaceId, then this line is going to throw an exception since the parent of a root node will be null, but seems to be a safe assumption that it won't be a problem in this case). The Parent property on XElement is also going to be another XElement.
The next part is the Attribute method of XElement, and you are selecting the first attribute of node called id.
So effectively you are selecting the id attribute of all parent nodes to each AdjacentSpaceId nodes.
And lastly, I am casting this to a String. There is a Value property that can be used instead, but my personal preference is to cast to a String because if the Attribute("id") method fails (which could happen if there is no attribute called "id"), calling the Value property will throw an exception, whereas casting to a string will just return null in these cases.
The other parts of this select clause are virtually the same.
The rest of this query are effectively separate queries. I just chained them together, but they could easily be broken out and put on their own.
The next piece:
GroupBy(sp => sp.SurfaceId)
is grouping the result of your LINQ query by the SurfaceId property. This is a key step because it sounds like you want to know where spaces are adjacent to each surface and this will effectively group all of the adjacent spaces together by the surface.
If you aren't familiar, sp => sp.SurfaceId is a Lambda Expression for creating anonymous functions or delegates quickly and easily.
And the final piece will take your grouped results and convert them into something more usable, usually a Dictionary<>, or in this case a Dictionary<String, IEnumerable<String>>
ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
Just one side note about the Descendants() extension method for Linq-to-Xml objects, while it can be very useful as I mentioned above, it can also be very dangerous. When you have multiple nodes in the same XML with the same name but with different purposes or in different parents of the tree, Descendants() will return ALL objects. If you only want to return some of the nodes matching a specific name, you need to filter it down first by using the Element() or Elements() extension methods first to select the proper parent node, then you can safely call Descendants().

Related

Sorting by count of matching related data Linq, ThenBy doesnt work

I wrote a query that sorts the result based on the amount of matching elements of a given list. This works when using a single OrderBy.
However, since I want to use Pagination, I need to use a ThenBy to make sure the order is always the same.
The current query somehow moves the subquery inside the OrderBy/ThenBy and can't be translated.
How can I re-write this query so that ThenBy would work?
Thanks.
Code:
products
.Select(product => new ProductDto(product)
{
MatchingContainsCount = (from contain in product.Contains
where allergens.Contains(contain.Allergen.Name)
select contain.Allergen).Count(),
MatchingMayContainCount = (from mayContain in product.MayContain
where allergens.Contains(mayContain.Allergen.Name)
select mayContain.Allergen).Count()
})
.OrderBy(dto => dto.MatchingContainsCount)
.ThenBy(dto => dto.Id); // Without this line it works
The Translation error:
Instead assigning Id property inside the constructor, assign the Id property and other properties within the PropertyDto body, similar to MatchingContainsCount property. EF core doesn't translate complex property assignment within the class constructor or methods to SQL. Only the simple assignments. This should fix the problem.

Query ODataV4 connected service with LINQ - Get last record from table

Im trying to query my OData webservice from a C# application.
When i do the following:
var SecurityDefs = from SD in nav.ICESecurityDefinition.Take(1)
orderby SD.Entry_No descending
select SD;
i get an exception because .top() and .orderby is not supposed to be used together.
I need to get the last record in the dataset and only the last.
The purpose is to get the last used entry number in a ledger and then continue creating new entries incrementing the found entry no.
I cant seem to find anything online that explains how to do this.
Its very important that the service only returns the last record from the feed since speed is paramount in this solution.
i get an exception because .top() and .orderby is not supposed to be used together.
Where did you read that? In general .top() or .Take() should ONLY be used in conjunction WITH .orderby(), otherwise the record being retrieved is not guaranteed to be repeatable or predictable.
Probably the compounding issue here is mixing query and fluent expression syntax, which is valid, but you have to understand the order of precedence.
Your syntax is taking 1 record, then applying a sort order... you might find it easier to start with a query like this:
// build your query
var SecurityDefsQuery = from SD in nav.ICESecurityDefinition
orderby SD.Entry_No descending
select SD;
// Take the first item from the list, if it exists, will be a single record.
var SecurityDefs = SecurityDefsQuery.FirstOrDefault();
// Take an array of only the first record if it exists
var SecurityDefsDeferred = SecurityDefsQuery.Take(1);
This can be executed on a single line using brackets, but you can see how the query is the same in both cases, SecurityDefs in this case is a single ICESecurityDefinition typed record, where as SecurityDefsDeferred is an IQueryable<ICESecurityDefinition> that only has a single record.
If you only need the record itself, you this one liner:
var SecurityDefs = (from SD in nav.ICESecurityDefinition
orderby SD.Entry_No descending
select SD).FirstOrDefault();
You can execute the same query using fluent notation as well:
var SecurityDefs = nav.ICESecurityDefinition.OrderByDescending(sd => sd.Entry_No)
.FirstOrDefault();
In both cases, .Take(1) or .top() is being implemented through .FirstOrDefault(). You have indicated that speed is important, so use .First() or .FirstOrDefault() instead of .Single() or .SingleOrDefault() because the single variants will actually request .Take(2) and will throw an exception if it returns 1 or no results.
The OrDefault variants on both of these queries will not impact the performance of the query itself and should have negligble affect on your code, use the one that is appriate for your logic that uses the returned record and if you need to handle the case when there is no existing record.
If the record being returned has many columns, and you are only interested in the Entry_No column value, then perhaps you should simply query for that specific value itself:
Query expression:
var lastEntryNo = (from SD in nav.ICESecurityDefinition
orderby SD.Entry_No descending
select SD.Entry_No).FirstOrDefault();
Fluent expression:
var lastEntryNo = nav.ICESecurityDefinition.OrderByDescending(sd => sd.Entry_No)
.Select(sd => sd.Entry_No)
.FirstOrDefault();
If Speed is paramount then look at providing a specific custom endpoint on the service to either serve the record or do not process the 'Entry_No` in the client at all, make that the job of the code that receives data from the client and compute it at the time the entries are inserted.
Making the query perform faster is not the silver bullet you might be looking for though, Even if this is highly optimised, your current pattern means that X number of clients could all call the service to get the current value of Entry_No, meaning all of them would start incrementing from the same value.
If you MUST increment the Entry_No from the client then you should look at putting a custom endpoint on the service to simply return the Next Entry_No to use. This should be optimistic meaning that you don't care if the Entry_No actually gets used in the end, but you can implement the end point such that every call will increment the field in the database and return the next value.
Its getting a bit beyond the scope of your initial post, but SQL Server now has support for Sequences that formalise this type of logic from a database and schema point of view, using Sequence simplifies how we can manage these types of incrementations from the client, because we no longer rely on the outcome of data updates to be comitted to the table before the client can increment the next record. (which is what your TOP, Order By Desc solution is trying to do.

EF Many to Many select intersection

I'm trying to implement a tagging system with C# entity framework. I cannot get the query required for the case that two or more tags are expected to all be present to return a result. I have a many to many relationship (just FKs, DB first) and I am attempting to get an object when all selected tags exist. Object - LookupTable - Attributes.
I parse the selected tags into a list and then try to get only those objects for which all tags in this list are present. It appears to result in what I'd expect from an "Any" operator, not the "All".
List<string> intersectTags = new List<string>();
foreach (object i in ef.objects.Where(o => o.Attributes.All(attribute =>
intersectTags.Contains(attribute.AttributeNK))))
Update: Also needed to get instances where ef.Object had more tags than intersectTags. Filtering for instances where intersectTags is a subset of Object.Attributes.
Your code fails in case your Attributes is a subset of selected tags.
If you are looking to match when intersectTags is a subset of o.Attributes, try reversing the check.
Unfortunately, Linq to Entity does not support this kind of syntax, we need ToList() to load the objects and perform Linq To Objects.
It should work but there is a performance implications (I'll post an update if I have a better solution):
List<string> intersectTags = new List<string>();
foreach (object i in ef.objects.ToList().Where(intersectTags.All(tags =>
o.Attributes.Any(attribute => attribute.AttributeNK == tags))))
I don't know if I understood well, if so I can give a solution in plain SQL. You have to lookup for all the records that contain one of the requested tag and then group them by the productId with the clause HAVING COUNT equals the number of tags you are passing.
SELECT ProductId FROM ProductTag
WHERE TagId IN (2,3,4)
GROUP BY ProductId
HAVING COUNT(*) = 3
Here's a demo:
http://sqlfiddle.com/#!3/dd4023/3
I'm sorry, currently I cannot give you an implementation in EF (don't have Visual Studio with me), I did something similar for LINQ TO SQL and it uses the PredicateBuilder class, you can find it here:
http://www.codeproject.com/Articles/36178/How-to-manage-product-options-with-different-price
Paolo

Unable to Create a Node with Label as a string parameter from a Function - in Neo4JClient

Im using Neo4JClient To populate the Nodes in a Neo4JDB.
I have a Function that is invoked to create Nodes and Label them as well.
The First Parameter is the Label, and the Second Parameter is the node attribute
function CreateConcpet( String sLabelString, String sDataSpaceName)
{
var newConcept = new Concept {DataSpace=sDataSpaceName};
client.Cypher
.Create("(concept:{labelstring} {ParamnewConcept})")
.WithParam("ParamnewConcept",newConcept)
.WithParam("labelstring",sLabelString)
.ExecuteWithoutResults();
}
This uses the Parameter to Substitute for the Label.
This does not work. From the various posts, I understand that WithParam does not work for Labels.
If the dreaded ExecuteCypher is the only way, can someone help me with a code snippet, that executes a string as query. I cannot find a code snippet, while the documentation gives the call, Im unable to find a sample which creates the query object and executes the String Query.
You can't parametize things that will affect the compiled query plan. Labels are used in the query plan, thus, no parameters.
This is a general restriction with how parameters work in Neo4j, not just Neo4jClient.
I had the very same problem which was (after much mashing of teeth) easily solved ...
I had two types of nodes, Person and Union. These two lines labeled them
MATCH (n) where n.surname >' ' SET n :Person
MATCH (n) where n.surname is null SET n :Union
This was dependent on the two node types having different metadata which distinguished them.

Linq to SQL Nested Objects

I have a object called Category which has an Id, Name, and OwnerId. I then nest these to create Subcategories. If a category has an Owner Id it is a sub category. The number of subcategories is unlimited but each item can only have 1 parent. Simple enough.
My Issue is, I need to access a subcategory after loaded. How do I get the Owning category using Linq. I know the Owner Id but I dont know how many lvls deep the owner could be.
Basically I am looking for a way to get the Category or subcategory where the Id == X but this can live in a subcategory 6 levels or more deep.
I am trying to avoid a loop for each sub category in each sub category....
There is another way to store/retrieve a tree hierarchy as explained in this fogbugz blog post:
Turns out there's a pretty cool
solution for this problem explained by
Joe Celko. Instead of attempting to
maintain a bunch of parent/child
relationships all over your database
-- which would necessitate recursive SQL queries to find all the
descendents of a node -- we mark each
case with a "left" and "right" value
calculated by traversing the tree
depth-first and counting as we go. A
node's "left" value is set whenever it
is first seen during traversal, and
the "right" value is set when walking
back up the tree away from the node.
A picture probably makes more sense:
The Nested Set SQL model lets us add
case hierarchies without sacrificing
performance.
How does this help? Now we just ask
for all the cases with a "left" value
between 2 and 9 to find all of the
descendents of B in one fast, indexed
query. Ancestors of G are found by
asking for nodes with "left" less than
6 (G's own "left") and "right" greater
than 6. Works in all databases.
Greatly increases performance --
particularly when querying large
hierarchies
Here's another post going into more detail. It's written using Sql and php but I think you can get the gist of it and easily translate in Linq to Sql.
In MS SQL 2005 and up you can create recursive queries. In LINQ to SQL however, you are out of luck. Without restructoring the data in the database, there is no way you can traverse the tree in a single database call.
However... there is 1 workaround I can think of. When you are able to group all Category elements of a single tree (or a part of the tree) together, you can pre-load that part of the complete tree in a single statement. After that, you will be able to traverse that part of the tree, without triggering new calls to the database. It would look something like this:
// Load the category that will be used as starting point.
var subCategory = db.Categories.Single(c => c.Id == 56);
// Performance: Load the complete group in one go.
var categories = (
from category in db.Categories
where category.GroupId == subCategory.GroupId
select category)
.ToArray();
// Traverse the tree and get the top-most parent (if any).
var parent = subCategory.GetParents().LastOrDefault();
// Extension method to get the parents.
public static IEnumerable<Category> GetParents(
this Category category)
{
while (category.Parent != null)
{
// NOTE: cat.Parent will not cause a database call
// when the Parent is already loaded by L2S.
yield return cat.Parent;
category = category.Parent;
}
}
This of course will only work if you will be able to determine elements as a group. Whether this solution will be faster also depends on the size of the group. When the group of objects that you load (and don't use) is very big, it will actually slow the application down.

Categories