Entity Framework: How do I invoke pairs of entities from a raw query - entity-framework

For instance, I have a query:
SELECT * FROM
persons
LEFT JOIN vehicles
ON persons.Id = vehicles.OwnerId
I would like execute this query on an EF data context and have array of pairs "person-vehicle". how do I do it?
Another example:
SELECT persons.*, COUNT(vehicles.*) as cnt FROM
persons
JOIN vehicles
ON persons.Id = vehicles.OwnerId
GROUP BY vehicles.Id
Here I want to have a dictionary of a person as a key and number of vehicles he owns as a value.
I know that these quesies are simple enough and it's better to avoid raw sql in these cases. But I want to know possibilities of raw query handling, because real life queries can be much more complex.

You probably want to do some reading ion LINQ to Entities. https://msdn.microsoft.com/en-us/library/vstudio/bb386964(v=vs.100).aspx
The first one is pretty basic:
var persons = context.Persons
.Include(p => p.Vehicles)
.ToList();
The second one is a little more advanced:
var persons = context.Persons
.Select(p => new { Person p, VehicleCount = p.Vehicles.Count() }
.ToList();
You could also do a group by which is described in the link.

Related

Query many to many relationship in Linq and EFCore

I'm trying to do the following query in linq, however I'm getting an exception error, though my query looks fine to me. So here is the story:
Diagram
I have a many to many relationship between the users and the organizations. A user can be a part of many organizations, and an organization can have many users.
What Im trying to query
So given a user id, i want to query all the team members (users) i have in all the organizations i belong to. So
Input: User X id (guid), and this user belongs to Organization A, and Organization B
Output:
User A, Organization A
User B, Organization A
User C, Organization B
The Actual Query
I though this would do just that
var user = db.Users.Include(q => q.UserOrganization).SingleOrDefault( q => q.Id == id.ToString());
var members = (from us in db.Users.Include(q => q.UserOrganization)
let orgs = user.UserOrganization.Select(z => z.OrganizationId)
where us.UserOrganization.Any(q => orgs.Contains(q.OrganizationId) )
select new UserResource{
id = Guid.Parse(us.Id),
email = us.Email
}
).ToArray();
My query fails on the where clause, with the error:
Processing of the LINQ expression 'AsQueryable<long>((Unhandled parameter: __Select_0))' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core
Not sure what to change in the query. Please help.
PS: I wrote the query initially in MySql as follows:
SELECT UU.`Id`, UU.`Email`, UUO.`OrganizationId`
FROM aspnetusers AS UU
LEFT JOIN userorganization AS UUO ON UUO.`UserId` = `UU`.Id
WHERE UUO.`OrganizationId` IN
(
SELECT UO.`OrganizationId` FROM aspnetusers AS U
LEFT JOIN userorganization AS UO ON UO.UserId = U.Id
WHERE u.Id = '6caa67e7-69f3-49a3-ad61-10b07d379f10'
)
AND UU.Id != '6caa67e7-69f3-49a3-ad61-10b07d379f10'
The "SingleOrDefault" always executes the Query. User is not an IQueryable.
So the let orgs = user.UserOrganization.Select(z => z.OrganizationId) cannot be translated to SQL, do your var orgs = user.UserOrganization.Select(z => z.OrganizationId) before the Query, in Plain C#. This cannot be used in SQL-Queries.
With orgs being an IList<int> it will work.
But it should be prefered to find a solution that can be solved with one query only. Here you have two.
The SingleOrDefault might be not useful, you go better without, than you have a simple IQueryable. And The "Any" can most often be realized with a simple (Inner) Join, returning only values, if you have a match between to tables. That is the Same as Where - Any - Contains

Selecting Subcollections in Union-Query

I'm trying to select objects from the database with Entity Framework into an anonymous type. When using Union and Selecting a Subcollection, I get an exception:
System.ArgumentException: The 'Distinct' operation cannot be applied to the collection ResultType of the specified argument.
My model contains several types derived from BaseType. This base type has a reference to RefType which contains a collection of ItemType. The types derived from BaseType are stored in separate tables, thus the Union.
The query looks like this:
var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items });
q1.Union(q2).ToList();
But to reproduce the error, you can even union queries of the same type, as long as you select a collection.
I would do the select after the union, but to union Type1, Type2, etc. I must cast them to BaseType, which is not allowed in LINQ-to-SQL.
Any way to do this in the same query?
The exception emerges from Entity Framework's query generation pipeline when the ExpressionConverter tries to translate the expression q1.Union(q2) into SQL.
In a valid query you'll see that EF adds a DISTINCT clause to the SQL query. A type with collection properties (x.Ref.Items) doesn't pass as a valid argument for a Distinct operation and EF throws the exception you see.
Unfortunately, using Concat instead of Union is not a valid work-around. EF will also throw an exception:
The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'
Which means that it's simply not supported to concat nested queries containing types with collection properties.
So you have to do the Union in memory:
var result = q1.AsEnumerable() // Continues the query in memory
.Union(q2).ToList();
C# doesn't have any problem with equating anonymous types containing collections: it simply considers any collection member as unequal to another collections member. This does mean that the query can produce a collection containing non-unique results (same Id, same Items) which may not be expected when relying on Union's implicit Distinct.
I am not sure why, for some reason distinct is failing, maybe because it is anonymous type, and it is still IQuerable, I would suggest firing the query something like this
var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
q1.Union(q2).ToList();
Note that in this case, Distinct will check for all properties equality, meaning if 2 objects have the same id but different items, both will be there.
if you don't care about distinct values, you can also use concat
if you care about distinct and first option didn't work, you can use group by and implement your own distinct,
something like this
var q1 = ctx.Set<Type1>().Select(x => new { Id = x.Id, Items =x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { Id = x.Id, Items = x.Ref.Items });
//this will group by id, and select the first object items
var qFinal = q1.concat(q2).GroupBy(e => e.id)
.select(e => new {e.key, e.First().Items})
.ToList();
maybe you don't want First(), you can use whatever you want

Entity Framework Conditional Count of Navigation Property 2 levels down

Just starting out with Entity Framework and am trying to work out how you would do something like this....
Say I have the following entities, Customers that have Orders that have OrderLineItems which are linked to Products. I would like to return the name of every customer with a count of the number of times they have ordered a particular product.
I have seen examples of using .Count() but these have always been for the first navigation property i.e. number of orders per customer.
Would appreciate some guidance here.
Something like this should work, where context is your DbContext instance.
It will return an IEnumerable<dynamic>, although obviously you could make a class to hold the results.
// The product to count
var productId = 12345;
context.Customers.Include("Orders.OrderLineItems.Products")
.Select(customer =>
new {
CustomerName = customer.Name,
ProductCount = customer.Orders
.SelectMany(o => o.OrderLineItems)
.SelectMany(i => i.Products.Where(p => p.Id = productId).Count()
});
The Include() extension method is useful, it will make sure that the resulting SQL query joins the relevant tables together - otherwise multiple queries would be executed for each customer (one to get orders, another for line items and a final one for products).

entity framework 4.0 multiple joins

This is my real world example.
I have 4 tables:
Person
Plan
Coverage
CoveredMembers
Each person can have many plans, each of those plans can have many coverages. Each of those coverages can have many CoveredMembers.
I need a query that will apply a filter on Plan.PlanType == 1 and CoveredMembers.TermDate == null.
This query should bring back any person who has a medical type plan that is not terminated.
This SQL statement would do just that:
SELECT Person.*, Plans.*, Coverages.*, CoveredMembers.*
FROM Person P
INNER JOIN Plan PL ON P.PersonID = PL.PersonID
INNER JOIN Coverage C on PL.PlanID = C.PlanID
INNER JOIN CoveredMember CM on C.CoverageID = CM.CoverageID
WHERE CM.TermDate = NULL AND PL.PlanType = 1
I have figured out how to do this using anonymous types, but I sometimes need to update the data and save back to the database - and anonymous types are read only.
I was given a solution that did work using JOIN but it only brought back the persons (albeit filtered the way I needed). I can then loop through each person:
foreach (var person in persons) {
foreach (var plan in person.Plans{
//do stuff
}
}
But wouldn't that make a db call for each iteration of the loop? I have 500 persons with 3 unterminated medical plans each, so it would call the db 1500 times?
This is why I want to bring the whole data tree from Persons to CoveredMembers back in one shot. Is this not possible?
I believe this is accomplished in two parts:
Your query to determine the people you wish to have returned based on your criteria as discussed in this question previously: Entity framework. Need help filtering results
Properly setting the navigation properties for entities you want brought together to be eagerly loaded: http://msdn.microsoft.com/en-us/data/jj574232.aspx
For example if your Person entity looks like:
public class Person {
public List<Plan> Plans {get; set;}
...
}
When returning data from the dbcontext you can also use explicit eager loading with the include option:
var people = context.People
.Include(p => p.Plans)
.ToList();
....
If these are nested - coverage is part of plan, etc (which it looks like, it goes something like):
var people = context.People
.Include(p => p.Plans.Select(pl=>pl.Coverage).Select(c=>c.CoveredMembers)))
.ToList();
....
I am making some assumptions about your data model here, and my code above probably needs a little tweaking.
EDIT:
I might need someone else to weigh in here, but I don't think you can add the where clause into an include like that (my example above leads you that way a bit by putting the include on the context object, instead return an IQueryable with your conditions set as solved in your first post (without a ToList() called on it) and then use the code you wrote above without the Where clauses:
From first post (you supplied different criteria in this one, but same concept)
var q = from q1 in dbContext.Parent
join q2 in dbContext.Children
on q1.key equals q2.fkey
join q3 in ........
where q4.col1 == 3000
select q1;
Then:
List<Person> people = q.Include(p => p.Plans
.Select(pl => pl.Coverages)
.Select(c => c.CoveredMembers).ToList();
Again, doing this without being able to troubleshoot - I am sure it would take me a few attempts to iron this one out too.

one to many join - taking only the last one on the many part

I'm quite a newbie in EF, so I'm sorry if my question has been answered before.. I just can't figure out the syntax..
I have two entities, Category & Product, where one category has many products.
I want to get all categories, with only their latest product (it has a date property named timestamp)
I have no idea how to do that. :-/
If possible I'd like to know the syntax of the two ways to write it, both the sql-like syntax, and the C# like syntax, e.g.:
ctx.Categories.Include("Products").ToList()
from c in ctx.Categories.Include("Products")
Thanks!
Here's the SQL-like way:
var categories =
from p in products
group p by p.Category into g
select new { Category = g.TheKey, LatestProduct = g.Max(p => p.TimeStamp) };
This is the Lambda-way (warning, untested):
var categories = products.GroupBy(p => p.Category)
.Select(g => new { Category = g.TheKey,
LatestProduct = g.Max(p => p.TimeStamp)});
A note on Categories.Include("Products"), you don't need this in your example. You use "Include" for eager-loading, so that for example if you had a list of Categories returned from EF, when you do Categories.Product you will get the associated product.
But all you require is a list of categories, and a single product for each one - which is already returned in the above LINQ query, so no need for Include.