Linq Conditional DefaultIfEmpty query filter - c#

I have a query as below:
bool variable = false//or true
var query = from e in _repository.GetAll<Entity>()
from u in e.Users
where (e.AuditQuestionGroupId != null ? e.AuditQuestionGroupId : 0) == this.LoggedInEntity.AuditQuestionGroupId
from p in e.PractitionerProfiles.DefaultIfEmpty()
select new { entity = e, user = u, profile = p };
This works correctly. However, I have a boolean variable that should determine whether the join to e.PractitionerProfiles should have DefaultIfEmpty, thereby making it a Left Outer Join instead of an Inner Join.
However, as I am using annoymous objects, I can't figure out how to do it correctly. So I want the ability to switch between Left and Inner Join without duplicating the whole query like:
if(variable) {
var query = from e in _repository.GetAll<Entity>()
from u in e.Users
where (e.AuditQuestionGroupId != null ? e.AuditQuestionGroupId : 0) == this.LoggedInEntity.AuditQuestionGroupId
from p in e.PractitionerProfiles
select new { entity = e, user = u, profile = p };
}
else {
var query = from e in _repository.GetAll<Entity>()
from u in e.Users
where (e.AuditQuestionGroupId != null ? e.AuditQuestionGroupId : 0) == this.LoggedInEntity.AuditQuestionGroupId
from p in e.PractitionerProfiles.DefaultIfEmpty()
select new { entity = e, user = u, profile = p };
}
Is there a clean way to do it with one query? The problem also is that I have a number of further conditions which get placed on it, so declaring query inside the loop means it doesn't have a local variable and I don't know how to create an empty IQueryable anonymous object.

Why not use ternary operator?
from p in (variable ? e.PractitionerProfiles : e.PractitionerProfiles.DefaultIfEmpty())

I solved it by adding the filter after the initial query, checking if the e.PractitionerProfiles were null.
var query = from e in _repository.GetAll<Entity>()
from u in e.Users
where (e.AuditQuestionGroupId != null ? e.AuditQuestionGroupId : 0) == this.LoggedInEntity.AuditQuestionGroupId
from p in e.PractitionerProfiles.DefaultIfEmpty()
select new { entity = e, user = u, profile = p };
then
if (variable)
{
query = query.Where(x => x.profile != null);
}

Related

Linq conditional select with null member collection

I have searched through a lot of similar questions, but cannot find one that solves this problem. Good chance I am overlooking something obvious, but please suggest how to do this with a single iteration.
var dataWithResults =
from d in data
where d.QCResults != null
from q in d.QCResults
select new QCAMaterial
{
Name = q == null ? nodata : q.ComponentName,
Value = q == null ? nodata : q.Result
};
var dataWithNullResults =
from d in data
where d.QCResults == null
select new QCAMaterial
{
Name = nodata,
Value = nodata
};
You can do a combination of Enumerable.Empty and Enumerable.DefaultIfEmpty:
var allData =
from d in data
from q in d.QCResults ?? Enumerable.Empty<QCResult>().DefaultIfEmpty()
select new QCAMaterial
{
Name = q == null ? nodata : q.ComponentName,
Value = q == null ? nodata : q.Result
};
In case you want to treat an empty list (QCResults) the same as a null list, you can modify the query:
var allData =
from d in data
from q in (d.QCResults ?? Enumerable.Empty<QCResult>()).DefaultIfEmpty()
select new QCAMaterial
{
Name = q == null ? nodata : q.ComponentName,
Value = q == null ? nodata : q.Result
};
Since both the classes without QCResults as well as QCResults with no specific data in their properties get the nodata value, you could omit the 'where' and combine them as:
IEnumerable<QCResult> dummycol = new[] {new QCResult()}; //assuming QCResult is the class exposed by the QCResults property
var res = from d in data
from q in d.QCResults ?? dummycol
select new QCAMaterial
{
Name = q == null ? nodata: q.ComponentName, //side note: can also be written as q?.ComponentName ?? nodata
Value = q == null ? nodata : q.Result
};
If QCResults is empty, a dummy collection is used with a single entry.
The dummy-collection could be created inline as well, but this should be more efficient.
You can use a SelectMany:
var result = data.SelectMany(d =>
{
return d.QCResults != null
? d.QCResults.Select(q => new QCAMaterial { Name = q.ComponentName, Value = q.Result})
: new List<QCAMaterial>{new QCAMaterial { Name = "no data", Value = "no data" }};
});

