SQL expression working with many-to-many to LINQ - c#

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

Related

C# LINQ query with joins and multiple counts

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()),
});

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; }
}

Join 3 One to Many Tables in Entity Framework

i have 2 tables that each one has a one-to-many relation to the table between and the table between has ids of 2 other tables
dbo.Posts dbo.Posts_Categories dbo.Categories
-ID -ID -ID
-Title -PostID -Name
-CategoryID
result i expect is :
Title = post1 Categories = web,mobile,desktop
Title = post2 Categories = app,game
...
i know how to query this in sql using Stuff function and For Xml Path but i have no idea how do i do this in entity framework!
any suggestion or book for how to do works in this way might help!
Edit: EF classes added:
public class Post : ReportingBase {
public Post() { }
[Required, MaxLength(500)]
public string Title { get; set; }
[Required, MaxLength(500)]
public string Address { get; set; }
[Required]
public string Body { get; set; }
[Required, MaxLength(500)]
public string Tags { get; set; }
[Required]
public int Visit { get; set; }
public virtual ICollection<Post_Category> Posts_Categories { get; set; }
public virtual ICollection<Post_AttachedFile> Posts_AttachedFiles { get; set; }
[ForeignKey("Image")]
public virtual int? ImageID { get; set; }
public virtual Image Image { get; set; }
}
public class Post_Category {
public Post_Category() { }
[Key, Column(Order = 0)]
public int PostID { get; set; }
[Key, Column(Order = 1)]
public int CategoryID { get; set; }
public virtual Post Post { get; set; }
public virtual Category Category { get; set; }
}
public class Category : EntityBase {
public Category() { }
[Required, MaxLength(50)]
public string Name { get; set; }
[Required, MaxLength(150)]
public string Address { get; set; }
public int? ParentID { get; set; }
public virtual ICollection<Post_Category> Posts_Categories { get; set; }
}
thank you in advance
Edit : According to #IvanStoev answer i did following :
List<P> p = context.Posts.Select(post => new {
Title = post.Title,
Categories = post.Posts_Categories.Select(pc => pc.Category.Name).ToList()
}).ToList();
and created a class called P :
public class P {
public string Title { get; set; }
public List<string> Categories { get; set; }
}
but it doesn't work correctly and the problem is how to return the result.
In EF it's even easier than in SQL thanks to the concept of so called navigation properties. All you need to know is a basic LINQ query syntax and just follow them (navigate) to get the data needed. For instance:
var result = db.Posts
.Select(post => new
{
Title = post.Title,
Categories = post.Posts_Categories
.Select(pc => pc.Category.Name)
.ToList()
})
.ToList();
The result is a list of anonymous type having string Title property and List<string> Categories property containing the related category names.
You can use Linqpad (software) to get familiarize with the Linq query it builds lambda expression for you by connecting to the database and provides output too to cross verify.
The below one is the lambda expression for joining the tables you have mentioned.
p - Post
pc - post_categories
c - categories
Code:
Posts.Join(Post_Categories, p => p.ID, pc => pc.ID, ( p, pc) => new { p = p, pc = pc})
.Join(Categories, pcc => pcc.pc.CategoryID, c => c.ID, ( pcc, c) => new { pcc = pcc, c = c})
.Select(p.Title)
.Select(c.Name)
You should be using .Include() for any join in EF Core.
I've come up with this simple example: one person can have many dogs.
public class Person
{
public Guid Id { get; set; }
public ICollection<Dog> Dogs { get; set; } // One Person can have many Dogs
}
public class Dogs
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
}
Generate the migrations after creating the models. Not going over how to do that in this answer.
Here's how you use .Include() to join upon the two different tables:
public class PersonRepository : RepositoryBase
{
public IEnumerable<Person> FetchPeopleWithManyDogs()
{
return DatabaseContext.Person
.Include(x => x.Dogs)
.Where(x => x.Dogs.Count() > 1).ToList();
}
}

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