ASP.NET Core / EF Core LINQ statement - linq

I have 3 related entities:
PurchaseOrder (Id, ProjectId, Source, CompanyId, ...)
Company (Id, Name, ...)
Contacts (Id, CompanyId, Email, AutoEM, ...)
Each purchase order has a related company and each company can have one or more contacts where one of them is set as the primary or default contact (eg: AutoEM == true).
I would like the LINQ to return a set of PurchaseOrder rows for a selected ProjectId and include, on each row, the company name its for, and the contact email for the primary contact for that company.
I was able to get the PO and Company info displayed on my page, but have not been able to figure out how to include the primary contact which has AutoEM == true. Thank you in advance for any help.
pos = context.PurchaseOrder
.Where(p => p.ProjectId == Int32.Parse(id))
.Include(p => p.company)
.ToList();

So, you want to include the primary contact which has AutoEM == true.
The condition (where statement) should be applied to the include of Contacts. The use of "ThenInclude" is to guarantee that related Company is added first and then including related Contacts where AutoEM == true.
I suggest the following update to the code:
pos = context.PurchaseOrder
.Where(p => p.ProjectId == Int32.Parse(id))
.Include(p => p.company)
.ThenInclude(p => p.Contacts.Where(c => c.AutoEM == true))
.ToList();

Related

Unable to translate the LINQ expression while using Group by

I have an Asp.net core web API that uses EF Core DB first approach. I have the following Tables in my SQL server database.
Application table - contains a list of Applications.
Role table - contains a list of Roles (user, admin, Super Admin, etc.)
User table - contains a list of Users
User Role table - contains a mapping between User and Role table
Feature tables - contains a list of Features (Home page, user management, etc.)
RoleFeature table - contains the mapping between Feature and Role table
I am trying to get the RoleName and list of features for the given UserId and appId.
Below is the Linq query I have so far:
RoleDto role =
from a in ctx.Application.Where(x => x.ApplicationId == appId)
from r in ctx.Role.Where(x => x.ApplicationId == a.ApplicationId)
from ur in ctx.UserRole.Where(x => x.UserId == userId && x.RoleId == r.RoleId)
from rf in ctx.RoleFeature.Where(x => x.RoleId == ur.RoleId)
from f in ctx.Feature.Where(x => x.FeatureId == rf.FeatureId).Where(x => x.IsActive)
group new { r.RoleName, f.FeatureId } by ur.RoleId into g
select new RoleDto
{
Name = g.Select(x => x.RoleName).FirstOrDefault(),
FeatureIds = g.Select(x => x.FeatureId).ToList()
}.AsNoTracking()
However, I am getting an error saying unable to translate the LINQ expression.
The problem is with the FirstOrDefault() in the select section of the "groupby" query.
You are probably using ef core version higher than 2.1 and there are some changes in handling groupby query. You should take a look at the following link :
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.1#linq-groupby-translation
Before version 2.1, in EF Core the GroupBy LINQ operator would always
be evaluated in memory. We now support translating it to the SQL GROUP
BY clause in most common cases.
So the query should be translated to SQL GROUP BY, but methods such as FirstOrDefault() can not be translated. As a fast solution you can change FirstOrDefault() with Max(), Min() or other aggregate functions that are supported in DB.
You don't need to select the Application. You can start by selecting Role directly as -
from r in dbCtx.Role.Where(x => x.ApplicationId == appId)
This will simplify the final SQL generated by EF. So the query will be faster.
If a User has multiple Role then you are trying to take the first one. You should do that when you are selecting Role as -
from r in ctx.Role.Where(x => x.ApplicationId == a.ApplicationId).Take(1)
Finally, you can fetch a list of RoleName and FeatureId, and then do the grouping on client side -
var query =
from r in dbCtx.Role.Where(x => x.ApplicationId == appId).Take(1)
from ur in dbCtx.UserRole.Where(x => x.UserId == userId && x.RoleId == r.RoleId)
from rf in dbCtx.RoleFeature.Where(x => x.RoleId == ur.RoleId && x.Feature.IsActive)
select new
{
RoleName = rf.Role.RoleName,
FeatureId = rf.FeatureId
};
var roleDto = query.AsNoTracking()
.AsEnumerable()
.GroupBy(p => p.RoleName)
.Select(g => new RoleDto
{
Name = g.Key,
FeatureIds = g.Select(p => p.FeatureId).ToList()
})
.FirstOrDefault();

LINQ to Entities - query across relationships and filter

