Linq GroupJoin with DefaultIfEmpty - c#

I have this GroupJoin:
var groupjoin = cData.GroupJoin(
aData,
c => c.Id,
a => a.Id,
(c, joined) => new { c, a = joined.DefaultIfEmpty() })
.ToList();
In my test data, there are NO matches. So, I have this code:
var difference = groupjoin.FirstOrDefault(g =>
g.a == null);
I was expecting difference to be an anonymous object with a "c" property that was an object from cData, and an "a" property that was null.
However, g.a == null is never true, so FirstOrDefault gives me a null for difference. g.a is, in fact, a DefaultIfEmptyIterator and g.a.ToList() gives me a count of 1, and g.a.ToList[0] == null is true.
What have I done wrong here?

That's how DefaultIfEmpty works. This method returns a collection with one element (type parameter's default) if the collection is empty, not null.
So in your case, if there are no matches, joined.DefaultIfEmpty() will return a collection with just one element, that is null for reference types.
If you want null when joined is empty try something like this:
joined.Any() ? joined : null
You can read more about DefaultIfEmpty here.

Related

Using Linq when getting value from nested list

I need to set a variable's value to the value of a property that is nested in several Lists. Each list only has one item except for one list. I'm trying to do this:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.Where(x => x.Name == "Misc Expenses").myListE[0].price;
This produces a compile time error that says myListD does not contain a definition for myListE. What is the correct syntax?
After the .Where clause, you need to to .First() (or .ToList()) in order to apply the Where clause:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.Where(x => x.Name == "Misc Expenses").First().myListE[0].price;
Technically, though, you can replace that .Where with .First directly, too:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.First(x => x.Name == "Misc Expenses").myListE[0].price;

Returning Wrong value in LINQ .Any()

I have a method which return a List of Class object.
After getting the list I want to check whether a specific entry exists or not
Below is my query
var myList = GetMethod()
if(myList != null && myList.Select(x => x.Id=='MyId').Any())
{
// Do work
}
If the mylist is not null then myList.Select(x => x.Id=='MyId').Any() is always returning true even if a matching entry is not there.
Can someone help me in this?
That is because you need a Where over a Select:
if(myList != null && myList.Where(x => x.Id=='MyId').Any())
Now the Select ends up with an enumerable of booleans... Some are true, some are false.
You could simply that to:
if(myList != null && myList.Any(x => x.Id=='MyId'))
Select returns an IEnumerable of booleans whether the condition was true for each item or not. So, Any() is always returning true as long as there are items in your list.
Just use:
myList?.Any(x => x.Id == "MyId") == true;
Note that I don't like boolean comparisons like == true but this is for the null-check with ?. upfront.

MySQL Entity Framework With LinQ Correlated Query Not Executing

I am using Visual Studio 2012 with MySQL 5.7 Community Edition. I am getting object reference not set to an instance of object on the below query.
var oList = (
from dm in dbContext.document_master
join um in dbContext.user_master on dm.Alocated_CAA equals um.UserId
where (dm.DocumentHandle != null)
select new TaskLogReport
{
documentDate = dm.Document_Date,
documentHandle = dm.DocumentHandle,
fileNumber = dm.FileNumber,
statusRemarks = dbContext.statushistories
.Where(x => x.DocumentHandle == 12345678).FirstOrDefault().Remarks
}).ToList();
In the above query If I get change 12345678 to dm.DocumentHandle, then getting object reference not set to an instance object.
Try with MS-SQL server , working fine.
You get the error because FirstOrDefault() returns null, because there are no objects who's DocumentHandle equals dm.DocumentHandle. Changing dm.DocumentHandle to 12345678 works because there is one matching element.
This line is the problem:
.Where(x => x.DocumentHandle == 12345678).FirstOrDefault().Remarks
C#'s Enumerable.FirstOrDefault() method returns either the first matching element of an enumerable, or, if there aren't any, the default value of the type. In the case of nullable types, FirstOrDefault() returns null if no elements are found.
The reason you get no error when you use 12345678 is because there is at least one matching value with that DocumentHandle. However, when you change that to dm.DocumentHandle, no matching elements are found, causing FirstOrDefault to return null. Then you essentially do null.Remarks, which causes the error.
You should change your code to this:
.FirstOrDefault(x => x.DocumentHandle == dm.DocumentHandle)?.Remarks
There are two differences here:
Got rid of the where and moved the predicate to the FirstOrDefault call. This is just a cleaner way of doing what you were doing before.
Added the ? at the end of the FirstOrDefault call. That is the null propagation operator.
What it does is it turns this:
int foo;
if (bar != null)
{
foo = bar.foo;
}
Into this:
int foo = bar?.foo;
Now, if there are matching elements, statusRemarks will be equal to the first one's Remarks. Otherwise, statusRemarks will be null.
EDIT: The null propagation operator was implemented in C# 6.0, which is .Net 4.6 onwards, so it won't work in .Net 4.0.
You'll have to modify your query and implement a check, like so:
var oList = (
from dm in dbContext.document_master
join um in dbContext.user_master on dm.Alocated_CAA equals um.UserId
where (dm.DocumentHandle != null && dbContext.statushistories.Count(x => x.DocumentHandle == dm.DocumentHandle) > 0)
select new TaskLogReport
{
documentDate = dm.Document_Date,
documentHandle = dm.DocumentHandle,
fileNumber = dm.FileNumber,
statusRemarks = dbContext.statushistories.First(x.DocumentHandle == dm.DocumentHandle).Remarks
}).ToList();

