Passing null as Contains() within "linq to sql" query - linq

I have a problem with Contains in linq to sql query as below:
public IAuditRecord[] Fetch(SearchConditions searchConditions)
{
IAuditRecord[] searchedList = (from rows in _dbContex.AuditTrails
where
(searchConditions.Owner == null || searchConditions.Owner == 0) ? true : rows.Owner == searchConditions.Owner
&&
/*This line cannot compile when ActionIDs array is empty*/
(searchConditions.ActionIDs != null && searchConditions.ActionIDs.Length != 0) ? searchConditions.ActionIDs.Contains(rows.UserActionID) : true
&& ((searchConditions.StartDate != null && searchConditions.EndDate != null) ? (rows.TimeStamp >= searchConditions.StartDate && rows.TimeStamp <= searchConditions.EndDate)
: (searchConditions.StartDate != null && searchConditions.EndDate == null) ? rows.TimeStamp >= searchConditions.StartDate : (searchConditions.StartDate == null && searchConditions.EndDate != null) ? (rows.TimeStamp <= searchConditions.EndDate)
: true)
select rows).ToArray();
return searchedList;
}
This query executes perfectly if searchCondition.ActionIDs array is not null or empty,
but when i pass the ActionIDs array as null the query cannot be compiled.
So the main question is Why contains cannot work when ActionIDs array is null?