I am having a very difficult time with querying a set of related entities with LINQ and Lambda expressions.
I have four entities that are related as such ...
Vehicles 1:n VehicleTypes n:1 Prices 1:n CustomerTypes
I am trying to obtain a list of Prices for a given Vehicle and CustomerType. For example I would like to obtain all the Prices for a Ford Mustang (VehicleTypeId = 2). In those Prices I would like included the CustomerType (Government, Commercial, Retail) that the Price pertains to.
I thought I might be able to do the following ...
Prices.Include(p => p.VehicleTypes)
.Include(p => p.CustomerTypes)
.Where(p => p.VehicleTypes.Vehicles.Select(v => v.Id == 2)
However I get this error ...
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<bool>' to 'bool'
I cannot seem to create a Where condition where I can filter the Id of the Vehicle to be purchased yet also include the CustomerType in the results.
EDIT: Just wanted to note that I have included using System.Data.Entity so I have access to the type safe Include extension
If you need the prices to that kind of vehicle and an specific customer type, you can filter as follows:
var prices= Prices.Include(p => p.VehicleTypes)
.Include(p => p.CustomerTypes)
.Where(p => p.VehicleTypes.Vehicles.Any(v => v.Id == 2)// With this condition you make sure that Mustang belong to this set of vehicles
&& p.CustomerTypes.Type=="Commercial");
But in case you want to filter the vehicles in the result, you are going to need to project your query to an anonymous type or a DTO:
var query= Prices.Include(p => p.VehicleTypes)
.Include(p => p.CustomerTypes)
.Where(p => p.VehicleTypes.Vehicles.Any(v => v.Id == 2)
&& p.CustomerTypes.Type=="Commercial")
.Select(p=>new {CustomerType=p.CustomerTypes.Type,
Vehicles=p.VehicleTypes.Vehicles.Where(v => v.Id == 2)});

Dynamics CRM 2011 Linq Group Join Method Syntax

How can I use group join with method syntax?
I can do a group join with query syntax like this:
var contAndAcc = from contact in linq.ContactSet
join account in linq.AccountSet
on contact.ParentCustomerId.Id equals account.AccountId
into accountGroup
from account in accountGroup.DefaultIfEmpty()
select new{contact = contact, account = account};
The compiler converts the query syntax to method syntax, so it must be possible to write the above in method syntax.
These do not work:
var contAndAcc = linq.ContactSet.GroupJoin(
linq.AccountSet, // collection to join to
contact => contact.ParentCustomerId.Id,
account => account.AccountId,
(contact, account) => contact);
var contAndAcc = linq.ContactSet.GroupJoin(
linq.AccountSet,
contact => contact.ParentCustomerId.Id,
account => account.AccountId,
(contact, account) => contact)
.DefaultIfEmpty()
.Select(contact=>contact);
var contAndAcc = linq.ContactSet.GroupJoin(
linq.AccountSet,
contact => contact.ParentCustomerId.Id,
account => account.AccountId,
(contact, account) => contact)
.DefaultIfEmpty()
.SelectMany((contact) => contact);
No matter what I try I get this message:
The 'GroupJoin' operation must be followed by a 'SelectMany'
operation where the collection selector is invoking the 'DefaultIfEmpty' method.
I would also really like to use an IEqualityComparer; will that be possible?
Total stab in the dark since I don't do much Linq to CRM, but the QueryExpressions that it gets converted to don't support returning an entire entity. Try listing every column that you want returned, rather than the entities themselves.

Nhibernate Fetch/FetchMany/ThenFetch duplicate results

I'm quering my database. The structure looks like below
Country 1..M CountryLocales
1
..
M
Cities 1..M CityLocales
So, each Country has multiple locales, each City has multiple locales and a Country has multiple cities.
I try to retrieve a city from the database. I want to prefetch the Citylocales, the country and the country locales.
To do this I perform this query:
City city = Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.Country)
.ThenFetch(c => c.CountryLocales)
.FetchMany(x => x.CityLocales)
.AsEnumerable()
.FirstOrDefault();
For some reason I now get both duplicate records for the CountryLocales and for the CityLocales (both twice)
How can I fix this?
You should look into the Future method. This allows you to perform many feteches without bumping into this issue. Your current query is returning a Cartesian Product which you don't want. Using the Future method you can perform multiple queries each using one Fetch whose results are then aggregated together thus resulting in the desired result.
It might go something like this:
var result = Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.Country)
.ToFuture();
Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.CountryLocales)
.ToFuture();
Session.Query<City>()
.Where(x => x.Id == id)
.Fetch(c => c.CityLocales)
.ToFuture();
// execute query
City city = result.AsEnumerable().FirstOrDefault();
Take a look at this answer for more information: Multiple Fetches in linq to nhibernate

SingleOrDefault query returns null (sometimes) even though the data is there

I'm a SubSonic newbe and am writing my first app with it. I have a database with two tables called Member and Club. The Club table has three fields which link to the primary key (ID) in the Member table (PresidentID, VicePresidentID, and TreasurerID). If I make three calls to Member.SingleOrDefault, they all return valid data.
i.e.:
President = Member.SingleOrDefault(x => x.ID == club.PresidentID);
VicePresident = Member.SingleOrDefault(x => x.ID == club.VicePresidentID);
Treasurer = Member.SingleOrDefault(x => x.ID == club.TreasurerID);
SubSonic nicely creates a Members property in the active record Club class which refers back to the Members table. However, if I make the same calls through the Members property of the Club activerecord class, some return nulls.
i.e.:
President = Club.Members.SingleOrDefault(x => x.ID == club.PresidentID);
VicePresident = Club.Members.SingleOrDefault(x => x.ID == club.VicePresidentID);
Treasurer = Club.Members.SingleOrDefault(x => x.ID == club.TreasurerID);
One or possibly two of the calls will return data, but the others will return null. This happens with the exact same member IDs, What am I doing wrong?
Many thanks for any pointers.
Dave Head
I think it's getting confused with the IQueryable FK setup - meaning that it's not honoring all of the foreign keys when creating the child IQueryables.
If you have SQL Profiler it would be great to know what's getting generated on those calls. For now you can get around this by using:
President = Members.SingleOrDefault(x=>x.ID==club.PresidentID && x.ClubID=club.ClubID);

Resources