To apply mulitple criteria in lambda expression in c# - c#

I have two main tables Listings and Place . In listing table there is a field PlaceId which referes to a Place entity/row/object . I want to query on both tables so that i get both of them like this .
var query = context.Listings
.Include("Place")
.Where(l => l.Place.TypeId == Type.Ro)
.OrderBy(l => l.Id).ToList();
after this now i want to put some filter on this query , here is the condition .
i got only a string like this var filter = "1,2,4"; . Now i want to filter on listing to gett all these listing where bedroom is equal to 1 OR 2 OR 4 .
What i have done
string minBeds = "1,2,4";
foreach (var item in minBeds.Split(','))
{
int minBed = int.Parse(item);
query = query.Where(l=>l.Place.Bedroom == minBed).ToList();
}
But doing this is giving me Zero result.

The problem with the way you're filtering it. After the first pass, you're filtering out everything except where Bedroom == 1, on the second pass you're filtering out everything except where Bedroom == 2, but since the only items in the list have Bedroom == 1, you won't have anything in the result set.
The solution is to use the conventional C# || operator:
query = query.Where(l => l.Place.Bedroom == "1" ||
l.Place.Bedroom == "2" ||
l.Place.Bedroom == "4");
Or if you want to be more flexible, use the Contains method:
string[] minBeds = "1,2,4".Split(',');
query = query.Where(l => minBeds.Contains(l.Place.Bedroom));
Note if Bedroom is an integer, you'll need to convert the input to an appropriate type first:
var minBeds = "1,2,4".Split(',').Select(int.Parse);
query = query.Where(l => minBeds.Contains(l.Place.Bedroom));
Also note, I've eliminated the ToList here. Unless you need to access items by index and add / remove items from the result collection, it's most likely just a waste of resources. You can usually rely on Linq's native laziness to delay processing to query until you really need the result.

Related

how to take 100 records from linq query based on a condition

I have a query, which will give the result set . based on a condition I want to take the 100 records. that means . I have a variable x, if the value of x is 100 then I have to do .take(100) else I need to get the complete records.
var abc=(from st in Context.STopics
where st.IsActive==true && st.StudentID == 123
select new result()
{
name = st.name }).ToList().Take(100);
Because LINQ returns an IQueryable which has deferred execution, you can create your query, then restrict it to the first 100 records if your condition is true and then get the results. That way, if your condition is false, you will get all results.
var abc = (from st in Context.STopics
where st.IsActive && st.StudentID == 123
select new result
{
name = st.name
});
if (x == 100)
abc = abc.Take(100);
abc = abc.ToList();
Note that it is important to do the Take before the ToList, otherwise, it would retrieve all the records, and then only keep the first 100 - it is much more efficient to get only the records you need, especially if it is a query on a database table that could contain hundreds of thousands of rows.
One of the most important concept in SQL TOP command is order by. You should not use TOP without order by because it may return different results at different situations.
The same concept is applicable to linq too.
var results = Context.STopics.Where(st => st.IsActive && st.StudentID == 123)
.Select(st => new result(){name = st.name})
.OrderBy(r => r.name)
.Take(100).ToList();
Take and Skip operations are well defined only against ordered sets. More info
Although the other users are correct in giving you the results you want...
This is NOT how you should be using Entity Framework.
This is the better way to use EF.
var query = from student in Context.Students
where student.Id == 123
from topic in student.Topics
order by topic.Name
select topic;
Notice how the structure more closely follows the logic of the business requirements.
You can almost read the code in English.

Order of Execution of Condition in a Where clause