You're building an IQueryable, which defines how to query for something, not actually doing it. To do this it builds an Expression, which defines all the query intents and can later be called to actually get the data. If you were using LINQ-to-Objects, this would likely work since it would likely call searchConditions.ActionIDs != null first, then know that it doesn't have to attempt to execute the second portion. Linq-to-Entities/SQL, etc. don't have that benefit.
Long story short, you can either do:
searchConditions.ActionIDs = searchConditions.ActionIDs ?? new int[];
Or do a different query if its null, like:
var query = _dbContext.AuditTrails;
if(searchConditions.ActionIDs != null && searchCondition.ActionIDs.Length != 0)
{
query = // Further filtered query where ActionIDs are taken into account.

Related

What is the most efficient way or best practice for null check when you use Split and FirstOrDefault methods together?

I use Split and LastOrDefault methods together, I use this code block for null check. But it does not seems the most efficient way to me. Because I use lots of check and && operators. It seem a little ugly. Is there any way to accomplish this null-check in a better way? (I searched on web but couldn' t find related answers.)
Note : C# Language version 4.0
Here is my code :
if (HttpContext.Current.Request.Url.AbsolutePath.Split('/') != null &&
HttpContext.Current.Request.Url.AbsolutePath.Split('/').Length > 0 &&
HttpContext.Current.Request.Url.AbsolutePath.Split('/').Last() != null &&
HttpContext.Current.Request.Url.AbsolutePath.Split('/').Last().Split('.') != null &&
HttpContext.Current.Request.Url.AbsolutePath.Split('/').Last().Split('.').Length > 0 &&
HttpContext.Current.Request.Url.AbsolutePath.Split('/').Last().Split('.').First() != null)
{
pageName = HttpContext.Current.Request.Url.AbsolutePath.Split('/').LastOrDefault().Split('.').FirstOrDefault();
}
Thanks for all answers.
The tests are all not needed:
First, don't run Split multiple times on the same data:
var splitSlashAbsPath = HttpContext.Current.Request.Url.AbsolutePath.Split('/');
The return array from Split can never be null
// if (splitSlashAbsPath != null &&
the return array from Split can never be zero length
// splitSlashAbsPath.Length > 0 &&
so the return from Last() can never be null
// splitSlashAbsPath.Last() != null &&
Don't run split multiple times on the same data (and calling Last on an array doesn't make sense)
var splitDotAbsPath = splitSlashAbsPath[splitSlashAbsPath.Length-1].Split('.');
the return array from Split can never be null
// splitDotAbsPath != null &&
the return array from Split can never be zero length
// splitDotAbsPath.Length > 0 &&
so, the First() from Split can never be null
// splitDotAbsPath.First() != null)
// {
since you can call Last, calling LastOrDefault makes no sense
same for FirstOrDefault
// pageName = splitDotAbsPath.FirstOrDefault();
Calling First on an array also doesn't make sense
pageName = splitDotAbsPath[0];
// }
So, in summary you have:
var splitSlashAbsPath = HttpContext.Current.Request.Url.AbsolutePath.Split('/');
var splitDotAbsPath = splitSlashAbsPath[splitSlashAbsPath.Length-1].Split('.');
pageName = splitDotAbsPath[0];
However, in general, using Split for just getting one element is very inefficient, so this would be better:
var path = HttpContext.Current.Request.Url.AbsolutePath;
var pastSlashPos = path.LastIndexOf('/') + 1;
var countUntilDot = path.IndexOf('.', pastSlashPos);
countUntilDot = (countUntilDot >= 0 ? countUntilDot : path.Length) - pastSlashPos;
pageName = path.Substring(pastSlashPos, countUntilDot);

How to pass an external parameter to LINQ where clause in CRM

I have a LINQ query which works fine as for stand alone lists but fails for CRM
var lst = new List<bool?>();
lst.Add(null);
lst.Add(true);
lst.Add(false);
bool IsWet = false;
var newlst = from exch_HideVoiceSignature in lst where
(((exch_HideVoiceSignature!=null && exch_HideVoiceSignature==false
|| exch_HideVoiceSignature== null) )&& !IsWet) select exch_HideVoiceSignature;
newlst.Dump();
var question = from q in exch_questionSet where ((q.exch_HideVoiceSignature != null
&& q.exch_HideVoiceSignature.Value == 0 )|| q.exch_HideVoiceSignature == null )
&& !IsWet select q.exch_HideVoiceSignature;
question.FirstOrDefault().Dump();
As you can see I can pass the variable IsWet to LINQ query for a standard list fine and get values for first list. But when I execute the same for second list, I get the following error
Invalid 'where' condition. An entity member is invoking an invalid property or method
The CRM LINQ provider won't support the evaluation you attempting. It only supports evaluation of where criteria is evaluating an entity field.
That's not a problem. Since you want the LINQ query to only use the where clause if IsWet is false (correct me if I'm wrong on that.) So we simply do the evaluation to determine if the where clause should be added or not. Then execute your query.
var question = from q in exch_questionSet
select q.exch_HideVoiceSignature;
if (!IsWet)
{
question.Where(x => ((x.exch_HideVoiceSignature != null
&& x.exch_HideVoiceSignature.Value == 0) || x.exch_HideVoiceSignature == null));
}
question.FirstOrDefault().Dump();
I am constantly confronted with that problem.
Try to "detach" (for example call .ToArray()) your query (while it is "clear") from CRM and then filter query using external parameter. This should help.
var question =
(from q in exch_questionSet
where (
(q.exch_HideVoiceSignature != null && q.exch_HideVoiceSignature.Value == 0 ) ||
q.exch_HideVoiceSignature == null )
select q.exch_HideVoiceSignature
).ToArray().Where(q => !IsWet);
question.FirstOrDefault().Dump();
UPDATE
If you are using IsWet flag to control blocks of conditions (enable and disable them from the one point in the code) then probably you may be interested in class named PredicateBuilder which allows you to dynamically construct predicates.
Just because I had an existing query with lot of other joins etc. and I wanted to pass this additional parameter to it I ended up using a var statement which dumps the rows to a list and applies the where clause in the same statement
bool IsWet =true ;
var question = ...existing query ...
select new {
...existing output ...,
Wet =q.exch_HideVoiceSignature != null &&
q.exch_HideVoiceSignature.Value == 119080001,
Voice = q.exch_HideVoiceSignature == null ||
(q.exch_HideVoiceSignature != null &&
q.exch_HideVoiceSignature.Value == 119080000) ,
}
;
var qq = IsWet ?
question.ToList().Where(X=> X.Wet ) :
question.ToList().Where(X=> X.Voice );
qq.FirstOrDefault().Dump();

Linq query skip querying null coditions

I am very new to linq. I have my clients table. I want to select clients depending on the two conditions
Client type
Client city
So I can write the query like
from c in clients
where c.Type == cType
&& c.City == cCity
Can I use this same query to get the result only providing client type(ignoring the City condition. somithing like *).
What I want to do is if cCity or cType is null ignore the condition.
Is this possible to do?
Isn't that what you're looking for?
from c in clients
where (c.Type == null || c.Type == cType)
&& (c.City == null || c.City == cCity)
You can compose a LINQ statement before actually executing it:
if (cType != null)
clients = clients.Where(c => c.Type == cType);
if (cCity != null)
clients = clients.Where(c => c.City== cCity);
// At this point the query is never executed yet.
Example of how the query can be executed for the first time :
var results = clients.ToList();
from c in clients
where (cType == null ? 1 == 1 : c.Type == cType)
&& (cCity == null ? 1 == 1 : c.City == cCity)

