Max count on group by - Entity Framework - entity-framework

I have following two tables:
1.) Articles - [ArticleID]
2.) ArticleComments - [CommentID], [ArticleID]
I want to retrieve ArticleID with maximum no. of comments e.g.
ArticleID - 2
TotalNoOfComments - 15
How do I do it in Entity Framework?
I access ArticleComments collection like following: article.ArticleComments. The following will be the object to store the result.
public class CommentStats
{
public int ContextId { get; set; }
public int CommentCount { get; set; }
}

var query = context.Articles.
Select(a => new CommentStats
{
ContextId = a.Id,
CommentCount = a.ArticleComments.Count
}
).OrderByDescending(cs => cs.commentCount);
You can then run FirstOrDefault for the one article with most comments, or ToList for the whole ordered list.

Related

Linq query to join three tables and return a object along with a list of another object in Entity Framework Core

I have three tables, Organization, Department, and OrganizationDepartments. here is the relationship between them.
Now I would like to join these three tables and create another object for a DTO class. This DTO object has some properties and a list of other DTOs. Here is the DTO Class.
Organization DTO:
public class OrganizationDto
{
public string Id { get; set; }
public string OrganizationName { get; set; }
public string Logo { get; set; }
public bool? IsActive { get; set; }
public IList<OrganizationDepartmentDto> OrganizationDepartments { get; set; }
}
OrganizationDepartment DTO:
public class OrganizationDepartmentDto
{
public string OrganizationId { get; set; }
public string OrganizationName { get; set; }
public string DepartmentId { get; set; }
public string DepartmentName { get; set; }
}
Now I would like to write a LINQ query to get a Organization object along with all the departments related to that organization. The query is imcomplete because I don't know how can I get all the department information as list in a single query. The code is below:
var organizationInfo = (from org in _dbContext.Organizations
join orgDept in _dbContext.OrganizationDepartments on org.Id equals orgDept.OrganizationId
join dept in _dbContext.Departments on orgDept.DepartmentId equals dept.Id
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
OrganizationDepartments = //TODO:..
}
);
Can anyone help me to get the department lists of that organization's object (see the TODO:)?
If your entities are mapped correctly, and the relationships are correctly configured.
you can use .Include("OrganizationDepartment") and .ThenInclude("Department")to ensure relations are included into the generated Query.
If you insist on using Query Syntax. e.g from org in context.Organization
you can write out the query like this.
var q = (from org in _dbContext.Organizations
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
OrganizationDepartments = org.OrganizationDepartments.ToList()
}
Depending on your usecase. Sometimes you are not interested in actually showing the "many to many" table outside of the scope of your database.
so it might make more sense to actually flatten the Dto.
that query would look like
var q = (from org in _dbContext.Organizations
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
Departments= org.OrganizationDepartments.Select(t => t.Departments).ToList()
}

.NET Core - join 2 tables with AsQueryable

