Using LINQ to retrieve value from XML (Multiple selection) - c#

I'm trying to convert this VB.Net LINQ to C# LINQ.
Basically, what the end solution is trying to achieve is to to take in an XML file; see snippet:
<BasicFee>
<TrialType>Trial</TrialType>
<A>1326.85575</A>
<B>992.409</B>
<C>668.67075</C>
<D>1260.50925</D>
<E>318.8955</E>
<F>323.30925</F>
<G>323.30925</G>
<H>323.44125</H>
<I>323.169</I>
<J>1326.85575</J>
<K>932.877</K>
</BasicFee>
And by passing parameters, "Trial" and "B", the result would give me this value "992.409" (result from Trial / B).
EDIT - This VB is not the correct syntax to achieve the result. Please see the accepted answer.
The VB equivalent is apparently something like this;
Dim sResult As String = (From oRecord In oXML.Descendants("BasicFee") Where oRecord.< Name >.Value = "Trial").FirstOrDefault.< B >.Value
I have tried tons of different ways and I keep getting the same result (either the Trial element OR the A element values (not being able to use them both).
I was hoping there would be something similar to this:
var example = root.Elements("BasicFee").Elements().Where((c=>c.Value == "Trial" && c.Value == "A"));
Any ideas?
Thanks.

As noted in comments, your VB example wouldn't work either, but it's pretty simple to do in both languages. You need to distinguish between names and values, and exactly how your filtering works:
var example = root.Elements("BasicFee")
.Where(x => (string) x.Element("TrialType") == "Trial")
.Select(x => (string) x.Element("B"))
.FirstOrDefault();
Or with C# 6 you could use:
var example = root.Elements("BasicFee")
.FirstOrDefault(x => (string) x.Element("TrialType") == "Trial")
?.Element("B")?.Value;
The value will be null if there's no such element (either no matching BasicFee or no B element within it).

To find the BasicFee that has that trial type and B value, in C#:
var trialType = "Trial";
var propertyName = "B";
var query = oXML.Descendants("BasicFee")
.Where(bf => (string)bf.Element("TrialType") == trialType)
.Select(bf => (string)bf.Element(propertyName))
.SingleOrDefault();
The VB version on the other hand, could be written like so:
Dim trialType = "Trial"
Dim propertyName = "B"
Dim query =
(From bf In oXML...<BasicFee> ''// ... equivalent to Descendants
Where bf.<TrialType>.Value = trialType
Select bf.Element(propertyName).Value).SingleOrDefault

Related

Dynamic Linq "Where statement" with Relation

I'm trying to build a query using Dynamic Linq and a where string statement calculated by myself. For example:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery = "(Id = 1 and Number = 2)";
IQueryable<Publication> query = db.Publications.Include(i => i.Product);
query = query.Where(filterQuery);
results = query.OrderBy(orderQuery).ToList();
This is working great and I get a List of Publications with Products. Now... the question is. How can I make a string statement to get results based on a relation to Product using Dynamic Linq and a string statement?
Something like:
string filterQuery = "(Id = 1 and Number = 2 and Products.Id = 1)"
After a lot of research and trying things, this is an easy and friendly way using the same Dynamic Linq library:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery = "(Id = 1 and Number = 2)";
string filterQueryChildren = "Products.Any(Id == 1)"
IQueryable<Publication> query = db.Publications.Include(i => i.Product).Where(filterQueryChildren);
query = query.Where(filterQuery);
results = query.OrderBy(orderQuery).ToList();
I am not sure why you're doing this, but the following should work:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery1 = "(Id = 1)"
string filterQuery2 = "(Id = 1 and Number = 2)";
IQueryable<Publication> query = db.Publications.
Where(filterQuery1).
Include(i => i.Product);
query = query.Where(filterQuery2);
results = query.OrderBy(orderQuery).ToList();
To make dynamic LINQ query, you need a library that support it or doing it yourself with Expression Tree
See: Entity Framework Dynamic Query Library
Disclaimer: I'm the owner of the project Eval-Expression.NET
This library allows you to evaluate, compile, and execute code at runtime.
The library also contains extension method for dynamic LINQ
Wiki: Eval Dynamic LINQ
Example
// using Z.Expressions; // Don't forget to include this.
// The filterQuery must use the C# syntax
string filterQuery = "x.Id == 1 && x.Number = 2";
IQueryable<Publication> query = db.Publications.Include(i => i.Product);
query = query.Where(x => filterQuery);
EDIT: Answer sub question
Great but how can I filter by Product?
Entity Framework doesn't support filter in Include method.
However, EF+ do
Disclaimer: I'm the owner of Entity Framework Plus
See: EF+ Query IncludeFilter
By combining both libraries, you can achieve your desired result:
// Extension method must be registered, otherwise the library cannot be aware of which extension method exists!
EvalManager.DefaultContext.RegisterExtensionMethod(typeof (QueryIncludeFilterExtensions));
string where1 = "x.Id == 1 && x.Number == 2";
string where2 = "y.Id == 3";
var left2 = ctx.Publications
.Where(x => where1)
.Execute<IQueryable<Publication>>("IncludeFilter(x => x.Product.Where(y => " + where2 + "))")
.ToList();
If you want to try this solution, make sure you download the latest version of Eval-Expression.NET. We just have fixed few min ago an issue we found by trying your scenario.
EDIT2: Answer sub question
Here is what I recommend you.
Try the query without our library then try it dynamically after.
That's easier to find the compilation error or what you want to do really.
By example, we probably didn't understand the initial problem correctly and suggested IncludeFilter which you don't want. I assumed you wanted to filter multiple products. However, it looks you only have one product per publication, so the Where clause method obviously doesn't exist.
Maybe this is more what you are looking for:
string where1 = "x.Id == 1 && x.Number == 2 && x.Product.Id == 3";
var left2 = ctx.Publications
.Include(x => x.Product)
.Where(where1)
.ToList();
So in short, try the query by hardcoding it (without dynamic) then you will know how to use it dynamically after.

C# how to linq.select

i want to do the following i have variables stored in an int array called Straight i want to use Linq and get all the values when divided by 4 return 0 i tried this but it will only give me some bool variables and I'm not sure why
var a = Straight.Select(o => o % 4==0).ToArray();
any help is appreciated also i want to note that I'm still learning c# and Linq is something completely new to me
also i want to be able to check the length of the variable
The part you're looking for is Where and not Select.
var a = Straight.Where(o => (o % 4) == 0).ToArray();
Select projects your list into a new returns type which in the case of the expression (o%4) == 0 is boolean.
Where returns you the same object that fulfill the desired expression.
You need Where, not Select
var a = Straight.Where(o => o % 4 == 0).ToArray();
Select creates a projection. In your example, it turns each element of Straight into a bool.

Object format and return empty string in LINQ query

I am using MVC 4 and entity framework, I am retrieving emails from the server:
var data = db.Candidates.Where(c => ids.Contains(c.ID) && c.Email1 != null).Select(c => new { c.Email1, c.ID }).ToList();
My first question: Does LINQ allow me to return an empty string form the Email1 field if it is null, similar to SQL coalesce? (I would remove the null test from the where clause).
2nd question: what would be the easiest object to use (to replace the "var data =" if I wanted to get c.Name along with the Email1, then use both in a loop? Should I create a model for just 2 fields?
Thanks so much in advance for any insights.
My first question: Does LINQ allow me to return an empty string form the Email1 field if it is null, similar to SQL coalesce? (I would remove the null test from the where clause).
Yes, there is the ?? operator that works similar to the coalesce.:
new { Email1 = c.Email1 ?? "", c.ID } //String.Empty would be nicer, but i think it depends on EF version if you are allowed to use it.
For your second question, if this is the only place you are going to use them, then anonymous is pretty fine.
If you want to use this on other places, yes create an object just with two properties... That's the object's purpose after all. (or maybe a struct?)
Ask one question at a time.
2a. The Null Coalescence operator in C# is ??.
2b. This may or may not be converted by your Linq Provider into a database query.
Do it like this,
var data = db.Candidates
.Where(c => ids.Contains(c.ID))
.Select(c => new
{
Id = c.Id,
Email1 = c.Email1 ?? string.Empty,
Name = c.Name
});
foreach(var row in data)
{
var name = row.Name // etc...
}
If your Linq Provider does not support the ?? operator, put in a .ToList() and use linq-to-objects to perform the tranformation like this,
var data = db.Candidates
.Where(c => ids.Contains(c.ID))
.ToList() // <-- from here is Linq-To-Objects
.Select(c => new
{
Id = c.Id,
Email1 = c.Email1 ?? string.Empty,
Name = c.Name
});

Convert code snippet from VB.NET to C#

I have tried a number of freely available code converters to convert the following piece, however without success.
Dim resultList = ((From e In p_Xml.Elements()
Where UCase(e.Name.LocalName) = searchName).Union(
From a In p_Xml.Attributes()
Where UCase(a.Name.LocalName) = searchName
Select <<%= propertyName %>><%= a.Value %></>)).ToList()
I think I got it here
var resultList = (from e in p_xml.Elements()
where e.Name.LocalName == searchName
select propertyName).
Union(from a in p_xml.Attributes()
where a.Name.LocalName == searchName
select a.Value).ToList();
Your conversion left out UCase, whose equivalent in C# is ToUpperCase.
This is not the recommended way to perform case-insensitive string comparisons, though.
e.Name.LocalName == searchName
should be replaced with something like:
String.Compare(e.Name.LocalName, searchNamename, StringComparison.InvariantCultureIgnoreCase) == 0
Also, what is propertyName? Whatever it is, its value is obviously not dependent on e. You're selecting one and the same thing for every e in your first query, which makes no sense. I guess you meant select e.
What you probably want then is something along the lines of:
var resultList = (from e in p_xml.Elements()
where String.Compare(e.Name.LocalName, searchName, StringComparison.InvariantCultureIgnoreCase) == 0
select e).
Union(from a in p_xml.Attributes()
where String.Compare(a.Name.LocalName, searchName, StringComparison.InvariantCultureIgnoreCase) == 0
select a.Value).ToList();
I'm only not sure how to represent Select <<%= propertyName %>><%= a.Value %></> in C#, since I don't know VB.
I heard from Microsoft's Roslyn project in a presentation. Maybe that can help you.
Microsoft want to integrate Roslyn in a future Visual Studio version. Then it would be possible to just copy code from VB to clipboard and paste it as C# code. There was also a presentation about that in last year, maybe the same one.
Good online Code Converter that I use all the time is http://www.developerfusion.com/tools/convert/vb-to-csharp/

Why is Trim() failing in this expression?

I have the following method:
var catIds = DetachedCriteria.For<Category>()
.Add<Category>(c => c.TypeCode == "IMA")
.SetProjection(LambdaProjection.Property<Category>(s => s.Id));
This is returning nothing because in the database the field is nchar(10). I want to Trim() the TypeCode value, as follows:
var catIds = DetachedCriteria.For<Category>()
.Add<Category>(c => c.TypeCode.Trim() == "IMA")
.SetProjection(LambdaProjection.Property<Category>(s => s.Id));
but it returns the NHibernate error:
Unrecognised method call in epression c.TypeCode.Trim()
One of the guys here in the office thinks it's because HHibernate doesn't know how to convert .Trim() to SQL (or something along those lines). Can anyone suggest how I can fix this?
Try right-padding the value you're comparing with to the required length, for example:
string cmpValue = "IMA".PadRight(10);
var catIds = DetachedCriteria.For<Category>()
.Add<Category>(c => c.TypeCode == cmpValue)
.SetProjection(LambdaProjection.Property<Category>(s => s.Id));
Your office guys are correct -- the linq provider doesn't know how to translate C# string.Trim() to whatever sql variant it is.
As for the fix, linq can make you do the damndest things -- like padding your data to match CHAR(10) rather than TRIM() to a 'normal' string.
I hope the following code may solve your problem:
Func<string, string> trimmer = (x) => {
return !string.IsNullOrEmpty(x) ? x.Trim() : string.Empty; };
and then use it like:
var catIds = DetachedCriteria.For<Category>()
.Add<Category>(c => trimmer(c.TypeCode) == "IMA")
.SetProjection(LambdaProjection.Property<Category>(s => s.Id));

Categories