left join linq when right list is empty

pdlist = (from a in context.EMPLOYEES
join b in context.PERSONS on a.PERSON_ID equals b.PERSON_ID
where a.SUPERVISOR_ID == empId
select new Pollidut.Models.Pollidut
{
PollidutId = a.EMPLOYEE_ID,
PollidutName = b.PERSON_NAME,
DistributionHouseId = a.DISTRIBUTION_HOUSE_ID == null ? 0 : (int)a.DISTRIBUTION_HOUSE_ID
}).ToList();
DateTime dt = DateTime.Now.Date;
var pdTargets = (from p in context.PALLYDUT_TARGET
where p.Active == true && p.StartDate <= dt && p.EndDate >= dt
group p by p.PallydutId into g
select new
{
PollidutId = g.Key,
Start = g.Select(x => x.StartDate).Min(),
End = g.Select(y => y.EndDate).Max(),
Target = g.Select(z => z.Target).Sum()
}).ToList();
var PdTargetsList = (from m in pdlist
join n in pdTargets on m.PollidutId equals n.PollidutId into t
from l in t.DefaultIfEmpty()
select new
{
PallydutId = m.PollidutId,
PallydutName = m.PollidutName,
DistributionId = m.DistributionHouseId,
StartDate = l.Start == null ? dt : l.Start,
EndDate = l.End == null ? dt : l.End,
Target = l.Target == null ? 0 : l.Target
}).ToList();
pdlist is employee list where pdTarget may set or not. pdTarget can be empty. When I left join pdlist with pdTargets I get Object reference not set error. pdTargets collection returns null.How to fix it. Anybody helps me greatly appreciated.
The reason I suspect why you are getting a Null reference exception is because your are directly accessing the properties on a null object (l in your case), even though you are using a DefaultIfEmpty() method which is going to return null if no matching rows are found. You should change your code like this:-
select new
{
PallydutId = m.PollidutId,
PallydutName = m.PollidutName,
DistributionId = m.DistributionHouseId,
StartDate = l == null ? dt : l.Start,
EndDate = l == null ? dt : l.End,
Target = l == null ? 0 : l.Target
}).ToList();
Also, If you think that your one object pdTarget can be null, then I don't think its about a left join or a normal join, your code will throw an exception in either case. Please check this MSDN documentation to handle nulls in query expressions.

How to write Outer Join using LINQ

I am using the below Inner Join to retrive the Data between the two tables, but all data is not getting populated. I tried implementing Outer join by connecting using CCY1== CCY1 and PCODE == PCODE, but no luck.
var q = from g1 in TableCCY1.AsEnumerable()
join g2 in TableCCY2.AsEnumerable()
on g1.Field<string>("CCY1") equals g2.Field<string>("CCY1")
where g1.Field<string>("PCODE") == g2.Field<string>("PCODE")
select new
{
g1currency = g1.Field<string>("CCY1"),
g2currency = g2.Field<string>("CCY1"),
g1code = g1.Field<string>("PCODE"),
g2code = g2.Field<string>("PCODE"),
g1Amt1 = g1.Field<string>("AMT1"),
g2Amt2 = g2.Field<string>("AMT2")
};
Thanks for your help.
For left join you can use this approuch: http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx
The code should be:
var q = from g1 in TableCCY1
join g2 in TableCCY2 on g1.CCY1 equals g2.CCY1 && g1.PCODE equals g2.PCODE into TableCCY3
from g3 in TableCCY3.DefaultIfEmpty()
select new
{
g1currency = g1.CCY1,
g2currency = (g3 == null ? String.Empty : g3.CCY1),
g1code = g1.PCODE,
g2code = (g3 == null ? String.Empty : g3.PCODE),
g1Amt1 = g1.AMT1,
g2Amt2 = (g3 == null ? 0 : g3.AMT2)
};
It looks like you just want to union/concat the two tables into one and then just group on those two columns. You're not logically joining the two tables. That actually makes it much easier.
var q = from row in TableCCY1.AsEnumerable().Concat(TableCCY2.AsEnumerable())
group row by new
{
CCY1 = row.Field<string>("CCY1"),
PCode = row.Field<string>("PCODE")
} into matches
select new
{
CCY1 = matches.Key.CCY1,
PCODE = matches.Key.PCode,
Sum = matches.Sum(match => match.Field<decimal?>("AMT2")),
};