I have the following statement with an Or condition. I am looking what would be the result when both conditions are true.
var result = db.Result.FirstOrDefault(x=>(x.ID==50||x.ID==60)&&x.Name="XYZ");
In a case where I have a row in the result table where both the conditions are true, i.e; x.ID=50 and x.ID=60. What would be the result of the query?
I have already tested to get the result in a test environment. But I wanted to make sure that the order of execution would always remain the same no matter what the size of the database is. As I have read that the where clause uses some sort of indexing for faster retrieval, what would be the course of execution of this statement.
The provided query is just a sample and the name ID has nothing to do with the unique identified of a table.
Question
How would the check be performed on the database? I would expect a result where it first checks if ID ==50 and on failure check if ID==60. If this is my expected result, would the query given above achieve my task?
Update after answer
I find it necessary to give a more clearer example so that the question is more understandable. (If this update makes the existing answers invalid, I am really sorry)
var result = db.result.firstordefault(x=>(x.foreignkeyid == someval|| foreignkeyid == 123)&& x.Name=="XYZ");
And my database sample
ID foreignkeyid Name
1 123 XYZ
2 somevalue XYZ
3 anothervalue XYZ
In this case when the query is executed the result would return the row with ID==1 , but I want row wiht ID==2 to be returned.
Worst case attempt to achieve the result
var result = new Result();
result =db.Result.firstordefault(x=>x.ID==somevalue&&x.name==xyz);
if(result==null)
var result = db.firstordefault(x=>x.ID ==123&& x.name==xyz);
Given this example of yours:
var result = db.result.firstordefault(x=>(x.foreignkeyid == someval|| foreignkeyid == 123)&& x.Name=="XYZ");
In which you want to prioritize the result where fk = someval (fk: foreign key), you can do the following:
db.set.OrderBy(x => x != someval) // false actually comes before true
.ThenBy(x => x != val2ndPriority)
.FirstOrDefault(x => (x.fk == someval ||
x.fk == val2ndPriority ||
x.fk == leastPriorityVal) &&
x.Name == "XYZ");
If you have a lot of "prioritized fk values", or if they are unknown at compile time, you can do:
var orderedEnum = db.set.OrderBy(x => x.Id);
foreach (var fk in fksByPriority)
orderedEnum = orderedEnum.ThenBy(x => x != fk);
var result = orderedEnum.FirstOrDefault(x => fksByPriority.Contains(x.fk) &&
x.Name == "XYZ");
How I would prefer it to look like:
Another different approach would be to get all possibly-relevant records and then run similar logic outside of the DB (your db Linq queries normally run smartly right inside the db):
var results = db.set.Where(x => x.Name == "XYZ" &&
fks.Contains(x.fk)).ToArray();
var highestPriorityResult =
results.OrderBy(x => fksByPriority.IndexOf(x.fk)).FirstOrDefault();
On a final note, I wish to say that your problem indicates a possibly flawed design. I can't imagine why you'd have this filtering-with-priority-foreign-key issue.

Dynamic Linq query not working as expected - what am I doing wrong?

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.

Displaying specific elements of a collection's subcollection

I have a List collection that contains a List subcollection as a property within it, and I want to filter out items in that subcollection based on the value of certain properties.
To simplify, I'll call the main collection THING and the subcollection SUBTHING. They are different types. THINGS can have 1 to many SUBTHINGS. SUBTHING has 2 properties I want to filter by, PROP1 should equal 1 (it can equal 1,2,3) and PROP2 should not be NULL (it can contain a string).
So when I use a query like the one below it seems to give me what I want (though I'm not sure All() is doing what I expect):
search = from c in search
where c.SUBTHING.All(s=>s.PROP1==1)
select c;
Then I get suspicious when I add the other property:
search = from c in search
where c.SUBTHING.All(s=>s.PROP1==1 && s.PROP2 != NULL)
select c;
And I get THINGS that have PROP2 as Null.
When I switch to Any() I lose all filtering on SUBTHING and it shows SUBTHINGS where PROP1 = 1,2,3 and where PROP2 is NULL and not NULL.
What I'm trying to get is a collection that lists all THING IDs and then lists the Name of all SUBTHINGS, sort of like this:
THING.ID
SUBTHING.Name
SUBTHING.Name
THING.ID
SUBTHING.Name
SUBTHING.Name
Is this possible to also filter SUBTHINGS while filtering THINGS with LINQ since THING and SUBTHING are two different types?
Try something like this:
search =
from c in search
where c.SUBTHING.All(s=>s.PROP1==1 && s.PROP2 != NULL)
select new {
ThingId = c.ThingID,
Something = c.SomeThing.Select(x=>x.Name)
};
To apply filter on subitems try:
from product in products
where product.productid == 1
from image in product.productimages
where image.ismainimage
select image.imagename
From : 101 linq queries
One way is using Enumerable.Where and an anonymous type:
var result = from thing in search
from subthing in thing.subthings
where subthing.prop1 == 1 && subthing.prop2 != null
select new {ID = thing.ID, Name = subthing.Name};
foreach(var x in result)
{
Console.WriteLine("ID={0} Name{1}", x.ID, x.Name);
}
You need a projection as you are querying over the parent entity (THING) but in the result set you want to only have a subset of its SUBTHINGS.
You can do it e.g. in the following way:
class Thing
{
Thing(Thing original, IEnumerable<Subthing> subthings)
{
// Initialize based on original and set the collection
//
...
}
}
and then run the query like this:
var filtered = from c in search
select new Thing(c, c.Subthings.Where(x => x.PROP1 == 1 && x.PROP2 != null))
I'm not sure any of these answers really give you what you want (although they're close). From my understanding, you want a list of THINGs in which at least 1 SUBTHING has the values you're interested in (in this case, Prop1 == 1 and Prop2 != null). There are a few options here, just depends on whether you're working from a THING or a SUBTHING perspective.
Option 1: THING approach.
You're looking at any THING that has a SUBTHING with your condition. So:
var result = from thing in search
where thing.Subthings.Any(tr => tr.Prop1 == 1 && tr.Prop2 != null)
select new { ID = thing.ID, Names = thing.Subthings.Where(tr => tr.Prop1 == 1 && tr.Prop2 != null) };
Option 2: SUBTHING approach.
You're looking at ALL SUBTHINGs and finding the ones where the condition is met, grouping by the ID at that point.
var result = from thing in search
from sub in thing.Subthings
where sub.Prop1 == 1 && sub.Prop2 != null
group sub by thing.id into sg
select new { ID = sg.Key, Names = sg.Select(tr => tr.Name) };
I like this approach just a little better, but still room for improvement. The reason I like this is because you find the SUBTHINGs first, and only then will it pull the THING that's associated with it (instead of first having to find if any SUBTHING matches the criteria, and THEN selecting it).
Option 3: Hybrid approach.
This is a little of both. We're going to select from SUBTHINGs either way, so might as well just perform the select. Then, if any of the projected subcollections have any elements, then we return our THING with the Names.
var result = from thing in search
let names = thing.Subthings
.Where(sub => sub.Prop1 == 1 && sub.Prop2 != null)
.Select(sub => sub.Name)
where names.Any()
select new { ID = thing.ID, Names = names };
Cleanest option, in my opinion. The Any extension method on a collection without any parameters will return true if there are any items in the collection. Perfect for our situation.
Hope that helps, let us know what you came up with.

