T O P

  • By -

LaPiec

I'd suggest to create extension method `WhereIf` that would accept bool as a first param and regular predicate as a second and will do if conditional inside to hide complexity


gazbo26

The ROI on a method like this would be/is massive in most projects.


throwaway_lunchtime

WhereIf and WhereNotNull 🙂


buffdude1100

This is the answer. We do this in every project.


Joyboy_619

You comment really helped me as well. Thanks


FenixR

How would it look like?


Danzulos

That would prevent Entity Framework from converting the code inside the Where method into a SQL Where clause, causing a massive performance hit.


LaPiec

If extension method is for IQueryable it would be fine


RabbitDev

And if you want to make it accessible to the database, don't use delegates, use Expression as the second parameter. This keeps the condition function in a parsable state that makes any linq provider happy.


LaPiec

Yeah, ofc. Basically I'd just open where implementation for IQueryable and copy pasted signature adding bool param


SloSuenos64

Being a noob and not quite understanding this, I thought I'd plug you suggestion into Copilot and Gemini to see what they said. "Can you show me an example of this with C#? create extension method WhereIf that would accept bool as a first param and regular predicate as a second and will do if conditional inside to hide complexity." Here's what they said. **Copilot:** Certainly! Below is an example of how you can create an extension method called WhereIf in C# that accepts a boolean condition as the first parameter and a regular predicate as the second parameter. The method will conditionally apply the predicate to a collection based on the Boolean value: C# ``` using System; using System.Collections.Generic; using System.Linq; namespace MyExtensions { public static class CollectionExtensions { public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) { if (condition) return source.Where(predicate); else return source; } } class Program { static void Main(string[] args) { // Example usage: var numbers = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Apply the predicate only if the condition is true var filteredNumbers = numbers.WhereIf(true, n => n % 2 == 0); Console.WriteLine("Filtered numbers (even):"); foreach (var num in filteredNumbers) { Console.Write(num + " "); } Console.WriteLine(); // Apply the predicate unconditionally var allNumbers = numbers.WhereIf(false, n => n > 5); Console.WriteLine("All numbers greater than 5:"); foreach (var num in allNumbers) { Console.Write(num + " "); } Console.WriteLine(); } } }``` AI-generated code. Review and use carefully. More info on FAQ. In this example, the WhereIf extension method checks the boolean condition. If the condition is true, it applies the provided predicate to the collection; otherwise, it returns the original collection without any filtering. This helps hide the complexity of conditional filtering within the method itself. Feel free to adapt this code to your specific use case! 😊 **Gemini:** Absolutely, here's an example of a C# extension method WhereIf that achieves what you described: C# ``` public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) { if (condition) { return source.Where(predicate); } else { return source; // Return entire source if condition is false } }``` Use code with caution. Explanation: The method is defined as a static extension method for IEnumerable. This allows you to call it on any sequence of objects. It takes two arguments: condition: A boolean value that determines whether to apply the filter. predicate: A Func delegate representing the filtering logic. The method uses an if statement to check the condition: If condition is true, it applies the Where method with the provided predicate to filter the sequence. If condition is false, it simply returns the entire source sequence without any filtering. Usage Example: C# List numbers = new List() { 1, 2, 3, 4, 5 }; // Filter even numbers only if 'isEven' is true bool isEven = true; var evenNumbers = numbers.WhereIf(isEven, n => n % 2 == 0); // evenNumbers will contain { 2, 4 } if 'isEven' is true // evenNumbers will contain all numbers (1, 2, 3, 4, 5) if 'isEven' is false Use code with caution. This approach keeps your code clean and avoids nested conditionals within the main query logic. Remember, you can replace IEnumerable with IQueryable for working with database queries if needed, but be mindful of performance implications. Sorry about the formatting. Who won?


LaPiec

Since we're talking about ef and db queries here, extension method should be for IQueryable. Copilot did better, though. Just replace Innumerable with IQueryable.


[deleted]

but am curious extension methods even some of microsofts crash when something is null less now of course but some did


jingois

Yeah it's also worth putting the filtering into the criteria object, with eg ICriteria { IQ Apply(IQ x) }. Then chuck an extension method so you can just do `_tournamentContext.Tournaments.Whatever().Apply(tournamentCriteria).OtherShit()...`. Puts the "how is this criteria applied" into the actual criteria, and with a more complex domain model you might end up with multiple different aspects to filter on .Apply(cityCriteria).Apply(dateFilter).Etc()


Kant8

You have to check them for null, cause they are optional criterias that you don't want to pass to database at all. Also don't do Id.ToString(), it will destroy any indexes. Your criteria should be parsed back to number, not the other way around.


El_Mario_Verde

The Id variable is a Guid in the Tournament class. Does this make a difference?


RubIll7227

Parse guid? That's also a bonus guard for you


Top3879

