LINQ Join: Object reference not set to an instance of an object - c#

I have a list that are generated from Linq to SQL. For simplicity it looks like this:
var myItems = (from my in db.MyTable
select new
{
UniqueID = my.ID,
UserName = my.UserName,
CreatedOn = my.CreatedOn
}).ToList();
This list contains 4 items.
And I have another:
var grid = (from q in AnotherLinqQuery
select new
{
UniqueID = q.ID,
Department = q.Department,
Comments = q.Comments
}).ToList();
This list contains 20 items.
All the ID's in myItems appear in grid.
Now I want to join it up with a left join.
var q = from A in grid
from B in myItems.Where(x => x.UniqueID == grid.UniqueID).DefaultIfEmpty()
select new
{
UniqueID = A.UniqueID,
Department = A.Department,
CreatedOn = B.CreatedOn
}
When I execute this, I get
Object reference not set to an instance of an object.
I've also tried other joins such as
from A in grid
from B in myItems.Where(x => x.UniqueID != null && x.UniqueID == grid.UniqueID).DefaultIfEmpty()

You are not joining correctly. Try this:
var q = from A in grid
join B in myItems on A.UniqueId equals B.UniqueId into LB
from B in LB.DefaultIfEmpty()
select new
{
UniqueID = A.UniqueID,
Department = A.Department,
CreatedOn = B.CreatedOn
};
You may want to refer to the documentation for further info on joining in linq.

Since you are doing a left join instead of an inner join, there will be no items from myItems for the 16 elements that are only in grid but not in myItems.
B will be null in that cases (as DefaultIfEmpty() creates a sequence with one null element), so you have to check for null here:
var q = from A in grid
from B in myItems.Where(x => x.UniqueID == grid.UniqueID).DefaultIfEmpty()
select new
{
UniqueID = A.UniqueID,
Department = A.Department,
CreatedOn = B?.CreatedOn ?? DateTime.MinValue // or whatever default value you like
}

Related

Implement ASP.NET MVC5 search functionality using 2 tables

