Linq Query - Multiple Joins to one table - c#

Account:
Id|Contact1|Contact2
Contact:
Id|status
I need a LINQ query that returns all of the accounts where contact1 and contact2 do not have a certain status.
var query = from a in accounts
join c1 in contact on a.contact1 equals c1.id
join c2 in contact on a.contact2 equals c2.id
where c1.status != 1 && c2.status != 1
select new {a.id}
I'm struggling with the logic. Clearly this isn't going to work, and doesn't. Just not sure how to join two tables on one in linq.
EDIT:
I discovered my issue is in most cases contact2 is null. I need some sort of conditional join that only occurs if a.contact2 is not null.
To clarify, the logic I'm attempting to achieve is: Retrieve all accounts where contact1 status does not equal 1. If contact1 == 1, check if contact2 == 1. If contact2 != 1 retrieve the account.
Running into issues because contact2 on the account is not always populated. When it's null, nothing is retrieved by my original query.

I think I've understood your logic correctly...
As long as either contact1 or contact2 on an account have a status other than 1 then retrieve the account. Only accounts where both contacts have a status of 1 are not returned. In the event that either contact is null it is ignored as you can't check the status.
var query = from a in accounts
from c in contacts
where c.Id == a.Contact1 || c.Id == a.Contact2
where c.Status != 1
select new { a.Id };

Related

Self Join in Entity Framework with Max()

I want to fetch record from self reference table parent record contain multiple child record from these multiple record i have to show maximum offer value ,I also want to show child record from another child table too my query like this
var query = (from pd in _context.Product_User_Transactions_Main
join od in _context.Product_User_Transactions_Yarn on pd.TransactionID equals od.TransactionID
into t
where (pd.UserPortalID == UserID && pd.TransactionTypeID == TransactionTypeID)
select new
{
pd.TransactionID,
pd.ProductName,
pd.Quantity,
pd.Price,
pd.TransactionTypeID,
pd.UOMID,
pd.UOM.UOM1,
pd.RefTransactionID,
pd.PaymentTermID,
pd.UserPortalID,
pd.UserSessionID,
pd.TransactionTime,
Offer = (from m in _context.Product_User_Transactions_Main join el in _context.Product_User_Transactions_Main on m.TransactionID equals el.RefTransactionID where el.RefTransactionID != null select el.Price).Max(),
pd.GEN_PaymentTerm.PaymentTermName,
Product_User_Transactions_Yarn = t.Select(p => new { p.YarnBlendID, p.YarnColorID, p.YarnCount, p.YarnDesc, p.YarnPly, p.YarnSourceID, p.YarnTypeID, p.TransactionID }),
}).ToList();
This query returns a wrong result as this query show Max of one record to all I want to show specific offer in front of it price. Where is my mistake? I don't know

Take any single record if matching records not available - Linq

