As per post Select element with given attribute using linq to xml what will be the equivalent lambda expression.
The below solution works fine
var artistsAndImage = from a in feed.Descendants("artist")
from img in a.Elements("image")
where img.Attribute("size").Value == "big"
select new { Name = a.Element("Name").Value
, Image = img.Value};
I tried the lambda expression but it's not working :-(
can somebody suggest the equivalent lambda expression.
Sure:
var artistsAndImage = feed.Descendants("artist")
.SelectMany(a => a.Elements("image"),
(a, img) => new { a, img })
.Where(z => z.img.Attribute("size").Value == "big")
.Select(z => new { Name = z.a.Element("Name").Value,
Image = z.img.Value });
(Untested, but I think it should work.)
The tricky bit here is that the second from clause calls SelectMany and introduces a transparent identifier which I've made somewhat less transparent by calling it z.
Any particular reason you want to avoid query expression syntax here though? It's simpler in this example - I just use whichever is simpler for the query I'm writing.
Related
Is it possible to do something like this:
public static Expression<Func<EntityA, bool>> IsAGood =
x => x.A != null &&
x.A.valid;
public static Expression<Func<EntityB, bool>> IsBGood=
x => IsAGood (x.EntityA);
var res = context.EntitiesB
.Where(x => x.count > 0)
.Where(IsBGood)
I know I can compile IsAGood and run it on x.EntityA but it loads the data to memory and I don't want to do this yet.
Is there a way to do this without loading to memory?
Yes, you can do it with some expression trees processing. Maybe there are easier ways to do this, but the one I know looks something like this (you can place this code in static constructor):
Expression<Func<EntityB, EntityA>> exp = b => b.EntityA;
var param = exp.Parameters.First();
var expression = new ReplacingExpressionVisitor(new[]{IsAGood.Parameters.First()}, new []{exp.Body}).Visit(IsAGood.Body);
IsBGood = Expression.Lambda<Func<EntityB, bool>>(expression, param);
ReplacingExpressionVisitor is available since EF Core 3.0, if you are using an older version you can write you own one, it should not be that hard.
Also you can try using Expression.Inkove (but I think I had some issues with it being translated previously):
Expression<Func<EntityB, EntityA>> exp = b => b.EntityA;
var param = exp.Parameters.First();
IsBGood = Expression.Lambda<Func<EntityB, bool>>(Expression.Invoke(IsAGood, exp.Body), param);
Note that you cannot call Compile on a lambda expression in this context, because this creates a delegate. But EF needs an Expression because the o/r-mapper needs the syntax information it contains to convert it into a SQL command. An executable delegate cannot be convert to SQL and cannot be executed by the DB.
Now to your concern about loading data into memory.
Neither IsAGood nor IsBGood nor your combined query loads any data into memory until you actually enumerate the query (e.g. with .ToList() or foreach).
You can even add parts dynamically as shown here:
var query = context.EntitiesB
.Where(x => x.count > 0); // No data access happens here.
if (useCondition) {
query = query.Where(IsBGood); // No data access happens here.
}
var result = query.ToList(); // The DB will be accessed here once.
Note that all the parts of your code only set up a query. They don't execute any query. Therefore I changed the name of the var from res to query.
Okay I see the problem. IsAGood cannot be called like this, since it is an Expression<>, not a delegate (and a delegate does not work anyway as I have explained earlier).
You can solve this by rewriting the expression as #GuruStron explains or change the test to
public static Expression<Func<EntityB, bool>> IsAinBGood =
b => b.EntityA.A != null &&
b.EntityA.A.valid;
I have the following code (this is just relevant part):
linqQuery.Select(invoice =>
new InvoiceDetailed
{
UnpaidAmount = e.SumAmount +
e.OverdueNotices.OrderByDescending(on => on.SendDate).Select(on => on.Fee).DefaultIfEmpty(0).Sum() +
e.CreditNotes.Select(c => c.CreditNoteAmount).DefaultIfEmpty(0).Sum() -
e.Payments.Select(p => p.Amount).DefaultIfEmpty(0).Sum()
}
And this calculation for UnpaidAmount I repeat in severl other queries also. My question is if there is a way to somehow wrap that expression in function like:
Expression<Func<crmInvoice_Invoice, double>> unpaidExpression = // that unpaid amount caluculation expression
And then call like this:
linqQuery.Select(invoice =>
new InvoiceDetailed
{
UnpaidAmount = unpaidExpression(invoice)
}
Then I could reuse it in more queries. Is it possible to do something similar in LINQ? And if it is not is there any alternative solution u could suggest me to avoid repeating that part of code?
No, it's impossible.
Select method gets Expression as an argument. LINQ to SQL parses Expression to SQl code. So, to solve your task you need to convert you expression to return InvoiceDetailed:
Expression<Func<crmInvoice_Invoice, InvoiceDetailed>> InvoiceDetailedExpression = ...
I am trying to combine a multiple selection with a lambda function into an lambda expression. How do I do that? I know the last line is wrong, but giving you an idea of what I mean.
Func<Event, bool> where = null;
if (!string.IsNullOrWhiteSpace(searchToken))
where = q => q.Name.ToUpper().Contains(searchToken.ToUpper());
where += q => q.Hidden = false;
Expression<Func<Event, bool>> where1 = q => where; <-- Erroring
I suspect you want PredicateBuilder. (The source is available on that page.) You'd use it like this:
var predicate = q => !q.Hidden;
if (!string.IsNullOrWhiteSpace(searchToken))
{
predicate = predicate.And(q => q.Name.ToUpper()
.Contains(searchToken.ToUpper());
}
return predicate;
That's assuming you want to "and" the conditions - you never made that clear...
Note that that is not a good way to compare in a case-insensitive way, either. If you could tell us what's going to consume the query (e.g. LINQ to SQL, LINQ to EF) we could suggest a provider-compatible way of performing a case-insensitive query.
Look at http://msdn.microsoft.com/en-us/library/bb882637.aspx. How to use expression trees to build dynamic queries.
AFAIK when using Expression <> like that the expression must be known in compile time, because the compiler then build AST abstract syntax three and stores it as data in your Expression <> instance.
How do I translate the following query expression to corresponding C# code? Thanks.
var list1 = (from ol in orderedList
from er in ol.Er
from rd in er.Rd
where rd.ftr != ""
select ol).ToList<CRInfo>();
It would translate to something like this:
var list1 = orderedList.SelectMany(ol => ol.Er, (ol, er) => new { ol, er })
.SelectMany(z => z.er.Rd, (z, rd) => new { z, rd })
.Where(z2 => z2.rd.frt != "")
.Select(z2 => z2.z.ol)
.ToList<CRInfo>();
The "z" and "z2" bits are transparent identifiers, used by the C# compiler to propagate multiple range variables through the query.
You may want to download LINQPad, which I believe lets you translate query expressions like this very easily.
Well, aside from the obvious fact that your code is already C# code...
I assume you want to obtain the actual Enumerable method calls? If so, you could just compile it and throw it into Reflector.
I have made myself an ExpressionBuilder class that helps me put together expressions that can be used as a predicate when doing Linq to Sql queries. It has worked great. However, I just discovered Expressions can only be used to filter on Tables, and not on EntitySets??Why on earth is this the case?
For example if I have Company and an Employee with a Salary. I could create these two expressions:
Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");
Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
I would then expect to be able to do the following, however it only partially works:
var companies = dataContext.Companies
.Where(cp) // Goes fine
.Select(x => new
{
x.Name,
SumOfSalaries = x.Employees
.Where(ep) // Causes compile-time error
.Sum(y => y.Salary),
}
.ToList();
Also, if I do a ep.Compile() it compiles, but then I get an error when running the query.
Why is this the case? Am I missing something? I don't find this logical. Can I fix this somehow? Or do you have a good workaround?
I know that I in this case could just use Where(x => x.Name.StartsWith("John")) instead, but the problem is that the expressions I need are not that trivial. They are longer strings of AndAlsos and OrElses.
If you are going to pass a lambda expression to a LINQ to SQL provider don't create it as an Expression<T> - let the provider do that for you.
The following works for me - note both are compiled:
var companies = dataContext.Companies.Where(cp.Compile())
.Select(x => new
{
x.Name,
SumOfSalaries = x.Employees
.Where( ep.Compile() )
.Sum(y => y.Salary),
}
).ToList();
The expression parser seems to be losing type information in there somewhere after the first where clause when you put in the second. To be honest, I'm not sure why yet.
Edit: To be clear, I do understand that EntitySet doesn't support passing an expression into the where clause. What I don't completely understand is why it fails when you add the Where(ep.Compile()).
My theory is that in compiling the first where (Where(cp.Compile()), Linq2Sql quits parsing the expression - that it can't parse the ep.Compile() against an entityset, and is unable to decide to break up the query into two until you compile the first where clause.
I think you need to rewrite your query. Another way to ask for the details you want, is: "Give me the sum of the salaries for the selected employees in the selected companies, organized by the company name".
So, with the employee salaries in focus, we can write:
//Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
//Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");
Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
Expression<Func<Employee, bool>> cp = x => x.Company.Name.StartsWith("Micro");
var salaryByCompany = dataContext.Employees
.Where(ep)
.Where(cp)
.GroupBy(employee => employee.Company.Name)
.Select(companyEmployees => new
{
Name = companyEmployees.Key,
SumOfSalaries = companyEmployees.Sum(employee => employee.Salary)
});
var companies = salaryByCompany.ToList();