EF Core: How can i Select (Distinct) after join? - entity-framework-core

Basically i want this as query:
SELECT DISTINCT c.description FROM students s
join courses c on s.courseId = c.Id
WHERE c.Id = 100
How do i do this in EF Core?
When i do :
db.Students
.Include(s => s.courseId)
.Select( -- how can i select for course description? --)
.Distinct()
Bear with me. I am new to Entity Framework.

First of all, your Include expression handles the joining for you, so you don't have to specify how your going to join the table. So your include will look like this:
_db.Students.Include(s => s.Course)
Then you'd accomplish your select through the use of a lamba expression like this:
_db.Students .Include(s => s.Course).Select(s => s.Course.Description).Distinct()

Related

Use Inner Join in FromSql Entity Framework Core method

Is it possible to use Inner Join in the FromSql method of Entity Framework core?
I used, but I got this error:
SqlException: The column 'Id' was specified multiple times for 'c'.
This is my code:
return DbContext.Contacts
.FromSql<Contact>("Select * From Contacts Inner Join Phones on Phones.ContactId = Contacts.Id Where Contacts.Id <= 2 And Phones.PhoneNumber='01234567890'")
.Include(o => o.RegisteredByUser)
.AsNoTracking()
.ToListAsync();
Your Id column appears in both the tables, Phones and Contacts. Instead of putting *, you better choose the fields required in your query like following.
Please note, you need to specify which Id you want, If you want both, in that case you can use alias names like following.
Select Phones.Id as PhoneId, Contacts.Id as ContactsId, ....
Your final query should look like following query.
Select Contacts.Id, ... From Contacts Inner Join Phones on Phones.ContactId = Contacts.Id Where Contacts.Id <= 2 And Phones.PhoneNumber='01234567890

Translate nested query SQL to EF