Left join is not working for complex query

I have very complex linq query which is working fine for inner join, i.e., without z_temp.DefaultIfEmpty(). But when I use this for left join, The query is not yielding results.
var q = from x in db.EmployeesList
where x.EmployeesListStartDate >= startDate && x.EmployeesListStartDate <= endDate
join y in db.Survey on x.Survey.SurveyID equals y.SurveyID
join z in
(from a in db.Commit
join b in
(from commit in db.Commit
where
commit.CommitListID != null &&
commit.CommitType.ToUpper() != "PREVIEW"
group commit by new
{
commit.CommitListID
} into g
select new
{
CommitListID = (Int32?)g.Key.CommitListID,
CommitId = (Int32?)g.Max(p => p.CommitId)
})
on new { a.CommitListID, a.CommitId }
equals new { b.CommitListID, CommitId = (Int32)b.CommitId }
select new
{
CommitListID = (Int32?)a.CommitListID,
CommitUsername= a.CommitUsername,
CommitStartDateTime=a.CommitStartDateTime,
CommitType=a.CommitType,
CommitSuccessCount=a.CommitSuccessCount
}) on new { EmployeesListID = x.EmployeesListID } equals new { EmployeesListID = (Int32)z.CommitListID }
into z_temp
from _z in z_temp.DefaultIfEmpty()
select new CustomEmployeesList
{
SurveyId = x.Survey.SurveyID != null ? (int)x.Survey.SurveyID : 0,
EmployeesListId = x.EmployeesListID != null ? (int)x.EmployeesListID : 0,
EmployeesListName = x.EmployeesListName,
SpecificMessage = x.SpecificMessage,
ListCriteria = x.ListCriteria,
Channel = x.Channel,
EmployeesListStartDate = (DateTime)x.EmployeesListStartDate,
EmployeesListEndDate = (DateTime)x.EmployeesListEndDate,
Records = x.Records != null ? (int)x.Records : 0,
QueryId = x.AppSqlQueries.QueryId != null ? (int)x.AppSqlQueries.QueryId : 0,
//AuditId = (Int32?)x.AuditEntry.AuditId,
StatusCommonCode = x.CommonCode.CommonCodeId != null ? (int)x.CommonCode.CommonCodeId : 0,
SurveyName = y.SurveyName,
LastCommitDateTime = _z.CommitStartDateTime.HasValue ? (DateTime)_z.CommitStartDateTime : DateTime.MinValue,
LastCommitType = _z.CommitType != null ? _z.CommitType : "",
LastCommitUsername = _z.CommitUsername != null ? _z.CommitUsername : "",
LastCommitCount = _z.CommitSuccessCount.HasValue ? (int)_z.CommitSuccessCount : 0
};
This is returning no results and
I am getting this exception message while viewing results in debug mode:
LINQ to Entities does not recognize the method
'System.Collections.Generic.IEnumerable1[<>f_AnonymousType351[<>f_AnonymousType35%5bSystem.Nullable1[System.Int32],System.String,System.Nullable1%5bSystem.DateTime%5d,System.String,System.Nullable`1%5bSystem.Int32%5d%5d%5d">System.Nullable1[System.Int32],System.String,System.Nullable1[System.DateTime],System.String,System.Nullable1[System.Int32]]]
DefaultIfEmpty[<>f__AnonymousType35' method, and this method cannot be
translated into a store expression.
Can anyone suggest the where the problem would be, this would be really helpful!
The problem is in this line:
from _z in z_temp.DefaultIfEmpty()
Calling DefaultIfEmpty() will return null if no rows matches the join. Ok, you a left join, but you have to test if _z is null before access its members:
...
LastCommitDateTime = _z == null ? DateTime.MinValue : (_z.CommitStartDateTime.HasValue ? (DateTime)_z.CommitStartDateTime : DateTime.MinValue),
LastCommitType = _z == null ? "" : (_z.CommitType != null ? _z.CommitType : ""),
...
etc.
A more elegant alternative is create a class that defines the fields you want and call _z.DefaultIfEmpty(new ZRow()), so you don't need to test if _z is null every time you need it. But in this case you'll need to change the select that produces the result for z_temp and replace it to select new ZRow(a.CommitListID, etc..). Not a big deal.

Using anonymous types - how to avoid lot of code

I have this situation:
if (condition){
var variable = (from el in ....) //linq to sql query
}
else{
var variable = (from el in ....) //linq to sql query
}
// code where i can`t use the variable
I want to avoid copying code in both conditions.
var getHistoryTips = (from el in objDC.tickets
where el.typeOfGame == cANDu[0]
&& el.username == Membership.GetUser(cANDu[1]).ProviderUserKey.ToString()
select new { el.AllGamesTickets, el.WGamesTickets}).FirstOrDefault();
var getHistoryTips = (from el in objDC.tickets
where el.typeOfGame == cANDu[0]
&& el.results != null
&& el.username == Membership.GetUser(cANDu[1]).ProviderUserKey.ToString()
select new { el.AllGamesTickets, el.WGamesTickets}).FirstOrDefault();
Three options:
Firstly, you can use an "example" - to type the variable:
// Same anonymous type properties and types as later:
var variable = Enumerable.Repeat(0, new { ... });
if (condition) {
variable = ...;
} else {
variable = ...;
}
Alternatively, you could use the conditional operator:
var variable = condition ? ... first query ...
: ... second query ...
Thirdly, you could build the query up using composition instead. For example, if the only difference is the ordering, you could do:
var firstPart = ...;
var secondPart = condition ? query.OrderBy(...) : query.OrderByDescending(...);
var query = secondPart.Select(...);
EDIT: Now you've given an example, we can make this concrete:
string username = membership.GetUser(cANDu[1]).ProviderUserKey.ToString();
var query = objDC.tickets
.Where(el => el.typeOfGame == cANDu[0] &&
el.username == username);
if (condition)
{
query = query.Where(el => el.results != null);
}
var result = query.Select(el => new { el.AllGamesTickets, el.WGamesTickets})
.FirstOrDefault();
You might be able to use the ternary operator:
var variable = (condition) ? (from el in...) : (from el in...);
Where the first linq-to-sql statement (before the colon) is the true case and the linq-to-sql after the colon is the false case. So your code might look like:
var getHistoryTips = (condition) ? (from el in objDC.tickets
where el.typeOfGame == cANDu[0]
&& el.username == Membership.GetUser(cANDu[1]).ProviderUserKey.ToString()
select new { el.AllGamesTickets, el.WGamesTickets}).FirstOrDefault() :
(from el in objDC.tickets
where el.typeOfGame == cANDu[0]
&& el.results != null
&& el.username == Membership.GetUser(cANDu[1]).ProviderUserKey.ToString()
select new { el.AllGamesTickets, el.WGamesTickets}).FirstOrDefault();
Use your condition in the query itself in the where clause.
You may have to write something like,
var variable = (from el in .... where (condition && trueBlockCondition) || (!condition && falseBlockCondition)
Functionality you are asking for can be implemented using extensions method, instead of LINQ syntax.
Here are the example:
var source = objDC.tickets.Where(el =>
el.typeOfGame == cANDu[0]
&& el.username == Membership.GetUser(cANDu[1]).ProviderUserKey.ToString());
if(condition)
source = source.Where(el => el.results != null);
var getHistoryTips = source
.Select(el => new { el.AllGamesTickets, el.WGamesTickets}))
.FirstOrDefault();
If query getting complicated, as an option, selector can be implemented via Expression feature.
Expression<Func<ObjDCType, ShallowObjDCType>> selector =
(el) => new ShallowObjDC{ el.AllGamesTickets, el.WGamesTickets});
var getHistoryTips = source.Select(selector).FirstOrDefault();
This technique will allow you yo pass selector inside other function, in case things are getting more complex, however it requires you to define additional class or use dynamic, instead of anonymous classes.
Note, that you can't use Func without Expression, since this will cause ORM (LINQ/EF) to pull all object from database.

Categories

Resources