Select only a single column in LINQ - linq

The EntityModel is defined as:
Personnel has a link to a Country
When executing this code in LinqPad, I see that the SQL which is generated is not optimized (all fields are returned) in the first query ? What am I missing here or doing wrong ?
Query 1 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
personnelIds.Dump();
Query 1 SQL
exec sp_executesql N'SELECT [t0].[Id], [t0].[Version], [t0].[Identifier], [t0].[Name], , [t0].[UpdatedBy] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
Query 2 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds2 = Personnels.Where(p => p.Country == Country).Select(p => p.Id).ToArray();
personnelIds2.Dump();
Query 2 SQL
exec sp_executesql N'SELECT [t0].[Id] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
The database used is SQL Express 2008. And LinqPad version is 4.43.06

//var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = context.Personnels
.Where(p => p.Country.Id == 100000581)
.Select(p => p.Id)
.ToArray();
personnelIds.Dump();
Try this, it should be better.

Personnels collection will be populated via lazy loading when accessed, hence retrieving all of the fields from the DB. Here's what's happening...
// retrieves data and builds the single Country entity (if not null result)
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
// Country.Personnels accessor will lazy load and construct all Personnel entity objects related to this country entity object
// hence loading all of the fields
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
You want something more like this:
// build base query projecting desired data
var personnelIdsQuery = dbContext.Countries
.Where( c => c.Id == 100000581 )
.Select( c => new
{
CountryId = c.Id,
PersonnelIds = c.Personnels.Select( p => p.Id )
}
// now do enumeration
// your example shows FirstOrDefault without OrderBy
// either use SingleOrDefault or specify an OrderBy prior to using FirstOrDefaul
var result = personnelIdsQuery.OrderBy( item => item.CountryId ).FirstOrDefault();
OR:
var result = personnelIdsQuery.SingleOrDefault();
Then get the array of IDs if not null
if( null != result )
{
var personnelIds = result.PersonnelIds;
}

Try can also try grouping personnel into a single query
var groups =
(from p in Personnel
group p by p.CountryId into g
select new
{
CountryId = g.Key
PersonnelIds = p.Select(x => x.Id)
});
var personnelIds = groups.FirstOrDefault(g => g.Key == 100000581);

Do you have the ForeignKey explicitly defined in your POCO for Personnel? It's common to leave it out in EF, but adding it would massively simplify both this code and the resulting SQL:
public class Personnel
{
public Country Country { get; set; }
[ForeignKey("Country")]
public int CountryId { get; set; }
. . .
}
> update-database -f -verbose
var ids = db.Personnel.Where(p => p.CountryId == 100000581).Select(p => p.Id).ToArray();

Related

.net core linq select overload index does not work

I got a linq lambda select code that works before I added the Select index overload. Before, I got the list of records but I need the index which I use to assign a unique Id to each record. When I add with ToList(), I get an exception with no error/inner exception. Only way I can get the code to not throw an error is to use .AsEnumberable() but I need a list. I read many post that .ToList() works with the overload but I have been unsuccessful.
Here is my code and my attempt to fix this
var emps = this.DbContext.Employees
.GroupJoin(this.DbContext.Depts,
employee => employee.EmployeeId,
dept => dept.EmployeeId,
(employee, dept) => new { employee, dept }
)
.SelectMany(
employee_dept_left => employee_dept_left.dept.DefaultIfEmpty(),
(employee_dept_left, dept) => new { employee_dept_left, dept }
)
.Join(this.DbContext.Divs,
emp_emp_dept => emp_emp_dept.employee_dept_left.employee.DivId,
division => division.DivId,
(emp_emp_dept, division) => new { emp_emp_dept, division }
)
.Where(s => !string.IsNullOrEmpty(filter.selectedDiv))
.GroupBy(grouped => new
{
grouped.emp_emp_dept.employee_dept_left.employee.EmployeeId,
grouped.emp_emp_dept.employee_dept_left.employee.LastNm,
grouped.emp_emp_dept.employee_dept_left.employee.FirstNm,
grouped.emp_emp_dept.employee_dept_left.employee.DivId
})
.Select((joined, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = joined.Key.EmployeeId,
LastNm = joined.Key.LastNm.Trim(),
FirstNm = joined.Key.FirstNm.Trim(),
DivisionId = joined.Key.DivId,
}).ToList();
The error message says
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 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
I tried using .AsEnumerable() instead of .ToList():
List<EmployeeViewModel> test = emps.Cast<EmployeeViewModel>().ToList();
but this throws an exception.
Any help is greatly appreciated.
Thanks in advance
Problem that this Select is not currently translatable to the SQL. You can make additional Select to solve issue with AsEnumerable().
...
.Select(joined => new
{
EmployeeId = joined.Key.EmployeeId,
LastNm = joined.Key.LastNm.Trim(),
FirstNm = joined.Key.FirstNm.Trim(),
DivisionId = joined.Key.DivisionId,
})
.AsEnumerable()
.Select((x, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = x.EmployeeId,
LastNm = x.LastNm,
FirstNm = x.FirstNm,
DivisionId = x.DivisionId,
}).ToList();
And note that query is more readable in Query syntax when there are joins.
var query =
from employee in this.DbTracsContext.Employees
join dept in his.DbTracsContext.Depts on employee.EmployeeId equals dept.EmployeeId into employee_dept_left
from dept in employee_dept_left.DefaultIfEmpty()
join division in this.DbTracsContext.Depts on employee.DivisionId equals division.DivisionId
where string.IsNullOrEmpty(filter.DivisionSelection) || filter.DivisionSelection == "0" || employee.DivisionId == filter.DivisionSelection
group employee by new { employee.EmployeeId, employee.LastNm, employee.FirstNm, employee.DivisionId } into g
select new
{
EmployeeId = g.Key.EmployeeId,
LastNm = g.Key.LastNm.Trim(),
FirstNm = g.Key.FirstNm.Trim(),
DivisionId = g.Key.DivisionId,
};
var emps = query
.AsEnumerable()
.Select((x, index) => new EmployeeViewModel
{
Id = index,
EmployeeId = x.EmployeeId,
LastNm = x.LastNm,
FirstNm = x.FirstNm,
DivisionId = x.DivisionId,
}).ToList();

Linq use value from select new to calculate another field

Is it possible to do the following in linq, where in my select new I use the value from TotalOrderItems as part of the calculation for TotalInStockItems?
var Order = (from o in orderItems
select new ShippingOrder { OrderId = orderItems.OrderId
TotalOrderItems = orderItems.GroupBy(x => x.Sku).Count(),
TotalInStockItems = TotalOrderItems - orderItems.Where(x => x.InStock =='F').GroupBy(x => x.Sku).Count(),
}).ToList();
try using the let clause (C# Reference) to store the value for use later in the query
(from o in orderItems
let totalOrderItems = orderItems.GroupBy(x => x.Sku).Count()
select new ShippingOrder {
OrderId = o.OrderId
TotalOrderItems = totalOrderItems,
TotalInStockItems = totalOrderItems - orderItems.Where(x => x.InStock =='F').GroupBy(x => x.Sku).Count(),
}).ToList();

linq query crossjoin groupby optimize

i have the following database-model: http://i.stack.imgur.com/gRtMD.png
the many to many relations for Kunde_Geraet/Kunde_Anwendung are in explicit Mapping-Table with additional Information.
i want to optimize the following LINQ-query:
var qkga = (from es in db.Eintrag_Systeme.Where(es => es.Eintrag_ID == id)
from kg in db.Kunde_Geraet.Where(kg => es.Geraet_ID == kg.Geraet_ID)
select new { Kunde = kg.Kunde, Geraet = es.Geraet, Anwendung = es.Anwendung })
.Union(
from es in db.Eintrag_Systeme.Where(es => es.Eintrag_ID == id)
from ka in db.Kunde_Anwendung.Where(ka => es.Anwendung_ID == ka.Anwendung_ID)
select new { Kunde = ka.Kunde, Geraet = es.Geraet, Anwendung = es.Anwendung })
.GroupBy(kga => kga.Kunde, kga => new {Geraet = kga.Geraet, Anwendung = kga.Anwendung});
it would be better, when the result is a IEnumerable(Kunde, IEnumerable(Geraet), IEnumerable(Anwendung)) without the null-Values for the union.
i try it as SQL command
select Count(es.Geraet_ID), null as Anwendung_ID
from Eintrag_Systeme es cross join Kunde_Geraet where es.Geraet_ID = Kunde_Geraet.Geraet_ID AND es.Eintrag_ID = #id
union
select null as Geraet_ID, Count(es.Anwendung_ID)
from Eintrag_Systeme es cross join Kunde_Anwendung where es.Anwendung_ID = Kunde_Anwendung.Anwendung_ID AND es.Eintrag_ID = #id
group by Kunde_ID
but donĀ“t get the Count() of Anwendungen(Apps)/Geraete(Devices) to Lists grouped by Key Kunde(Client)
Don't use join but navigation properties:
from k in context.Kunden
select new
{
Kunde = k,
Geraete = k.Kunde_Geraete.Select(kg => kg.Geraet),
Anwendungen = k.Kunde_Anwendungen.Select(ka => ka.Anwendung)
}
Now you have a basis from which you get counts, etc.

Simplified Linq for return to View Model

How do I simplified the below statement?
var Orders = db.Orders
.Include(o => o.shipment)
.Where(o => o.ID == 3 || o.ID == 5 || o.ID == 10)
.ToList();
Ultimately, I would like to do like:
SELECT * FROM Orders WHERE ID IN (3,5,10)
in strongly typed Entity to return to View model
I've tried .Any, .Contains or .Intersect but cannot "form" the Linq query.
Something like this?
var items = new int[] {3,5,10};
var Orders = db.Orders
.Include(o => o.shipment)
.Where(o => items.Contains(o.ID))
.ToList();
How about this ?
List<int> items = new List<int>(new []{3,5,10});
var Orders = db.Orders
.Include(o => o.shipment)
.Where(o => items.Contains(o.ID))
.ToList();
In place of int you should use the same datatype as of ID
Hope it helps!!!

Co-related Queries using lambda expressions

How can I convert this LINQ query from query syntax to method syntax? I am performing a co-related query operation.
var query = (from r in objEntities.Employee
where r.Location == (from q in objEntities.Department
where q.Location == r.Location
select q.Location).FirstOrDefault()
select new
{
FirstName = r.FirstName,
LastName = r.LastName,
Age = r.Age,
Location = r.Location
});
GridView1.DataSource = query;
GridView1.DataBind();
I think you are trying to convert the query to method-based query instead of syntax-based query.
var query = objEntities.Employee
.Where(e => e.Location == objEntities.Department
.Where(d => d.Location == r.Location)
.Select(d => d.Location)
.FirstOrDefault())
.Select(e => new {
FirstName = e.FirstName,
LastName = e.LastName,
Age = e.Age,
Location = e.Location
});
I'm also pretty sure your inner expression within where clause could be replaced with something like that:
.Where(e => objEntities.Department.Any(d => d.Location == e.Location)
Nested queries always have performance issue instead you should use join:
In the lambda expression query should be
var query = objEntities.Employee.Join(objEntities.Department, E => E.Location,
D => D.Location,
(E,D) => new {
FirstName = E.FirstName,
LastName = E.LastName,
Age = E.Age,
Location = E.Location
});

Resources