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

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.

Related

How to correct my code. To simplify my answer in a single array result

Here's my code. I was wondering if how can I store the value in a single variable array format. I don't know the next step. I am newly in LINQ and C#.
var result = (from x in _context.DwPropertyMasters
where
x.ShowMapPoint == "Y"
select new
{
x.LandId,
a = x.Development == null || x.Development == "" ? x.Location : x.Development,
x.MapPointX,
x.MapPointY,
AreaSize = x.AreaSize ?? 0,
Premium = x.Premium ?? 0,
b = (x.Premium == 0 ? null : x.Premium) * 100000000 / (x.AreaSize == 0 ? null : x.AreaSize),
c = (from z in _context.DwPropertyDetails
where (z.TransactionPrice > 0 || z.TransactionPrice != null) && z.LandId == x.LandId
group z by z.LandId into g
select (g.Sum(p => p.TransactionPrice) == 0 ? null : g.Sum(p => p.TransactionPrice)) / (g.Sum(p => p.ActualSize) == 0 ? null : g.Sum(p => p.ActualSize))).FirstOrDefault(),
d = (x.AreaSize2 == 0 ? null : x.AreaSize2) == 0 ? 0 : (x.Premium == 0 ? null : x.Premium) * 100000000 / (x.AreaSize2 == 0 ? null : x.AreaSize2),
x.LandType,
e = (from y in _context.DwPropertyDetails
where (y.TransactionPrice > 0 || y.TransactionPrice != null) && y.LandId == x.LandId
select new
{
a = 1
}).Count()
}).ToArray();
return View(result);
The result I get is like this:
[
{
"LandId":1,
"a":"2GETHER",
"MapPointX":"22.37607871816074",
"MapPointY":"113.96758139133453",
"AreaSize":118046,
"Premium":5.51,
"b":4667.671924,
"c":13198,
"d":4148.815215,
"LandType":"PROPERTY",
"e":169
}
]
All I want is like this:
[1,'2GETHER',22.37607871816074,113.96758139133453,118046,5.51,4668.00000000000000000000,13198,4149.00000000000000000000,'PROPERTY',169]
Something like this?
return View(new object[] {
result.LandId,
result.a,
result.MapPointX,
result.MapPointY,
result.AreaSize,
result.Premium,
result.b,
result.c,
result.d,
result.LandType,
result.e
});
Just to expand a bit on the previous answer from devio, when you do:
.Select( new { ... } ).ToArray()
You're actually telling it to give you an array of new dynamically created objects. When the dynamic object is created, it derives property names automatically for you. It ends up looking like a dictionary of key value pairs. You could try to force it to give you an object array instead of a dynamic type. Something like:
var result = _context.DwPropertyMasters
.Where( x => x.ShowMapPoint == "Y")
.Select( x => new object[] { x.LandId, x.MapPointX, ... })
.ToArray();
The difference is that instead of asking it for an array of dynamic objects, you're asking it for an array of object arrays.

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

Declaring anonymous type as array correctly to keep scope

All I want to do is declare var place correctly so it is still in scope once I get to the foreach loop. I'm assuming I need to declare it before the if statement for connections. Is this a correct assumption and if so how do I declare it? Thanks!
using (var db = new DataClasses1DataContext())
{
if (connections == "Connections")
{
var place = (from v in db.pdx_aparts
where v.Latitude != null && v.Region == region && v.WD_Connect >= 1
select new
{
locName = v.Apartment_complex.Trim().Replace(#"""", ""),
latitude = v.Latitude,
longitude = v.Longitude
}).Distinct().ToArray();
}
else
{
var place = (from v in db.pdx_aparts
where v.Latitude != null && v.Region == region && ((v.WD_Connect == null) || (v.WD_Connect == 0))
select new
{
locName = v.Apartment_complex.Trim().Replace(#"""", ""),
latitude = v.Latitude,
longitude = v.Longitude
}).Distinct().ToArray();
}
foreach (var result in place)
....
You can create an array with a single entry whose value you ignore later:
// Note: names *and types* must match the ones you use later on.
var place = new[] { new { locName = "", latitude = 0.0, longitude = 0.0 } };
if (connections = "Connections")
{
// Note: not a variable declaration
place = ...;
}
else
{
place = ...;
}
This works because every use of anonymous types using properties with the same names and types, in the same order, will use the same concrete type.
I think it would be better to make the code only differ in the parts that it needs to though:
var query = v.db.pdx_aparts.Where(v => v.Latitude != null && v.Region == region);
query = connections == "Connections"
? query.Where(v => v.WD_Connect >= 1)
: query.Where(v => v.WD_Connect == null || v.WD_Connect == 0);
var places = query.Select(v => new
{
locName = v.Apartment_complex
.Trim()
.Replace("\"", ""),
latitude = v.Latitude,
longitude = v.Longitude
})
.Distinct()
.ToArray();
Here it's much easier to tell that the only part which depends on the connections value is the section of the query which deals with WD_Connect.
You could convert the if to a ?:.
var place = connections == "Connections" ? monsterQuery1 : monsterQuery2;
I do not think this is a good solution because your queries are too big (unreadable).
It would be much better if you introduced a named class that you use instead of the anonymous type. R# does that for you in a "light bulb menu" refactoring.
you could just use the 1 query since they are pretty much the same, and just add the extra condition in the where clause
var place = (from v in db.pdx_aparts
where v.Latitude != null && v.Region == region
&& connections == "Connections"
? v.WD_Connect >= 1
: ((v.WD_Connect == null) || (v.WD_Connect == 0))
select new
{
locName = v.Apartment_complex.Trim().Replace(#"""", ""),
latitude = v.Latitude,
longitude = v.Longitude
}).Distinct().ToArray();
foreach (var result in place)
....

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.

Combining 2 LINQ into one call

I'm using 2 similar LINQ queries to return a result, the only difference is the where clause (&& s.OptIn == "Yes"). Is there a way to execute this with only one query?
Instead of having a result of
A 2
B 3
and another result of
A 1
B 1
I want to have
A 2 1
B 3 1
Here's the LINQ:
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
group 1 by new { ce.EventID } into d
select new {
EventID = d.Key.EventID,
Count = d.Count()
};
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
&& s.OptIn == "Yes"
group 1 by new { ce.EventID } into d
select new {
EventID = d.Key.EventID,
Count = d.Count()
};
You can supply a predicate in the Count method. An example is below:
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
var counts = new { CountAll = list.Count(), CountEven = list.Count(i => i % 2 == 0) };
Console.WriteLine(counts.CountEven);
A similar query written for Linq-To-Entities also worked and produced working SQL.
I haven't fully reconstructed your sample, but you should be able to rework it to something like this.
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
group new { s, e, ce } by new { ce.EventID } into d
select new
{
EventID = d.Key.EventID,
Count = d.Count(),
CountOptIn = d.Count(item => item.s.OptIn == "Yes")
};
IQueryable<ScanLog> scanlogs = pdc.ScanLogs;
if (filter) scanlogs = scanlogs.Where(...);
var result = from s in scanlogs
...

Categories

Resources