Linq with Logic

I have simple Linq statement (using EF4)
var efCars = (from d in myentity.Cars
where d.CarName == inputCar.CarName
&& d.CarIdNumber == inputCar.IdNumber
&& d.Make == inputCar.Make
select d.Car);
I want it to be smarter so that it will only query across one or more of the 3 fields IF they have values.
I can do a test before, and then have a separate linq statement for each permutation of valyes for inputcar
(i.e. one for all 3, one for if only carname has a value, one for if carname AND CarIdNumber has a value etc etc)
but there must be a smarter way
Thanks!
If "has no value" means null then you can use the null coalescing operator ?? to say take the first value if populated, otherwise take the second:
var efCars = (from d in myentity.Cars
where d.CarName == (inputCar.CarName ?? d.CarName
&& d.CarIdNumber == (inputCar.IdNumber && d.CarIdNumber)
&& d.Make == (inputCar.Make && d.Make)
select d.Car);
This basically says if a value exists it must match, otherwise treat it as matching
However if instead you're saying "when a special value (empty string) ignore it, otherwise match" then you can do one of two approaches (or possibly more!):
where (inputCar.CarName == "" || d.CarName == inputCar.CarName)
where (string.IsNullOrEmpty(inputCar.CarName) || d.CarName == inputCar.CarName)
For performance (when dealing with database queries) it can sometimes be beneficial to let EF generate queries based on the filters, instead of using one generic query. Of course you will need to profile whether it helps you in this case (never optimize prematurely), but this is how it would look if you dynamically build your query:
var efCars =
from car in myentity.Cars
select car;
if (inputCar.CarName != null)
{
efCars =
from car in efCars
where care.CarName == inputCar.CarName
select car;
}
if (inputCar.IdNumber != null)
{
efCars =
from car in efCars
where care.CarIdNumber == inputCar.IdNumber
select car;
}
if (inputCar.Make != null)
{
efCars =
from car in efCars
where care.Make == inputCar.Make
select car;
}
where (inputCar.CarName != null || d.CarName == inputCar.CarName) &&...

LINQ Lambda Where() not filtering as expected

I'm trying to build a query using LINQ for EF to filter results based on some basic logic. For some reason, even with the following Where() functions being executed and setting the right parameters, all data is being returned instead of the filtered results from Where().
I have run debug to make sure that my if() statements are indeed allowing the Where() to run when appropriate, and it is.
What am I missing?
var dbReports = db.SubmitReports;
if (Referee != String.Empty)
dbReports.Where(u => (u.Refree == Referee || u.Ar1Official == Referee || u.Ar2Official == Referee || u.FourthOfficial == Referee));
if (TeamName != String.Empty)
dbReports.Where(u => (u.HomeTeam == TeamName || u.VisitingTeam == TeamName));
if (PlayedOnStart != DateTime.MinValue && PlayedOnEnd != DateTime.MinValue)
dbReports.Where(u => (u.PlayedOn >= PlayedOnStart && u.PlayedOn <= PlayedOnEnd));
if (StateAssociation != String.Empty)
dbReports.Where(u => (u.StateAssociation == StateAssociation || u.StateAssociation2 == StateAssociation));
if (Division != String.Empty)
dbReports.Where(u => u.Division == Division);
if (ProfessionalLeague != String.Empty)
dbReports.Where(u => u.ProfessionalLeague == ProfessionalLeague);
if (AgeGroup != String.Empty)
dbReports.Where(u => u.AgeGroup == AgeGroup);
return dbReports.ToList();
Where doesn't modify the existing query - it creates a new query. You need to assign the result of the call to Where to something otherwise the result is simply discarded. Try this:
IQueryable<Report> dbReports = db.SubmitReports;
if (...)
{
dbReports = dbReports.Where(...);
}
You never use the return value of the Where method. Where does not modify the IEnumerable it is apply on but returns a Linq expression that will create a modify IEnumerable when executed (i.e when ToList is called).

Resources