How do I select a person only if their Id appears in another table? - linq

In the following LINQ statement, I'm trying to select people but only if their Id appears in another table (a join table). What am I missing?
In this example, I have a People table and a Contractors table. The Person's contactId may appear in the Contractors table. I want to grab the contactId of People that appear in the Contractors table.
var allPeople = People.Where(x => x.Contractors
.Where(m=> m.ContactID == x.ContactID)
.Select(x => x.ContactID));

How about regular LINQ JOIN statement:
var peopleWithContact = People.Join(
Contractors,
p => p.ContactId, // the field to join by from People table
c => c.ContactId, // the field to join by from Contractors table
(p, c) => p.ContactId); // the result if match; could be just p.

The Where function must return a boolean expression. If I understand your question correctly, you only want people from the peoples table that are in the contractors table as well. Then we can ask just that: Give me a person if any contractor has that ID:
var allPeople = People.Where(x => x.Contractors
.Any(m => m.ContactID == x.ContactID));

Related

LINQ Left Outer Join only the first record

I'm working on a LINQ query that joins three tables. For the Orders and OrderInfo table I expect a single record in each table for a given order id. However for the ShipRate table, there could be 0, 1 or more records for a given order id. So for this table I am using a left outer join. The query shown below is working if 0 or 1 records exist in the ShipRate table, but for instances where the number of records is > 1, I need to select only the most recent ShipRate record. I tried to do this by replacing the line:
from shipRate in sr.DefaultIfEmpty()
with this:
from shipRate in sr.OrderByDescending(r => r.CreateDate).Take(1).DefaultIfEmpty()
but the query takes forever, as if it is loading the entire ShipRate table. Where have I gone wrong?
var query = (from order in db.Orders
join info in db.OrderInfo
on order.OrderId equals info.OrderId
join shipRate in db.ShipRate
on info.OrderId equals shipRate.OrderId
into sr
from shipRate in sr.DefaultIfEmpty()
where order.OrderId == orderId
select new
{
OrderId = order.OrderId,
OrderDetail = info.OrderDetail,
Carrier = shipRate.Carrier
}).SingleOrDefault();
With a proper model definition your query would be like:
var query = (from order in db.Orders
where order.OrderId == orderId
select new
{
OrderId = order.OrderId,
OrderDetail = order.OrderInfo.OrderDetail,
Carrier = order.OrderInfo.ShipRates.OrderBy(sr =>sr.CreateDate).FirstOrDefault()
}).SingleOrDefault();
I can't be sure though, because you didn't supply sample data and model.
Cetin Basoz's answer is a good one: ideally you'd set up your model in a way that allows you to use navigation properties. If you're using a model generated from your database schema, that typically means setting up foreign and primary keys properly.
If you can't do that, you should still be able to get a similar effect by writing SQL like this:
var query = (from order in db.Orders
where order.OrderId == orderId
let orderInfo = db.OrderInfo.FirstOrDefault(info => order.OrderId == info.OrderId)
let currentShipRate = db.ShipRate
.Where(shipRate => info.OrderId == shipRate.OrderId)
.OrderByDescending(shipRate => shipRate.CreateDate)
.FirstOrDefault()
select new
{
OrderId = order.OrderId,
OrderDetail = orderInfo.OrderDetail,
Carrier = currentShipRate.Carrier
}).SingleOrDefault();
However, LINQ to SQL isn't nearly as good at building advanced queries as Entity Framework, and the symptoms you're describing might be an indication that it's actually doing multiple database round-trips instead of a join. I'd recommend logging the query that you're producing (prior to the .SingleOrDefault()) either by calling .ToString() on the query or by executing your query in LINQPad and clicking on the SQL tab. That might give you a clue as to why the query is misbehaving.
There seems to be a one-to-one relation between Orders and OrderInfos: every Order has exactly one OrderInfo, and every OrderInfo is the info of exactly one Order, namely the Order that the foreign key OrderId refers to.
On the other hand, there seems to be a one-to-many relation between Orders and ShipRates. Every Order has zero or more ShipRates, every ShipRate is a ShipRate of exactly one Order, namely the Order that the foreign key OrderId refers to.
You want several properties of "Orders, each Order with its one and only OrderInfo and its zero or more ShipRates"
Whenever you have a one-to-many relation, and you want "items with their zero or more sub-items", like Schools with their Students, Customers with their Orders, or in your case: Orders with their ShipRates, consider to use one of the overloads of Queryable.GroupJoin
In the other direction: if you want an item with its one and only other item that the foreign key refers to, like Student with the School he attends, Order with the Customer who created the Order, or Order with its one and only OrderInfo, use Queryable.Join.
I mostly use the overload of GroupJoin that has a parameter resultSelector, so I can select exactly what properties I want.
int orderId = ...
var ordersWithShipRates = dbContext.Orders.GroupJoin(dbContext.ShipRates,
order => order.Id, // from every Order take the primary key
shipRate => shipRate.OrderId, // from every ShipRate take the foreign key to Order
// parameter resultSelector: from every Order, with its zero or more ShipRates
// make one new
(order, shipRatesOfThisOrder) => new
{
// Select the Order properties that you plan to use:
Id = order.Id,
Date = order.Date,
...
ShipRates = shipRatesOfThisOrder.Select(shipRate => new
{
// Select the ShipRate properties that you plan to use:
Id = shipRate.Id,
Value = shipRate.Value,
...
})
.ToList(),
// A simple join to get the one and only OrderInfo
OrderInfo = dbContext.OrderInfos.Where(orderInfo => orderInfo.Id == order.Id)
.Select(orderInfo => new
{
// Select the orderInfo properties that you plan to use
Name = orderInfo.Name,
...
})
.FirstOrDefault(),
});

