Linq conditional select with null member collection - c#

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" }};
});

Related

Linq "where" condition with nullable object property result in "invoke non static method requires a target"

I've tried to find a similar question but I haven't found exactly what I want.
I have this issue: about the following code I am not able to find a way to manage that if devTab is null then his property ID is not available and the where condition t.IDDevTab ==devTab.ID leads to null reference error. Mind that t.IDDevTab is not nullable in the DB. I have tried to insert some additional conditions to the Where but it resulted in error: invoke non static method requires a target. I would obtain a empty list in "DeviceTabColumnsNameAndDesc" if "devTab" is null!
DeviceTable devTab = (from t in _db.DeviceTables
where t.DeviceType == devtype && t.IDPlant == id
select t)
.FirstOrDefault();
var DeviceTabColumnsNameAndDesc = (from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID
&& t.MeasureFamily != "KEY"
&& t.MeasureFamily != "DATETIME"
select new
{
colName = t.ColumnName,
colDescr = t.ColumnDescr
})
.ToList();
Is there a workaround of this problem? Thank you in advance.
So, if devTab is null you want a new empty list of anonymous type.. Awkward, but doable:
DeviceTable devTab = (from t in _db.DeviceTables
where t.DeviceType == devtype && t.IDPlant == id
select t)
.FirstOrDefault();
var DeviceTabColumnsNameAndDesc = devTab == null ?
Enumerable.Empty<object>().Select(x => new { colName = "", colDescr = "" }).ToList() :
(
from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID && t.MeasureFamily != "KEY" && t.MeasureFamily != "DATETIME"
select new {
colName = t.ColumnName,
colDescr = t.ColumnDescr
}
).ToList();
If you wanted to switch away from anonymous types in this case, you could look at ValueTuple:
var DeviceTabColumnsNameAndDesc = devTab == null ?
new List<(string colName, string colDesc)>() :
(
from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID && t.MeasureFamily != "KEY" && t.MeasureFamily != "DATETIME"
select (
colName: t.ColumnName,
colDescr: t.ColumnDescr
)
).ToList();
Or make a record, which is like a class but with some extra bits that make it useful as a data holder:
//defined in a namespace
public record ColThing(string ColName, string ColDesc);
var DeviceTabColumnsNameAndDesc = devTab == null ?
new List<ColThing>() :
(
from t in _db.DeviceTabCols
where t.IDDevTab == devTab.ID && t.MeasureFamily != "KEY" && t.MeasureFamily != "DATETIME"
select new ColThing(t.ColumnName, t.ColumnDescr)
).ToList();

How to combine this two LINQ Query in a single var result

Here's my code. I would like to combine this 2 LINQ Query in a single var result. I use these techniques:
var result = queryOne.Union(queryTwo);
var result = queryOne.Concat(queryTwo);
var result = Enumerable.Union(queryOne, queryTwo);
This doesn't work. How to do this right? I am newly in LINQ and C#.
var queryOne = await (from x in _context.DwPropertyMasters
where
x.LandId == 2
select new
{
x.LandId,
x.MapPointX,
x.MapPointY,
x.Location,
a = x.Development == null || x.Development == "" ? x.Location : x.Development,
AreaSize = x.AreaSize ?? 0,
Premium = x.Premium ?? 0,
b = (((x.Premium == 0 ? null : x.Premium) * 100000000) / (x.AreaSize == 0 ? null : x.AreaSize)) ?? 0,
x.Developer,
x.YearTender,
c = x.Development ?? x.Location,
AreaSize2 = x.AreaSize2 ?? 0,
d = (((x.Premium == 0 ? null : x.Premium) * 100000000) / (x.AreaSize2 == 0 ? null : x.AreaSize2)) ?? 0,
}).ToArrayAsync();
var queryTwo = await (from y in _context.DwPropertyDetails
where
y.LandId == 2
orderby
y.Block,
y.Asp descending
select new
{
y.LandDetailId,
y.LandId,
a = y.Pasp ?? "",
b = y.Asp ?? "",
c = y.Tasp ?? "",
y.Block,
y.Floor,
y.Unit,
d = y.CarParking ?? "",
y.SalePrice,
e = y.Revision ?? "",
y.VendorRelate,
y.TransactionPrice,
y.FlatType,
y.ActualSize,
f =
((y.TransactionPrice == 0 ? null : y.TransactionPrice) / (y.ActualSize == 0 ? null : y.ActualSize)) ??
0
}).ToArrayAsync();
var singleQuery = queryOne.Union(queryTwo);
var result = queryOne.Union(queryTwo);
var result = queryOne.Concat(queryTwo);
var result = Enumerable.Union(queryOne, queryTwo);
This doesn't work. How to do this right? I am newly in LINQ and C#.
Your first LINQ returns an enumerable of different objects than the second one, and there is no way you can concatenate those two. They have to return the same type of objects.
var query1 = Enumerable.Range(1, 10)
.Select(i => new
{
Id = i,
Ids = 0 // See, this object has more properties than the second query's object.
});
var query2 = Enumerable.Range(11, 20)
.Select(i => new
{
Id = i,
});
query1.Concat(query2); // Won't compile!
You have to use the same number of properties for each in the same order and type, or you can define a custom class for that.
Your question lacks context right now, but since the only reason why you wouldn't be able to concatinate 2 IEnumerable<T> is because they are of different type.
If you are using an anonymous type on 2 places that's a good time to make this anonymous type into it's own class, this way you should be able to .Concat the 2 results.
But again you can see some obvious differences in your 2 query types. Another option would be to create third type which accepts contains all of the members of the other 2 queries.

Linq Conditional DefaultIfEmpty query filter

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);
}

LINQ where condition filtering

String Sex = getSex(); // return M or F
String[] members = getMembers(); // return member codes in array or null
//if members array is null, no filtering for member codes
var query = from tb in MemberTable
where tb.sex.Equals(Sex) &&
(members != null ? members.Contains(tb.membercode) : true)
select tb;
The code doesn't return correct result. It returns all members no matter what members[] is.
Actually the original LINQ is complex so if there are any other possible solutions, I do not want to write the following:
if (members == null){ /*LINQ1*/ }
else { /*LINQ2*/ }
which is not a good coding style.
Any suggestion for solving this problem?
var query = MemberTable.Where(x=>x.sex.Equals(Sex))
if (members != null)
query = query.Where(x=>members.Contains(x.membercode))
//use your query
query.ToList();
OR
var query = from tb in MemberTable
where tb.sex.Equals(Sex) &&
(members == null || members.Contains(tb.membercode))
select tb;
I prefer the first.
Since || short-circuits, you should be able to do this:
var query = from tb in MemberTable
where tb.sex.Equals(Sex) &&
(members == null || members.Contains(tb.membercode))
select tb;
The (members == null || members.Contains(tb.membercode)) subexpression will be true if members is null, so Contains would not be evaluated.
var list = new List<ModelName>();
list = ctx.MemberTable
.Where(c => c.sex==Sex)
.Where(c => c.membercode==true)
.ToList();

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.

Categories

Resources