C# LINQ query with joins and multiple counts - c#

I need to translate this SQL-query to Linq-Entity query
SELECT Company.name, COUNT(DISTINCT User.id), COUNT(DISTINCT Office.id)
FROM Company
INNER JOIN Office ON Company.id = Office.companyId
INNER JOIN Employee ON Office.id = Employee.officeId
GROUP BY Company.name
So I want a result that gives me a name of the company, count of unique employees and count of offices in a single row.
I have these entities
public class Company
{
public int id { get; set; }
public string name { get; set; }
public List<Office> offices { get; set; }
}
public class Office
{
public int id { get; set; }
public string name { get; set; }
public int companyId { get; set; }
public List<Employee> employees { get; set; }
}
public class Employee
{
public int id { get; set; }
public string name { get; set; }
public int officeId { get; set; }
}
and ViewModel:
public class MyViewModel
{
public Company company { get; set; }
public int employeeCount { get; set; }
public int officeCount { get; set; }
}
What I have been trying in my controller:
var viewModel =
from c in _context.Companies
join o in _context.Offices on c.id equals o.companyId
join e in _context.Employees on o.id equals e.officeId
select new MyViewModel { company = c, employeeCount = ??, officeCount =
??}
return View(viewModel);
So I don't know how the count() and group by are working.

First, LINQ has no direct equivalent of the SQL COUNT(DISTINCT expr) construct.
Second and more important, in LINQ to Entities you don't need to follow the SQL rules. We don't use joins, but navigation properties, and basically write the query like if we are working with objects, and let EF translate it to SQL.
The LINQ to Entities query in question is natural and simple as that:
var query = _context.Companies
.Select(c => new MyViewModel
{
company = c,
officeCount = c.offices.Count(),
employeeCount = c.offices.Sum(o => o.employees.Count()),
});

Related

SQL expression working with many-to-many to LINQ

ASP CORE MVC application.
I have 2 tables:
public class StudentModel
{
public Guid Id { get; set; }
public string ChineseName { get; set; }
public string EnglishName { get; set; }
public DateTime BirthdayDate { get; set; }
public List<StudentParent> StudentParent { get; set; }
public List<StudentCourse> StudentCourse { get; set; }
public StudentModel()
{
StudentParent = new List<StudentParent>();
StudentCourse = new List<StudentCourse>();
}
}
public class ParentModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public List<StudentParent> StudentParent { get; set; }
public ParentModel()
{
StudentParent = new List<StudentParent>();
}
}
With relation many-to-many.
public class StudentParent
{
public Guid StudentId { get; set; }
public StudentModel Student { get; set; }
public Guid ParentId { get; set; }
public ParentModel Parent { get; set; }
}
The question is: How to get Id from ParentModel table if I know student Id?
In Microsoft SQL Server Management Studio I can execute this command and get what I want:
SELECT ParentModels.Id
FROM StudentModels
LEFT OUTER JOIN StudentParent
ON StudentModels.Id = StudentParent.StudentId
LEFT OUTER JOIN ParentModels
ON StudentParent.ParentId = ParentModels.Id
WHERE StudentModels.Id = 'A1F38C12-AE65-464C-C489-08D814F4CDDC'
How to write this code in Visual Studio with LINQ?
I tried this instead:
Guid parentID = context.StudentModels.FromSqlRaw("SELECT ParentModels.Id FROM StudentModels LEFT OUTER JOIN StudentParent ON StudentModels.Id = StudentParent.StudentId LEFT OUTER JOIN ParentModels ON StudentParent.ParentId = ParentModels.Id WHERE StudentModels.Id = '{0}'", guid).FirstOrDefault();
But it doesn't work.
With this being many-to-many, there could be more than 1 ParentModel.Id for each student.
This being said, you could find them like this:
IEnumerable<Guid> parentIds = context.ParentModels
.Where(p => p.StudentParent
.Any(sp => sp.Student.Id == new Guid("A1F38C12-AE65-464C-C489-08D814F4CDDC")))
.Select(p => p.Id);
In such complex query where you need to make a lot of joins most developers prefer query LINQ syntax. It seems to be easier to read and understand.
IEnumerable<Guid> parentIds =
(from sm in context.StudentModels
join sp in context.StudentParent on sm.Id equals sp.StudentId
join pm in context.ParantModels on sp.ParentId equals pm.Id
where sm.Id == new Guid("A1F38C12-AE65-464C-C489-08D814F4CDDC")
select pm.Id).ToList();
More info about query and method syntax you can find in Miscrosoft Docs

Is there any in build functions available in NPoco for applying joins without in query joins

I want to use in built NPoco functions for using join rather than any in query join support like:
var users = db.FetchOneToMany<UserDto, CarDto>(x => x.UserId,
"select u.*, c.* from Users u inner join Cars c on u.UserId = c.UserId order by u.UserId");
Please anybody give some idea how can use Joins in NPoco using C#.
Have a look at this example which has a one-to-many relationship from Person to Address, i.e. a person can have many addresses. You use IncludeMany combined with the Reference attribute.
[TableName("Person")]
[PrimaryKey("Id")]
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
[Reference(ReferenceType.Many, ColumnName = "Id", ReferenceMemberName = "PersonId")]
public List<Address> Addresses { get; set; } = new List<Address>();
}
[TableName("Address")]
[PrimaryKey("Id")]
public class Address
{
public int Id { get; set; }
public int PersonId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public AddressStatus Status { get; set; }
}
This is how you retrieve all the people, each person with a list of addresses.
public async Task<People[]> GetPeople()
{
var query = _database.QueryAsync<People>()
.IncludeMany(s => s.Address);
var people = await query.ToArray();
return people;
}
To take it to the next level, you may need to filter out the addresses based on their Status or something.

