Delaying LINQ to SQL Select Query Execution - c#

I'm building an ASP.NET MVC site that uses LINQ to SQL.
In my search method that has some required and some optional parameters, I want to build a LINQ query while testing for the existence of those optional parameters.
Here's what I'm currently thinking:
using(var db = new DBDataContext())
{
IQueryable<Listing> query = null;
//Handle required parameter
query = db.Listings.Where(l => l.Lat >= form.bounds.extent1.latitude && l.Lat <= form.bounds.extent2.latitude);
//Handle optional parameter
if (numStars != null)
query = query.Where(l => l.Stars == (int)numStars);
//Other parameters...
//Execute query (does this happen here?)
var result = query.ToList();
//Process query...
Will this implementation "bundle" the where clauses and then execute the bundled query? If not, how should I implement this feature?
Also, is there anything else that I can improve?
Thanks in advance.

Yes, the query will only be executed once ToList() is called. If you follow this pattern and are using anonymous types, be aware of OrderBy() returning IOrderedQueryable instead of IQueryable.
Be aware that you are able to just iterate over the IQueryable, you don't have to call ToList to access the data.

Related

PostgreSQL, Linq C# error 'could not determine data type of parameter $1'

I am getting this error "could not determine data type of parameter $1"
and my where clause where I am getting the error is like the following:
var result = from Table in model.Table
where (filter.XId.HasValue ? Table.XId == filter.XId: true)
select new TableEntity
{
ID = Table.XId
};
If my code was only like this 'Table.X == filter.X', it works ...
How can I fix this?
and I am getting this problem only with PostgreSQL database ....
First about what that error usually means. When making parametrized queries to PostgreSQL, all parameters should be referenced in query itself. When you add more parameters than used in query, usually the error above appears.
It seems that when whatever EF provider for PosgreSQL you use converted your statement to SQL, it created more parameters than needed.
In general, it might be hard for EF providers to analyze and correctly parse statements like you used, so good practice is to use statements that are "closer" to SQL in certain sense. In your case equivalent query which is "closer" to SQL would be:
where (filter.XId == null || Table.XId == filter.XId)
If you want to generate different queries based on the value of filter, you can do something like this:
var query = (IQueryable<Table>) model.Table;
if (filter.XId != null) {
query = query.Where(row => row.XId == filter.XId);
}
var result = query.Select(row => new TableEntity {
Id = row.XId
});
As it is the first SO answer in Google -
Marked answer didn't help me in similar case (EF6 + Postgre), so I had to use .AsEnumerable();
Yeah, it could be bad for perfomance but it fitted well in my case.
So this one would work:
var query = model.Table
.AsEnumerable()
.Where (p => filter.XId.HasValue ? p.XId == filter.XId: true);

Does converting an IEnumerable to IQueryable enumerate the query

I am using JQuery widgets's datatable to have a table control in an MVC application with server-side paging, sorting and filtering. When doing the filtering, I have the following method:
private IQueryable<ImportRfqViewModel> BuildLinqQuery(System.Collections.Specialized.NameValueCollection query)
{
var result = query.GetValues("filterslength");
var filtersCount = int.Parse(query.GetValues("filterslength")[0]);
if (result == null || filtersCount == 0)
{
return AllImportRfq();
}
Predicate<ImportRfqViewModel> orResultPredicate = PredicateExtensions.False<ImportRfqViewModel>();
for (var i = 0; i < filtersCount; i += 1)
{
var filterValue = query.GetValues("filtervalue" + i)[0].ToUpper();
var filterCondition = query.GetValues("filtercondition" + i)[0];
var filterDataField = query.GetValues("filterdatafield" + i)[0];
var filterOperator = query.GetValues("filteroperator" + i)[0];
if (filterDataField == "ImportRfqId")
{
Predicate<ImportRfqViewModel> predicate = p => p.ImportRfqId.ToString().Contains(filterValue);
orResultPredicate = orResultPredicate.Or(predicate);
}
else if (filterDataField == "DateCreated")
{
Predicate<ImportRfqViewModel> predicate = p => p.DateCreated.ToString("yyyy/MM/dd hh:mm:ss").Contains(filterValue);
orResultPredicate = orResultPredicate.Or(predicate);
}
...
}
Func<ImportRfqViewModel, bool> funcOr = l => orResultPredicate(l);
var allResearch = AllImportRfq().Where(funcOr).AsQueryable();
return allResearch;
}
I'm using the predicates in order to chain Or conditions. I obviously want to return an IQueryable so that the query is not run before I get to the part:
dbResult = dbResult.Skip(pagesize * pagenum).Take(pagesize);
The result of the predicate though, is an IEnumerable. This is why I call the .AsQueryable();
What I'm concerned about and don't know is whether the thing potentially is enumerating the query and then returning a IQueryable afterwards and throwing away the enumeration, if I'm making sense.
In all this I'm assuming that if I return an IEnumerable, it would execute the query.
Returning an IEnumerable by itself does not mean you are actually executing the query. It depends on the IEnumerable's creator, but normally IEnumerable is considered lazy and the underlying data source is touched only when enumerating an IEnumerator. Linq operators and extension methods behave nicely, so your AsQueryable() call does not enumerate the target, and you should be safe.
BTW, did you try changing your code from this:
Func<ImportRfqViewModel, bool> funcOr = l => orResultPredicate(l);
to this (as long as your query provider fully supports Where and the predicate you are building, of course)?
Expression<Func<ImportRfqViewModel, bool>> funcOr = ...;
This way you should be able to avoid going back and forth across IQueryable and IEnumerable, you would not need calling AsQueryable() at all and therefore you'd have no more doubts. If your query provider supports it, it would be much more efficient than what you are doing, which would execute the filtering after querying the underlying data source.
EDIT: just to make it clearer, the move from Func<> to Expression<> is not just a matter of changing the type of your variable, it actually means you'd have to review the whole process of building your predicates so that you always work by composing Expression instances, and not just Func ones, otherwise your resulting expression tree will hardly be understandable to your query provider.

Use a function within LINQ

I am using LINQ to create a list. But I want to use a function at the end to generate the object iself, something LINQ complains about
LINQ to Entities does not recognize the method 'WashroomStatusItem GetWashroomStatusForItem(WashroomStatus)' method, and this method cannot be translated into a store expression.
What am I doing wrong?
var query = (from c in context.WashroomStatus
where c.WashroomId == GroupItem.WashroomID
select GetWashroomStatusForItem(c));
private WashroomStatusItem GetWashroomStatusForItem(WashroomStatus item)
{
WashroomStatusItem temp = new WashroomMonitorWCF.WashroomStatusItem();
//do stuff with it
return temp;
}
The problem is that the SQL conversion can't convert your method into SQL. You should use AsEnumerable() to "switch" from the out-of-process provider to LINQ to Objects. For example:
var query = context.WashroomStatus
.Where(c => c.WashroomId == GroupItem.WashroomID)
.AsEnumerable()
.Select(c => GetWashroomStatusForItem(c));
Note that if GetWashroomStatusForItem only uses some properties, you may want to project to those separately first, to reduce the amount of information fetched from the server:
var query = context.WashroomStatus
.Where(c => c.WashroomId == GroupItem.WashroomID)
.Select(c => new { c.Location, c.Date };
.AsEnumerable()
.Select(p => GetWashroomStatusForItem(p.Location, p.Date));
Jon Skeet's answer is correct, but I'd add that depending on the nature of GetWashroomStatusForItem(), it should probably either be broken down into LINQ statements and added into the query itself, or it should be executed after the query has returned.
So, lets say GetWashroomStatusForItem() looks something like this: note that this is extremely oversimplified.
public static WashroomStatus GetWashroomStatusForItem(Item c)
{
return c.WashroomStatus;
}
it should just be added to the LINQ query like this:
var query = (from c in context.WashroomStatus
where c.WashroomId == GroupItem.WashroomID
select c.WashroomStatus);
But if it relies heavily on stuff not in the db, I'd just end the Linq statement before you get the WashroomStatus, and then call GetWashroomStatusForItem() on the results. It's not gonna a performance difference since Linq uses lazy evaluation, and you generally want to keep db operations separate from "programmatic" ones.

How to Expression.Invoke an arbitrary LINQ to SQL Query

Say I take an arbitrary LINQ to SQL query's Expression, is it possible to invoke it somehow?
MyContext ctx1 = new MyContext("...");
var q = from t in ctx1.table1 where t.id = 1 select t;
Expression qe = q.Expression;
var res = Expression.Invoke(qe);
This throws:
ArgumentException "Expression of type System.Linq.IQueryable`1[...]' cannot be invoked".
My ultimate goal is to evaluate the same query on several different data contexts.
Queries are not Expressions. A Query has an ExpressionTree.
Queries are not Methods to be invoked.
Queries may be Enumerated, yielding their results. This code will Enumerate any IQueryable:
List<object> result = query.Cast<object>().ToList();
My ultimate goal is to evaluate the
same query on several different data
contexts.
Then you should write your queries as query generators, that accept DataContext as a parameter.
Func<MyDataContext, IQueryable<Customer>> queryGen =
(dc) => dc.Customers.Where(c => c.Name == "Bob");
//now we can get some queries
IQueryable<Customer> query1 = queryGen(new MyDataContext());
IQueryable<Customer> query2 = queryGen(new MyDataContext());
If you goal is to run an Expression across different contexts, why not create just the expression like so:-
Expression<Func<MyClass, bool>> myExpression = x => x.id == 1;
Then you can do whatever you like with it, including using it in .Where() clauses.
LINQ to SQL expressions are parsed by the LINQ to SQL Provider and converted into T-SQL. My guess is that exception is being raised explicitly - that Microsoft did not intend for those expressions to be Invoked directly (you could confirm this using .NET Reflector.)

How dynamic can I make my LINQ To SQL Statements?

I have the need to construct a LINQ To SQL statement at runtime based on input from a user and I can't seem to figure out how to dynamically build the WHERE clause.
I have no problem with the following:
string Filters = "<value>FOO</value>";
Where("FormattedMessage.Contains(#0)",Filters)
But what I really need is to make the entire WHERE clause dynamic. This way I can add multiple conditions at runtime like this (rough idea):
foreach (Filter filter in filterlist)
{
whereclause = whereclause + "&& formattedmessage.contains(filter)";
}
I don't know what data types are being used here, but why don't you try to use general query?
var query = context.Messages
.AsQueryable();
foreach (Filter filter in filterlist)
{
query = query
.Where(m => m.Contains(filter));
}
this will concatenate all the conditions using AND (as is in your question).
You may also consider using the PredicateBuilder class. Using that will allow you to dynamically add AND/OR conditions to your tree.
Refer to http://www.albahari.com/nutshell/predicatebuilder.aspx

Categories

Resources