Use a function within LINQ - c#

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.

Related

Where Clauses Causing Errors in LINQ

I am trying to return rows based on a search term that may include a space.
The code below is is generating the following error. I cannot figure out what I'm doing wrong, any suggestions?
Local sequence cannot be used in LINQ to SQL implementations of query
operators except the Contains operator.
var searchTerms = term.Split(' ').ToList();
var surveys = (from s in dc.BasicNeedsSurveys where s.Hidden ==
false orderby s.CreatedOn descending select s)
.Where(x => searchTerms.Any(y => y.Contains(x.FirstName))
|| searchTerms.Any(y => y.Contains(x.LastName))
|| searchTerms.Any(y => y.Contains(x.FEMANumber)));
according to the error message you are using linq to sql (or EF). linq to sql generate SQL query behind the scene, and you cant use a local var such as searchTerms inside an sql query. if i understand it correctly and dc.BasicNeedsSurveys is actually a data base entity (like in entity framework for example) your solution will be to first execute the sql query and then run the test if substring of search terms contains the search result. ToList is one function that can do that.
var searchTerms = term.Split(' ').ToList();
var surveys = (from s in dc.BasicNeedsSurveys where s.Hidden ==
false orderby s.CreatedOn descending select s)
.ToList()
.Where(x => searchTerms.Any(y => y.Contains(x.FirstName))
|| searchTerms.Any(y => y.Contains(x.LastName))
|| searchTerms.Any(y => y.Contains(x.FEMANumber)));
of course, there might be better ways to do that with better performance since here you are filtering the results only after you read all of them from the hard drive, but there is really not enough information in the question for that

NOT IN Condition in Linq

I have a simple scenario.I want to list out all the employees except the logged in user.
Similar SQL Condition is
select * from employee where id not in(_loggedUserId)
How can I acheive the above using LINQ.I have tried the following query but not getting the desired list
int _loggedUserId = Convert.ToInt32(Session["LoggedUserId"]);
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(_loggedUserId)
.ToList();
Except expects argument of type IEnumerable<T>, not T, so it should be something like
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(new[] {_loggedUserId})
.ToList();
Also note, this is really redundant in the case when exclusion list contains only one item and can be replaces with something like .Where(x => x != _loggedUserId)
Why not use a very simple Where condition?
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId).Where(e=>e.Id != _loggedUserId).ToList();
The title of your question is how to perform a not in query against a database using LINQ. However, as others have pointed out your specific problem is better solved by a using users.Where(user => user.Id != loggedInUserId).
But there is still an answer on how to perform a query against a database using LINQ that results in NOT IN SQL being generated:
var userIdsToFilter = new[] { ... };
var filteredUsers = users.Where(user => !userIdsToFilter.Contains(user.Id));
That should generate the desired SQL using either Entity Framework or LINQ to SQL.
Entity Framework also allows you to use Except but then you will have to project the sequence to ID's before filtering them and if you need to original rows you need to fetch them again from the filtered sequence of ID's. So my advice is use Where with a Contains in the predicate.
Use LINQ without filtering. This will make your query execute much faster:
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id).ToList();
Now use List.Remove() to remove the logged-in user.
_empIds.Remove(_loggedUserId);

Issue using Where method in LINQ

