How can I embed optional Where parameters in a Linq to EF statement? - entity-framework

Let's say we have a description field on my form with optional check boxes. The check boxes represent which fields to search when doing the lookup. Right now I have a matrix of look ups that call their unique version of where clause. It works but I think it smells a bit.
Here is an excerpt
// Look for part numbers decide how many fields to search and use that one.
// 0 0 X
if (!PartOpt[0] && !PartOpt[1] && PartOpt[2])
{
query = query.Where(p => (p.PartNumAlt2.Contains(partSearchRec.inventory.PartNum)));
}
// 0 X 0
if (!PartOpt[0] && PartOpt[1] && !PartOpt[2])
{
query = query.Where(p => (p.PartNumAlt.Contains(partSearchRec.inventory.PartNum)));
}
// 0 X X
if (!PartOpt[0] && PartOpt[1] && PartOpt[2])
{
query = query.Where(p => (p.PartNumAlt.Contains(partSearchRec.inventory.PartNum)
|| p.PartNumAlt2.Contains(partSearchRec.inventory.PartNum)));
}
// X 0 0
if (PartOpt[0] && !PartOpt[1] && !PartOpt[2])
{
query = query.Where(p => (p.PartNum.Contains(partSearchRec.inventory.PartNum)));
}
. . .
This goes on for a while and seems to be prone to coding errors. In each case we are looking for the same information in any of the selected fields. If I was doing this in SQL I could simply build up the WHERE clause as needed.

Once again I rubber ducked my way to an answer. Rather than throw the question away, here is what I came up with. Is it efficient?
if (partSearchRec.optPartNum || partSearchRec.optAltPartNum1 || partSearchRec.optAltPartNum2)
{
query = query.Where(p => (
(partSearchRec.optPartNum && p.PartNum.Contains(partSearchRec.inventory.PartNum))
|| (partSearchRec.optAltPartNum1 && p.PartNumAlt.Contains(partSearchRec.inventory.PartNum))
|| (partSearchRec.optAltPartNum2 && p.PartNumAlt2.Contains(partSearchRec.inventory.PartNum))));
}
Basically if any of the check boxes are set we will execute the query. Each line of the query will be processed only if the check box was checked. If the left side of an AND is false it doesn't process the right.
This is an aera that Delphi's with statement would be handy. I also learned that you can't use an array inside the LINQ statement.

Related