I have two nested classes: Partner contains a Company (that has a field "Name")
I do a search by Id on the partner's Id
I want to do a search on the company's "Name" field
here is my poco:
public class Partner
{
[Required]
public int? Id { get; set; }
[Required]
public Company Company { get; set; }
using AsQueryable, I can then stack filters one by one
I try to have a query that joins the second table to do a search on that entity's name field
public DbSet<Partner> Partners { get; set; }
...
var data = _context.Partners.AsQueryable();
if (partnersSearch.SearchById != null)
{
data = data.Where(p => p.Id == partnersSearch.SearchById.GetValueOrDefault());
}
if (partnersSearch.SearchByName != null)
{
data = data.Include(a => a.Company.Select(b => b.Name = partnersSearch.SearchByName));
but for the join between the tables, the last line cannot compile
it complains that Company does not contain a definition of has no Select
what am I doing wrong ?
thanks for helping me on this
If you try a where after your include. Does that help?
data.Include(a => a.Company).Where(partner=>partner.Company.Name.equals(partnersSearch.SearchByName))

Linq to Entities Many to Many return nested objects

If I have three tables in a many to many relationship, including the junction table, Students, StudentCourses, Courses:
How can I return the Student objects with their associated course objects?
To further clarify, here is the Student View model:
public class StudentViewModel
{
public int StudentID { get; set; }
public string Title { get; set; }
public string Name { get; set; }
...
...
...
public ICollection<StudentCourseViewModel> StudentCourses { get; set; }
}
The following Linq to Entities query only returns student objects, hence my problem!
var query = from student in context.Students
from studentcourse in student.StudentCourses
where studentcourse.CourseID == 4
select student;
Unfortunately, in the View when I debug the student object, there are no studentcourses being returned. Looking at the syntax of the query, this makes sense as it is only returning students.
I've tried projection, e.g.
var query = from student in context.Students
from studentcourse in student.StudentCourses
where studentcourse.CourseID == 4
select new
{
student,
StudentCourses = studentcourse
}
But projection reshapes the output Student object and does not meet the Student shape(type) since I'm using the Student view model.
Here is a snippet of my View code:
#foreach (var item in Model)
{
#Html.DisplayFor(s => item.Name)
...
...
#foreach (var subItem in item.StudentCourses)
{
#Html.DisplayFor(sc => subItem.Description)
#Html.DisplayFor(c => subItem.Course.Name)
...
So, really stuck at this point. I would have thought this would be very simple, but I've spent an entire day researching, trial and error.
You can force the related entities to be loaded by using Include:
var query = from student in context.Students
.Include("StudentCourses")
.Include("StudentCourses.Course")
where studentcourse.CourseID == 4
select student;

What is right way to deal with "N+1" + Count problem?

Supose the model as below:
class public Post
{
public int Id {get; set;}
public virtual ICollection<Comment> Comments {get;set;}
}
in the Posts/Index Page, I want to show a list of Post, with the Count of comments of each post (not total number of comments of all posts).
1: If I use
context.Posts.Include("Comments")
it will load the whole entity of all related commments , in fact I only need the Count of Comments.
2: If I get the count of each post one by one:
var commentCount = context.Entry(post)
.Collection(p => p.Comments)
.Query()
.Count();
that is a N+1 problem.
Any one knows the right way?
Thank you!
Do you need this for your presentation layer / view model? In such case create specialized ViewModel
public class PostListView
{
public Post Post { get; set; }
public int CommentsCount { get; set; }
}
And use query with projection:
var data = context.Posts
.Select(p => new PostListView
{
Post = p,
CommentsCount = p.Comments.Count()
});
And you are done. If you need it you can flatten your PostListView so that it contains Post's properties instead of Post entity.
What about something like this:
public class PostView
{
public String PostName { get; set; }
public Int32 PostCount { get; set; }
}
public static IEnumerable<PostView> GetPosts()
{
var context = new PostsEntities();
IQueryable<PostView> query = from posts in context.Posts
select new PostView
{
PostName = posts.Title,
PostCount = posts.PostComments.Count()
};
return query;
}
Then use something like this:
foreach (PostView post in GetPosts())
{
Console.WriteLine(String.Format("Post Name: {0}, Post Count: {1}", post.PostName, post.PostCount));
}
Should display the list as so:
Post name (12)
Post name (1)
Etc etc

MongoDB array object Update and Page

I've a entity class stored in MongoDB that looks similar to these:
public class Theme
{
public ObjectId Id { get; set; }
public string Description { get; set; }
public string Title { get; set; }
public List<Comment> CommentList { get; set; }
}
public class Comment
{
public string Content { get; set; }
public string Creator { get; set; }
public string Date { get; set; }
}
Using the MongoDB C# driver,and based on the model, how can i resolve for the following questions:
Update a comment of the theme, eg: theme1.CommetList[10].Creator = "Jack"
How to page for the array object
Thanks.
Wentel
#Andrew Orsich
Thanks for your help.
And there is another trouble:
var query = Query.EQ("Id", id); //The 'id' type is ObjectId
List<Theme> newDatas = themeCollection.FindAs<Theme>(query).ToList();
Theme newData = themeCollection.FindOneByIdAs<Theme>(allDatas[0].Id);
Result: 'newDatas' is null and 'newData' has data, why?
1.Using positional operator:
var query = Query.And(Query.EQ("Id", id));
var update = Update.Set("CommetList.10.Creator", "Jack");
Also you probably need to add id to the Comment class. In this case you can updated matched by query comment like this:
var query = Query.And(Query.EQ("Id", id), Query.EQ("CommentList.Id", commentId));
var update = Update.Set("CommentList.$.Creator", "Jack");
2.You can load entire theme and do paging of comments from c# using linq for example. Or you can also use $slice like this:
var comments = themeCollection
.FindAs<Comment>()
.SetFields(Fields.Slice("Comments", 40, 20))
.ToList();
For your second question you need to do the following:
ObjectId oid = new ObjectId(id);
var query = Query.EQ("_id", oid);