Linq In Clause & Predicate building - c#

I have two tables. Report and ReportData.
ReportData has a constraint ReportID.
How can I write my linq query to return all Report objects where the predicate conditions are met for ReportData? Something like this in SQL:
SELECT * FROM Report as r
Where r.ServiceID = 3 and r.ReportID IN (Select ReportID FROM ReportData WHERE JobID LIKE 'Something%')
This is how I'm building my predicate:
Expression<Func<ReportData, bool>> predicate = PredicateBuilder.True<ReportData>();
predicate = predicate.And(x => x.JobID.StartsWith(QueryConfig.Instance.DataStreamName));
var q = engine.GetReports(predicate, reportsDataContext);
reports = q.ToList();
This is my query construction at the moment:
public override IQueryable<Report> GetReports(Expression<Func<ReportData, bool>> predicate, LLReportsDataContext reportDC)
{
if (reportDC == null) throw new ArgumentNullException("reportDC");
var q = reportDC.ReportDatas.Where(predicate).Where(r => r.ServiceID.Equals(1)).Select(r => r.Report);
return q;
}
I've tried doing the following as well:
public override IQueryable GetReports(Expression> predicate, LLReportsDataContext reportDC)
{
if (reportDC == null) throw new ArgumentNullException("reportDC");
var q = from r in reportDC.Reports
where r.ServiceID.Equals(1)
where r.ReportDatas.Where(predicate.Compile()).Select(x => r.ReportID).Contains(r.ReportID)
select r;
return q;
}
However, I get the this Exception: "Unsupported overload used for query operator 'Where'."
UPDATE
This fixed it:
var q = reportDC.Reports.AsExpandable().
Where(r => r.ReportDatas.Any(predicate.Compile()))
.Where(r => r.ServiceID.Equals(1));

Query
ReportDatas
.Where( reportData => reportData.StartsWith( "Something%" ) &&
reportData.Report.Id ==3)
.Select( reportData => reportData.Report )
.Distinct()
AboutLinqKit
When using LinqKit, sometimes you need to call AsExpandable() in the entity collection and to compile the predicate expression. see this example : ): how-to-use-predicate-builder-with-linq2sql-and-or-operator

Related

predicate not working in join lambda expression