I have this table structure:
Classic many to many relationship. I want to get all the orders for products belonging to the category for a small number of products I provide. It may be easier to show the SQL that does exactly what I want:
select o.*
from [Order] o join Product p2 on o.FKCatalogNumber=p2.CatalogNumber
where p2.FKCategoryId IN
(select c.Id
from Category c join Product p1 on p1.FKCategoryId=c.Id
where p1.CatalogNumber in ('0001', '0002')
This example gives me all the orders belonging to the categories that catalog #'s 0001 and 0002 are in.
But I am unable to wrap my head around the equivalent EF syntax for this query. I'm embarrassed to say I spent half the day on this. I bet it's easy for someone out there.
I came up with this but it's not working (and probably not even close):
string[] catNumbers = {"0001", "0002"};
var orders = ctx.Categories
.SelectMany(c => c.Products, (c, p) => new {c, p})
.Where(#t => catNumbers.Contains(#t.p.CatalogNumber))
.Select(#t => #t.p.Orders)
.ToList();
You can use query syntax (which looks very similar to SQL) in LINQ, so if you're more comfortable with SQL then you may prefer to write your query like this:
string[] catNumbers = {"0001", "0002"};
var orders = from o in ctx.Orders
join p2 in ctx.Products on o.FKCatalogNumber equals p2.CatalogNumber
where
(
from c in ctx.Categories
join p1 in ctx.Products on c.ID equals p1.FKCategoryId
where catNumbers.Contains(p1.CatalogNumber)
select c.ID
).Contains(p2.FKCategoryId)
select o;
As you can see, it's actually just your SQL query rearranged slightly, but it compiles as C#.
Note that:
the [Order] o syntax for referencing tables is replaced by o in ctx.Orders
LINQ enforces which way round you do the join condition so I had to flip your on o.FKCatalogNumber=p2.CatalogNumber to be on o.FKCatalogNumber equals p2.CatalogNumber
instead of your where p2.FKCategoryId IN (...), the equivalent c# is (...).Contains(p2.FKCategoryId)
the select comes last, not first
but those are the only major changes. Otherwise, it's written just like SQL.
I'd also draw your attention to a distinction regarding this comment:
the equivalent EF syntax for this query
The syntax here isn't specific to EF, but is just LINQ - Language Integrated Querying. It has two flavours: query syntax (sometimes called declarative) and method syntax (sometimes called fluent). LINQ works on just about any collection that implements IEnumerable or IQueryable, including EF's DbSet.
For more info on the different ways of querying, this MSDN page is a decent place to start. There's also this handy reference table showing the equivalent query syntax for each method-syntax operator, where applicable.
You can still nest queries in EF. The following looks like it works for me:
string[] catNumbers = {"0001", "0002"};
var orders = ctx.Orders
.Where(o => ctx.Products
.Where(p => catNumbers.Contains(p.CatalogNumber))
.Select(p => p.CategoryId)
.Contains(o.Product.CategoryId)
);
This produces the following SQL:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[CatalogNumber] AS [CatalogNumber]
FROM [dbo].[Orders] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Products] AS [Extent2]
INNER JOIN [dbo].[Products] AS [Extent3] ON [Extent2].[CategoryId] = [Extent3].[CategoryId]
WHERE ([Extent1].[CatalogNumber] = [Extent3].[CatalogNumber]) AND ([Extent2].[CatalogNumber] IN (N'0001', N'0002'))
)

Subqueries in DBIx::Class

I have spent too much time on this, and still cannot get the syntax to work.
Is this select statement possible in DBIx::Class?
"SELECT A.id, A.name, count(C.a_id) AS count1,
(SELECT count(B.id FROM A, B WHERE A.id=B.a_id GROUP BY B.a_id, A.id) AS count2
FROM A LEFT OUTER JOIN C on A.id=C.a_id GROUP BY C.a_id, A.id"
This code below works in DBIx::Class to pull the count for table 'C', but multiple efforts of mine to add in the count for table 'B' have repeatedly failed:
my $data= $c->model('DB::Model')
->search({},
{
join => 'C',
join_type => 'LEFT_OUTER',
distinct => 1,
'select' => [ 'me.id','name',{ count => 'C.id', -as => 'count1'} ],
'as' => [qw/id name count1 /],
group_by => ['C.a_id','me.id'],
}
)
->all();
I am trying to get two counts in one query so that the results are saved in one data structure. On another forum it was suggested that I make two separate search calls and then union the results. When I looked at the DBIx::Class documentation though, it mentioned that 'union' is being deprecated. Using the 'literal' DBIx::Class doesn't work because it's only meant to be used as a where clause. I do not want to use a view (another's suggestion) because the SQL will eventually be expanded to match upon one of the id's. How do I format this query to work in DBIx::Class? Thank you.
DBIx::Class supports subqueries pretty conveniently by using the as_query method on your subquery resultset. There are some examples in the cookbook. For your use case, it'd look something like:
# your subquery goes in its own resultset object
my $subq_rs = $schema->resultset('A')->search(undef, {
join => 'B',
group_by => [qw/A.id B.id/],
})->count_rs;
# the subquery is attached to the parent query with ->as_query
my $combo_rs = $schema->resultset('A')->search(undef, {
select => [qw/ me.id me.name /,{ count => 'C.id' }, $subq_rs->as_query],
as => [qw/ id name c_count b_count/],
});

How do you join two many to many tables in Entity Framework?

I have a few Tables I want to join together:
Users
UserRoles
WorkflowRoles
Role
Workflow
The equivalent sql I want to generate is something like
select * from Users u
inner join UserRoles ur on u.UserId = ur.UserId
inner join WorkflowRoles wr on wr.RoleId = ur.RoleId
inner join Workflow w on wr.WorkflowId = w.Id
where u.Id = x
I want to get all the workflows a user is part of based on their roles in one query. I've found that you can get the results like this:
user.Roles.SelectMany(r => r.Workflows)
but this generates a query for each role which is obviously less than ideal.
Is there a proper way to do this without having to resort to hacks like generating a view or writing straight sql?
You could try the following two queries:
This one is better readable, I think:
var workflows = context.Users
.Where(u => u.UserId == givenUserId)
.SelectMany(u => u.Roles.SelectMany(r => r.Workflows))
.Distinct()
.ToList();
(Distinct because a user could have two roles and these roles may contain the same workflow. Without Distinct duplicate workflows would be returned.)
But this one performs better, I believe:
var workflows = context.WorkFlows
.Where(w => w.Roles.Any(r => r.Users.Any(u => u.UserId == givenUserId)))
.ToList();
So it turns out the order which you select makes the difference:
user.Select(x => x.Roles.SelectMany(y => y.Workflows)).FirstOrDefault()
Have not had a chance to test this, but it should work:
Users.Include(user => user.UserRoles).Include(user => user.UserRole.WorkflowRoles.Workflow)
If the above is not correct then is it possible that you post your class structure?

Dynamic Query with Entity Framework 4

Okay I'm new to EF and I'm having issues grasping on filtering results...
I'd like to emulate the ef code to do something like:
select *
from order o
inner join orderdetail d on (o.orderid = d.orderid)
where d.amount > 20.00
just not sure how this would be done in EF (linq to entities syntax)
Your SQL gives multiple results per order if there's multiple details > 20.00. That seems wrong to me. I think you want:
var q = from o in Context.Orders
where o.OrderDetails.Any(d => d.Amount > 20.00)
select o;
I would do it like that:
context.OrderDetails.Where(od => od.Amount > 20).Include("Order").ToList().Select(od => od.Order).Distinct();
We are taking details first, include orders, and take distinct orders.