I have been trying to convert a simple if else to a linq statement.
The if else statement goes something like this:
if ( MessageStatus = 1 )
then MessageCount > 0
else if ( MessageStatus = 2 )
then MessageCount = 0
else
do nothing
This is what I have in my linq so far and it didn't query out the data I wanted
var query = _context.Message
.Where(c => request.MessageStatus.Equal(2) ? c.MessageCount.Equals(0) :
request.MessageStatus.Equal(1) ? c.MessageCount > 0 :
request.MessageStatus.Equal(0));
It should be able to return rows of data but instead it return nothing.
Where does the problems lie at?
Thanks
What you're asking about is conditional filtering. request.MessageStatus is a value you know before you execute the query, so you can use it to define which Where to add to the query.
This works by creating a IQueryable<...> for the whole data set (your table), then adding on the Where that you want based on some condition. It's important to note that you're always doing query = query.Where(...);, if you don't re-assign query you lose the additional filter.
// "request" is defined earlier
IQueryable<Message> query = _context.Message; // might need .AsQueryable() here
if (request.MessageStatus == 1)
{
query = query.Where(r => r.MessageCount > 0);
}
else if (request.MessageStatus == 2)
{
query = query.Where(r => r.MessageCount = 0);
}
else
{
// assuming that this means "include all records",
// don't perform any additional filtering
// you don't need this "else"
}
List<Message> results = query.ToList();
It's also good to know that this pattern works outside of EF as well with basic IEnumerables and in memory collections.
Related
I'm working on an API; how can I set my linq query up to return the max value with a where condition?
See the example code below; I can return the max value of the field I want, but I need to filter it where another column value equals something.
var lot = db.ShrinkLotData.Where(x => x.SupplierMfgLot.ToLower() == label.SupplierMfgLot.ToLower() && x.CatPattern.ToLower() == label.CatPattern.ToLower())
.SingleOrDefaultAsync();
if (lot.Result == null)
{
var lots = db.ShrinkLotData.Where(x => x.CatPattern.ToLower() == label.CatPattern.ToLower());
int internallot = db.ShrinkLotData.Max(x => x.InternalLotNum).Value;
return Ok(lot);
}
return Ok(lot);
}
for the internallot, I want to return the highest value using similar syntax as the lots syntax.. (Where the catpattern equals a specific value)
What am I overlooking?
Thanks!
If I understand correctly, you basically need to use Where and Max together, so that you can select max value with a where condition.
db.ShrinkLotData.Where(x => x.CatPattern.ToLower() == label.CatPattern.ToLower()).Max(x => x.InternalLotNum).Value;
More Info : Composability of Queries:
..., you compose them in method syntax by
chaining the method calls together. This is what the compiler does
behind the scenes when you write queries by using query syntax. And
because a query variable does not store the results of the query, you
can modify it or use it as the basis for a new query at any time, even
after it has been executed.
I am trying to search record(s) from table by appying multiple search parameters.
as per below snap.
here by using various parameters as per above snap i want to filter the records.
here user could enter any combination of parameter(s) to search record.
i tried something like below code hich works for single condition but fails for combination of any search paramets.
public List<students> SearchStudents(students search)
{
var result = new List<students>();
var records= from stud in db.students
where stud.enrollmentNumber== search.enrollmentNumber
|| stud.enrollmentDate==search.enrollmenttDate
|| stud.enrollmentType==search.enrollmentType
|| stud.className==search.className
select new Search()
{
enrollmentNumber= stud.enrollmentNumber,
enrollmentDate = stud.enrollmentDate,
enrollmentType = stud.enrollmentType,
Name = stud.Name,
className=stud.className,
Description = stud.Description
};
result = records.ToList();
return result;
}
but this is not working properly. means it returns same result whatever parameters I pass.
Like in the table i ahve 20 records and the enrollment number is the unique value field in DB so here when i am passing enrollment number thats like "2018-0001" it returns all records when it should return only single reocrd.
can someone guide me with this?
Without further explanation in your question about how this isn't working, the best we can do is guess. However, one very plausible reason for this is because you're including parameters you don't want to be filtering on.
Because you're using ORs in your statement, if any of those other properties are defaulted in the database, you're going to be returning those records. What you need to be doing is conditionally including your pieces of the WHERE clauses for only the properties that you want to search on. Unfortunately, that is not possible with the "SQL syntax" version of LINQ, so you will need to convert your query to that. (Good news: It's slightly more performant as well as it usually has to convert the SQL to the method syntax.)
Because of deferred execution, your query will not be sent to the database until you call a .ToList() or something to actually start processing the results. This allows you to chain method calls together, even if they are completely different C# statements. This is what you'll want to do:
public List<students> SearchStudents(students search)
{
var query = db.students;
if (!string.IsNullOrWhiteSpace(search.enrollmentNumber))
{
query = query.Where(s => s.enrollmentNumber == search.enrollmentNumber);
}
if (search.enrollmentDate != DateTime.MinValue)
{
query = query.Where(s => s.enrollmentDate == search.enrollmentDate);
}
if (!string.IsNullOrWhiteSpace(search.enrollmentType))
{
query = query.Where(s => s.enrollmentType == search.enrollmentType);
}
if (!string.IsNullOrWhiteSpace(search.className))
{
query = query.Where(s => s.className == search.className);
}
return query.Select(stud => new Search
{
enrollmentNumber= stud.enrollmentNumber,
enrollmentDate = stud.enrollmentDate,
enrollmentType = stud.enrollmentType,
Name = stud.Name,
className=stud.className,
Description = stud.Description
})
.ToList();
}
You may need to adjust the if statements in there to accommodate different data types than what is intuitive from the names, but this will only add the filter if a value has been provided.
I having some problem in Optional condition in Lambda express like
var shouldCheckDate = !string.IsNullOrEmpty(fromDate);
var result = (from r in db.Notify
where r.ApplicationId == applicationId
&& (shouldCheckDate || r.CreatedDate.Date > date)
select r
).Count;
problem is here it always excuting both condition either shouldCheckDate true are false. I am doing any thing wrong?
This is due to the fact, that LINQ is translated to SQL and SQL, in contrast to C#, executes all parts of a condition, no matter if one of them allready returned true.
You could compose the IQueryable by adding the where clauses only when needed:
var query = db.Notify;
query = query.Where(r => r.ApplicationId == applicationId);
if (shouldCheckDate) {
query = query.Where(r => r.CreatedDate.Date > date);
}
var result = query.Count();
You have OR statement here,
(shouldCheckDate || r.CreatedDate.Date > date)
so even one of these conditions which is true is enough for whole statement to be true.
Maybe r.CreatedDate.Date is always > date ?
I'm trying to build a basic dynamic Linq query using LinqPad. My method asks users to select 1 - 3 options, and then dynamically builds the query based on their input.
public void FilteredPropertySearch()
{
bool addAnotherOption = true;
int option;
Dictionary<int, bool> parameters = new Dictionary<int, bool>();
var predicate = PredicateBuilder.False<ResidentialProperty>();
Console.WriteLine("Follow instructions to filter property search results");
do
{
// get user input
option = int.Parse(Console.ReadLine());
switch(option)
{
case 1:
parameters.Add(1, true);
break;
// more cases - when case 0 addAnotherOption = false, loop ends
default:
Console.WriteLine("That was not a valid option");
break;
}
}while(addAnotherOption == true);
foreach(KeyValuePair<int, bool> p in parameters)
{
if(p.Key == 1 && p.Value == true)
predicate = predicate.Or (c => c.HasBackGarden);
if(p.Key == 2 && p.Value == true)
predicate = predicate.Or (c => c.HasFrontGarden);
if(p.Key == 3 && p.Value == true)
predicate = predicate.Or (c => c.HasSecureParking);
}
ResidentialProperties.Where (predicate).Dump();
}
The foreach loop should build the query based on the user's input, but when, for example, I make the first value in the dictionary true and select no others, it doesn't return any results. It definitely should as I know there are some values in my database table that satisfy Key(1) being true.
Should I be doing something else with query after the if's in the foreach?
EDIT
I've used the predicate builder instead and it seems to be (kind of) working when I use predicate.Or (as per edited code), but it only returns the first option that I select, it's not building an expression tree. I thought that changing predicate.Or to predicate.And would have added each selected user input to a filter expression.
If all three options were selected by the user, I want only rows to be returned where columns HasBackGarden, HasFrontGarden and HasSecureParking are true. How do I accomplish this?
If you want to restrict your result set to those records that satisfy all of the conditions, then you are correct that you should use .And instead of .Or, but in order to do that, you need to start with PredicateBuilder.True<ResidentialProperty>() instead of False.
This is because before any filters are added, the correct result set contains all records, and each subsequent filter restricts the result set. If you start with False, none of the records can ever possibly satisfy the predicate.
From your code it looks like you are operating on the var "query" which is an empty enumerable. Remember, what you have called query is the data set, the Where statement is the query on that data set. So the first query is on an empty data set and it looks like the additional queries from the foreach will just refine that empty set.
Also, you can just do .Where(q => q.HasWhatever) since those are bools.
I have an optional part of query that needs to be executed on a certain condition. Here is the example code:
int cat = 1;
int UserID = 12;
string qry = "select * from articles";
if(cat > 0)
qry += " where categoryID = " + cat;
if(UserID > 0)
qry += " AND userid = " + UserID; //The AND may be a WHERE if first condition is false
As you can see I have an if statement in the query. i am currently using Entity Framework and it does not support this kind of scenario. Is there an ORM out there that support this?
Edit
I tried to dummy down the query. But I have about 20 "IF" statements and the querys are very long.
The ORMs I was looking at were:
NHibernate
LLBLGen
Subsonic
I am open to any ORM. Thanks
As it was already mentioned here, LINQ allows to extend any query by simply adding more criteria to it.
var query =
from x in xs
where x==1
select x;
if (mustAddCriteria1)
query =
from x in query
where ... // criteria 1
select x;
if (mustAddCriteria2)
query =
from x in query
where ... // criteria 2
select x;
And so on. This approach works just perfectly. But likely, you know that compilation of LINQ queries is pretty expensive: e.g. Entity Framework can compile just about 500 relatively simple queries per second (see e.g. ORMBattle.NET).
On the other hand, many ORM tools support compiled queries:
You pass an IQueryable instance to some Compile method, and get a delegate allowing to execute it much faster later, because no recompilation would occur in this case.
But if we'd try to use this approach here, we immediately notice that our query is actually dynamic: IQueryable we execute each time might differ from the previous one. Presence of query parts there is determined by values of external parameters.
So can we execute such queries as compiled without e.g. explicit caching?
DataObjects.Net 4 support so-called "boolean branching" feature. It implies any constant boolean expression is evaluated during query compilation and its actual value is injected into SQL query as true boolean constant (i.e. not as parameter value or as an expression utilizing parameters).
This feature allows to generate different query plans dependently on values of such boolean expressions with ease. E.g. this code:
int all = new Random().Next(2);
var query =
from c in Query<Customer>.All
where all!=0 || c.Id=="ALFKI"
select c;
will be executed using two different SQL queries, and thus - two different query plans:
Query plan based on index seek (quite fast), if all==0
Query plan based on index scan (quite slow), if all!=0
Case when all==null, SQL query:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( CAST( 0 AS bit ) <> 0 ) OR( [a].[CustomerId] = 'ALFKI' ) );
Case when all==null, query plan:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Seek(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]), SEEK:([a].[CustomerId]=N'ALFKI') ORDERED FORWARD)
Second case (when all!=null), SQL query:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( CAST( 1 AS bit ) <> 0 ) OR( [a].[CustomerId] = 'ALFKI' ) );
-- Notice the ^ value is changed!
Second case (when all!=null), query plan:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Scan(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]))
-- There is index scan instead of index seek!
Note that almost any other ORM would compile this to a query utilizing integer parameter:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( #p <> 0 ) OR ( [a].[CustomerId] = 'ALFKI' ) );
-- ^^ parameter is used here
Since SQL Server (as well as most of databases) generates a single version of query plan for a particular query, it has the only option in this case - generate a plan with index scan:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Scan(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]), WHERE:(CONVERT(bit,[#p],0)<>(0) OR [DO40-Tests].[dbo].[Customers].[CustomerId] as [a].[CustomerId]=N'ALFKI'))
Ok, that was a "quick" explanation of usefulness of this feature. Let's return back to your case now.
Boolean branching allows to implement it in very simple fashion:
var categoryId = 1;
var userId = 1;
var query =
from product in Query<Product>.All
let skipCategoryCriteria = !(categoryId > 0)
let skipUserCriteria = !(userId > 0)
where skipCategoryCriteria ? true : product.Category.Id==categoryId
where skipUserCriteria ? true :
(
from order in Query<Order>.All
from detail in order.OrderDetails
where detail.Product==product
select true
).Any()
select product;
The example differs from yours, but it illustrates the idea. I used different model mainly to be able to test this (my example is based om Northwind model).
This query is:
Not a dynamic query, so you can safely pass it to Query.Execute(...) method to get it executed as compiled query.
Nevertheless each its execution will lead to the same result as if this would be done with "appending" to IQueryable.
this can be done using linq to sql...
IQueryable<Article> query = yourDataContext.Articles;
if (catId > 0)
query = query.Where(x => x.CategoryId == catId);
return query.ToList();
NHibernate supports this using the Criteria API:
ICriteria criteria = session.CreateCriteria<Article>();
if (cat > 0)
criteria.Add(Expression.Eq("categoryID", cat));
You can probably do this with any LINQ provider, but I know the LightSpeed ORM supports it:
var query = UnitOfWork.Articles;
if (cat > 0)
query = query.Where(a => a.CategoryId == cat);
I do this kind of thing in NHibernate all the time.
(I've done similar things in Rails. I'm kind of surprised that there are ORMs that don't support this.)
You can easily build queries in this way using NHibernate's HQL (Hibernate Query Language). It would be an almost identical implementation but I would personally use parameters.
public List<Article> GetCat(int cat)
{
string qry = "select ap from Article a";
if(cat > 0)
qry += " where a.categoryID = :cat";
IQuery query = session.CreateQuery(qry).SetInt32("cat",cat);
return query.List<Article>();
}
This returns a List<> of Article objects ready for use.
No love for LLBLGen? Well it can can do it too.
Using the 'adapter' style:
RelationPredicateBucket filters = new RelationPredicateBucket();
if (cat > 0)
filters.Predicate.Add(Article.Fields.CategoryID == cat);
if (userId > 0)
filters.Predicate.Add(Article.Fields.UserID == userId);
// And so on.
var adapter = new DataAccessAdapter();
var results = new EntityCollection<Article>(new ArticleFactory());
adapter.FetchEntityCollection(results, filters);
I would suspect most ORMs should be able to do this pretty easily.
You can use the Predicate Builder and LINQ to NHibernate to generate dynamic query's like this:
//using Predicate Builder
public List<Location> FindAllMatching(string[] filters)
{
var db = Session.Linq<Location>();
var expr = PredicateBuilder.False<Location>(); //-OR-
foreach (var filter in filters)
{
string temp = filter;
expr = expr.Or(p => p.Name.Contains(temp));
}
return db.Where(expr).ToList();
}
You get the advantage of Type Save Query's and Compiler check.
You can also use the same approach of predicate builder with Linq to Sql and Entity Framework.
EDIT: Added example.
It could be something like get all the locations matching N regions of the world, where the user select the regions he want to see, we don't know how many the user will select, we must build the (OR) expression on the fly, you can do something like:
public ActionResult Action(string[] filters)
{
/*This values are provided by the user, maybe its better to use
an ID instead of the name, but for the example is OK.
filters will be something like : string[] filters = {"America", "Europe", "Africa"};
*/
List<Location> LocationList = FindAllMatchingRegions(filters);
return View(LocationList);
}
public List<Location> FindAllMatchingRegions(string[] filters)
{
var db = Session.Linq<Location>();
var expr = PredicateBuilder.False<Location>(); //-OR-
foreach (var filter in filters)
{
string temp = filter;
expr = expr.Or(p => p.Region.Name == filter);
}
return db.Where(expr).ToList();
}
You can Nest Predicates for a complex scenarios like this:
If you want to do something like
p => p.Price > 99 &&
p.Price < 999 &&
(p.Description.Contains ("foo") || p.Description.Contains ("far"))
you can build:
var inner = PredicateBuilder.False<Product>();
inner = inner.Or (p => p.Description.Contains ("foo"));
inner = inner.Or (p => p.Description.Contains ("far"));
var outer = PredicateBuilder.True<Product>();
outer = outer.And (p => p.Price > 99);
outer = outer.And (p => p.Price < 999);
outer = outer.And (inner);
And use it like :
var pr = db.Products.Where(outer).ToList();
The Predicate Builder Source and examples are available at http://www.albahari.com/nutshell/predicatebuilder.aspx