Consider this line of code:
List<SIDB_TransactionInformation> transaction = SIDB.SIDB_TransactionInformations
.Where(k => k.iscurrent == true & k.objectid == SIDB.func_GetObjectID("dbo.SIDB_Module")).ToList();
List<SIDB_Module> module = SIDB.SIDB_Modules
.Where(k => k.moduleid == transaction
.Where(j => j.transactionid == k.moduleid)
.SingleOrDefault().transactionid).ToList();
I do have 2 invocation of where method in different collection. First i distinct my list via iscurrent and objectid after that I do have other invocation of where method (for SIDB_Modules) to distinct the list via moduleid where in the the values refer to the transactionid of my previous list. Now i have an error message like this Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
sorry i'm new in lambda expression. need help badly
I think this is what you're looking for
List<SIDB_Module> module = SIDB
.SIDB_Modules
.Where(k => transaction.Any(j => j.transactionid == k.moduleid))
.ToList();
Make a list of SIDB_Modules where there is a transaction whose transactionid is equal to the moduleid. LINQ to Sql might have an issue with Any, I don't remember, if it does you can rewrite it with an extra step like this
var transactionIds = transaction.Select(j => j.transactionid);
List<SIDB_Module> module = SIDB
.SIDB_Modules
.Where(k => transactionIds.Contains(k.moduleid))
.ToList();
If performance is an issue you might consider going with the second method and putting transactionIds into something that implements ISet<T> and has a constant time lookup.
Well, it looks like you're trying to do a join between SIDB_TransactionInformations and SIDB.SIDB_Modules. If so, try
var objectID = SIDB.func_GetObjectID("dbo.SIDB_Module");
List<SIDB_Module> modules = (from module in SIDB.SIDB_Modules
join transaction in SIDB.SIDB_TransactionInformations on module.moduleid equals transaction.transactionid
where transaction.iscurrent && transaction.objectid == objectID
select module).ToList();

Join between in memory collection and EntityFramework

Is there any mechanism for doing a JOIN between an in-memory collection and entity framework while preserving the order.
What I am trying is
var itemsToAdd =
myInMemoryList.Join(efRepo.All(), listitem => listitem.RECORD_NUMBER,
efRepoItem => efRepoItem.RECORD_NUMBER, (left, right) => right);
which gives me the rather curiously titled "This method supports the LINQ to Entities infrastructure and is not intended to be used directly from your code." error.
Now of course I can do this iteratively with something like
foreach (var item in myInMemoryList)
{
var ho = efRepo.Where(h => h.RECORD_NUMBER == item.RECORD_NUMBER).FirstOrDefault();
tmp.Add(ho);
}
but this is an N+1 query. Which is nasty as myInMemoryList might be quite large!
Resharper can refactor that for me to
tmp = (from TypeOfItemInTheList item in myInMemoryList
select efRepo.Where(h => h.RECORD_NUMBER == item.RECORD_NUMBER)
.FirstOrDefault());
which I suspect is still doing N+1 queries. So any ideas for a better approach to getting ef entities that match (on key field) with an in-memory collection. The resulting set must be in the same order as the in-memory collection was.
No you cannot join in-memory collection with database result set without loading whole result set to the memory and performing the join with linq-to-objects. Try using contains instead of join:
var myNumbers = myInMemoryList.Select(i => i.RECORD_NUMBER);
var itemsToAdd = efRepo.Where(e => myNumbers.Contains(e.RECORD_NUMBER));
This will generate query with IN operator
You can read how you can do this with the PredicateBuilder from the LINQKit or Stored Procedures in my blog post.
http://kalcik.net/2014/01/05/joining-data-in-memory-with-data-in-database-table/
try this:
var list = (from n in efRepo
where myInMemoryList.Select(m=>m.RECORD_NUMBER).Contains(n.RECORD_NUMBER)
select n).ToList();
Contains will be translated to IN operator in SQL (only if your RECORD_NUMBER member is a primitive type like int, string, Guid, etc)
What about loading the whole efRepo? I mean something like this (ToArray()):
var itemsToAdd = myInMemoryList.Join(
efRepo.ToArray(),
listitem => listitem.RECORD_NUMBER, efRepoItem => efRepoItem.RECORD_NUMBER, (left, right) => right);

Performing part of a IQueryable query and deferring the rest to Linq for Objects

