Can't get Contains to work in Linq query where clause - c#

List<Guid> toBeFilteredCarIds = new List<Guid>();
toBeFilteredCarIds.Add(new Guid("4cc70c3a-405c-4a5c-b2cd-0429a5bc06ef"));
var cars = ef.Cars
.Include("CarProfile.Option.OptionType")
.Where(c => c.CarStatusId == 1);
cars.Join(ef.CarProfile,
t1 => t1.CarId,
t2 => t2.Car.CarId,
(t1, t2) => new { t1, t2 }).Where(o => o.t2.IsActive == true).Select(o => o.t1);
var filteredCars = cars.ToList().Where(u => toBeFilteredCarIds.Contains(u.CarId));
the above code is trying to get list of Cars, where CarProfile is active, and the CarId is in the toBeFilteredCarIds list.
However as you can see in the last line, I am doing .ToList() first and then doing Where clause to filter by the CarIds.
This obviously will get all cars first from DB, and then do the filter. Which is very expensive.
I have tried the way others have suggested on other answers:
List<Guid> toBeFilteredCarIds = new List<Guid>();
toBeFilteredCarIds.Add(new Guid("4cc70c3a-405c-4a5c-b2cd-0429a5bc06ef"));
var cars = ef.Cars
.Include("CarProfile.Option.OptionType")
.Where(c => toBeFilteredCarIds.Contains(c.CarId) && c.CarStatusId == 1);
cars.Join(ef.CarProfile,
t1 => t1.CarId,
t2 => t2.Car.CarId,
(t1, t2) => new { t1, t2 }).Where(o => o.t2.IsActive == true).Select(o => o.t1);
var filteredCars = cars.ToList();
but that's not working for me, it gives me this error:
LINQ to Entities does not recognize the method 'Boolean Contains(System.Guid)' method, and this method cannot be translated into a store expression.
I can see on stackoverflow, many have marked as answered the above approach:
.Where(c => toBeFilteredCarIds.Contains(c.CarId)
but its not working for me.
By they way am using: VS2008, EF 3.5, and I have got using System.Data.Entity; in my using statements.
"Include" used above is important, as I need to get everything before hand, as there will be huge set of loops reading the data afterwords.

Contains on IEnumerable wasn't added until 4.0 so if you're stuck in 3.5 you'll need to do something like unrolling the values before sending the query.
Look at: http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx

Since .NET 3.5 doesn't support Contains, you can use a Dynamic Linq Library which would allow you generate your where clause using a string. That way, you wouldn't have to fiddle with all the Expression lingo.
Available on NuGet: https://www.nuget.org/packages/System.Linq.Dynamic.Library/.
Your Where clause would look something like this:
.Where("CarStatusId=1 AND (CarId=1 OR CarId=2 OR CarId=3"))
You'd just have to generate that string based on your toBeFilteredCarIds list.

You can use Any(). Not as elegant as Contains(), but it does the job in .NET 3.5:
.Where(c => toBeFilteredCarIds.Any(g => g == c.CarId) && c.CarStatusId == 1);

Related

c# Can I apply a method to an IQueryable to convert it to a list?