Linq two left outer join Error Non-static method requires a target

I am unable to execute this error "Non-static method requires a target." and I am not sure why I am having this error.
The below query has four tables in it with one join and two left outer join. it is possible that the last left out join will contain a null row to match
var model = from p in Uow.Instance.RepoOf<RoleMenuMetrix>().GetAll()
from n in Uow.Instance.RepoOf<NavigationMenu>().GetAll().Where(q => q.Id == p.MenuId)
from m in
Uow.Instance.RepoOf<NavigationButton>()
.GetAll()
.Where(q => q.NavigationMenuId == n.Id)
.DefaultIfEmpty()
from o in // when I comment this from it works fine, but i donot get the values from this table.
Uow.Instance.RepoOf<RoleButtonMatrix>().GetAll().Where(q => q.ButtonId == m.Id).DefaultIfEmpty()
where p.RoleId == Guid.Parse("96246E99-6BF2-4A3D-8D2C-263DDEF2F97B")
&& n.IsActive && n.ApplicationName == "MEM"
select new
{
p.MenuId,
p.RoleId,
n.Name,
n.ParentId,
ButtonName = m != null ? m.ButtonName : String.Empty,
ButtonId = m != null ? m.Id : 0,
ischecked = o.RoleId == "96246E99-6BF2-4A3D-8D2C-263DDEF2F97B" ? "true" : "false"//// when I comment this it works fine, but i do not get the values from this table
};
Error Screen Shot it generates from the model variable
below is the Expected output required
The expression, reformatted to make things clearer:
Uow.Instance.RepoOf<RoleButtonMatrix>()
.GetAll()
.Where(q => q.ButtonId == m.Id)
.DefaultIfEmpty()
is used to populate o. But 1. that looks like it is returned a single object, and not a collection; 2. it will be null of the collection passed to DefaultIfEmpty is empty.
Due to a from clause needing a collection you get an error. I expect you can just drop the DefaultIfEmpty operator and let LINQ work with an empty collection when there is nothing that matches your criterion.

Lambda vs LINQ- "Expression is always false"

I have the following code:
var thing = (from t in things
where t.Type == 1 && t.IsActive
select t).SingleOrDefault();
if (thing == null)
{
// throw exception
}
things is a collection of Entity Framework Self-Tracking Entities
This works nicely, however I want to use a Lambda expression instead and changed the LINQ to this:
var thing = things.Select(t => t.Type == 1 && t.IsActive).SingleOrDefault();
Now Resharper is telling me Expression is always false for (thing == null).
What have I missed?
You want:
var thing = things.Where(t => t.Type == 1 && t.IsActive).SingleOrDefault();
Select performs a projection (converting the type of the IEnumerable from IEnumerable<Thing> to IEnumerable<bool> with values true if t.Type == 1 && t.IsActive == true, otherwise false), then the SingleOrDefault returns either the only bool in this sequence, or the default value of a bool which is false if the sequence is empty. This can never be null since bool is not a reference type.
Where performs a filtering action (pulling out only those objects that meet a given criterion - in this case only selecting those where Type is 1 and IsActive is true), leaving the type of the IEnumerable as IEnumerable<Thing>. Assuming Thing is a class, the SingleOrDefault will return the only item in the sequence or null.
In either case, SingleOrDefault will throw an exception if the sequence contains more than one item (which is far more likely in the Select version!).

Categories