Linq Union: How to add a literal value to the query?

I need to add a literal value to a query. My attempt
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
In the above example, I get an error:
"Local sequence cannot be used in LINQ to SQL implementation
of query operators except the Contains() operator."
If I am using Entity Framework 4 for example, what could I add to the Union statement to always include the "seed" ID?
I am trying to produce SQL code like the following:
select distinct ID
from product
union
select 0 as ID
So later I can join the list to itself so I can find all values where the next highest value is not present (finding the lowest available ID in the set).
Edit: Original Linq Query to find lowest available ID
var skuQuery = Context.Products
.Where(p => p.sku > skuSeedStart &&
p.sku < skuSeedEnd)
.Select(p => p.sku).Distinct();
var lowestSkuAvailableList =
(from p1 in skuQuery
from p2 in skuQuery.Where(a => a == p1 + 1).DefaultIfEmpty()
where p2 == 0 // zero is default for long where it would be null
select p1).ToList();
var Answer = (lowestSkuAvailableList.Count == 0
? skuSeedStart :
lowestSkuAvailableList.Min()) + 1;
This code creates two SKU sets offset by one, then selects the SKU where the next highest doesn't exist. Afterward, it selects the minimum of that (lowest SKU where next highest is available).
For this to work, the seed must be in the set joined together.
Your problem is that your query is being turned entirely into a LINQ-to-SQL query, when what you need is a LINQ-to-SQL query with local manipulation on top of it.
The solution is to tell the compiler that you want to use LINQ-to-Objects after processing the query (in other words, change the extension method resolution to look at IEnumerable<T>, not IQueryable<T>). The easiest way to do this is to tack AsEnumerable() onto the end of your query, like so:
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().AsEnumerable().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
Up front: not answering exactly the question you asked, but solving your problem in a different way.
How about this:
var a = Products.Select(p => p.sku).Distinct().ToList();
a.Add(0);
a.Dump(); // LinqPad's way of showing the values
You should create database table for storing constant values and pass query from this table to Union operator.
For example, let's imagine table "Defaults" with fields "Name" and "Value" with only one record ("SKU", 0).
Then you can rewrite your expression like this:
var zero = context.Defaults.Where(_=>_.Name == "SKU").Select(_=>_.Value);
var result = context.Products.Select(p => p.sku).Distinct().Union(zero).ToList();

Categories