I'm trying to inject some inline work as a Statement Lambda into a LINQ query select like so...
// NOTE: mcontext.Gettype() == System.Data.Linq.DataContext
// Okay - compiles, nothing unusual
var qPeople1 = from ME.tblPeople person in mcontext.tblPeoples
select person;
// ERROR - see below compile Error - Can I retrofit this thing?
var qPeople2 = from ME.tblPeople person in mcontext.tblPeoples
select (() => {
return person;
})();
Error:
Error 2 Method name
expected file.cs 166 27 MigrationCore
... however I'd also be equally happy to see an Expression Lambda work inline first.
Note: I know the code sample is redundant in its effort but I'm looking for the basic concept. If it's workable I will be expanding it.
Query syntax requires a method reference - it won't accept a lambda and in your second example you're giving it a ME.tblPeople instance.
However, if you use the extension method syntax you can achieve this easily:
int i = 0;
var qPeople3 = (from ME.tblPeople person in mcontext.tblPeoples
select person).Select(person => { i += 1; return person; });
(I've added the incrementing integer as an example, though note that it doesn't actually change from zero until you've enumerated qPeople3.)
Addendum
This only works with LINQ to Objects. To use it with a LINQ to SQL query an AsEnumerable() call is required before the Select() call.
Notes
You don't actually need the from-in-select construct for this example, the snippets below are (AFAICT) identical, but I've left it in above for similarity to the earlier examples and to illustrate that it works. The second snippet splits the two statements into separate lines, also with identical results.
int i = 0;
var qPeople4 = mcontext.tblPeoples.Select<ME.tblPeople,ME.tblPeople>(person => { i += 1; return person; });
int i = 0;
var qPeople1 = from ME.tblPeople person in mcontext.tblPeoples
select person;
var qPeople5 = qPeople1.Select(person => { i += 1; return person; });
There are two kinds of lambda expressions: anonymous delegates and expression trees. The former kind is used by LINQ to Objects, and allows any valid anonymous method body. The latter kind is used by LINQ to SQL and requires that its body be a single expression. This expression is then passed into the L2SQL runtime and manipulated into the SQL sent to the server.
To perform your inline work, you will need to use two steps: 1) get SQL data with a valid select expression, then 2) manipulate that data as an IEnumerable<> with LINQ to Objects to do your inline work. That could look something like this:
var qPeople1 = from ME.tblPeople person in mcontext.tblPeoples
select person;
var i = 0;
var qPeople2 = qPeople1.AsEnumerable().Select(person => {
i += 1;
return person;
});
Related
Before try to use a variable (this has sql server do the calculation):
IQueryable<MyEntity> query = _dbSet; // IQueryable<TEntity>
var results = query.Select(m => new MyViewModel
{
MyCalculation = m.Column1 * m.Column2
}).ToList();
What I want to do (dynamically create part of my select statement from a Func variable or some other kind of variable to allow this):
IQueryable<MyEntity> query = _dbSet; // IQueryable<TEntity>
Func<MyEntity, decimal> funcVariableAttempt = m => m.Column1 * m.Column2;
var results = query.Select(m => new MyViewModel
{
MyCalculation = funcVariableAttempt.Invoke(m) // My foolish attempt does not work.
}).ToList();
The error I get when I try what I want (aka my foolish attempt):
LINQ to Entities does not recognize the method 'System.Decimal Invoke(MyProject.Repository.Models.MyEntity)' method, and this method cannot be translated into a store expression.
How do I define and utilize a variable (maybe a Func variable) to define part of a Select statement?
It's a completely valid question that I stumbeled across earlier and found a solution that works well for me.
The problem with your Func<MyEntity, decimal> is that it is a delegate and that the O/R mapper has to have access to the internal expression (the multiplication of two properties in your case). But this information is compiled into the delegate and hidden forever.
If you however start off with a Expression<Func<MyEntity, decimal>> customCalculation, things look more promising as you have the internal logic as an expression tree.
Problem with this is: you cannot invoke an expression. customCalculation(m) doesn't compile.
The compiler would let you write
m => new MyViewModel { MyCalculation = customCalculation.Compile()(m) }
, but this wouldn't be understood by most O/R mappers.
But you see that it at least somehow contains the customCalculation lambda expression and also how it relates to its surrounding expressions.
Getting from here to the expression tree as in your original working version involves some expression manipulation:
We have to replace customCalculation.Compile()(m) with the body of the lambda that is to be Compile()d, but with the lambda's parameter(s) replaced with the respective expression(s) of the delegate invocation. So if customCalculation were x => x.Column1 * x.Column2, customCalculation.Compile()(m) would have to be replaced with m.Column1 * m.Column2
Doing so is not trivial, since the lambda itself has to be dug out of a field inside an instance of a compiler generated closure class.
I've posted my implementation of this expression manipulator in another similar question . Hope that helps.
With that, you should be able to:
var customCalculation = (Expression<Func<MyEntity, decimal>>)(x => x.Column1 * x.Column2);
var selector = Express.Prepare((Expression<Func<MyEntity, MyViewModel>>)(m => new MyViewModel { MyCalculation = customCalculation.Compile()(m) }));
var result = query.Select(selector).ToList();
As you already know, your funcVariableAttempt makes no sense to your database, so you have to call your method in the linq-to-object context. i.e. for instance first fetch the data as an Enumerable, then call your method:
var results = query.Select(m => new {
Column1= col1,
Column2= col2
}).AsEnumerable()
.Select(m => new MyViewModel
{
MyCalculation = Foo(m.Column1, m.Column2)
});
*Note: Code is not tested.
You should call ToList() first and perform the Select() on the result in memory.
var results = query.ToList()
.Select(m => new MyViewModel {
MyCalculation = Foo(m.Column1, m.Column2)
});
You're trying to perform the Select as part of the query. You should just use a regular function for the mapping calculation. Lambda functions are useful with LINQ but in this case they're not needed.
Let's say I'm doing a LINQ query like this (this is LINQ to Objects, BTW):
var rows =
from t in totals
let name = Utilities.GetName(t)
orderby name
select t;
So the GetName method just calculates a display name from a Total object and is a decent use of the let keyword. But let's say I have another method, Utilities.Sum() that applies some math on a Total object and sets some properties on it. I can use let to achieve this, like so:
var rows =
from t in totals
let unused = Utilities.Sum(t)
select t;
The thing that is weird here, is that Utilities.Sum() has to return a value, even if I don't use it. Is there a way to use it inside a LINQ statement if it returns void? I obviously can't do something like this:
var rows =
from t in totals
Utilities.Sum(t)
select t;
PS - I know this is probably not good practice to call a method with side effects in a LINQ expression. Just trying to understand LINQ syntax completely.
No, there is no LINQ method that performs an Action on all of the items in the IEnumerable<T>. It was very specifically left out because the designers actively didn't want it to be in there.
Answering the question
No, but you could cheat by creating a Func which just calls the intended method and spits out a random return value, bool for example:
Func<Total, bool> dummy = (total) =>
{
Utilities.Sum(total);
return true;
};
var rows = from t in totals
let unused = dummy(t)
select t;
But this is not a good idea - it's not particularly readable.
The let statement behind the scenes
What the above query will translate to is something similar to this:
var rows = totals.Select(t => new { t, unused = dummy(t) })
.Select(x => x.t);
So another option if you want to use method-syntax instead of query-syntax, what you could do is:
var rows = totals.Select(t =>
{
Utilities.Sum(t);
return t;
});
A little better, but still abusing LINQ.
... but what you should do
But I really see no reason not to just simply loop around totals separately:
foreach (var t in totals)
Utilities.Sum(t);
You should download the "Interactive Extensions" (NuGet Ix-Main) from Microsoft's Reactive Extensions team. It has a load of useful extensions. It'll let you do this:
var rows =
from t in totals.Do(x => Utilities.Sum(x))
select t;
It's there to allow side-effects on a traversed enumerable.
Please, read my comment to the question. The simplest way to achieve such of functionality is to use query like this:
var rows = from t in totals
group t by t.name into grp
select new
{
Name = t.Key,
Sum = grp.Sum()
};
Above query returns IEnumerable object.
For further information, please see: 101 LINQ Samples
I have a function like that :
public int? calculateContractPrice(int? comid)
{
int? sum = 0;
var q = from i in dbconnect.tblMaterialGroups
where i.tenderId == _tenderId
select i.id;
foreach (int i in q )
{
var q2 = from g in dbconnect.tblMaterialTenderAnnouncePrices
where g.MaterialGroupId == i && g.companyId == comid
select g;
sum = q2.First().amount*q2.First().price + q2.First().amount*q2.First().PriceForElse + sum;
}
return sum ;
}
When i try to execute this :
List<presentationcontract> q = (from i in dbconnect.tblContracts
where i.tender == _tenderId
select new presentationcontract()
{
tax =(calculateContractPrice(i.companyId)*(6/100)).ToString()
}).ToList();
Tax is string .after executing i got this error :
couldn't translate expression calculateContractPrice(i.companyId)*(6/100),invoke(value(system.Func1[system.nullable1[system.Int32]]))).ToString() into SQL and could not treat it as a local expression
Your edit makes clear the issue. You're trying to do
tax =(calculateContractPrice(i.companyId)*(6/100)).ToString()
in a sql statement but calculateContractPrice is in c#! To understand what's going on you really need to understand a bit how LINQ works.
First of all, stop using the silly sql-style syntax for LINQ. It is less powerful than the lambda syntax and hides what is going on under the hood in a way that makes it hard to understand.
Second consider a LINQ statement
users.Where(u => u.Name == "George").ToList();
where users is IEnumerable<User>. What happens here is that the lambda part is of type Func<User, bool> and gets compiled to a method that gets run against every single instance of User.
Now consider this LINQ statement
db.Users.Where(u => u.Name == "George").ToList();
where db.Users is IQueryable<T>. This looks the same but what happens is VERY different. What happens is that lambda is actually of type Expression<Func<User, bool>> this doesn't get compiled to a method, instead it gets compiled into something called an expression tree. This gets passed to the LINQ provider (in your case Entity Framework I'm guessing) which examines it and converts that into a SQL statement
SELECT Id, Name, Address FROM users WHERE Name = 'George'
What is happening in your case is that it sees the call to calculateContractPrice and simply has no way of converting that to SQL.
What you should therefore do is ensure the query runs first, then use the IEnumerable<T> form of LINQ that runs in c# to call your method.
var contracts = dbconnect.tblContracts.Where(i => i.tender == _tenderId)
.ToList() //Query executes here, now you have IEnumerable<T>
.Select(i => new PresentationContract {
Tax = ...
}).ToList(); //this ToList is only necessary if you want to prevent multiple iteration
You will want to solve all the other problems everyone else pointed out as well of course.
A few other notes - you will want to read up on .Net naming conventions. Usually, anything public,protected, or internal (classes, fields, properties, etc.) is recommended to be PascalCase. Also you probably want to move the division portion into the PresentationContract class. This class that has a Tax property should probably be the one that knows how to generate it.
Try this:
int? ret = calculateContractPrice(i.companyId);
if(ret.HasValue)
{
tax =(ret.Value*(6/100)).ToString();
}
You should make sure that the function indeed returned a value and then you use that integer value in calculation.
I'm trying to create a general search query against an EF entity type (person). In general, the search takes a string, splits it by commas, and then searches for people whose various attributes contain all of the key words.
I have a function called getProperties(Person p) that takes an entity (overridden by entity type), and returns a string of the various relevant properties joined together with a delimiter... such as:
John~Doe~Team A~Full Time
If the user searches for "Team A, Full" person corresponding to the above flattened entity should be returned... however, if the enter "John, Smith" it shouldn't.
I think the following looks right, but it just doesn't work...
public IEnumerable<Person> SearchPeople(string searchString)
{
if (searchString == null || string.IsNullOrEmpty(searchString.Trim()))
return base._objectSet.ToList();
string[] SearchWords = searchString.Split(',').Select(s => s.Trim()).ToArray();
return (from person
in base._objectSet
let t = (getProperties(person))
where SearchWords.All(word => t.Contains(word))
select person).ToList();
}
and the getProperties function is:
public static string getProperties(Person p)
{
string[] values = { p.Surname, p.GivenName, p.Team, p.Status };
return values.Aggregate((x, y) => String.IsNullOrEmpty(y) ? x : string.Concat(x, "~", y));
}
Does anyone see where I'm going wrong?
Edit
No exceptions are raised, but when I step through the code, when I get to the linq, it steps into the dispose method of the unitofwork that is hosting the query. Very odd.
If I change it so that it searches against a hard-coded string, it works as expected:
var test = (from person
in base._objectSet
where SearchWords.All(word => "John~Doe~Team A~Full Time".Contains(word))
select person).ToList();
well, it works in that it matches the queries I expect it to, but as it's static, it returns every person record (pretty much like having where(true) =P)
Edit the Second
Even odder is that if I store the results into a var, then return the var with a breakpoint on the return, execution never hits the breakpoint... this linq is like a black hole... I can step into it, but it never returns me back to my SearchPeople method... it just disposes the context and forgets about it.
Edit the Third
If I don't call ToString() right away and look at the linq expression in debugger, it says "Linq to Entities does not recognize the method getProperties(Person)" Looks like it was silently choking on my method... any way to use my method without linq choking on it?
You are returning a List? try the return type to be a List or change the .ToList() to be AsEnumerable()
public List<Person> SearchPeople(string searchString)
{
if (searchString == null || string.IsNullOrEmpty(searchString.Trim()))
return base._objectSet.ToList();
string[] SearchWords = searchString.Split(',').Select(s => s.Trim()).ToArray();
return (from person
in base._objectSet
let t = (getProperties(person))
where SearchWords.All(word => t.Contains(word))
select person).ToList();
}
Well, after finding out that linq 2 entites doesn't like methods (as is doesn't know how to translate it to sql), i rewrote my linq is a very tedious but functioning manner:
var people = from p
in base._objectSet
where SearchWords.All(p.GivenName.Contains(word) || p.Surname.Contains(word) || p.(???).Contains(word) || etc.)
select p;
Annoying, but there you go.
Since LINQ query expression are translated "under the covers" to call the same methods that a corresponding method query would call (at least I think so), I would expect these two queries to return the same type. For some reason though this:
var result = from i in db.Invoices
select new { i.InvoiceNum };
sets result as an IQueryable<'a> with each member having an InvoiceNum property, while this
IQueryable<string> result2 = db.Invoices.Select(i => i.InvoiceNum);
Is smart enough to return IQueryable<string> (obviously, since that compiles)
Clearly one of my assumptions is wrong, and I was hoping an expert could help me understand a bit better.
(this is EF4, but the same happens with linq-to-objects, and I'm guessing the same would also happen with L2S)
When you write new { } you are creating an anonymous type
try
var result = from i in db.Invoices
select i.InvoiceNum;
and you will see that it returns the type you expect.
those are not the same, the first one is returning anonymousn type, to make them the same you need to have th first one as:
var result = from i in db.Invoices
select i.InvoiceNum;
In your first statement, you create an anonymous type with one property named "InvoiceNum". This happens because you use the new { } syntax. That anonymous type is not a String. The equivalent method syntax would be:
var result = db.Invoices.Select(i => new { i.InvoiceNum });