How to sort on DB side, if entities are not connected via Navigation (since it's not possible)

I want to have my EfCore query translated into the following SQL query:
select
c.blablabla
from
codes c
left join lookups l on c.codeId = l.entityid and l.languageCode = <variable - language code of current thread> and l.lookuptype = 'CODE'
where
..something..
order by
l.displayname
Note: tables 'codes' and 'lookups' are not connected! 'lookups' contains a lot of different lookup data in different languages!
I am stuck into limitations of EfCore (like 'NavigationExpandingExpressionVisitor' failed). I don't want to make in-memory filtering, it looks silly to me... Am I missing something obvious?
In perspective, I'd like to make universal method to help sort by displayname (or other lookup name) for different kind of entities - not only codes.
Seems like I figured it out. If there's a better approach - please let me know:
protected override IQueryable<FixCode> SortByDisplayName(IQueryable<FixCode> queryable, string languageCode = null)
{
return queryable
.GroupJoin(
DbContext.FixCodeValues.Where(x =>
x.DomainId == CentralToolConsts.Domains.CENTRAL_TOOLS
&& x.CodeName == CentralToolsFieldTypes.CODE_ORIGIN
&& (x.LanguageCode == languageCode || x.LanguageCode == CentralToolsDbLanguageCodes.English)),
//TODO: this will be a 'selector' parameter
code => code.CodeOriginId,
codeOrigin => codeOrigin.StringValue,
(c, co) => new
{
Code = c,
CodeOrigin = co
}
)
.SelectMany(
x => x.CodeOrigin.DefaultIfEmpty(),
(x, codeOrigin) => new { Code = x.Code, CodeOrigin = codeOrigin }
)
.OrderBy(x => x.CodeOrigin.ShortName)
.Select(x => x.Code);
}

LINQ to Entities query to verify all rows exist

I have a need to verify that all the rows in a given set exist in the database and the current user has access to all the rows. I'd like to do this in a single query to the database, something like a .All() query but I can't quite come up with the right syntax (maybe it's not possible).
The iterative version of the code would look like:
bool canAccess;
foreach(var taskId in taskIds)
{
canAccess = await DataContext.WorkTasks.AnyAsync(wt => wt.DealerId == dealerId && wt.Id == taskId);
if(!canAccess) break;
}
I was thinking about something like:
var canAccess = await DataContext.WorkTasks.AllAsync(wt => wt.DealerId == dealerId && taskIds.Contains(wt.Id));
But I don't think that's what I want. Can this be done using LINQ?
Something like this:
var dbCount = await DataContext.WorkTasks.Where(wt => wt.DealerId == dealerId && taskIds.Contains(wt.Id)).Count();
Will send all your IDs to the server and count the matching rows.
To combine into a single query:
var q = DataContext.WorkTasks.Take(0); // setup q to right type
foreach (var taskId in taskIds)
q = q.Concat(DataContext.WorkTasks.Where(wt => wt.Id == taskId && wt.DealerId == dealerId));
var canAccess = (taskIds.Count() == q.Count());

Linq to Entities: multiple criteria query for one table

using (WinFileContextContainer c = new WinFileContextContainer())
{
IQueryable<File> dbfiles = (from f in c.File
where //(f.Category.Any((category => category.name == categoryname)) &&
f.alive //&&
//f.description == "" &&
//f.Category.Count == 1)
select f);
// the rest....
}
The query works only as it is now - I left just one criteria (the rest is in comment sections). But I want the other criteria to be taken into account too. I tried with multiple "where"s :D or all the criteria in brackets with one "where", but still no success. I'm kinda new to LINQ so any help is appreciated. Thanks in advance !
Finally got it to work:
IQueryable<File> dbfiles =
c.File.Where(f => f.Category.Any(cat => cat.name == categoryname) &&
f.alive &&
f.description == null &&
f.Category.Count == 1);

how to change multiple function arguments into coffescrpt

here is the javascript:
$inputor.on("keyup.inputor", $.proxy(function(e) {
var stop_key = e.keyCode == 40 || e.keyCode == 38
lookup = !(this.view.isShowing() && stop_key)
if (lookup) this.lookup()
},this))
how can i translate it into coffesscript? the first argument of a function like $.proxy is a function and still have a second one.
my solution is assign a variable for the first argument, the function, and poss it to $.proxy.
but i want a better solution.
coffeescript:
??????
Instead of using the jQuery.proxy function, you could use the CoffeeScript fat arrow => since the context you're trying to use is this
$inputor.on "keyup.inputor", (e) =>
stop_key = e.keyCode == 40 || e.keyCode == 38
lookup = !(#view.isShowing() && stop_key)
#lookup() if lookup

Entity Framework + conditionally appended Where() clauses

This is driving me nuts. What am I missing here. I'm using EF and if I have code like the following:
using (LexiconEntities ctx = new LexiconEntities())
{
var query = from w in ctx.Words
select new WordEntryDataModel
{
Word = w.Anagram,
NumOfAnagrams = w.NumAnagrams.Value,
Length = w.Length.Value,
...
};
SearchCriterion c1 = new SearchCriterion();
SearchCriterion c2 = new SearchCriterion();
c1.MinValue = 3;
c1.MaxValue = 3;
c2.MinValue = 4;
c2.MaxValue = 4;
query = query.Where(w => w.Length >= c1.MinValue && w.Length <= c1.MaxValue);
query = query.Where(w => w.NumOfAnagrams >= c2.MinValue && w.NumOfAnagrams <= c2.MaxValue);
...
}
And when I debug the query, I get the proper results (8 records). This also works as expected in Linqpad (which frickin' rocks).
But if I construct the search criteria as a List of criterion objects, and I dynamically add on Where() clauses by iterating over the search criteria as follows:
foreach (SearchCriterion c in criteria.SearchCriteria)
{
switch (c.Type)
{
case SearchCriterionType.WordLength:
query = query.Where(w => w.Length >= c.MinValue && w.Length <= c.MaxValue);
break;
case SearchCriterionType.NumberOfAnagrams:
query = query.Where(w => w.NumOfAnagrams >= c.MinValue && w.NumOfAnagrams <= c.MaxValue);
break;
...
case SearchCriterionType.NumberOfVowels:
query = query.Where(w => w.NumOfVowels >= c.MinValue && w.NumOfVowels <= c.MaxValue);
break;
}
}
...
I get the totally different (and incorrect) results. I've debugged the switch statement and my search criteria has two properly constructed criterion objects set to correct values. There's something about the conditionally added where clauses that my query doesn't like.
What am I doing wrong?
Closure. Assign c to a local variable within the loop. Also see my SO answer here