I have a Linq provider that sucessfully goes and gets data from my chosen datasource, but what I would like to do now that I have my filtered resultset, is allow Linq to Objects to process the rest of the Expression tree (for things like Joins, projection etc)
My thought was that I could just replace the expression constant that contains my IQueryProvider with the result-sets IEnumerable via an ExpressionVisitor and then return that new expression. Also return the IEnumerable's provider from my IQueryable...but this does not seem to work :-(
Any idea's?
Edit:
Some good answers here, but given the form...
var qry = from c in MyProv.Table<Customer>()
Join o in MyProv.Table<Order>() on c.OrderID equals o.ID
select new
{
CustID = c.ID,
OrderID = o.ID
}
In my provider I can easily get back the 2 resultsets from Customers and Orders, if the data was from a SQL source I would just construct and pass on the SQL Join syntax, but it this case the data is not from a SQL source so I need to do the join in code...but as I said I have the 2 result sets, and Linq to Objects can do a join...(and later the projection) it would be real nice to just substitute the Expression constants MyProv.Table<Customer> and MyProv.Table<Order> with List<Customer> and List<Order> and let a List<> provider process the expression...is that possible? how?
Both of the previous answers work, but it reads better if you use AsEnumerable() to cast the IQueryable to IEnumerable:
// Using Bob's code...
var result = datacontext.Table
.Where(x => x.Prop == val)
.OrderBy(x => x.Prop2)
.AsEnumerable() // <---- anything after this is done by LINQ to Objects
.Select(x => new { CoolProperty = x.Prop, OtherProperty = x.Prop2 });
EDIT:
// ... or MichaelGG's
var res = dc.Foos
.Where(x => x.Bla > 0) // uses IQueryable provider
.AsEnumerable()
.Where(y => y.Snag > 0); // IEnumerable, uses LINQ to Objects
The kind of thing that I was after was replacing the Queryable<> constant in the expression tree with a concrete IEnumerable (or IQueryable via .AsQueryable()) result set...this is a complex topic that probably only makes any sense to Linq Provider writers who are knee deep in expression tree visitors etc.
I found a snippet on the msdn walkthrough that does something like what I am after, this gives me a way forward...
using System;
using System.Linq;
using System.Linq.Expressions;
namespace LinqToTerraServerProvider
{
internal class ExpressionTreeModifier : ExpressionVisitor
{
private IQueryable<Place> queryablePlaces;
internal ExpressionTreeModifier(IQueryable<Place> places)
{
this.queryablePlaces = places;
}
internal Expression CopyAndModify(Expression expression)
{
return this.Visit(expression);
}
protected override Expression VisitConstant(ConstantExpression c)
{
// Replace the constant QueryableTerraServerData arg with the queryable Place collection.
if (c.Type == typeof(QueryableTerraServerData<Place>))
return Expression.Constant(this.queryablePlaces);
else
return c;
}
}
}
If you implemented a Repository Pattern you could get away with just providing an IQueryable back and abstract away the table.
Example:
var qry = from c in MyProv.Repository<Customer>()
Join o in MyProv.Repository<Order>() on c.OrderID equals o.ID
select new
{
CustID = c.ID,
OrderID = o.ID
}
and then just build your provider to model the IQueryable pattern in your Repository method just like this article illustrates.
This way, you can write all kinds of providers to use for whatever you need. You can have a LINQ 2 SQL provider, or write an in memory provider for your unit tests.
The Repository method for the LINQ 2 SQL provider would look something like this:
public IQueryable<T> Repository<T>() where T : class
{
ITable table = _context.GetTable(typeof(T));
return table.Cast<T>();
}
Unless I'm misunderstanding, I generally just add .ToArray() in the chain of linq methods at the point where I want the linq provider to execute.
For example (think Linq to SQL)
var result = datacontext.Table
.Where(x => x.Prop == val)
.OrderBy(x => x.Prop2)
.ToArray()
.Select(x => new {CoolProperty = x.Prop, OtherProperty = x.Prop2});
So through OrderBy() gets translated into SQL, but the Select() is LINQ to Objects.
Rob's answer is good, but forces full enumeration. You could cast to keep extension method syntax and lazy evaluation:
var res = ((IEnumerable<Foo>)dc.Foos
.Where(x => x.Bla > 0)) // IQueryable
.Where(y => y.Snag > 0) // IEnumerable

Categories

Resources