I have linq query which has multiple records & I am filtering these records using a where clause.
Now, if the filtered records return nothing then I need to make it default to take default any single record from list.
var resultStaffGua = (from s in _db.Students
join sg in _db.StudentStaffGuardians on s.StudentID equals sg.StudentId
join g in _db.Staffs on sg.StaffId equals g.StaffID
join lr in _db.luRelationTypes on sg.RelationTypeId equals lr.RelationTypeID
join ga in _db.StaffAddresses on g.StaffID equals ga.StaffID
join ad in _db.Addresses on ga.AddressID equals ad.AddressID
where
lse.StatusID == (int?)Extension.StatusType.Active
&& lse.TenantID == tenantid
select new
{
g.FirstName,
g.LastName,
IsPrimary = sg.IsPrimaryGuardian,
se.Email,
Phone = sphon.PhoneNumber,
lr.RelationCD,
gdnr.GenderCD,
ad.Zipcode
}).Where(i=>i.IsPrimary==true);
if resultStaffGua count 0, I need one record from resultStaffGua. Thank you
if result count 0, I need one record from parentList.
Sometimes the obvious solution is the best. Why not add this after your code?
if (resultStaffGua.Count() == 0)
{
resultStaffGua = parentList.First();
}
If you want to be "clever" and do it all in one line (and I guess it would save a DB transaction too possibly) you could exchange your Where for an OrderBy and a Take.
So instead of:
).Where(i=>i.IsPrimary==true);
You could do:
).OrderBy( i => i.IsPrimary ? 0 : 1 ).Take(1);
This will prioritize any record that has an IsPrimary set to true, but it'll get one record regardless of whether any match.
Assuming that your intent is to retrieve one single record (there's at most one record with IsPrimary==true):
var query = (from s in...); //The whole query except the "where"
var resultStaffGua = query.SingleOrDefault(i=>i.IsPrimary==true) ?? query.First();
Otherwise, if the query could actually return more than one result:
var query = (from s in...);
var resultStaffGua = query.Where(i=>i.IsPrimary==true);
if(resultStaffGua.Count() == 0) resultStaffGua = new[] { query.First(); }

LINQ Null Join with Pivot Table

I'm trying to get a list of servers thay may or may not belong to 1 or more groups to display in a grid.
Example
ServerID IP GroupID
1 192.168.1.44 1
1 192.168.1.44 10
2 192.168.1.45 1
3 192.168.1.46 2
4 192.168.1.47 null
5 192.168.1.48 null
If I have no records In the GroupServer Table. (Since there is no groups or groups exist but they are not assigned) I expect to get something like this:
ServerID IP GroupID
1 192.168.1.44 null
2 192.168.1.45 null
3 192.168.1.46 null
4 192.168.1.47 null
5 192.168.1.48 null
Since is a Many-to-Many relationship. I have
Group Table
Server Table
GroupServer Table
I could not find a LINQ Pivot Table example.
So I tried to buid my own.
var query = (from sg in context.ServerGroups
join servers in context.Servers on sg.ServerID equals servers.ID
join groups in context.Groups on sg.GroupID equals groups.ID
into serverxgroup
from gAddrBilling in serverxgroup.DefaultIfEmpty()
select new
{
ServerID = sg.ServerID,
ServerIP = server.IP,
GroupID = sg.GroupID
});
The Query above does not retrieve anything
And I quiet dont understand what the "from gAddrBilling" is for. Since I modify a snippet I was trying to make work. So I wonder if someone has already faced a problem like this and give me some hint, snippet or advice about what is what I'm missing.
Thank you.
First, this is not a pivot query, but a regular query on many-to-may relationship via explicit junction table.
Second, looks like you are using Entity Framework, in which case you'd better define and use navigation properties rather than manual joins.
Third, and the most important, the structure of the query is wrong. If you want to get a list of servers that may or may not belong to 1 or more groups, then you should start your query from Servers (the table which records you want to be always included, not from link table where some ServerID are missing) and then use left outer joins to the other tables like this:
var query =
from s in servers in context.Servers
join sg in context.ServerGroups on s.ID equals sg.ServerID
into s_sg from sg in s_sg.DefaultIfEmpty() // make the above LEFT OUTER JOIN
// You can remove the next two lines if all you need is the GroupId
// and keep them if you need some other Group field in the select
join g in context.Groups on sg.GroupID equals g.ID
into sg_g from g in sg_g.DefaultIfEmpty() // make the above LEFT OUTER JOIN
select new
{
ServerID = s.ID,
ServerIP = s.IP, // or sg.IP?
GroupID = (int?)sg.GroupID
};

Converting a SQL join into LINQ with EF with filtering

I'm learning Entity Framework and attempting to convert an existing SQL query to a LINQ query, but struggling to convert it.
SELECT taskItems.Description,taskItemResponses.IsCompleted,TaskItemResponses.userId,TaskItemResponses.Notes
FROM TaskLists
LEFT JOIN TaskItems ON TaskLists.TaskListId = TaskItems.TaskListId
LEFT JOIN TaskItemResponses ON TaskItemResponses.TaskItemId = TaskItems.TaskItemId
AND TaskItemResponses.UserId = '1'
This works fine for me, it brings back the following data, always showing the list of Tasks, and if a user has responded to any of them, if they've completed it and what notes they've added.
Description IsCompleted userId Notes
Task A NULL NULL NULL
Task B NULL NULL NULL
Task C NULL NULL NULL
Task D 1 1 I've done this now.
Task E NULL NULL NULL
But when I'm trying to convert this to a LINQ query within C# I can't figure out the syntax, so far i've got
var query = from t in DbContext.TaskList
join ti in DbContext.TaskItem on t.TaskListId equals ti.TaskListId
join tr in DbContext.TaskItemResponse on ti.TaskItemId equals tr.TaskItemId into trj
from x in trj.DefaultIfEmpty()
where x.UserId == userId
select t;
But this isn't filtering for a particular UserId, and instead returns
Description IsCompleted userId Notes
Task A 0 2 Great
Task B 1 2 Okay
Task C 1 3 Nope
Task D 1 1 I've done this now.
Task E 0 5 Ok.
See that. May be you need ome minor changes.
var query = from t in DbContext.TaskList
join ti in DbContext.TaskItem on t.TaskListId equals ti.TaskListId
join tr in DbContext.TaskItemResponse on ti.TaskItemId equals tr.TaskItemId
where(tr.UserId == '1')
select new tempObjList
{
Description = taskItems.Description,
IsCompleted = taskItemResponses.IsCompleted,
userId = TaskItemResponses.userId,
Notes = TaskItemResponses.Notes
};
make "tempObjList" model class.
The correct way to convert SQL LEFT JOIN with right side filter to LINQ is to apply the right side filter before the join operator.
Here is the LINQ equivalent of your SQL query (of course you can correct the field names/types if needed):
var query =
from t in DbContext.TaskList
join ti in DbContext.TaskItem on t.TaskListId equals ti.TaskListId
into tij from ti in tij.DefaultIfEmpty() // left join
join tr in DbContext.TaskItemResponse.Where(x => x.UserId == userId) // filter
on ti.TaskItemId equals tr.TaskItemId
into trj from tr in trj.DefaultIfEmpty() // left join
select new
{
ti.Description,
IsCompleted = (bool?)tr.IsCompleted,
userId = (int?)tr.userId,
tr.Notes
};
In your EF code, the first join is an INNER join and your second one is a LEFT OUTER join
Have a look at these:
First Option
Second Option
Hope these could save you some time.

Simple left join in LINQ

I have a Private-Message Table and a User-Table.
I connect them here:
var messageUsers = (from pm in dc.PrivateMessages
join user in dc.Users
on pm.SenderID equals user.UserID
where !pm.IsDeletedRecipient && pm.RecipientID == id
select new PMInbox
{
SenderUsername = user.Username,
PrivateMessageID = pm.PrivateMessageID,
SenderID = pm.SenderID,
Subject = pm.Subject,
Text = pm.Text,
SenderType = pm.SenderType,
IsDeletedRecipient = pm.IsDeletedRecipient,
IsDeletedSender = pm.IsDeletedSender,
IsRead = pm.IsRead,
Timestamp = pm.TimestampSend
}).ToList();
How to make this in a left-join, so if the userID is not the SenderID (e.g. The Sender is deleted allready), the Username should be empty?
var messageUsers = (
from pm in dc.PrivateMessages
where !pm.IsDeletedRecipient && pm.RecipientID == id
select new PMInbox {
SenderUsername = (
from user in dc.Users
where user.UserID == pm.SenderID
select user.Username
).SingleOrDefault(),
PrivateMessageID = pm.PrivateMessageID,
//...
}
).ToList();
In general, to achieve a Left outer join in LINQ, you should be using SingleOrDefault or possibly FirstOrDefault when you wish to include 1 or 0 rows of the joined "table", and DefaultIfEmpty when you may need to include several lines (in my experience, that's a less common occasion, however). In your specific case, it sounds like UserID is a unique identifier for Users - as such, I translated your left join into a SingleOrDefault call rather than a DefaultIfEmpty call.
Unlike SQL, .NET cannot resolve properties on null values. So if you do something like user.Username and user is null, you'll get a NullReferenceException. You can then either check for null every single time you access user, or you can project (i.e. select) before calling SingleOrDefault or DefaultIfEmpty. After all, an empty sequence of users selects an empty sequence of usernames - which, after SingleOrDefault creates a name or null without the need for any manual null-checking.
Do a group join, then unpack the group by querying it. The DefaultIfEmpty generates a null element when the collection is empty.
var messageUsers = (
from pm in dc.PrivateMessages
join u in dc.Users
on pm.SenderID equals u.UserID
into users
from user in users.DefaultIfEmpty()
where !pm.IsDeletedRecipient && pm.RecipientID == id
...
Also, if you have Associations set up between the tables in the dbml, there should be a relational property from PrivateMessage to User that can be used to express the query more simply. (I'm naming that property Senders, but it might be auto-named Users or Users1. You can rename the property in the dbml.)
var messageUsers = (
from pm in dc.PrivateMessages
where !pm.IsDeletedRecipient && pm.RecipientID == id
from user in pm.Senders.DefaultIfEmpty()
...

Categories