I have created two tables: Claim and ClaimAttachments.
I'm trying to join them on ClaimID in order to get the filtered data from both the tables.
public ActionResult Index(int? search)
{
if (search!=null)
{
var Product = (from P in db.Claims
join C in db.ClaimAttachments on
P.ClaimID equals C.ClaimID
select new Claim
{
ClaimID = P.ClaimID,
ClaimBatchID = P.ClaimBatchID,
PatientControlNumber = P.PatientControlNumber,
PatientFirstName = P.PatientFirstName,
PatientLastName = P.PatientLastName,
ServiceFromDate = P.ServiceFromDate,
ServiceToDate = P.ServiceToDate,
});
return View(db.Claims.Where(x => x.ClaimID == search).ToList());
}
else
{
return View(db.Claims.ToList());
}
I'm able to get the searched result but from single table. The join is not working.
Currently you're only selecting from the Claims data:
return View(db.Claims.Where(x => x.ClaimID == search).ToList());
You have a join query just above that line of code:
var Product = (from P in db.Claims
join C in db.ClaimAttachments on
P.ClaimID equals C.ClaimID
select new Claim
{
ClaimID = P.ClaimID,
ClaimBatchID = P.ClaimBatchID,
PatientControlNumber = P.PatientControlNumber,
PatientFirstName = P.PatientFirstName,
PatientLastName = P.PatientLastName,
ServiceFromDate = P.ServiceFromDate,
ServiceToDate = P.ServiceToDate
});
But you don't do anything with the results of that query. It sounds like you meant to use the results of that query (which is in the Product variable, which incidentally should probably have a plural name since it's a collection) instead of just selecting from db.Claims. Something like this:
return View(Product.Where(x => x.ClaimID == search).ToList());
Note however that you're still only selecting data from one table. Though the join operation may alter the results of that selection. But the selection itself is here:
select new Claim
{
ClaimID = P.ClaimID,
ClaimBatchID = P.ClaimBatchID,
PatientControlNumber = P.PatientControlNumber,
PatientFirstName = P.PatientFirstName,
PatientLastName = P.PatientLastName,
ServiceFromDate = P.ServiceFromDate,
ServiceToDate = P.ServiceToDate
}
Notice how every value selected is from the P alias, which is defined here:
from P in db.Claims
So you're successfully joining the two tables, but only selecting data from one of the two tables. If you want to also select data from the other table then, well, you need to select data from the other table. For example, if there's a property on that table called SomeProperty that you want to select then you'd need to select it, and into an object which has that property.
For example, you might create a view model (let's call it ClaimViewModel as an example) which represents a combined record of the two tables, containing the properties you want from each. Then you'd select into that type:
select new ClaimViewModel
{
ClaimID = P.ClaimID,
ClaimBatchID = P.ClaimBatchID,
PatientControlNumber = P.PatientControlNumber,
PatientFirstName = P.PatientFirstName,
PatientLastName = P.PatientLastName,
ServiceFromDate = P.ServiceFromDate,
ServiceToDate = P.ServiceToDate,
SomeProperty = C.SomeProperty // <--- here
}
This would select the combined data into a list of ClaimViewModel objects, which you'd then filter based on your "search" and return to your view just like you do with the Claims objects now. And of course that view would need to be updated to expect a collection of ClaimViewModel objects instead of a collection of Claim objects.

Joining Collections and Assigning values

I have two collections and i want to join them based on key attribute and assign one collection's values to other. I am doing it in following way
var joinedData = from collection_one in Office.Employees
join collection_two in NewOffice.Employees
on collection_1.OfficeId equals collection_two.OfficeId
select new { collection_one, collection_two};
// Declare a new Collection
ICollection<Office.Employees> updatedCollection = New List<Office.Employees>();
// Assign New Collection_Two Values to Collection_One
foreach (var item in joinedData.ToList())
{
item.collection_one.Deleted = item.collection_two.Deleted;
updatedCollection .Add(item.obp);
}
this is not producing the right result. My Join is producing more records than it should for an inner join. Can anyone spot an issue ?
Try left join using DefaultIfEmpty() :
var updatedCollection =
(from collection_one in Office.Employees
from collection_two in NewOffice.Employees.Where(x => x.OfficeId == collection_one.OfficeId).DefaultIfEmpty()
where collection_two == null
select new {
collection_one,
collection_two
}).Select(x => {
x.collection_one.Deleted = x.collection_two.Deleted;
return x.obp;
});
But what is x.obp? Maybe result should be x.collection_one?

Linq to Entities Left outer join grouped into a collection

from component in Materials.OfType<Container>().Where(m => m.Active)
join segmentFinanceRating in segmentFinanceRatingView on component.Id equals segmentFinanceRating.MaterialId into segmentFinanceRatingGroup
from segmentFinanceRatingWithDefault in segmentFinanceRatingGroup.DefaultIfEmpty()
select new
{
id = component.Id,
name = component.Name,
subType = component.SubType,
size = component.Size,
MaterialIds = component.Materials.Select(x => x.Id),
BrandNames = component.Brands.Select(x => x.Name),
SegmentRatings = segmentFinanceRatingWithDefault
}
I have the above LINQ to Entities query that has a LEFT JOIN to get rating values for 1 or more segments for a given component.
The segmentFinanceRating entity has the properties, { MaterialId, SegmentId, Rating, LowRated }
At the moment the results are not grouped to the relevant component, i.e. the SegmentRatings property is not a single collection of segmentFinanceRating objects, instead I have multiple data rows with 1 segmentFinanceRating object in each.
I have seen some examples of using group x by y into z but I couldn't get it working, possibly due to some of the collections on the component that I need too, I'm not sure.
Any help would be appreciated on how to do this, thanks.
GroupBy in List doesn't work for you?
var list = (from component in Materials.OfType<Container>().Where(m => m.Active)
join segmentFinanceRating in segmentFinanceRatingView on component.Id equals segmentFinanceRating.MaterialId into segmentFinanceRatingGroup
from segmentFinanceRatingWithDefault in segmentFinanceRatingGroup.DefaultIfEmpty()
select new
{
id = component.Id,
name = component.Name,
subType = component.SubType,
size = component.Size,
MaterialIds = component.Materials.Select(x => x.Id),
BrandNames = component.Brands.Select(x => x.Name),
SegmentRatings = segmentFinanceRatingWithDefault
}).ToList().GroupBy(s=> s.SegmentRatings);
In this case it's much easier to do the join in the anonymous type:
from component in Materials.OfType<Container>().Where(m => m.Active)
select new
{
id = component.Id,
name = component.Name,
subType = component.SubType,
size = component.Size,
MaterialIds = component.Materials.Select(x => x.Id),
BrandNames = component.Brands.Select(x => x.Name),
SegmentRatings = (from segmentFinanceRating in segmentFinanceRatingView
where segmentFinanceRating.MaterialId == component.Id
select segmentFinanceRating)
}
You will have an empty collection of SegmentRatings when there are none for a specific component, giving the same effect as outer join.

Linq returned results as condition to another linq query

var query_1 = from m in db.tableA
join n in db.tableB
on m.OwnerID equals n.OwnerID into tabA
from a in tabA
join o in db.tableC
on m.OwnerID equals o.OwnerID
where m.UserID == userSessionID
&& m.ActiveStatus == 1
select new { OwnerName = m.OwnerName };
var query_2 = from m in db.tableD
join n in db.tableE
on m.OwnerID equals n.OwnerID into tabX
from a in tabX
join o in db.tableF
on m.OwnerID equals o.OwnerID
where {?????????????}
select new { ...... };
From the above code, query_1 will return me a list of elements retrieve from database. What I need is I only want the first item in the query_1 and using this element's column as a condition in my query_2. All this while i was using foreach to loop thru query_1 to get all the element but this time i only want the first item. Note that the list of query_1 may return me null and hence hardcode is not allow. Any luck?
after query_1;
var firstOwner = query_1.FirstOrDefault() ?? string.Empty;
then in query_2, assuming m (db.tableD) has an OwnerName property...
where m.OwnerName = firstOwner;
Version 2, If you need an OwnerId from query_1 :
change it to
select new { OwnerName = m.OwnerName, OwnerId = m.OwnerId };
than
var firstOwner = query_1.FirstOrDefault();
var firstOwnerId = firstOwner == null ? -1 : firstOwner.OwnerId;
and in query_2
where m.OwnerId = firstOwnerId;
you can use .FirstOrDefault() to get the first element from first query then you can use its value as needed.
You may also like to have a look at and this
Try putting it on a list then get the top most record as .First()?
List<object> first = new List<object>();
first = query_1.toList();
then on your query_2 put this:
where m.OwnerId = first.First();
EDITED:
Or simply put this on your query_2:
where m.OwnerId = query_1.First();

Linq using Select and an Indexer

I have the following query that runs successfully in LinqPad:
var results =
from container in Container
join containerType in ContainerType on container.ContainerType equals containerType
where containerType.ContainerTypeID == 2
select new { ContainerID = container.ContainerID, TypeID = container.ContainerTypeID};
results.Dump();
I would like to change the select to use an indexer so that the select would look something like this:
select new { ContainerID = container.ContainerID, TypeID = container.ContainerTypeID, ContainerIndex = index };
What I cannot seem to get right is the proper syntax for select to use the select indexer.
Thanks for your help.
You can't get the index with the query expression format, but there's an overload for Select that you can use in dot notation that will do it. You can stick to query expression format for the bulk of it, and then add the index in an extra select projection:
var tmp =
from container in Container
join containerType in ContainerType
on container.ContainerType equals containerType
where containerType.ContainerTypeID == 2
select new { ContainerID = container.ContainerID,
TypeID = container.ContainerTypeID};
var results = tmp.Select((x, index) => new { x.ContainerID, x.TypeID,
ContainerIndex = index });
I'm probably missing something, but if your items in Container already have property ContainerTypeID, I don't understand why you need the join. It appears to me that joining to ContainerType is not providing any extra properties that are required for this operation.
As such:
Container
.Where(c => c.ContainerTypeID==2)
.Select((c,i) => new {c.ContainerID, c.TypeID, Index = i})

Categories

Resources