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...
Related
I want to pass a lambda to my .Select() method depending on a condition.
I set my lambda up like this:
Func<Monthly, int?> f = x => x.CLDD;
I then set up my .Select() like this:
IQueryable query =
db.Monthlies
.GroupBy(o => o.Date.Value.Year)
.Select(
o => new {
Year = o.Key,
MaxDate = o.Max(x => x.Date),
Data = o.Sum(f)
}
)
.Where(o => o.Year != currentYear)
.OrderBy(o => o.Year);
The code compiles and runs fine but the query does not send back any results. When I debug and watch query I see it says:
+ base {"Internal .NET Framework Data Provider error 1025."}
System.SystemException {System.InvalidOperationException}
Note if instead I do:
Expression<Func<Monthly, int?>> f = x => x.CLDD;
Then o.Sum(f) errors saying:
Error 1 Instance argument: cannot convert from
'System.Linq.IGrouping<int,MyWeb.Models.Monthly>' to
System.Linq.IQueryable<MyWeb.Models.Monthly>'
Thank you!
You were close, Entity Framework needs an Expression to work not Func, but the Sum extension method that receives an Expression works only with IQueryable.
Now inside Select you are getting an IGrouping from GroupBy which does not implement IQueryable only IEnumerable.
So you just need to cast it to get the right extension method:
Data = o.AsQueryable().Sum(f)
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);
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.
IQueryable<Organization> query = context.Organizations;
Func<Reservation, bool> predicate = r => !r.IsDeleted;
query.Select(o => new {
Reservations = o.Reservations.Where(predicate)
}).ToList();
this query throws "Internal .NET Framework Data Provider error 1025" exception but the query below does not.
query.Select(o => new {
Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();
I need to use the first one because I need to check a few if statements for constructing the right predicate. I know that I can not use if statements in this circumstance that is why I pass a delegate as parameter.
How can I make the first query work?
While the other answers are true, note that when trying to use it after a select statement one has to call AsQueryable() explicitly, otherwise the compiler will assume that we are trying to use IEnumerable methods, which expect a Func and not Expression<Func>.
This was probably the issue of the original poster, as otherwise the compiler will complain most of the time that it is looking for Expression<Func> and not Func.
Demo:
The following will fail:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).Any(expr))
.Load()
While the following will work:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).AsQueryable().Any(expr))
.Load()
After creating the bounty (rats!), I found this answer, which solved my problem. (My problem involved a .Any() call, which is a little more complicated than this question...)
In short, here's your answer:
IQueryable<Organization> query = context.Organizations;
Expression<Func<Reservation, bool>> expr = r => !r.IsDeleted;
query.Select(o => new { Reservations = o.Reservations.Where(expr) })
.ToList();
Read the referenced answer for an explanation of why you need the local variable expr, and you can't directly reference another method of return type Expression<Func<Reservation, bool>>.
Thanks for pinging me. I guess I was on the right track after all.
Anyway, to reiterate, LINQ to Entities (thanks to Jon Skeet for correcting me when I got mixed up in my own thought process in the comments) operates on Expression Trees; it allows for a projection to translate the lambda expression to SQL by the QueryProvider.
Regular Func<> works well for LINQ to Objects.
So in this case, when you're using the Entity Framework, any predicate passed to the EF's IQueryable has to be the Expression<Func<>>.
I just experienced this issue in a different scenario.
I have a static class full of Expression predicates which I can then combine or pass to an EF query. One of them was:
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(statuses.Contains);
}
This was throwing the 1025 error due to the Contains method group call. The entity framework expected an Expression and found a method group, which resulted in the error. Converting the code to use a lambda (which can be implicitly cast to an Expression) fixed the error
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(x => statuses.Contains(x));
}
Aside: I then simplified the expression to ce => ce.Event.AttendeeStatuses.Any(a => a.ClientId == ce.Client.Id && statuses.Contains(a.Status.Value));
Had a similar problem. Library of ViewModels that look like this:
public class TagViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public static Expression<Func<SiteTag, TagViewModel>> Select = t => new TagViewModel
{
Id = t.Id,
Name = t.Name,
};
This works:
var tags = await db.Tags.Take(10).Select(TagViewModel.Select)
.ToArrayAsync();
But, this won't compile:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).Select(TagViewModel.Select)
})
.ToArrayAsync();
Because the second .Select is a mess - the first one is actually called off of an ICollection, which is not IQueryable, so it consumes that first Expression as a plain Func, not Expression<Func.... That returns IEnumerable<..., as discussed on this page. So .AsQueryable() to the rescue:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();
But that creates a new, weirder problem: Either I get Internal Framework...Error 1025, or I get the post variable with a fully loaded .Post property, but the .Tags property has an EF proxy object that seems to be used for Lazy-Loading.
The solution is to control the return type of Tags, by ending use of the Anonymous class:
public class PostViewModel
{
public Post Post { get; set; }
public IEnumerable<TagViewModel> Tags { get; set; }
Now select into this and it all works:
var post = await db.Posts.Take(10)
.Select(p => new PostViewModel {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();
I am trying to use predicate builder in the following code:
public ListResults<DBAccountDetail> GetAccountDetail(string[] salesForceKey)
{
try
{
using (var c = new SalesForceDataContext())
{
var predicate = PredicateBuilder.False<DBAccountDetail>();
foreach (var keyword in salesForceKey)
{
var temp = keyword;
predicate = predicate.Or(p => p.Id.Contains(temp));
}
var lret = c.DBAccountDetails.AsQueryable().Where(predicate).ToList();
return new ListResults<DBAccountDetail>(lret);
}
}
catch (Exception ex)
{
LogError("GetLegacyRateLetters()", ex);
return new ListResults<DBAccountDetail>(ex);
}
}
The problem is that on this line:
predicate = predicate.Or(p => p.Id.Contains(temp));
p.Id will not intellisense out and throws a compilation error of:
No overload for method 'Or' takes 1 arguments
What is PredicateBuilder
The fact that you're not getting Intellisense on p.Id tells me that DBAccountDetail.Id probably either doesn't exist, doesn't have a getter, or is private. If you aren't getting intellisense on the "p", then maybe the compiler isn't resolving DBAccountDetail correctly? Without more information, it's not clear what the problem may be.
However, it is probably worthwhile to note that the latest versions of Entity Framework and LINQ to SQL both support syntax like this:
c.DBAccountDetails.Where(d => salesForceKey.Contains(d))
... and this:
c.DBAccountDetails.Where(d => salesForceKey.Any(k => k == d))
Either of these would make PredicateBuilder unnecessary in this case.
It is possible that the compiler cannot guess the generic type for Or. Try providing it directly
predicate = predicate.Or<DBAccountDetail>(p => p.Id.Contains(temp))
As an aside, you should be able to 1-line that foreach with
var predicate =
salesForceKey.Aggregate(
PredicateBuilder.False<DBAccountDetail>(),
(accumulatedPredicate, keyword) => accumulatedPredicate.OrDBAccountDetail>(p => p.Id.Contains(temp))
);
I have the same issue but I had placed the PredicateBuilder class in another project. Once I moved it into the same project as my Linq To Sql classes the error went away.