Transform Sql to EF Core Linq query

I am trying to translate the following query from SQL to EF Core. I can easily just use a stored procedure (I already have the SQL), but am trying to learn how some of the linq queries work. Unfortunately this is not by any means an ideal database schema that I inherited and I don't have the time to convert it to something better.
DECLARE #userId INT = 3
SELECT *
FROM dbo.CardGamePairs
WHERE EXISTS (SELECT 1
FROM dbo.Users
WHERE Users.Id = CardGamePairs.player1Id
AND Users.userId = #userId)
UNION
SELECT *
FROM dbo.CardGamePairs
WHERE EXISTS (SELECT 1
FROM dbo.Users
WHERE Users.Id = TableB.player2Id
AND Users.userId = #userId)
So basically I have an id that can exist in one of two separate columns in table b and I don't know in which column it may be in, but I need all rows that have that ID in either column. The following is what I tried to make this work:
//Find data from table A where id matches (part of the subquery from above)
var userResults = _userRepository.GetAllAsQueryable(x => x.userId == userId).ToList();
//Get data from table b
var cardGamePairsResults = _cardGamePairsRepository.GetAllAsQueryable(x => userResults .Any(y => y.userId == x.player1Id || y.userId == x.player2Id));
When I run the code above I get this error message:
predicate: (y) => y.userId == x.player1Id || y.userId == x.player2Id))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
Any ideas on how I can make this work? (I tried changing the column and table names to something that would actually make sense, hopefully I didn't miss any spots and make it more confusing.)
Because you are already have user id use it to query both columns.
var userResults = _userRepository
.GetAllAsQueryable(x => x.userId == userId)
.ToList();
var cardGamePairsResults = _cardGamePairsRepository
.GetAllAsQueryable(x => x.player1Id == userId || x.player2Id == userId));

Use linq to count in many to many table

I have two table with many-to-many relation ship: director(id, name), movie(id, name, year)
and the relation ship table is dir_mov(did,mid)
I want to use and group by the number of movie a director direct using linq.
How can i solve this?
You can use join and group by. Joining director table and relationship table is enough.
var result = yourDbContext.director
.Join(yourDbContext.Dir_Mov, d => d.Id, m => m.did,
(d, m) => new {DirectorId = d.Id, MovieId = m.mid})
.GroupBy(g => g.DirectorId)
.Select(s => new {s.Key, CountOfMovie = s.Count()}).ToList();
You have to change yourDbContext is your own context or repository.

Sorting issue with LINQ query and join using tables from different databases

I'm having trouble writing my LINQ query.
Here's the scenario:
I have 2 databases: A and B
In database A: I have a tableX which has the following fields: Employee ID, Name, Address, Phone, ..., Active
In database B: I have a tableY which has the following fields: Employee ID, Visible, Order
the number of records in table Y is less than or equal to the number of records in table X.
Basically, I need to extract the employee records from table X who have the attribute 'Visible' (in table Y) set to True and would like to sort them using the 'Order' attribute.
This is what I have so far:
ADataContext dbA = new ADataContext();
BDataContext dbB = new BDataContext();
//Get the list of records from tableY where 'Visbile' is set to True
var List = dbB.tableY
.Where(x => x.Visible == true).OrderBy(x => x.Order)
.ToList();
//Extract the list of employee IDs
IEnumerable<int> ids = List.Select(x => x.EmployeeID).Distinct();
var employees = dbA.tableX
.Where(x => ids.Contains(x.EmployeeID) && x.Active == true)
.ToList();
I'm able to get the correct list of employees, but cannot figure out how to apply the sorting order (present in tableY) on tableX
Currently, regardless of the order specified in tableY, the records returned from tableX are sorted as they were entered in the table (oldest to most recent).
Any ideas how I can fix my query.
Thanks,
I've re-written it all as a single query:
var employees =
from x in dbA.tableX
where x.Active
from y in dbB.tableY
where x.EmployeeID == y.EmployeeID
orderby y.Order
select x;

How do I return a record even if they have no records in a child table?

The following query returns a list of employees and their names, email address, and respective company names. The problem is with employees that do not have an email address in our system. If an employee doesn't have an email address, they are excluded entirely from the resultset.
How do I return null (or empty string, etc) for an email address that doesn't exist, rather than excluding the employee?
var employees = from e in Employees where e.ContactAttributes.Any (ca => ca.AttributeID == 19730317 )
from om in e.ChildOrganizationMaps
from ea in e.EmailAddresses
select new {e.FName, e.LName, ea.EmailAddress, om.Parent.CompanyName};
employees.Dump();
Your query is using joins. You can either use an outer join, or use Include for "eager loading" so that you will pull all entities and their relations:
var employees = Employees.Include("ChildOrganizationMaps").Include("EmailAddresses").Where(e=>e.ContactAttributes.Any(ca.AttributeID = 19730317)
.Select(e => new {e.FName, e.LName, e.EmailAddress.Address});
Use an outer join. It will return null for fields in the table which doesn't have a row.
Edit:
Ok, do it with explicit left join (DefaultIfEmpty) than.
var employees =
from e in Employees
from om in ChildOrganizationMaps
.where(o => e.pkKey == o.fkKey).DefaultIfEmpty()
from ea in EmailAddresses
.where(o => e.pkKey == o.fkKey).DefaultIfEmpty()
where
e.ContactAttributes.Any (ca => ca.AttributeID == 19730317 )
select new
{
e.FName,
e.LName,
ea.EmailAddress,
om.Parent.CompanyName
};

Resources