I have using PredicateBuilder class to dynamically creating Where clause in LINQ statements.
Following is my code which is When using predicate
var predicate = PredicateBuilder.True<tbl_login>();
predicate = predicate.And(x => x.DAT_LOGIN_TIME >= startDate && x.DAT_LOGIN_TIME <= endDate);`
var data = context.tbl_login.Join(context.tbl_user, x => x.LNG_USER_PRIMARY_ID, y => y.LNG_USER_PRIMARY_ID, (x, y) => new
{
userID = x.LNG_USER_PRIMARY_ID,
loginTime = x.DAT_LOGIN_TIME,
ageGroup = y.INT_AGE_GROUP
}).Where(predicate)
.Select(x => new
{
userID = x.userID,
ageGroup = x.ageGroup
}).Distinct().ToList();
It gives me compile time exception cannot convert from System.Linq.Expressions.Expression<System.Func<JoyRydeAnalysis.Data.tbl_login,bool>>' to System.Linq.Expressions.Expression<System.Func<AnonymousType#1,int,bool>
What i am missing?
Your predicate is a predicate for tbl_login. But here, you're projecting to an anonymous type:
(x, y) => new
{
userID = x.LNG_USER_PRIMARY_ID,
loginTime = x.DAT_LOGIN_TIME,
ageGroup = y.INT_AGE_GROUP
}
That predicate can't be applied to that projection.
I suspect you may want to apply the predicate earlier instead though:
var data = context.tbl_login
.Where(predicate)
.Join(...)
.Select(...)
.Distinct()
.ToList();
Note that at this point, your Select will be redundant, as you can just change the projection in the Join call.
It's also note clear why you're using PredicateBuilder at all, to be honest. You're only joining PredicateBuilder.True with a single expression - so you can just use:
var data = context.tbl_login
.Where(x => x.DAT_LOGIN_TIME >= startDate && x.DAT_LOGIN_TIME <= endDate)
.Join(...)
.Select(...)
.Distinct()
.ToList();

C# predicate list passed to Linq Where clause

I have a long Linq Where clause that I would like to populate with a predicate list.
List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();
filters.Add(p => p.Title != null && p.Title.ToLower().Contains(searchString));
filters.Add(p => p.Notes != null && p.Notes.ToLower().Contains(searchString));
filters.Add(GlobalSearchUser((List < User > users = new List<User>() { p.user1, p.user2, p.user3, p.user4 }), searchString));
notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.Where(filters.ToArray()).Take(10).ToList();
However I'm getting this error:
cannot convert from 'System.Linq.Expressions.Expression<System.Func<project.Contracts.DTOs.Note,bool>>[]' to 'System.Func<project.Contracts.DTOs.Note,bool>'
Which is an error on the .where clause. Pulling out the .where compiles just fine.
I think great answer from Hogan can be simplified and shorten a bit by use of Any and All Linq methods.
To get items that fulfill all the conditions:
var resultAll = listOfItems.Where(p => filters.All(f => f(p)));
And to get the items that fulfill any condition:
var resultAny = listOfItems.Where(p => filters.Any(f => f(p)));
There are at least two errors in your code:
List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();
change it to
List<Func<Note, bool>> filters = new List<Func<Note, bool>>();
You don't need Expression trees here. You are using IEnumerable<>, not IQueryable<>
notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.Where(filters.ToArray()).Take(10).ToList();
There .Where() accepts a single predicate at a time. You could:
notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.Where(x => filters.All(x)).Take(10).ToList();
or various other solutions, like:
var notesEnu = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.AsEnumerable();
foreach (var filter in filters)
{
notesEmu = notesEmu.Where(filter);
}
notes = notesEnu.Take(10).ToList();
Because all the .Where() conditions are implicitly in &&.
You have to loop over your filters and run a test on each one.
You can do it with linq like this to return true if any of your filters are true:
.Where(p => { foreach(f in filters) if (f(p) == true) return(true); return(false)})
or like this to to return true if all of your filters are true:
.Where(p => { foreach(f in filters) if (f(p) == false) return(false); return(true)})
You can't just pass an array of predicates to the where method. You need to either iterate over the array and keep calling Where() for each expression in the array, or find a way to merge them all together into one expression and use that. You'll want to use LinqKit if you go the second route.

PredicateBuilder with multiple (OR) and (AND)

I'm trying to create an expression in order to retrieve a Store object that should be on a list of countries and also that their Store.Id = X.
I'm trying to do that with the following expression, but that returns all Stores stored on the database, I don't know what I'm missing.
public Expression<Func<Store, bool>> CreateExpression(List<Country> countries, long storeId)
{
var predicate = PredicateBuilder.False<Store>();
predicate = countries.Aggregate(predicate, (current, p) =>
current.Or(e => e.Country.Id == p.Id));
predicate = predicate.And(e => e.Id == storeId);
return predicate;
}

How do I define a SELECT TOP using LINQ with a dynamic query?

I want to pass dynamic lambda expressions to the function below, but I'm not sure how to define the .Take() or .OrderByDescending() on the expression object.
If I want to call the function below, then I want to be able to do this:
dbprovider.Query = (x => x.ConfigurationReference == "172.16.59.175")
.Take(100)
.OrderByDescending(x.Date)
FindEntities(db, dbprovider.Query)
But I can't (this syntax is invalid). Any ideas?
public static List<T> FindEntities<T>(TrackingDataContext dataContext, System.Linq.Expressions.Expression<Func<T, bool>> find) where T : class
{
try
{
var val = dataContext.GetTable<T>().Where(find).ToList<T>();
return val;
}
catch (Exception ex)
{
throw ex;
}
}
The parameter is of type:
System.Linq.Expressions.Expression<Func<T, bool>> find
That means it can take a predicate (the "where" clause), and only a predicate. Thus the only bit you can pass in there is the filter:
x => x.ConfigurationReference == "172.16.59.175"
To do what you want, you would need to add the rest of the code in FindEntities, so that it becomes:
var val = dataContext.GetTable<T>().Where(find)
.OrderByDescending(x => x.Date).Take(100).ToList<T>();
(note also that the Take should really be after the OrderByDescending)
One way you could do that would be:
public static List<T> FindEntities<T>(TrackingDataContext dataContext,
System.Linq.Expressions.Expression<Func<T, bool>> find,
Func<IQueryable<T>, IQueryable<T>> additonalProcessing = null
) where T : class
{
var query = dataContext.GetTable<T>().Where(find);
if(additonalProcessing != null) query = additonalProcessing(query);
return query.ToList<T>();
}
and call:
var data = FindEntities(db, x => x.ConfigurationReference == "172.16.58.175",
q => q.OrderByDescending(x => x.Date).Take(100));
However, frankly I'm not sure what the point of this would be... the caller could do all of that themselves locally more conveniently, without using FindEntities at all. Just:
var data = db.GetTable<T>()
.Where(x => x.ConfigurationReference == "172.16.58.175")
.OrderByDescending(x => x.Date).Take(100).ToList();
or even:
var data = db.SomeTable
.Where(x => x.ConfigurationReference == "172.16.58.175")
.OrderByDescending(x => x.Date).Take(100).ToList();
or just:
var data = (from row in db.SomeTable
where row.ConfigurationReference == "172.16.58.175"
orderby row.Date descending
select row).Take(100).ToList();

Linq - NHibernate. How to combine LINQ conditions in group with OR operator. IQuerable.Concat alternative

I have IQuerable<MyEntity> and filters(conditions list), that i want to apply to my query.
Schematically I want to get looks like
//First filter group // Second filter group
QUERY = from MYTABLE where ( Name = "User" and Age = 19 ) OR ( Name = "Alex", and Age > 20 )
query = (query.Where(x => x.Name == value)
.Where(x => x.Age == value)
.Where(x => x.Post == value))
OR (
(query.Where(x => x.Name == value)
.Where(x => x.RegistrationDate == value))
I need to create IQuerable, that will contain result first and second filter group(IQuerable).
But, I can't execute at first 1-st filter group, then 2-nd group and concat results, because after appling my filters I must to do a few operations with IQuarable.
I tried create queryes and query.Concat() with main, but NHibernate does not support this method
public static IQueryable<IDevice> ApplyGroups(this IQueryable<IDevice> query, IList<ITerminalGroup> groups)
{
foreach (ITerminalGroup group in groups)
{
IQueryable<IDevice> groupQuery = query.AsQueryable();
// apply to groupQuery conditions
groupQuery = groupQuery.ApplyGroup(group);
query = query.Concat(groupQuery);
}
return query;
}
static IQueryable<IDevice> ApplyGroup(this IQueryable<IDevice> query, ITerminalGroup group)
{
foreach (ITermGroupCondition condition in group.Conditions)
{
query = query.Where(x => x.SpecialAttributes.Any(sa => x.Id == condition.AttribId && sa.AttribValue.Equals(condition.Value)));
}
return query;
}
Count of conditions may be different, I do not know it in advance
How to combine LINQ conditions in group with OR operator ?
You can use PredicateBuilder to do that; combine the conditions with the PredicateBuilder.Or method, and pass the resulting condition to the Where method.

Categories

Resources