In Linq to XML, how to you get ALL descendents of root? - linq

In Linq to XML, how to you get ALL descendents of the root?
I can't get my statement to even compile as it always says
A query body must end with a select clause or group clause
So given this XML (SVG in this case):
I would like to enumerate all the shape nodes (rect/ellipse/path etc):
var xml = XDocument.Load(#"C:\diagram.svg");
var query = from c in xml.Root.Descendants().SelectMany(); // <- doesn't compile

It turns out I just needed to add select c linq expression instead of the .Select() or .SelectMany() function call:
var xml = XDocument.Load(#"C:\diagram.svg");
var query = from c in xml.Root.Elements()
select c;
foreach (var element in elements)
{
// do work
}
Also in my case I need .Elements() of root rather than .Descendents() as the latter flattens the tree structure of the SVG which is probably not what most people want when parsing the SVG. So the Elements() gives you all the immediate children of the root element.

Related

linq select clause syntax

Linq Select method takes Func as input parameter. This means I can have multiple statements in selector for Select, such as
var myresult = sources.Select(s =>
{int x; if (s.val = high) {x=1} else if (s.val = med) {x=2} else {x=3}; return x;
}
)
How can I do this using Linq query syntax
var myresult = from s in sources
select ...
Here, the code in Func part (if ... else if .. else) is artificial. What I really want to know is the syntax of select clause, which may be described as
select select-expression
What is the syntax of
select-expression
I wouldn't want to see your first version in my code. If you need to have what is basically a full function in the lambda, I would rather see the lambda simply invoke a full function! In other words...
theQuery.Select(s => GetX(s)); // just define a GetX function
And that would also be a straightforward translation to query expression syntax
from s in sources
select GetX(s);
You would not be able to put your full code block into the query expression syntax. You could translate your given logic to something usable (yet messy), however I'm quite sure your snippet is just a general example. On the offhand change it isn't, you might try
select s.val == high ? 1 : (s.val == med ? 2 : 3); // totally messy
Instead of special-casing values, with the if/else equivalent of a switch statement, it is more Linq-friendly to group and filter your values:
var myResult = from s in sources
group by s.val into g
select new { Val = g.Key, Sources = g };
var groupHigh = myResult.Where(i => i.Val == high);
var groupMedium = myResult.Where(i => i.Val == medium);
var groupOther = myResult.Except(groupHigh.Concat(groupMedium));
Note that the code I've provided is just a starting place, and isn't the best way to achieve your specific goal. I'd address this in one of these ways:
Change how group by is used (use SomeFunction(s.Val) instead of directly using s.Val)
Change the code around this query to flow better with the natural groupings, so I didn't require the groups to be transformed
This is not possible.
If you really want to, you could create an Func<T> from an anonymous method and invoke it, but that would be horrible.
MSDN indicates select is a contextual keyword of C# 4.0. So I checked the C# Language Specififcation 4.0. Its Select clauses section (7.16.2.5) specifies that
A query expression of the form
from x in e select v
is translated into
( e ) . Select ( x => v )
except when v is the identifier x, the translation is simply
( e )
As the result, the syntax for
select select-expresion
select-expression should be anything that can be used as TResult in Select Method. So the functionality can be done using anonymous Func in Select method may not be able to achieved using select clause.
Conclusion is that you should stick with Method syntax as this is how the code really runs behind the scene.

Longish LINQ query breakes SQLite-parser - simplify?

I'm programming a search for a SQLite-database using C# and LINQ.
The idea of the search is, that you can provide one or more keywords, any of which must be contained in any of several column-entries for that row to be added to the results.
The implementation consists of several linq-queries which are all put together by union. More keywords and columns that have to be considered result in a more complicated query that way. This can lead to SQL-code, which is to long for the SQLite-parser.
Here is some sample code to illustrate:
IQueryable<Reference> query = null;
if (searchAuthor)
foreach (string w in words)
{
string word = w;
var result = from r in _dbConnection.GetTable<Reference>()
where r.ReferenceAuthor.Any(a => a.Person.LastName.Contains(word) || a.Person.FirstName.Contains(word))
orderby r.Title
select r;
query = query == null ? result : query.Union(result);
}
if (searchTitle)
foreach (string word in words)
{
var result = from r in _dbConnection.GetTable<Reference>()
where r.Title.Contains(word)
orderby r.Title
select r;
query = query == null ? result : query.Union(result);
}
//...
Is there a way to structure the query in a way that results in more compact SQL?
I tried to force the creation of smaller SQL-statments by calling GetEnumerator() on the query after every loop. But apparently Union() doesn't operate on data, but on the underlying LINQ/SQL statement, so I was generating to long statements regardless.
The only solution I can think of right now, is to really gather the data after every "sub-query" and doing a union on the actual data and not in the statement. Any ideas?
For something like that, you might want to use a PredicateBuilder, as shown in the chosen answer to this question.

Nhibernate linq. The where extension method does not add the where clause to the SQL command, why?

I want to add the where clause to a linq statement, but it doesn't behave as i would expected it to.
When i use this code:
IQueryable<Employee> EmpQuery = from e in Session.Query<Employee>() where e.Surname == "Test" select e;
EmpQuery.ToList();
or i use this code:
IQueryable<Employee> EmpQuery = (from e in Session.Query<Employee>() select e).Where(e => e.Surname == "Test");
EmpQuery.ToList();
The where clause is included in the SQL command, but when i try it this way:
IQueryable<Employee> EmpQuery = from e in Session.Query<Employee>() select e;
EmpQuery.Where(e => e.Surname == "Test");
The where clause is not included in the SQL command. Why is this? Is there another way to dynamically add criteria to a Nhibernate Linq query?
You're not using the return value of Where. LINQ is designed around functional concepts - calling Where doesn't modify the existing query, it returns a new query which applies the filter. The existing query remains as it was - which means you can reuse it for (say) a different filter.
Note that your current query expression (from x in y select x, effectively) is pretty pointless. I would suggest simply writing:
var query = Session.Query<Employee>().Where(e => e.Surname == "Test");
Just to clarify on Jon's remark, your implementation would be fine with the following tweak:
IQueryable<Employee> modifiedQuery = EmpQuery.Where(e => e.Surname == "Test");
Then just invoke the appropriate enumerator (ToList, ToArray, foreach) on modifiedQuery. And I wouldn't say that it create a complete new query, but instead creates a query which wraps around the original (kind of along the lines of the adapter pattern). Granted, your example doesn't need the additions, but this is how you would add additional criteria onto an existing LINQ expression, and that is what your question actually asked.

C# HashSet union on IEnumerable within LINQ Func expression does not work (possible precompiler bug)

I am using using Microsoft .NET Framework 4.0.
I have run into this using Aggregate on a Dictionary<T, List<T>> to extract the set of type T values used across all type List<T> lists in the dictionary. Here is the simplest case I could come up with that exhibits the same behaviour.
First, as the documentation states, the following does work:
var set = new HashSet<int>();
var list = new LinkedList<int>();
var newSet = set.Union(list);
That is, I can call Union on a HashSet with a List as the argument (since it implements IEnumerable).
Whereas, the equivalent expression within a Func argument of the LINQ Aggregate expression produces an error (precompiler at least):
new List<int>[] { new List<int>() }.Aggregate(new HashSet<int>(), (acc, list) => acc.Union(list));
It expects the argument of Union to be HashSet, and will cooperate if it is given one, contrary to its behaviour outside LINQ/Func expressions.
The real world example I was using when I came across the problem was:
public AdjacencyListGraph(Dictionary<TVertex, LinkedList<TVertex>> adjacencyList)
{
var vertices = adjacencyList.Aggregate(new HashSet<TVertex>(),
(vertices, list) => vertices.Union(list.Value));
}
Which complains that it cannot convert IEnumerable<TVertex> to HashSet<TVertex>...
The problem here is in your understanding of the Select method. The lambda passed in does not recieve the list but instead elements of the list. So the variable you've named list is in fact of type int which is not compatible with Union.
Here is a more explicit example of what you're trying to do
new List<int>().Select( (int list) => new HashSet<int>().Union(list));
With the type infererence removed it's much clearer why this doesn't work.
The problem actually lies in trying to replace the accumulator type of HashSet with the IEnumerable, .Union method doesn't add items to HashSet, but returns the new IEnumerable on the resulting union.
You should change your code to the following:
// select from keys
var vertices = new HashSet<TVertex>(adjacencyList.Keys);
or
// select from values
var vertices = new HashSet<TVertex>(adjacencyList.SelectMany(dictEntry => dictEntry.Value));
new List<int>().Select(list => new HashSet<int>().Union(list));
I think you would expect to use SelectMany here, unless you want the result to be IEnumerable<IEnumerable<T>>.

Linq: Dynamic Query Contruction: query moves to client-side

I've been following with great interest the converstaion here:
Construct Query with Linq rather than SQL strings
with regards to constructing expression trees where even the table name is dynamic.
Toward that end, I've created a Extension method, addWhere, that looks like:
static public IQueryable<TResult> addWhere<TResult>(this IQueryable<TResult> query, string columnName, string value)
{
var providerType = query.Provider.GetType();
// Find the specific type parameter (the T in IQueryable<T>)
var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
var tableType = iqueryableT.GetGenericArguments()[0];
var tableName = tableType.Name;
var tableParam = Expression.Parameter(tableType, tableName);
var columnExpression = Expression.Equal(
Expression.Property(tableParam, columnName),
Expression.Constant(value));
var predicate = Expression.Lambda(columnExpression, tableParam);
var function = (Func<TResult, Boolean>)predicate.Compile();
var whereRes = query.Where(function);
var newquery = whereRes.AsQueryable();
return newquery;
}
[thanks to Timwi for the basis of that code]
Which functionally, works.
I can call:
query = query.addWhere("CurUnitType", "ML 15521.1");
and it's functionally equivalent to :
query = query.Where(l => l.CurUnitType.Equals("ML 15521.1"));
ie, the rows returned are the same.
However, I started watching the sql log, and I noticed with the line:
query = query.Where(l => l.CurUnitType.Equals("ML 15521.1"));
The Query generated is:
SELECT (A bunch of columns)
FROM [dbo].[ObjCurLocView] AS [t0]
WHERE [t0].[CurUnitType] = #p0
whereas when I use the line
query = query.addWhere("CurUnitType", "ML 15521.1");
The query generated is :
SELECT (the same bunch of columns)
FROM [dbo].[ObjCurLocView] AS [t0]
So, the comparison is now happening on the client side, instead of being added to the sql.
Obviously, this isn't so hot.
To be honest, I mostly cut-and-pasted the addWhere code from Timwi's (slightly different) example, so some of it is over my head. I'm wondering if there's any adjustment I can make to this code, so the expression is converted into the SQL statement, instead of being determined client-side
Thanks for taking the time to read through this, I welcome any comments, solutions, links, etc, that could help me with this. And of course if I find the solution through other means, I'll post the answer here.
Cheers.
The big problem is that you're converting the expression tree into a delegate. Look at the signature of Queryable.Where - it's expressed in expression trees, not delegates. So you're actually calling Enumerable.Where instead. That's why you need to call AsQueryable afterwards - but that doesn't do enough magic here. It doesn't really put it back into "just expression trees internally" land, because you've still got the delegate in there. It's now wrapped in an expression tree, but you've lost the details of what's going on inside.
I suspect what you want is this:
var predicate = Expression.Lambda<Func<TResult, Boolean>>
(columnExpression, tableParam);
return query.Where(predicate);
I readily admit that I haven't read the rest of your code, so there may be other things going on... but that's the core bit. You want a strongly typed expression tree (hence the call to the generic form of Expression.Lambda) which you can then pass into Queryable.Where. Give it a shot :)

Resources