I have this query:
if(!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(name))
{
return this.context.Table.Where(
x => EF.Functions.Contains(x.Code, $"\"{code}\"")
&& EF.Functions.Contains(x.Name, $"\"{name}\""));
}
else if(string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(name))
{
return this.context.Table.Where(x => EF.Functions.Contains(x.Name, $"\"{name}\""));
}
else
{
return this.context.Table.Where(x => EF.Functions.Contains(x.Code, $"\"{code}\""));
}
I tried to do it again using the ternary operators then check if the string is not null or empty and if so add the where clause or not
I tried such a thing but obviously expects that after the "?" there is the alternative of ":"
return this.context.Table.Where(
x => !string.IsNullOrEmpty(code)
? EF.Functions.Contains(x.Code, $"\"{code}\"")
&& !string.IsNullOrEmpty(name)
? EF.Functions.Contains(x.Name, $"\"{name}\""));
Since unlike the example in my case I have to check 8 different input parameters that if not passed must not be used in the where for the controls, I wanted to avoid filling the code of many if cases and rewriting the query n times for the different combinations, is there a way or should I resign myself?
You can just return true for any you don't want to check for like below
!string.IsNullOrEmpty(code) ? EF.Functions.Contains(x.Code, $"\"{code}\"") : true;
This means if the string is null or empty then it will return true which should provide the behaviour you're expecting.
Do not use ternary operators for combining query. EF will create non optimal SQL.
Usually such task is done in the following way:
var query = this.context.Table.AsQueryable();
if (!string.IsNullOrEmpty(code))
{
query = query.Where(
x => EF.Functions.Contains(x.Code, $"\"{code}\""));
}
if (!string.IsNullOrEmpty(name))
{
query = query.Where(
x => EF.Functions.Contains(x.Name, $"\"{name}\""));
}
var result = query.ToList();
Related
I have two conditions, they may be both set or just one of them. If both set to a value a linq where statement shall be triggered having AND as boolean operator else the linq where statement shall include only the filter based on the set condition.
For example:
both conditions set: linq.Where(condition1 && condition2)...
only condition1 set: linq.Where(condition1)..
only condition2 set: linq.Where(condition2)..
This is not allowed: linq.Where(condition1 || condition2) because the filtering shall be very accurate
Now my problem is how do I write ONE linq statement that considers all those requirements? Thanks in advance
EDIT:
Predicates might look like this: x => x > 10 or x => x == "Hello" or x => x + 2 == 3.. any expression that fits inside where statement.
I get the conditions/predicates, i do not create them myself
With predicates i mean those predicates in math and not the class in c#.
there is not much code to post since i do not know how to do this in ONE linq statement
The result has to be ONE linq statement handling all this.
I am new I do not know how to write this that why i was hoping you could help me out.
If you dont like it dont downvote it just pass on, somebody will help me out.
EDIT2:
I mean this. Not more, not less:
method<T> filterList(ienumerable<T> linq, Predicate<T> p1, Predicate<T> p2)
{
return linq.Where(only p1 or only p2 or p1 && p2)
}
This is the beauty of monads - you don't have to do everything in one step.
// bool? flag1
// bool? flag2
// IQueryable<something> collection
if(flag1.HasValue)
collection = collection.Where(x => x.Flag1 == flag1);
if(flag2.HasValue)
collection = collection.Where(x => x.Flag2 == flag2);
//...
Edit: if you want to do it in a single LINQ statement you'll need to do some trickery by invoking predicates:
public IEnumerable<T> FilterList<T>(IEnumerable<T> collection, Predicate<T> p1, Predicate<T> p2)
{
return collection.Where(x =>
(p1 == null || p1.Invoke(x)) &&
(p2 == null || p2.Invoke(x))
);
}
you don't say what to do if both are unset, I assume in this case no filtering at all
I assume by 'has no value' you mean for example p1 is null.
I'm not 100% sure what you mean by ONE linq statement but I would assume at least one of the below would meet your needs.
in all cases I'm saying basically [replace a null predicate with an always true predicate and and the predicates]
Solution 1
return linq.Where(x => p1 != null ? p1(x) : true).Where(x => p2 != null ? p2(x) : true);
Solution 2
var p1a = p1 ?? (x => true);
var p2a = p2 ?? (x => true);
return linq.Where(x => p1a(x) && p2a(x));
Solution 3
return linq.Where(x => (p1 ?? (y=>true))(x) && (p2 ?? (z=>true))(x));
If you use the third party PredicateBuilder, you can iteratively build or filters as well as and.
example
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
I've got a method which can accept an optional int? value as a number of items to Take from a collection. I want to return all items if a null value is passed. Right now I have to duplicate my query to accomplish this
if(take == null)
{
x = db.WalkingDeadEps.Where(x => x.BicyclesCouldHaveSavedLives == true).ToList()
}
else
{
x = db.WalkingDeadEps.Where(x => x.BicyclesCouldHaveSavedLives == true).Take(take).ToList()
}
Is there a simpler way? Something like this?
.Take(take != null ? take : "all")
with Linq you have the option to store your query in variables. it will not be executed until you call ToList or equivalent methods on it.
var query = db.WalkingDeadEps.Where(x => x.BicyclesCouldHaveSavedLives == true);
x = take.HasValue ? query.Take(take.Value).ToList() : query.ToList();
My project is using MVC 4 C# LINQ to SQL.
For some reason the method used to get data for one of my properties is giving a "has no supported translation to SQL" error. The method to fetch this data is nearly identical to the method of another property in the same query except the one that works grabs a string where the one that doesn't gets a decimal.
Exact error code:
Method 'System.Decimal GetModelDifficulty(System.String)' has no supported translation to SQL.
I've tried numerous variations on the below code but I always get the same error as above:
public List<ProductionSchedule> GetBaseProductionSchedule(DateTime selectedDate)
{
var spaList = (from x in db.WO010032s
join y in db.MOP1042s on x.MANUFACTUREORDER_I equals y.MANUFACTUREORDER_I into x_y
where x.STRTDATE == selectedDate && (x.MANUFACTUREORDERST_I == 2 || x.MANUFACTUREORDERST_I == 3)
from y in x_y.DefaultIfEmpty()
select new ProductionSchedule()
{
MO = x.MANUFACTUREORDER_I,
BOMNAME = x.BOMNAME_I,
SpaModel = x.ITEMNMBR,
MoldType = GetMoldType(x.ITEMNMBR.Trim()),
SerialNumber = y.SERLNMBR,
Difficulty = GetModelDifficulty(x.ITEMNMBR.Trim())
}).OrderByDescending(x => x.Difficulty).ToList();
return spaList;
}
public string GetMoldType(string model)
{
return db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.MoldType).FirstOrDefault();
}
public decimal GetModelDifficulty(string model)
{
return (decimal)db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.Difficulty).FirstOrDefault();
}
Well I've tweaked the code around enough times to where I've stumbled on a variation that works:
public List<ProductionSchedule> GetBaseProductionSchedule(DateTime selectedDate)
{
var spaList = (from x in db.WO010032s
join y in db.MOP1042s on x.MANUFACTUREORDER_I equals y.MANUFACTUREORDER_I into x_y
where x.STRTDATE == selectedDate && (x.MANUFACTUREORDERST_I == 2 || x.MANUFACTUREORDERST_I == 3)
from y in x_y.DefaultIfEmpty()
select new ProductionSchedule()
{
MO = x.MANUFACTUREORDER_I,
BOMNAME = x.BOMNAME_I,
SpaModel = x.ITEMNMBR,
MoldType = GetMoldType(x.ITEMNMBR.Trim()),
SerialNumber = y.SERLNMBR,
Difficulty = GetModelDifficulty(x.ITEMNMBR)
}).ToList();
return spaList.OrderByDescending(x => x.Difficulty).ToList();
}
public string GetMoldType(string model)
{
return db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.MoldType).FirstOrDefault();
}
public decimal GetModelDifficulty(string model)
{
decimal difficulty = (String.IsNullOrEmpty(model)) ? 0M : Convert.ToDecimal(db.SkuModelDatas.Where(x => x.Value == model.Trim()).Select(x => x.Difficulty).FirstOrDefault());
return difficulty;
}
Why it worked when trapping for null string for x.ITEMNMBR (model parameter) in one method and not the other and needing to OrderByDescending outside of the main LINQ query, I have no idea.
Thanks for all the suggestions and help with this.
The problem is your query is calling code that LINQ cannot translate into SQL.
First try this, it may help. There may be a problem with your (decimal) cast. Modify your method GetModelDifficulty to the following:
public decimal GetModelDifficulty(string model)
{
return Convert.ToDecimal(db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.Difficulty).FirstOrDefault());
}
If that doesn't work I'm afraid you'll have to break your query down further to narrow down the issue. Use the documentation provided here: Standard Query Operator Translation (LINQ to SQL) to be sure that any extension methods you are using have supported translations.
If you run into a piece of code that cannot be translated, you may need to declare a separate variable with the necessary value already stored inside of it, that you can then use inside of your query.
I think it's because your call to FirstOrDefault() can return a null value. When you assign to a typed object you can use ? operator to signify that it can be null:
decimal? myDec = <some code returning decimal value or null>
Then you can check if myDec is null or not by:
myDec.HasValue
Is it possible to write a linq select with a where clause which can select either ALL items or specific ones? In SQL I could use "where currency like '%'" to return everything.
I am trying to write a method so I can pass in the currency (amongst a few other things) and re-use the same method.
e.g.
Just GBP
from a in accounts
where currency.Contains('GBP')
select a
Just GBP
from a in accounts
where currency == 'GBP'
select a
ALL currencies?
from a in accounts
where currency like '%'
select a
Have you tried to "store" the query and filter it in a later step, like so:
IEnumerable<AccountClass> GetAccounts(string filter = null)
{
var query = from a in accounts select a;
if (!string.IsNullOrEmpty(filter))
{
query = query.Where(a => a.Currency.Contains(filter));
}
return query;
}
This could be collapsed in a single query, but seems less readable to me and may not work with LINQ-to-SQL (or other LINQ-to-DB where the expression is translated to a query):
from a in accounts
where string.IsNullOrEmpty(filter) || a.Currency.Contains(filter)
select a
You could try
.Where(c => currencyToFind == null ? true : c==currencyToFind)
And pass in a null for the currency you want if you want them all.
In a linq query expression:
from a in accounts
where (currencyToFind == null ? true : account.Currency==currencyToFind)
select a
If you want to return all currencies, just don't use any where (both in LINQ and SQL):
from a in accounts
select a
Or, if you really don't need to do anything else, just use accounts.
EDIT: If you want to have one method, and, say, any currency is represented by null, you could do it like this:
IQueryable<Account> result = accounts;
if (currency != null)
result = result.Where(a => a.Currency == currency);
return result;
It's hard to answer this without more information, but here's the sort of pattern that you appear to want (this would be overengineering in my book unless really warranted):
public static Expression<Func<Account, bool>> GetAccountCurrencyPredicate
(this FilterKind filter, string value)
{
switch (filter)
{
case FilterKind.Exact:
return account => account.Currency == value;
case FilterKind.Contains:
return account => account.Currency.Contains(value);
case FilterKind.All:
return account => true;
default:
throw new ArgumentException("Unknown FilterKind.", "filter");
}
}
And then use it as:
FilterKind filter = ...
string value = ...
IQueryable<Account> accounts = ...
var predicate = filter.GetAccountCurrencyPredicate(value);
var matchingAccounts = accounts.Where(predicate);
Can't you just filter on a condition?
var selectedAccounts = accounts.Select(y => y);
if(!string.IsNullOrEmpty(currency))
{
selectedAccounts = selectedAccounts.Where(y => y.Currency.Contains(currency));
}
You could even try an even more generic version:
IEnumerable<Account> GetAccounts(Func<Account, bool> filter)
{
var selectedAccounts = accounts.Select(y => y);
if(filter != null)
{
selectedAccounts = selectedAccounts.Where(filter);
}
return selectedAccounts;
}
IEnumerable<Account> GetAccountsForCurrency(string currency)
{
if(string.IsNullOrEmpty(currency))
{
return GetAccounts(null);
}
return GetAccounts((y) => y.Currency.Contains(currency));
}
Now you have one more specific and one more general method that could be used for different types of filtering.
Why are you using a where statement if you want to return anything?
where is made for filtering, so if you want to return anything you can either try
from a in accounts
select a
or, if you want to have a where statement in it
from a in accounts
where 1 = 1
select a
or, if you want a like statement i suggest using a regex
from a in accounts
let r = new Regex("expression")
where r.IsMatch(a.currency)
select a;
I am using c#.net
I have two textboxes which if !empty need to be part of a WHERE clause within a LINQ query.
Here is my code
var result = from a in xxxx select a;
if(!string.IsNullOrEmpty(personName))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName)
}
else if(!string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.appStartDateTime >= dateFrom.Date)
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date);
}
I thought this would work but it doesn't like the .Where and I cant access the 'a' for example a.forename (The name 'a' does not exist in the current context)
What am I going wrong, or can this not actually be done?
Thanks in advance for any help.
Clare
Instead of this:
result.Where(a.forename.Contains(personName))
Try this:
result.Where(a => a.forename.Contains(personName))
You appear to be missing the Lambda operator (=>).
try this
var result = from a in xxxx select a
where (string.IsNullOrEmpty(personName) || a.forename.Contains(personName)
|| a.surname.Contains(personName))
&& (string.IsNullOrEmpty(dateFrom)
|| a.appStartDateTime >= DateTime.Parse(dateFrom).Date);
dateFrom appears to be a string so you have to parse it to get a date time.
This logic should work but I have not tested it. I could be wrong.
You seem to only care if the forename or surname contain personName if the personName is not null or empty. So you can rewrite it to return things if the personName is null or empty or the fore or sur name contains person name. Since the || operator is short circuiting it will not check Contains if the personName is null or empty.
You can also combine the predicates and make the logic shorter and easier to read:
var result = from a in xxxx select a;
if (!string.IsNullOrEmpty(personName))
result = result.Where(a => a.forename.Contains(personName) || a.surname.Contains(personName)
if (!string.IsNullOrEmpty(dateFrom))
result = result.Where(a >= a.appStartDateTime >= dateFrom.Date)
return result;
This method of combining predicates works well with 'AND' conditions. If you need to 'OR' conditions, it's a little bit more involved. Thankfully, Joe Albahari has created PredicateBuilder (as part of LINQKit) that helps with this greatly.
Hope this helps.
..or with just one exit point:
var result = from a in xxxx select a;
Func<string, bool> func = null;
if(!string.IsNullOrEmpty(personName))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName)};
}
else if(!string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.appStartDateTime >= dateFrom.Date};
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date;};
}
return result.Where(func);