I'm trying to return a list of Obj1 types but I first need to pull these using an IQueryable LINQ statement which gives me a list of Obj2 types, and have a method that converts them to the required type which I apply by using a foreach loop
public List<Obj1> GetObj1List(int id)
{
List<Obj2> obj2List = Select(x => x.ObjTable.Where(a => a.Id == id)).ToList();
List<Obj1> obj1List = new List<Obj1>();
foreach(Obj2 o in obj2List)
{
obj1List.Add(MapObject2ToObject1(o, OtherMethod));
}
return obj1List;
}
Is there any way I can wrap my Select statement to apply the MapObject2ToObject1 method to the results without having the foreach line?
Should be quite easy.
public List<Obj1> GetObj1List(int id)
{
var obj1List = Select(x => x.ObjTable.Where(a => a.Id == id))
.Select(o => MapObject2ToObject1(o, OtherMethod))
.ToList();
return obj1List;
}
Updated with updated method signature for MapObject2ToObject1
You want AsEnumerable to effectively use LINQ to Objects for the last mapping part:
public List<Obj1> GetObj1List(int id) =>
Select(x => x.ObjTable.Where(a => a.Id == id)
.AsEnumerable()
.Select(MapObject2ToObject1)
.ToList();
(I'm assuming that Select is a method in the current class that returns an IQueryable<Object2>, given your sample. I'm also assuming you're using C# 6 or higher, so you can use an expression-bodied method like this. If not, put it all in braces and add a return statement.)

Entity Framework (using In and Select Distinct)

I am relatively new to Entity Framework 6.0 and I have come across a situation where I want to execute a query in my C# app that would be similar to this SQL Query:
select * from periods where id in (select distinct periodid from ratedetails where rateid = 3)
Is it actually possible to execute a query like this in EF or would I need to break it into smaller steps?
Assuming that you have in your Context class:
DbSet<Period> Periods...
DbSet<RateDetail> RateDetails...
You could use some Linq like this:
var distincts = dbContext.RateDetails
.Where(i => i.rateId == 3)
.Select(i => i.PeriodId)
.Distinct();
var result = dbContext.Periods
.Where(i => i.Id)
.Any(j => distincts.Contains(j.Id));
Edit: Depending on your entities, you will probably need a custom Comparer for Distinct(). You can find a tutorial here, and also here
or use some more Linq magic to split the results.
Yes, this can be done but you should really provide a better example for your query. You are already providing a bad starting point there. Lets use this one:
SELECT value1, value2, commonValue
FROM table1
WHERE EXISTS (
SELECT 1
FROM table2
WHERE table1.commonValue = table2.commonValue
// include some more filters here on table2
)
First, its almost always better to use EXISTS instead of IN.
Now to turn this into a Lambda would be something like this, again you provided no objects or object graph so I will just make something up.
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Type1>().Where(x => myContext.DbSet<Type2>().Any(y => y.commonValue == x.commonValue)).Select(x => x);
EDIT - updated after you provided the new sql statement
Using your example objects this would produce the best result. Again, this is more efficient than a Contains which translates to an IN clause.
Sql you really want:
SELECT *
FROM periods
WHERE EXISTS (SELECT 1 FROM ratedetails WHERE rateid = 3 AND periods.id = ratedetails.periodid)
The Lamda statement you are after
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Periods>()
.Where(x => myContext.DbSet<RateDetails>().Any(y => y.periodid == x.id && y.rateid == 3))
.Select(x => x);
Here is a good starting point for learning about lamda's and how to use them.
Lambda Expressions (C# Programming Guide).
this is your second where clause in your query
var priodidList=ratedetails.where(x=>x.rateid ==3).DistinctBy(x=>x.rateid);
now for first part of query
var selected = periods.Where(p => p.id
.Any(a => priodidList.Contains(a.periodid ))
.ToList();

Why can't I use include?

That's my code:
ProjetoTipoCargaModelo projAux =
dbContext.ProjetoTipoCargaDbSet.Find(idProjetoTipoCarga);
ICollection<ProjetoTipoCargaRegraModelo> regras =
projAux.ListaRegra.Where(x => x.Ativo).ToList();
IQueryable<ProjetoTipoCargaRegraModelo> pr =
dbContext.ProjetoTipoCargaDbSet.Select(
x => regras.FirstOrDefault(y => y.IdProjetoTipoCarga == x.IdProjetoTipoCarga));
var projetoCompleto = pr.
Include(x => x.ListaRegraLiberacaoInicioViagem).
Include(x => x.ListaRegraTecnologiaAceita).
Include(x => x.RegraAreaSombra).
Include(x => x.RegraAtuadorNecessario)
It's showing an error at first include, but I'm trying to do it on Iquerable object!
What's wrong where?
My problem is make this include in a filtered set of results.
[Edit]
Error:
Cannot convert lambda expression to type 'string' because it is not a delegate type
It's not a runtime error, it's a compilation error.
[Edit 2]
My usings:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
[Edit 3]
Answer:
ICollection<ProjetoTipoCargaRegraModelo> regras = projAux.ListaRegra.Where(x => x.Ativo).ToList();
IQueryable<ProjetoTipoCargaModelo> pr = dbContext.ProjetoTipoCargaDbSet.Where(x => x.IdProjetoTipoCarga == regras.FirstOrDefault(y => y.IdProjetoTipoCarga == x.IdProjetoTipoCarga).IdProjetoTipoCarga);
var projetoCompleto = pr.
Include(x => x.ListaRegraLiberacaoInicioViagem).
Include(x => x.ListaRegraTecnologiaAceita).
Include(x => x.RegraAreaSombra).
The .Include() method only works on ObjectQuery<TEntity>
Try:
context.EntitySet.Include(...).Select(...)
instead of:
context.EntitySet.Select(...).Include(...)
or use an extenion method like this:
public static class MyExtensions
{
public static IQueryable<TEntity> Include<TEntity>(
this IQueryable<TEntity> query, string path)
{
var efQuery = query as ObjectQuery<TEntity>;
if (efQuery == null)
return query;
return efQuery.Include(path);
}
}
or better yet, use the already available extension method that supports lambda expressions instead of strings as paths.
Also, do not use so many includes unless most are 1:1 or :1 relationships, 1: (or :) relationships greatly increase the IO from the database, resulting in bad performance.
Consider using multiple queries with .Future() to enable a single access to the database instead.
It's not related to the Includes.
You can't use regas in a LINQ-to-entities query because it's an ICollection. EF can't translate that into SQL.
Your "answer" can't possibly work with EF. The object regas is an in-memory list of Regra entities (I guess). If you use that directly in...
var pr = dbContext.ProjetoTipoCargaDbSet
.Where(x => x.IdProjetoTipoCarga == regras.FirstOrDefault(y => y.IdProjetoTipoCarga == x.IdProjetoTipoCarga).IdProjetoTipoCarga);
...you should get an exception like
Unable to create a constant value of type 'Regra'. Only primitive types or enumeration types are supported...
But, boy, what a tortuous way to get where you want to be! First you get a ProjetoTipoCargaModelo object by an idProjetoTipoCarga. Then you fetch its active Regras. Then you basically use the IdProjetoTipoCarga values of the Regras to see if one of them is equal to the original idProjetoTipoCarga and if so, you use its value to get a ProjetoTipoCargaModelo object.
If you remove all the redundancies, what's left is:
var pr = dbContext.ProjetoTipoCargaDbSet
.Where(x => x.IdProjetoTipoCarga == idProjetoTipoCarga
&& x.ListaRegra.Any(r => r.Ativo));
I you us this LINQ statement, you append your includes to pr.

how to filter entity type framework object by its child object value properties?

I have an entity framework object called batch, this object has a 1 to many relationship to items.
so 1 batch has many items. and each item has many issues.
I want to filter the for batch items that have a certain issue code (x.code == issueNo).
I have written the following but Im getting this error:
items = batch.Select(b => b.Items
.Where(i => i.ItemOrganisations
.Select(o => o
.Issues.Select(x => x.Code == issueNo))));
Error 1:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<bool>>' to 'bool'
Error 2:
Cannot convert lambda expression to delegate type 'System.Func<Ebiquity.Reputation.Neptune.Model.Item,bool>' because some of the return types in the block are not implicitly convertible to the delegate return type
Select extension method needs a lambda expression that returns a boolean, but the inner o.Issues.Select returns an IEnumerable of boolean to the outer Select(o => o which result in the exception you're getting.
Try using Any instead which verifies that at least one element verifies the condition:
items = batch.Select(
b => b.Items.Where(
i => i.ItemOrganisations.Any(
o => o.Issues.Any(x => x.Code == issueNo)
)
)
);
If I understand correctly, you're trying to select through multiple layers of enumerables. In those cases you need SelectMany which flattens out the layers, not Select. LINQ's syntax sugar is made specifically to make SelectMany easier to reason about:
var items = from item in batch.Items
from org in item.ItemOrganizations
from issue in org.Issues
where issue.Code == issueNo
select item;
The compiler translates that into something like this:
var items = batch.Items
.SelectMany(item => item.ItemOrganizations, (item, org) => new {item, org})
.SelectMany(#t => #t.org.Issues, (#t, issue) => new {#t, issue})
.Where(#t => #t.issue.Code == issueNo)
.Select(#t => #t.#t.item);
You can always wrap this in a Distinct if you need to avoid duplicate items:
var items = (from item in batch.Items
from org in item.ItemOrganizations
from issue in org.Issues
where issue.Code == issueNo
select item).Distinct();
It's hard to tell what you're trying to do based on your code but I think you're looking for something like this;
var issue = batch.Select(b => b.Items).Select(i => i.Issues).Where(x => x.Code == issueNo).Select(x => x).FirstOrDefault();
The above query will return the first issue where the Issues Code property is equal to issueNo. If no such issue exists it will return null.
One problem (the cause of your first error) in your query is that you're using select like it's a where clause at the end of your query. Select is used to project an argument, when you do Select(x => x.Code == issueNo) what you're doing is projecting x.Code to a bool, the value returned by that select is the result of x.Code == issueNo, it seems like you want that condition in a where clause and then you want to return the issue which satisfies it which is what my query is doing.
items = from b in batch.Include("Items")
where b.Items.Any(x=>x.Code==issueNo)
select b;
You're getting lost in lambdas. Your LINQ chains are all embedded in each other, making it harder to reason about. I'd recommend some helper functions here:
static bool HasIssueWithCode(this ItemOrganization org, int issueNo)
{
return org.Issues.Any(issue => issue.Code == issueNo);
}
static bool HasIssueWithCode(this Item items, int issueNo)
{
return items.ItemOrganizations.Any(org => org.HasIssueWithCode(issueNo));
}
Then your answer is simply and obviously
var items = batch.Items.Where(item => item.HasIssueWithCode(issueNo));
If you inline these functions, the result is the exact same as manji's (so give manji credit for the correct answer), but I think it's a bit easier to read.

Func declared as a variable used in .Where() crashes my application

I have something like this:
Func<Thread, bool> tmpFunc = thread => true;
threads = Threads.Where(tmpFunc).(...)
Now, when I do Threads.Where(thread => true).(...) everything is okay, but using variable in .Where() crashes my application. Why? Am I doing something wrong?
Okay, so here is the code that reproduces the error:
var threads = context.Categories
.Where(c => c.Name == variable)
.Select(c => new
{
threads = c.Threads
.Where(tmpFunc)
.OrderByDescending(t => t.DateCreated)
.Skip(threadsToSkip)
.Take(threadsPerPage)
.Select(t => new
{
t,
CategoryName = t.Category.Name,
AuthorName = t.Author.UserName,
t.Posts.Count,
LastPost = t.Posts
.OrderByDescending(post => post.DateCreated)
.Select(p => new{p.Author.UserName, p.DateCreated})
.FirstOrDefault()
}),
c.Threads.Count
}).Single();
And the error it gives me is internal .net framework data provider error 1025
Try using an Expression instead of the Func directly:
Expression<Func<Thread, bool>> tmpFuncExpr = thread => true;
While Linq2Objects will be happy with the Func, Linq2Sql will not.
My assumption is that there is no translatiin of a custom func to sql and the func is used in the projection. Although I would expect a "no sql translation for ... exists" exception.
To fix this, you could try declare your func as
Expression<Func<Thread, bool>> tmpFunc = thread => true;
threads = Threads.Where(tmpFunc).(...)
When you're using the Entity Framework, any predicate passed to the EF's IQueryable has to be the Expression<Func<>>.
Check this answer stackoverflow.com/questions/11990158...

Categories