I'm trying to setup a left-join query where I'm only pulling the FirstOrDefault record from the orders table, but it's not working the way I have it, is this even possible using LINQ?
var customerOrder = (from customer in customers
join orderJoin in orders.FirstOrDefault(x => x.CustomerId == customer.CustomerId) on customer.CustomerId equals orderJoin.CustomerId
join shippingJoin in shipping on customer.CustomerId equals shippingJoin.CustomerId into shippingGroup
from shipping in shippingGroup.DefaultIfEmpty()
select new
{
Customer.CustomerId,
Order = orderJoin,
Shipping = shipping
}).ToList();
make all your joins left joins and check for nullable values
var query = from company in companies
join product in products on company.CompanyId equals product.CompanyId into productleftjoin
from product in productleftjoin.DefaultIfEmpty()
join transaction in transactions on product.ProductId equals transaction.ProductId into transactionleftjoin
from transaction in transactionleftjoin.DefaultIfEmpty()
where transaction?.Cost * transaction?.Quantity>0
select new { company.CompanyName,productName=product.Name, extendPrice=transaction?.Cost* transaction?.Quantity };
You cannot use FirstOrDefaut as source of the query. There is antoher technnique with Take(1).DefaultIfEmpty(). Note that I have added default OrderBy because databse do not guarattee records order in such case and probably DateCreated or something like that should be used.
var customerOrder = (
from customer in customers
from orderJoin in orders
.Where(x => x.CustomerId == customer.CustomerId)
.OrderByDescending(x => x.Id)
.Take(1)
.DefaultIfEmpty()
join shippingJoin in shipping on customer.CustomerId equals shippingJoin.CustomerId into shippingGroup
from shipping in shippingGroup.DefaultIfEmpty()
select new
{
Customer.CustomerId,
Order = orderJoin,
Shipping = shipping
}).ToList();
Related
I have two tables: users and transactions. The transactions table stores user wallet transactions (+ balance, - balance) etc. I'm trying to obtain a list of users based on the current balance of their wallet ordered by largest first. (I can get the total balance by summing the Amount column in the Transactions table).
var query = from u in _context.users
join t in _context.Transactions on u.id equals t.UserId into gj
from txn in gj.DefaultIfEmpty()
where txn.TransactionStatus == Transaction.Status.Success && !u.Deleted.HasValue
select new
{
balance = gj.Sum(a => a.Amount),
user = u,
};
var result = await query.OrderByDescending(t => t.balance).Skip(offset).Take(range).ToListAsync();
I am getting the error:
The LINQ expression 'gj' could not be translated.
This is the equivalent SQL I'm trying to achieve:
SELECT balance, u.* FROM
(SELECT COALESCE(SUM(Amount), 0) as balance, u.id
FROM dbo.users u
LEFT JOIN dbo.Transactions t ON(u.id = t.UserId AND t.TransactionStatus = 0)
WHERE u.Deleted IS NULL
GROUP BY u.id) as tbl
JOIN dbo.users u ON(u.id = tbl.id)
ORDER BY balance DESC
Starting from users and using the navigation props to go through to Trans should make this trivial. Give something like this a go:
users.Where(u => u.Deleted == null)
.Select(u => new {
User = u,
Balance = u.Transactions.Where(t => t.Status == 0).Sum(t => t.Amount)
})
.OrderByDescending(at => at.Balance);
If the logic truly is "list all users but only add up transactions for users that are non deleted, and show deleted users as 0 balance" then:
users.Where(u => u.Deleted == null)
.Select(u => new {
User = u,
Balance = u.Deleted != null ? 0 : u.Transactions.Where(t => t.Status == 0).Sum(t => t.Amount)
})
.OrderByDescending(at => at.Balance);
Try not to write EF queries like "right, in SQL I would have this table and join that table, and it'd be a left join, and that would be summed... so I'll tell EF to do this context set join that context set, and it's a left join, and sum..." - EF will write joins for you; express your requirements in terms of how you want the C# object graph to be manipulated and let EF do the conversion to SQL how it can; use the navigation props between entities so it can work out how you want to bring your data together and arrange the necessary joins. It seldom needs micromanaging in a SQL-ey flavored approach
I have this query:
SELECT *
FROM public.lifecycle_data lifecycle_data
INNER JOIN
(SELECT *
FROM public.users
WHERE id = 123) AS t1 ON lifecycle_data.reference = t1.id
WHERE updated IS NULL
ORDER BY created DESC
I want to rewrite this query with EF Core LINQ and I tried this:
var users = db.LifeCycle
.Where(l => l.Updated == null)
.Join(db.Users,
l => l.Reference,
u => u.Id,
(lifeCycle, user) => new User()
{
Id = lifeCycle.Id,
FieldOne = user.FieldOne,
FieldTwo = user.FieldTwo,
Created = lifeCycle.Created
})
.Where(u => u.Id == 123)
.OrderBy(c => c.Created)
.ToList();
But it's interpreted as:
SELECT
l.id AS "Id", u.field_one AS "FieldOne", u.field_two AS "FieldTwo", l.created AS "Created"
FROM
lifecycle.lifecycle_data AS l
INNER JOIN
users.users AS u ON l.reference = u.id
WHERE
FALSE
ORDER BY l.created
It's the ORM's job to generate the query, especially the JOINs, from the relations between entities. There are no tables in EF (or any other ORM), there are entities. A DbContext isn't a model of the database and LINQ isn't a replacement for SQL.
The equivalent in EF Core 5 and later would be a Filtered Include, assuming User has a LifecycleData collection property:
var users = dbContext.Users
.Include(u=>u.LifeCycleData
.Where(l=>l.Updated==null)
.OrderBy(l=>l.Created))
.Where(u=>u.d==123);
I'm trying to create a linq query from the following sql.
Couldn't find an example that had a join in it with multiple tables and a group by that had different fields from different tables.
select t2.field1, t1.field1, count(t2.field1) from Table1 t1
join Table2 t2 on t1.pkfield = t2.pkfield
group by t2.field1, t1.field1
having count(*) > 1
order by t2.pkfield1
Tried the following:
var test =
from t1 in db.Table1
join t2 in db.Table2 on t1.pkfield equals t2.pkfield
group x by (t1.field1, t2.field1)
select t1.field1, t2.field2
You do not post entity classes and input examples so I can't verify my code. But I hope that concept is clear
db.Table1.SelectMany(t1 => db.Table2.Select(t2 => new { t2.field1, t1.field1, t2.pkfield1 }))
.GroupBy(x => new { t1Field1 = t2.field1, t2Field1 = t1.field1 })
.Where(g => g.Count() > 1)
.OrderBy(g => g.Min(x => x.pkfield1))
.Select(g => new { g.Key.t1Field1, g.Key.t2Field1, g.Count() });
In code above db is your custom DbContext and SelectMany will be translated into inner join
I don't know how to do it in a single query, but I have accomplished something similar as follows. Hope it helps you or at least points you in the right direction.
var result = from p in person
join o in orders on p.OrderId equals o.OrderId
// build your custom object with columns from multiple tables.
select new { o.OrderId, p.OrderId};
// now group the result
var grouped = result.GroupBy(x => x.OrderId)
.Where(y=> y.Count() > 1);
I want to perform 1 query using EF Core where I have to filter on a child entity and also apply paging. See my (sample) data model below:
I want to retrieve all data (customers, orders order details and products). I have to apply a filter on Order.OrderState and I only want the 1st 10 records (customers)
This is the LINQ query I tried:
var customers = await _ctx.Customer
.Include(c => c.Order.Where(o => o.OrderState == 0))
.ThenInclude(o => o.OrderDetail)
.ThenInclude(d => d.Product)
.Skip(0).Take(10)
.ToListAsync();
When executing this query I get the following error: InvalidOperationException: The property expression 'c => {from Order o in c.Order where ([o].OrderState == 0) select [o]}' is not valid. The expression should represent a property access: 't => t.MyProperty'. For more information on including related data
So I tried another query:
var qry = from c in _ctx.Customer
join o in _ctx.Order on c.Id equals o.CustomerId
join d in _ctx.OrderDetail on o.Id equals d.OrderId
join p in _ctx.Product on d.ProductId equals p.Id
where o.OrderState == 0
select new { Customer = c, Order = o, OrderDetail = d, Product = p };
var customers = await qry.Skip(0).Take(10).ToListAsync();
Now the query does not produce an error, but the result is not what I want. Because of the 1-n relationships this query returns customers multiple times in the result, so I do not get the 1st 10 customers.
Does anybody have a better query to get the results that I want?
I think you can use ".Distinct()" while building the query. Hope this will solve the problem.
var qry = (from c in _ctx.Customer
join o in _ctx.Order on c.Id equals o.CustomerId
join d in _ctx.OrderDetail on o.Id equals d.OrderId
join p in _ctx.Product on d.ProductId equals p.Id
where o.OrderState == 0
select new { Customer = c, Order = o, OrderDetail = d,
Product = p }).Distinct();
var customers = await qry.Skip(0).Take(10).ToListAsync();
I am trying to get all the Images and bullet_points of a single product. Images and Bullet_Points have foreign key Product_ID for reference. However I get a total of 40 rows as a result even when i only have 8 bullet_points ,5 images for a single product.what is it that i am doing wrong?
Below is my linq query that i am performing .
from p in Products
from i in Images
from s in Specifications
where p.ProductID==i.Product_ID && p.ProductID==5002
where p.ProductID==s.Product_ID && p.ProductID==5002
select new { p.ProductID,i.Image_URL,s.Bullet_Point}
Try the following query:
from p in Products
join i in Images on i.Product_ID equals p.ProductID into imgs
join s in Specifications on s.Product_ID equals p.ProductID into specs
where p.ProductID == 5002
select new { p.ProductID,
urls = imgs.Select(x => x.Image_URL),
bulletPoints = specs.Select(x => x.Bullet_Point) };
Why not to use navigation properties from Product? I can see your Product model has Images and Specifications properties. So you may also try:
Products.Where(p => p.ProductID == 5002).Select(p => new {
p.ProductID,
urls = p.Images.Select(x => x.Image_URL),
bulletPoints = p.Specifications.Select(x => x.Bullet_Point) })