Need help in writing LINQ Query to get count of employee based on department.
Department Model
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public List<Employee> Employees { get; set; }
}
Employee Model
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
MyViewModel
public class MyViewModel
{
public string Department { get; set; }
public int count { get; set; }
}
LINQ Query
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => e.DepartmentId)
.Select(y=> new MyViewModel{
Department= y.Key, // ERROR HERE AS Cannot type cast string to integer
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
Output Expected
Department Count (or employees)
Human Resource 10
Information Tech 5
Question
How to write a LINQ Query to get the output as above. Please guide me.
You have 2 options for modifying your query. I am assuming your Department model contains a property string Name which you want to assign to the Department property of your view model.
Get the first Department in the grouped collection and assign its
Name property to your view models Department property. Note that
you need to add .ToList() before .GroupBy() so that the
first part of the query is executed before grouping
var x = _context.Employees
.Include("Department")
.ToList()
.GroupBy(e => e.DepartmentId)
.Select(y => new MyViewModel
{
Department = y.First().Department.Name,
count = y.Count()
}).ToList();
Change the .GroupBy() expression to group by the Name property
of the Department property
var x = _context.Employees
.Include("Department")
.GroupBy(e => e.Department.Name)
.Select(y=> new MyViewModel
{
Department = y.Key,
count = y.Count()
}).ToList();
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => new { e.DepartmentId, e.Department.Name})
.Select(y=> new MyViewModel{
Department= y.Key.Name
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
the key here is to group by DepartmentId AND Name
Try it like this:
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => e.DepartmentId)
.Select(y=> new MyViewModel{
Department= y.Key.DepartmentName, // or whatever property you have for storing the name
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
you can use this code :
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var dataContext = new MovieDataContext();
int count = (from row in dataContext.Movies
where row.IsEnquiry == true
select row).Count();
ViewBag.ItemCount = count;
return View();
}
}
}
Related
I have this entity class:
public class PoDetail
{
[Key]
public int Id { get; set; }
public string ProductId { get; set; }
public virtual Product Product { get; set; }
}
And I'm trying to run a query on it:
public IActionResult Data()
{
var result = _context.PoDetail
.FromSqlRaw("select count(ProductId) as count, ProductId from dbo.PoDetail group by ProductId").ToList();
return Json(result);
}
It works fine when I run in query console.
But on the browser I am getting this error:
The required column 'id' was not present in the results of a FromSql operation
But if I include the id column in query. I won't get the desired output.
You need to create a new class to hold the desired result:
public class PoSummary
{
public int Count { get; set; }
public string ProductId { get; set; }
}
Add it to your DbContext:
modelBuilder.Entity<PoSummary>().HasNoKey().ToView(null);
And then:
public IActionResult Data()
{
var result = _context.PoSummary
.FromSqlRaw("select count(ProductId) as count, ProductId from dbo.PoDetail group by ProductId").ToList();
return Json(result);
}
I've used it this way before.
select ProductId , count(*) as TotalId from dbo.PoDetail group by ProductId
For such simple query, you do not need SQL:
public IActionResult Data()
{
var result = _context.PoDetail
.GroupBy(x => x.ProductId)
.Select(g => new
{
count = g.Count()
ProductId = g.Key
})
.ToList();
return Json(result);
}
I have a Model like this
public class Product
{
public int Id { get;set;}
...
public virtual ICollection<Supplier> Suppliers {get;set;}
}
public class Suppliers
{
public int Id {get;set;}
...
public string Name {get;set;}
}
My Linq query is constructed like this to get Products which are unique
var suppliers = _context.Products.Where(condition).Select(u => u.Suppliers).ToList(); //
The result of this query returns a List<Iqueryable<Supplier>> How do I get a list of Supplier Names from this list??
Thanks to Camilo Terevinto this query works.
var suppliers = _context.Products.Where(condition).SelectMany(u => u.Suppliers).GroupBy(u => u.Name).Select(u => u.First()).ToList()
You can try this
var supplier_names = _context.Products.Where(condition)
.SelectMany(u => u.Suppliers).Select(a => a.Name).ToList();
I have the following entity:
public class Item
{
public int Id { get; set; }
public int? ParentId { get; set; }
public Item Parent { get; set; }
public List<Item> Children { get; set; }
public double PropertyA { get; set; }
public double PropertyB { get; set; }
...
}
Now I want to query the database and retrieve data of all the nested children.
I could achieve this by using Eager Loading with Include():
var allItems = dbContext.Items
.Include(x => Children)
.ToList();
But instead of Eager Loading, I want to do the following projection:
public class Projection
{
public int Id { get; set; }
public List<Projection> Children { get; set; }
public double PropertyA { get; set; }
}
Is it possible to retrieve only the desired data with a single select?
We are using Entity Framework 6.1.3.
Edit:
This is what I have tried so far.
I really don't know how to tell EF to map all child Projection the same way than their parents.
An unhandled exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll
Additional information: The type 'Projection' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
var allItems = dbContext.Items
.Select(x => new Projection
{
Id = x.Id,
PropertyA = x.PropertyA,
Children = x.Children.Select(c => new Projection()
{
Id = c.Id,
PropertyA = c.PropertyA,
Children = ???
})
})
.ToList();
Generally speaking, you can't load a recursive structure of unknown unlimited depth in a single SQL query, unless you bulk-load all potentially relevant data irregardless whether they belong to the requested structure.
So if you just want to limit the loaded columns (exclude PropertyB) but its ok to load all rows, the result could look something like the following:
var parentGroups = dbContext.Items.ToLookup(x => x.ParentId, x => new Projection
{
Id = x.Id,
PropertyA = x.PropertyA
});
// fix up children
foreach (var item in parentGroups.SelectMany(x => x))
{
item.Children = parentGroups[item.Id].ToList();
}
If you want to limit the number of loaded rows, you have to accept multiple db queries in order to load child entries. Loading a single child collection could look like this for example
entry.Children = dbContext.Items
.Where(x => x.ParentId == entry.Id)
.Select(... /* projection*/)
.ToList()
I see only a way with first mapping to anonymous type, like this:
var allItems = dbContext.Items
.Select(x => new {
Id = x.Id,
PropertyA = x.PropertyA,
Children = x.Children.Select(c => new {
Id = c.Id,
PropertyA = c.PropertyA,
})
})
.AsEnumerable()
.Select(x => new Projection() {
Id = x.Id,
PropertyA = x.PropertyA,
Children = x.Children.Select(c => new Projection {
Id = c.Id,
PropertyA = c.PropertyA
}).ToList()
}).ToList();
A bit more code but will get the desired result (in one database query).
Let's say we have the following self-referencing table:
public class Person
{
public Person()
{
Childern= new HashSet<Person>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? ParentId { get; set; }
[StringLength(50)]
public string Name{ get; set; }
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
And for some point of time you need to get all grandsons for specific persons.
So, first of all I will create stored procedure(using code-first migration) to get all persons in the hierarchy for those specific persons:
public override void Up()
{
Sql(#"CREATE TYPE IdsList AS TABLE
(
Id Int
)
GO
Create Procedure getChildIds(
#IdsList dbo.IdsList ReadOnly
)
As
Begin
WITH RecursiveCTE AS
(
SELECT Id
FROM dbo.Persons
WHERE ParentId in (Select * from #IdsList)
UNION ALL
SELECT t.Id
FROM dbo.Persons t
INNER JOIN RecursiveCTE cte ON t.ParentId = cte.Id
)
SELECT Id From RecursiveCTE
End");
}
public override void Down()
{
Sql(#" Drop Procedure getChildIds
Go
Drop Type IdsList
");
}
After that you can use Entity Framework to load the ids(you could modify stored procedure to return persons instead of only returning ids) of persons under the passed persons(ex grandfather) :
var dataTable = new DataTable();
dataTable.TableName = "idsList";
dataTable.Columns.Add("Id", typeof(int));
//here you add the ids of root persons you would like to get all persons under them
dataTable.Rows.Add(1);
dataTable.Rows.Add(2);
//here we are creating the input parameter(which is array of ids)
SqlParameter idsList = new SqlParameter("idsList", SqlDbType.Structured);
idsList.TypeName = dataTable.TableName;
idsList.Value = dataTable;
//executing stored procedure
var ids= dbContext.Database.SqlQuery<int>("exec getChildIds #idsList", idsList).ToList();
I hope my answer will help others to load hierarchical data for specific entities using entity framework.
I have the following model:
public Class Category{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<SubCategory> SubCategories {get;set;}
}
public Class SubCategory{
public int Id {get;set;}
public string Name {get;set;}
public int CategoryId { get; set; }
public virtual Category Category{ get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
public class Ticket {
public Ticket();
public int Id { get; set; }
public virtual SubCategory SubCategory{ get; set; }
public int SubCategoryId{ get; set; }
}
I want to get data groupBy Category and get the count of tickets in each subcategory using this query:
Entities
.Include(h => h.SubCategories )
.ThenInclude(s => s.Tickets)
.GroupBy(s => s.Id)
.Select(t => new Cata {
Name = t.FirstOrDefault().Name,
Children = GetChildern(t.FirstOrDefault().SubCategories )
});
public List<SubCat> GetChildern(IEnumerable<SubCategories> subs)
{
var output = new List<SubCat>();
foreach (var sub in subs) {
var subcat = new SubCat();
subcat.Name = sub.Name;
if (sub.Tickets != null) {
subcat.Size = sub.Tickets.Count;
}
output.Add(subcat);
}
return output;
}
With the Query above the ticket is always zero for all, but tickets exists.
I don't see why you need to do a group by if you start your query in Categories
var result= Entities
.Include(h => h.TicketSubCategories)
.ThenInclude(s => s.Tickets)
.Select(t => new Cata {
Name = t.Name,
Children= t.TicketSubCategories
.Select(ts=>new SubCat{
Name=ts.Name,
Count=ts.Tickets.Count()})
};
Agreed with #Ivan about he commented above, here you don't need to use a custom method, using it you will force the projection of your query to be executed on the client side and not on the server (your DB)
So each Category has zero or more SubCategories, and each SubCategory has zero or more Tickets. Each Ticket belongs to exactly one SubCategory and each SubCategory belongs to exactly one Category
And you want a query, that results in groups of SubCategories that have the same Category. You want some (or all) properties of each SubCategory, but above all, you want the number of Tickets each SubCategory has.
All elements in every group of SubCategories belong to the same Category. You also want some (if not all) properties of this Category.
The solution is to group all SubCategories into groups of same Category (for efficiency use CategoryId). Then use a Select to get the properties you want.
var result = SubCategories
// group them into groups with same CategoryId
.GroupBy(subCategory => subCategory.CategoryId
// from every group take the properties you want:
.Select(group => new
{
// All SubCategories in one group belong to the same Category.
// For efficiency, take only the Category properties you plan to use,
CommonCategory = group.Key.Select(category => new
{
// take the category properties you want to use
}
// The group has a lot of SubCategories.
// For each subcategory select only the properties you want to use
SubCategories = group.Select(subCategory => new
{
// one of the properties you want is the number of Tickets of this SubCategory:
TicketCount = subCategory.Tickets.Count(),
// for efficiency: select only SubCategory properties you plan to use:
Property1 = subCategory.Property1,
Property2 = subCategory.Property2,
...
}),
});
So the result is a sequence of objects. Each object has two Properties:
SubCategories: a sequence of some properties of all SubCategories that belong to the same Category.
CommonCategory. Several properties of the Category that all SubCategories belong to.
The SubCategories is a sequence. Each element of the sequence is an object with several properties:
TicketCount: the number of tickets in the SubCategory
other properties: several other properties of the SubCategory
From this it is easy to construct the code to GetChildren
I got a List that contains all the employees, now I need to dig in to a specific employee on a new page. I want to get all the values from the employee where the ID is 1 for example. Is there a sollution for this in LINQ?
It's practically a Query SELECT * FROM Employee WHERE id = 1;
class Employee
{
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Title { get; set; }
public string TitleOfCourtesy { get; set; }
public DateTime BirthDate { get; set; }
public DateTime HireDate { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string HomePhone { get; set; }
public string Extension { get; set; }
//public Image Photo { get; set; }
public string Notes { get; set; }
public int ReportsTo { get; set; }
public string PhotoPath { get; set; }
}
I tried it like this but it doesn't work:
List<Employee> employees = Database.getEmployees();
var uniqUsers = employees.Where(x => employees.Contains(x.EmployeeID == 1)).ToList();
Where employee is type of IEnumerable<Employee>
If you are expecting 1 record:
var result = employee.FirstOrDefault(x => x.EmployeeID == 1); // Returns Employee
If you are expecting more than 1 record:
var result = employee.Where(x => x.EmployeeID == 1); // Return IEnumerable<Employee>
Please note, when using FirstOrDefault if there is no items in your collection (or doesn't match your lambda) then it will return default T which in your case will be Employee and it will be null.
If you want a "single" item that meets that critera use the Single Linq statement:
Employee employee = employees.Single(e => e.EmployeeID == 1);
or
Employee employee = employees.SingleOrDefault(e => e.EmployeeID == 1);
if you want the query to return null instead of throwing an exception if there is not an item in the list that meets that criteria.
Let EmployeeList is the current List of Employees. You can use LINQ to filter the required details as like the specified query by using this(IT will give you all sublist satisfies the specified condition):
int empIdToSearch=1;
List<Employee> FilteredList=EmployeeList.Where(x=>x.EmployeeID ==empIdToSearch).ToList();
If the EmployeeID is unique then there will be one item in the list with particular ID, You can use FirstOrDefault to get the First item from the collection that satisfies the condition.ie.,
Employee EmployeeObject= FilteredList.FirstOrDefault(x => x.EmployeeID == empIdToSearch);
The concept that you need to get is how most linq queries operate.
When you say .Where(x => x.EmployeeID == 1) then x is a single empolyee as if you said:
foreach(Employee x in employees)
{
if(x.EmployeeID == 1)
// take it
}
So the correct syntax would be:
List<Employee> uniqUsers = employees.Where(x => x.EmployeeID == 1).ToList();
Single Optional Result:
Employee uniqUser = employees.SingleOrDefault(x => x.EmployeeID == 1);
Single Mandatory Result:
Employee uniqUser = employees.Single(x => x.EmployeeID == 1);
First Optional Result:
Employee uniqUser = employees.FirstOrDefault(x => x.EmployeeID == 1);
First Mandatory Result:
Employee uniqUser = employees.First(x => x.EmployeeID == 1);
We can fetch the records from collection in two ways.
Linq to sql like query
var employee= from emp in employees where emp.ID==1;
Linq to extension methods.
var employee = employees.Where(emp=>emp.ID==1);
Linq supports a query syntax that is closer to SQL.
var employee1 = from employee in employees where employee.EmployeeID == 1 select employee;
foreach (var x in employee1)
{
Console.WriteLine(x.EmployeeID);
}
The compiler converts all query syntax to method syntax. Not all things can be done with query syntax. The 'from' comes before the 'select' so auto-complete is more useful. It is important to note the linq query is not executed until it is used. The foreach loop is where it is first used in this example.