select specific field in each joined table in LINQ sql query entity framework

public class OnLoginData
{
public List<TableDetails> lstTableDetails { get; set; }
public List<CategoryDetails> lstCategoryDetails { get; set; }
}
public class TableDetails
{
public int TableId { get; set; }
public int TableNumber { get; set; }
public string TableName { get; set; }
}
public partial class CategoryDetails
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public string Image { get; set; }
public long? SystemId { get; set; }
public int? SortId { get; set; }
}
var queryLoginAdimin = from admin in conDb.SystemAdminMasters
join system in conDb.SystemMasters on admin.SystemID equals system.SystemId into SM
join category in conDb.MenuCategoryMasters on admin.SystemID equals category.SystemId into CM
join menu in conDb.MenuMasters on admin.SystemID equals menu.SystemId into MM
join table in conDb.TableMasters on admin.SystemID equals table.SystemId into TM
select new OnLoginData
{
lstTableDetails = TM.Select(o => new
{
o.TableId,
o.TableName,
o.TableNumber
}).ToList()
};
Please check the above code, I'm trying to do joins using multiple tables and I don't require all fields from database tables. I only need those fields where I have take a separate class TableDetails and CategoryDetails. I want to select those fields from above linq query and create a whole List that is On LoginData.
How do I do that?
You Can Query in Below Way
var queryLoginAdimin =
from admin in conDb.SystemAdminMasters
join system in conDb.SystemMasters on admin.SystemID equals system.SystemId into SM
join category in conDb.MenuCategoryMasters on admin.SystemID equals category.SystemId into CM
join menu in conDb.MenuMasters on admin.SystemID equals menu.SystemId into MM
select new OnLoginData
{
lstTableDetails = conDb.TableMasters
.Where(table => table.SystemId == admin.SystemID)
.Select(o => new TableDetails
{
TableId = o.TableId,
TableName = o.TableName,
TableNumber = o.TableNumber
}),
//Same for lstCategoryDetails
};
You Can't use .ToList() inside select so you can change OnLoginData To IEnumerable
public class OnLoginData
{
public IEnumerable<TableDetails> lstTableDetails { get; set; }
public IEnumerable<CategoryDetails> lstCategoryDetails { get; set; }
}

Grouping 4 Tables using Linq

I have the following requirements:
One rating can have zero or many RatingPictures
One Rating can have zero or many Comments
One Comment belongs to one User
This is what I have so far:
from rating in Ratings
where rating.LocationID == 1007
join ratingpicture in RatingPictures
on rating.ID equals ratingpicture.RatingID into j3
from ratingpicture in j3.DefaultIfEmpty()
join comment in Comments
on rating.ID equals comment.RatingID into j1
from comment in j1.DefaultIfEmpty()
join user in Users
on comment.UserID equals user.ID into j2
from user in j2.DefaultIfEmpty()
group new { ratingpicture, comment, user } by rating into g
select new { rating = g.Key, ratingpicture= g.Key, comment = g.ToList() }
If you model your entity classes like this:
public class Comment
{
public int CommentId { get; set; }
public int RatingId { get; set; }
public virtual Rating Rating { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; }
}
public class Rating
{
public Rating()
{
RatingPictures = new HashSet<RatingPicture>();
Comments = new HashSet<Comment>();
}
public int RatingId { get; set; }
public virtual ICollection<RatingPicture> RatingPictures { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public int LocationID { get; set; }
}
public class RatingPicture
{
public int RatingPictureId { get; set; }
public int RatingId { get; set; }
public virtual Rating Rating { get; set; }
}
Then your query would be as simple as this:
var query = context.Ratings.Where(r => r.LocationID == 1007)
.Include(r => r.RatingPictures)
.Include(r => r.Comments.Select(c => c.User));
var result = query.ToList();
Under the hood, this query will be translated into a join query. But this is the beauty of ORMs, we get to work at a more abstract level.
Take a look at this reference for more information about relationships and navigation properties in Entity Framework.

How to query with a join with linq to sql

I'm trying to query for gifts given a term from my categories table. Entity Framework created a bridge table to connect my "Gift" with my "GiftCategroies". But the query I have yielded no results.
From DbContext:
public DbSet<Gift> Gifts { get; set; }
public DbSet<GiftCategory> Categories { get; set; }
The two entities I created:
public class Gift
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<GiftCategory> Categories { get; set; }
public int Rating { get; set; }
}
public class GiftCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Gift> Gifts { get; set; }
}
Here is my query to try and fetch the gifts given a giftcategory term. No results returned. I'm not sure if I even need a join for this type of query.
var model =
from gifts in db.Gifts
join giftCategory in db.Categories on gifts.Id equals giftCategory.Id
where giftCategory.Name.Contains(searchTerm)
select gifts;
You should use navigation properties instead of joins:
var gifts = (from c in db.Categories
from g in c.Gifts
where c.Name.Contains(searchTerm)
select g).Distinct().ToList();

Categories

Resources