How to simplify the next query to database:
public Plan? Get(DateTime now)
{
return context.Plans
.Where(x => IsActivePlan(x, now)) // 1 condition
.Where(x => IsPlolongingPlan(x, now)) // 2 condition
.OrderBy(x => x.StartedAt)
.FirstOrDefault();
}
What I need:
If there are objects after 1 condition, then execute "OrderBy" and return the first element. If there are no objects, then go to the 2 condition, execute "OrderBy" and return "FirstOrDefault". I don't want the objects to be taken at once by two conditions.
Thank you
something like this
public Plan? Get(DateTime now)
{
return context.Plans.Where(x => IsActivePlan(x, now)).OrderBy(x => x.StartedAt).FirstOrDefault()
?? context.Plans.Where(x => IsPlolongingPlan(x, now)).OrderBy(x => x.StartedAt).FirstOrDefault();
}
because you don't want two conditions been true at once you have to:
return context.Plans
.Where(x => (IsActivePlan(x, now) && !IsPlolongingPlan(x, now)) ||
(!IsActivePlan(x, now) && IsPlolongingPlan(x, now)))
.OrderBy(x => x.StartedAt)
.FirstOrDefault();
You can check before if exist any
Like this:
var result = null;
if (context.Plans.Any(x => IsActivePlan(x, now)))
{
result = context.Plans.Where(x => IsActivePlan(x, now))
.OrderBy(x => x.StartedAt)
.FirstOrDefault();
}
else
{
result = context.Plans.Where(x => IsPlolongingPlan(x, now))
.OrderBy(x => x.StartedAt)
.FirstOrDefault();
}
Related
I am having trouble converting the list type from group by statement back to the type I can use.
I know group by adds a key to the list but how do I get rid of it and it my case convert it back to List<UserAnswers> ? Can anybody provide some insights or point me in the right direction?
public async Task<List<UserAnswers>>
GetQuestionsWellAsync(string UserName) // get questions
{
return await ctx.UserAnswers.Where(x => x.UserId == UserName && x.Correct == 1).
GroupBy(c => c.QuestionId).Where(grp => grp.Count() > 2).ToListAsync();
You can use .SelectMany() to flatten your groupings back out into a single aggregated list.
public async Task<List<UserAnswers>> GetQuestionsWellAsync(string UserName)
{
return await ctx.UserAnswers
.Where(x => x.UserId == UserName && x.Correct == 1)
.GroupBy(c => c.QuestionId)
.Where(grp => grp.Count() > 2)
.SelectMany(grp => grp)
.ToListAsync();
}
Based on your update, it seems like EF cannot translate the GroupBy + Where Count() expression. You may need to project the grouped data into memory first and then filter.
public async Task<List<UserAnswers>> GetQuestionsWellAsync(string UserName)
{
return (await userAnswers
.Where(x => x.UserId == UserName && x.Correct == 1)
.GroupBy(c => c.QuestionId)
.ToListAsync())
.Where(grp => grp.Count() > 2)
.SelectMany(grp => grp)
.ToList();
}
Currently I am doing a keyword search on the Plates table (Name column) but also have a Search (searching on SearchTerm column) table which contains Plat Id's that I also want to search and return the corresponding platforms.
The code below works but I'd like to simplify the logic using an .Include statement if possible although I'm not quite sure how. Any help would be greatly appreciated.
if (!string.IsNullOrEmpty(request.Keyword))
{
var searchTermPlateIds = await _db.Search
.Where(x=> x.SearchTerm.ToLower().Contains(request.Keyword.Trim().ToLower()))
.Select(x => x.PlatformId)
.ToListAsync(ct);
var plateFromPlateIds = await _db.Plate
.OrderBy(x => x.Name)
.Where(x => searchTermPlateIds.Contains(x.Id) && x.Status != PlateStatus.Disabled)
.ToListAsync(ct);
plates = await _db.Plates
.OrderBy(x => x.Name)
.Where(x => !string.IsNullOrEmpty(request.Keyword.Trim()) && x.Name.ToLower().Contains(request.Keyword.Trim().ToLower()) && x.Status != PlateStatus.Disabled)
.ToListAsync(ct);
plates = plates.Union(platesFromPlateIds).ToList();
}
Remember simple thing, Include ONLY for loading related data, not for filtering.
What we can do here - optimize query, to make only one request to database, instead of three.
var query = _db.Plates
.Where(x => x.Status != PlateStatus.Disabled);
if (!string.IsNullOrEmpty(request.Keyword))
{
// do not materialize Ids
var searchTermPlateIds = _db.Search
.Where(x => x.SearchTerm.ToLower().Contains(request.Keyword.Trim().ToLower()))
.Select(x => x.PlatformId);
// queryable will be combined into one query
query = query
.Where(x => searchTermPlateIds.Contains(x.Id);
}
// final materialization, here you can add Includes if needed.
var plates = await query
.OrderBy(x => x.Name)
.ToListAsync(ct);
I'm struggling to transform this piece of code in a single LINQ expression.
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.FirstOrDefault();
if (x == null)
{
x = values
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value)
.FirstOrDefault();
}
Basically I have a list of objects. Each objects contains a list of Column objects and a Value.
I want to filter the object that contains a specific Column object (based on Code and Value), but if this combination does not exist, I want to fall back to the entity that contains a list of Column objects all having Code equals to string.Empty (a wild card).
I have tried different approches like the following but without success:
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value)
? v.Columns.Any(c => c.Code == code && c.Value == value)
: v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value)
.FirstOrDefault();
I suggest Concat both alternatives:
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.Concat(values // second alternative if 1st returns empty cursor
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value))
.FirstOrDefault();
Edit: You can simplify the query (see CSharpie's comment) by extracting .Select(v => v.Value) into
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Concat(values // second alternative if 1st returns empty cursor
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code)))
.Select(v => v.Value)
.FirstOrDefault();
You can use DefaultIfEmpty(fallback):
var fallBackValue = values
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value)
.FirstOrDefault();
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.DefaultIfEmpty(fallBackValue)
.First(); // FirstOrDefault not nessesary anymore;
This has the advantage that you can even select multiple without breaking the logic, so the fallback value would still be returned if Take(3)(for example) would not return any items.
It is also efficient since the fallback value will be calculated independently of the main query and could be returned from a property, so that it needs to be initialized only once.
Another (similar option) is the null coalescing operator(if Value is a reference type):
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.FirstOrDefault() ?? fallBackValue;
But i'd prefer the first because it can be chained and also modified easily(i.e. Take(x)).
I have this C# code that works, but I'd like to be able to choose the Agency if a person is in it all in the query. Is there a way to do that all in the query?
var retVal = new List<Agency>();
var items=_db.Agencies
.Include(x => x.AgencyMembers.Select(y => y.Person))
.Where(w => w.NationId == User.NationId).ToList();
foreach (var agency in items)
{
if(agency.AgencyMembers.Any(c=>c.Person.Id==personId))
retVal.Add(agency);
}
return retVal;
You should be able to just add that predicate to your query.
return _db.Agencies
.Include(x => x.AgencyMembers.Select(y => y.Person))
.Where(w => w.NationId == User.NationId)
.Where(agency => agency.AgencyMembers.Any(c=>c.Person.Id==personId))
.ToList();
Depending what navigation properties you have, you may be able to simplify it by starting from the person.
return _db.People
.Single(p => p.Id == personId)
.Agencies
.Where(w => w.NationId == User.NationId)
.ToList();
You can try this:
var items=_db.Agencies
.Include(x => x.AgencyMembers.Select(y => y.Person))
.Where(agency=> agency.NationId == User.NationId && agency.AgencyMembers.Any(c=>c.Person.Id==personId))
.ToList();
What I'm doing now is getting my SQL database table: IsAcross varchar(45) into my if/else statement. My IsAcross table only consist of YES and NO.
So now I want to take out only select YES statement from SQL Server, so therefore I have put the thingy in the whole list first. Then I use if/else statement to extract out the YES but how am I supposed to do that?
Example: I have a total of 7 items in a list, 4 yes 3 no. I want to take out the all the 4 yes only. Something like that:
ViewModels.WordSearchVM wsvm = new ViewModels.WordSearchVM();
wsvm.ActivityID = id;
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Select(m => m.IsAcross)
.AsEnumerable()
.ToList();
if (results = "yes")
{
else
}
As far as I understand your problem, you can forget your if statement and simply extend the .Where part:
.Where(m => m.ActivityID == id && m.results=="yes")
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Where(m => m.IsAcross)
.AsEnumerable()
.ToList();
//OR
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Where(m => m.IsAcross == "YES")
.AsEnumerable()
.ToList();
var results = db.CrossPuzzles
.Where(m => m.ActivityID == id)
.Select(m => m.IsAcross)
.Where(x => x == "YES") // filter to "YES"
.ToList();
if (results.Count > 0)
// YES
else
// NO
This what you're looking for?