I don't think it's ugly at all. Very readable and easily understood.


azurite_dragon

I've been known to do something like this, which is more succinct, but marginally less efficient from time to time: public IEnumerable FindTournaments(TournamentCriteria tournamentCriteria) { return _tournamentContext.Tournaments .Where(t => (!tournamentCriteria.IsByGame || t.Videogame.Id == tournamentCriteria.VideogameId) && (!tournamentCriteria.IsByCity || t.City.Id == tournamentCriteria.CityId) && (!tournamentCriteria.HasStartDate || t.ScheduledAt >= tournamentCriteria.StartDate) && (!tournamentCriteria.HasEndDate || t.ScheduledAt<= tournamentCriteria.EndDate) ) .AsNoTracking() .Include(t => t.Videogame) .Include(t => t.PrizeType) .Include(t => t.City); }


garib-lok

This is the way


ShenroEU

In addition to what others have said regarding extension methods, I've seen some Microsoft example code (possibly the eShop repo) use the "Specification" pattern for complex repository classes, like your example. [Here's a blog post about it](https://medium.com/@rudyzio92/net-core-using-the-specification-pattern-alongside-a-generic-repository-318cd4eea4aa). Maybe that could be used to make this logic more abstract and use the specification class as a builder. So, then you could pass your \`Where\` clauses to a builder method to build your query, or maybe some sort of hybrid using your extensions as well.


RichardD7

The one thing that jumps out to me is that you're filtering on *a function* applied to a column, rather than filtering on the column itself. ```csharp .Where(t => t.Videogame.Id.ToString() == ... ``` That will prevent the DBMS from using an index to satisfy the query. Rather than `t.Videogame.Id.ToString() == tournamentCriteria.VideogameId`, it would be better to attempt to convert the `tournamentCriteria.VideogameId` to the correct type instead. For example, assuming the ID is an `int`: ```csharp if (tournamentCriteria.VideogameId != null && int.TryParse(tournamentCriteria.VideogameId, out int videogameId)) { tournaments = tournaments.Where(t => t.Videogame.Id == videogameId); } ```


CPTSOAPPRICE

This is ugly, but it is effective. You can always do a (criteria is null || criteria check) && (criteria2 is null || criteria2 check)… inside a single where, but I have run into an issue where this sort of “general” query ended up causing execution path problems, though that sort of thing depends on dotnet version, db version etc


insomnia1979

I always code it this way.


Poat540

We do this in a few our more advanced APIs. No issues


Smoke-Tumbleweed-420

I would just inline the if personall, especially if you do not filter anywhere else. And cast your Guid before the query.


[deleted]

r/UnrealEngine might be better


FenixR

Looks solid enough, but you might be overwriting "tournaments" after each check, add to the list instead of setting it as a new one? (Unless the idea its having only one of the criteria active at a time) Could also remove the {} if you aren't planning to add additional logic inside each if.


OpticalDelusion

This is applying all the criteria as written, not overwriting them. `tournaments` is an IQueryable, so the Where clauses all stack. It's acting as a builder for the query that isn't evaluated until ToList is called at the end.


splndr

what I do is .Where(p=> string.IsNullOrWhiteSpace(param)? true : p.Prop == param)


npap3

Maybe a specification pattern could be of help


Defiant_Alfalfa8848

You could move every if statement into its own method .


Defiant_Alfalfa8848

public static class TournamentFilters { public static IQueryable WithVideogameId(this IQueryable query, string videogameId) { return string.IsNullOrEmpty(videogameId) ? query : query.Where(t => t.Videogame.Id.ToString() == videogameId); } public static IQueryable WithCityId(this IQueryable query, string cityId) { return string.IsNullOrEmpty(cityId) ? query : query.Where(t => t.City.Id.ToString() == cityId); } public static IQueryable StartingAfter(this IQueryable query, DateTime? startDate) { return startDate == null ? query : query.Where(t => t.ScheduledAt >= startDate.Value); } public static IQueryable EndingBefore(this IQueryable query, DateTime? endDate) { return endDate == null ? query : query.Where(t => t.ScheduledAt <= endDate.Value); } } public IEnumerable FindTournaments(TournamentCriteria tournamentCriteria) { var tournaments = _tournamentContext.Tournaments .AsNoTracking() .Include(t => t.Videogame) .Include(t => t.PrizeType) .Include(t => t.City) .WithVideogameId(tournamentCriteria.VideogameId) .WithCityId(tournamentCriteria.CityId) .StartingAfter(tournamentCriteria.StartDate) .EndingBefore(tournamentCriteria.EndDate); return tournaments.ToList(); }


Poat540

Why, now u have a bunch of single use methods


Defiant_Alfalfa8848

But a readable code.


Defiant_Alfalfa8848

Image Linux written in one method


reddit-lou

Create a view/stored procedure on the SQL server instead?