C# Predicate Builder not working - c#

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.

Related

Exception handling in Linq queries

I m using ...
tmpLst = (from e in App.lstAllChilds where e.Id == Id select e).ToList();
where lstAllChilds is the list, which contains some corrupted data as well.
So now i m tying to handle Try-Catch block inside this query.
Please help.
In case if you just want to ignore "bad elements" then:
App.lstAllChilds.SkipExceptions().Where( e => e.Id == Id).ToList();
Extension method:
public static class Extensions
{
public static IEnumerable<T> SkipExceptions<T>(this IEnumerable<T> values)
{
using (var enumerator = values.GetEnumerator())
{
bool next = true;
while (next)
{
try
{
next = enumerator.MoveNext();
}
catch
{
continue;
}
if (next) yield return enumerator.Current;
}
}
}
}
Here is the simplest change you can make to simply exclude items which are null or are raising an exception.
tmpLst = App.lstAllChilds.Where(e =>
{
try
{
return e != null && e.Id == Id;
}
catch
{
return false;
}
}).ToList();
But in my opinion you probably should investigate and solve the underlying problem. This doesn't seem like a scenario where exceptions should be expected.
So, here's the thing. There's language integrated query (Linq), and then there's yielded enumeration (a.k.a. Iterators).
Linq allows you to define an expression tree that is later executed by something that may or may not be C# (for example the expression could be translated into a SQL query). If you're writing linq there is a good chance your query provider (the thing that does the expression translation) does not support exception handling (much less whatever you're doing that throws exceptions).
Interators on the other hand (or "linq to objects") just ends up executing in C#, so you can just go wild with your exception handling.
For example w/ linq to objects you can do this:
var myList = new[] { "1", "2", "BARF", "3" };
var sum = myList.Select(str => {
try {
return Int32.Parse(str);
} catch {
return 0;
}
}).Aggregate((x, y) => x + y);
If you're indeed doing linq to objects, and you just want to skip elements where your source IEnumerable threw an exception check out Vladimir Gondarev's answer.
The important thing to understand however, is that the anonymous function we just passed to that Select call is not an Expression (an uncompiled expression tree), it is a Func (a delegate pointing to compiled c# code), which means that it will run in the .Net process, even if we replaced myList with a linq to entities table (or some other linq provider). The reason for this is that the C# expression syntax does not support blocks, nor does it support try-catch. Unsurprisingly, given that, the SQL-style Linq statements (from xxx select yyy) also do not support try-catch blocks.
However, just because the C# expression syntax doesn't support it doesn't mean you can't do it. But, to be clear, I do not recommend doing this because I highly doubt there is a QueryProvider in existence that supports it (aside from the linq to objects provider). For the curious here's how you would create a lambda expression that contains a try-catch block.
var parseMethod = typeof(Int32).GetMethod("Parse", new[] { typeof(String) });
var param = Expression.Parameter(typeof(String));
var selectExp =
Expression.Lambda<Func<String, Int32>>(
Expression.TryCatch(
Expression.Call(parseMethod, param),
Expression.Catch(typeof(Exception), Expression.Constant(0))
),
param
);
var sum = myList.Select(selectExp).Aggregate((x, y) => x + y);
So when somebody implements a QueryProvider backed by a store that supports exception handling, you could use this.
It depends what exactly you are trying to achieve:
Return the list except "broken" items.
You can try using a Where clause to check and filter them out:
App.lstAllChilds.Where(x => IsValidNode(x, Id)).ToList();
Apparently you need to implement IsValidNode which should check for null, if it can throw an InvalidObjectException (not sure if you can easily detect it but you can always wrap it in a try-catch block) and Id equality. Like that:
private bool IsValidNode(Child c, int id)
{
if (x == null) return false;
try {
return c.Id == id;
}
catch (InvalidObjectException)
{
}
return false;
}
Return an empty list if there are any broken items.
Just wrap the entire thing in a try-catch block

Internal .NET Framework Data Provider error 1025

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();

Creating a Dynamic Where Clause as a Linq Expression

I am trying to dynamically append where conditions into a single expression-object, then pass that expression-object into a method that will use it. However, I keep getting "class name is not available at this point".
Thanks in advance!
UPDATE:
I finally was able to create a working example here.
The Code Look Like:
var view = new vw_QuickFindResult();
// This wont compile
Expression<Func<vw_QuickFindResult, bool>> where = Expression<Func<vw_QuickFindResult, bool>>(view, true);
// Build LIKE Statement
var searches = new List<String>(searchText.Split(' '));
searches.ForEach(productName =>
{
productName.Replace('"', '%');
productName.Replace('*', '%');
where = x => SqlMethods.Like(view.DocumentName, productName);
});
return DocumentCollectionService.ListQuickFind(where);
This is one problem:
where = x => SqlMethods.Like(view.DocumentName, productName);
You're ignoring the x here, instead using view which you've just initialized. I suspect you want:
where = x => SqlMethods.Like(x.DocumentName, productName);
However that will replace the existing where expression each time. I think you should be using PredicateBuilder by Joe Albhari. I'd avoid using ForEach too, personally:
var where = PredicateBuilder.False<vw_QuickFindResult>();
// Build LIKE Statement
foreach (string productName in searchText.Split(' '))
{
string like = productName.Replace('"', '%');
.Replace('*', '%');
where = where.Or(x => SqlMethods.Like(view.DocumentName, like));
}
return DocumentCollectionService.ListQuickFind(where);
(I'm guessing at the functionality you want; you may want PredicateBuilder.True and the And extension method instead.)

LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression

I'm migrating some stuff from one mysql server to a sql server but i can't figure out how to make this code work:
using (var context = new Context())
{
...
foreach (var item in collection)
{
IQueryable<entity> pages = from p in context.pages
where p.Serial == item.Key.ToString()
select p;
foreach (var page in pages)
{
DataManager.AddPageToDocument(page, item.Value);
}
}
Console.WriteLine("Done!");
Console.Read();
}
When it enters into the second foreach (var page in pages) it throws an exception saying:
LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression.
Anyone know why this happens?
Just save the string to a temp variable and then use that in your expression:
var strItem = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == strItem
select p;
The problem arises because ToString() isn't really executed, it is turned into a MethodGroup and then parsed and translated to SQL. Since there is no ToString() equivalent, the expression fails.
Note:
Make sure you also check out Alex's answer regarding the SqlFunctions helper class that was added later. In many cases it can eliminate the need for the temporary variable.
As others have answered, this breaks because .ToString fails to translate to relevant SQL on the way into the database.
However, Microsoft provides the SqlFunctions class that is a collection of methods that can be used in situations like this.
For this case, what you are looking for here is SqlFunctions.StringConvert:
from p in context.pages
where p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;
Good when the solution with temporary variables is not desirable for whatever reasons.
Similar to SqlFunctions you also have the EntityFunctions (with EF6 obsoleted by DbFunctions) that provides a different set of functions that also are data source agnostic (not limited to e.g. SQL).
The problem is that you are calling ToString in a LINQ to Entities query. That means the parser is trying to convert the ToString call into its equivalent SQL (which isn't possible...hence the exception).
All you have to do is move the ToString call to a separate line:
var keyString = item.Key.ToString();
var pages = from p in context.entities
where p.Serial == keyString
select p;
Cast table to Enumerable, then you call LINQ methods with using ToString() method inside:
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
But be careful, when you calling AsEnumerable or ToList methods because you will request all data from all entity before this method. In my case above I read all table_name rows by one request.
Had a similar problem.
Solved it by calling ToList() on the entity collection and querying the list.
If the collection is small this is an option.
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
Hope this helps.
Upgrading to Entity Framework Version 6.2.0 worked for me.
I was previously on Version 6.0.0.
Hope this helps,
Change it like this and it should work:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
The reason why the exception is not thrown in the line the LINQ query is declared but in the line of the foreach is the deferred execution feature, i.e. the LINQ query is not executed until you try to access the result. And this happens in the foreach and not earlier.
If you really want to type ToString inside your query, you could write an expression tree visitor that rewrites the call to ToString with a call to the appropriate StringConvert function:
using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;
namespace ToStringRewriting {
class ToStringRewriter : ExpressionVisitor {
static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
.Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
protected override Expression VisitMethodCall(MethodCallExpression node) {
var method = node.Method;
if (method.Name=="ToString") {
if (node.Object.GetType() == typeof(string)) { return node.Object; }
node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
}
return base.VisitMethodCall(node);
}
}
class Person {
string Name { get; set; }
long SocialSecurityNumber { get; set; }
}
class Program {
void Main() {
Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
var rewriter = new ToStringRewriter();
var finalExpression = rewriter.Visit(expr);
var dcx = new MyDataContext();
var query = dcx.Persons.Where(finalExpression);
}
}
}
In MVC, assume you are searching record(s) based on your requirement or information.
It is working properly.
[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{
EmployeeContext employeeContext = new EmployeeContext();
string searchby=formcollection["SearchBy"];
string value=formcollection["Value"];
if (formcollection["SearchBy"] == "Gender")
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
return View("Index", emplist);
}
else
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
return View("Index", emplist);
}
}
I got the same error in this case:
var result = Db.SystemLog
.Where(log =>
eventTypeValues.Contains(log.EventType)
&& (
search.Contains(log.Id.ToString())
|| log.Message.Contains(search)
|| log.PayLoad.Contains(search)
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
)
)
.OrderByDescending(log => log.Id)
.Select(r => r);
After spending way too much time debugging, I figured out that error appeared in the logic expression.
The first line search.Contains(log.Id.ToString()) does work fine, but the last line that deals with a DateTime object made it fail miserably:
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
Remove the problematic line and problem solved.
I do not fully understand why, but it seems as ToString() is a LINQ expression for strings, but not for Entities. LINQ for Entities deals with database queries like SQL, and SQL has no notion of ToString(). As such, we can not throw ToString() into a .Where() clause.
But how then does the first line work? Instead of ToString(), SQL have CAST and CONVERT, so my best guess so far is that linq for entities uses that in some simple cases. DateTime objects are not always found to be so simple...
My problem was that I had a 'text' data type for this column (due to a migration from sqlite).
Solution: just change the data type to 'nvarchar()' and regenerate the table.
Then Linq accepts the string comparison.
I am working on retiring Telerik Open Access and replacing it with Entity Framework 4.0. I came across same issue that telerik:GridBoundColumn filtering stopped working.
I find out that its not working only on System.String DataTypes. So I found this thread and solved it by just using .List() at the end of my Linq query as follows:
var x = (from y in db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();
Just turn the LINQ to Entity query into a LINQ to Objects query (e.g. call ToArray) anytime you need to use a method call in your LINQ query.

C# PredicateBuilder Entities: The parameter 'f' was not bound in the specified LINQ to Entities query expression

I needed to build a dynamic filter and I wanted to keep using entities. Because of this reason I wanted to use the PredicateBuilder from albahari.
I created the following code:
var invoerDatums = PredicateBuilder.True<OnderzoeksVragen>();
var inner = PredicateBuilder.False<OnderzoeksVragen>();
foreach (var filter in set.RapportInvoerFilter.ToList())
{
if(filter.IsDate)
{
var date = DateTime.Parse(filter.Waarde);
invoerDatums = invoerDatums.Or(o => o.Van >= date && o.Tot <= date);
}
else
{
string temp = filter.Waarde;
inner = inner.Or(o => o.OnderzoekType == temp);
}
}
invoerDatums = invoerDatums.And(inner);
var onderzoeksVragen = entities.OnderzoeksVragen
.AsExpandable()
.Where(invoerDatums)
.ToList();
When I ran the code there was only 1 filter which wasn't a date filter. So only the inner predicate was filled. When the predicate was executed I got the following error.
The parameter 'f' was not bound in the
specified LINQ to Entities query
expression.
While searching for an answer I found the following page. But this is already implemented in the LINQKit.
Does anyone else experienced this error and know how to solve it?
I ran across the same error, the issue seemed to be when I had predicates made with PredicateBuilder that were in turn made up of other predicates made with PredicateBuilder
e.g. (A OR B) AND (X OR Y) where one builder creates A OR B, one creates X OR Y and a third ANDs them together.
With just one level of predicates AsExpandable worked fine, when more than one level was introduced I got the same error.
I wasn't able to find any help but through some trial and error I was able to get things to work.
Every time I called a predicate I followed it with the Expand extension method.
Here is a bit of the code, cut down for simplicity:
public static IQueryable<Submission> AddOptionFilter(
this IQueryable<Submission> query,
IEnumerable<IGrouping<int, int>> options)
{
var predicate = options.Aggregate(
PredicateBuilder.False<Submission>(),
(accumulator, optionIds) => accumulator.Or(ConstructOptionMatchPredicate(optionIds).Expand()));
query = query.Where(predicate.Expand());
return query;
}
Query is an IQueryable which has already had AsExpandable called, ConstructOptionNotMatchPredicate returns an Expression.
Once we got past the error we were certainly able to build up complicated filters at runtime against the entity framework.
Edit:
Since people are still commenting on and up voting this I assume it is still useful so I am sharing another fix. Basically I have stopped using LinqKit and it's predicate builder in favour of this Universal Predicate Builder that has the same API but doesn't need Expand calls, well worth checking out.
I got this error and Mant101's explanation got me the answer, but you might be looking for a simpler example that causes the problem:
// This predicate is the 1st predicate builder
var predicate = PredicateBuilder.True<Widget>();
// and I am adding more predicates to it (all no problem here)
predicate = predicate.And(c => c.ColumnA == 1);
predicate = predicate.And(c => c.ColumnB > 32);
predicate = predicate.And(c => c.ColumnC == 73);
// Now I want to add another "AND" predicate which actually comprises
// of a whole list of sub-"OR" predicates
if(keywords.Length > 0)
{
// NOTICE: Here I am starting off a brand new 2nd predicate builder....
// (I'm not "AND"ing it to the existing one (yet))
var subpredicate = PredicateBuilder.False<Widget>();
foreach(string s in keywords)
{
string t = s; // s is part of enumerable so need to make a copy of it
subpredicate = subpredicate.Or(c => c.Name.Contains(t));
}
// This is the "gotcha" bit... ANDing the independent
// sub-predicate to the 1st one....
// If done like this, you will FAIL!
// predicate = predicate.And(subpredicate); // FAIL at runtime!
// To correct it, you must do this...
predicate = predicate.And(subpredicate.Expand()); // OK at runtime!
}
Hope this helps! :-)

Categories