ASP NET MVC How to call Count on an attribute then print out a sorted list? - entity-framework

Say I had a class:
public class Post
{
public int PostId { get; set; }
public string Topic { get; set; }
public int UserId { get; set; }
[StringLength(5000)]
public string Body { get; set; }
public DateTime DateCreated { get; set; }
public string Name { get; set; }
public int Votes { get; set; }
}
And for each post, a user could input a topic. for example, if the topics were "Red" "Green" "Blue" and "Yellow", how could I create a list based on how many times those were used?
An example output:
Red | 70
Blue | 60
Green | 40
Yellow| 35
EDIT: How come this doesn't work and gives me an error where I cannot implicitly convert the type?
public List<string> GetPopularTopics(int count)
{
var posts = from p in db.Posts
group p by p.Topic into myGroup
select new
{
Topic = myGroup.Key,
Count = myGroup.Count()
};
return posts.ToList();
}
EDIT 2:
So I tried your solution out Dustin, and I'm getting an error. This is what I used:
public IEnumerable<IGrouping<string,int>> GetPosts()
{
var posts = from p in db.Posts
group p by p.Topic into topicCounts
select new
{
Topic = topicCounts.Key,
Count = topicCounts.Count()
};
return posts.ToList();
}
This is giving me an error under posts.ToList():
Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable>'. An explicit conversion exists (are you missing a cast?)

To create the grouping you create an anonymous type such as:
var posts = from p in context.Posts
group p by p.Topic into topicCounts
select new
{
Topic = topicCounts.Key,
Count = topicCounts.Count()
};
Then to work with the date, lets say iterate over it:
foreach(var p in posts)
{
Response.Write(String.Format("{0} - {1}", p.Topic, p.Count));
}

You must create a new type if you do a projection and return it form method!
public class MyCounts
{
public string Topic { get; set; }
public int Count { get; set; }
}
public List<MyCounts> GetPopularTopics(int count)
{
var posts = from p in db.Posts
group p by p.Topic into myGroup
select new MyCounts
{
Topic = myGroup.Key,
Count = myGroup.Count()
};
return posts.ToList();
}

The problem is that you need to use an non anonymous type for your return value.
This query creates an IEnumerable of anonymous types.
var posts = from p in context.Posts
group p by p.Topic into topicCounts
select new
{
Topic = topicCounts.Key,
Count = topicCounts.Count()
};
It's the select new statement that creates the anonymous objects.
What you need to do is to create something that is non anonymous - an object that can be shared within and outside this method.
Like this:
public IEnumerable<TopicAndCount> GetPosts()
{
var posts = from p in context.Posts
group p by p.Topic into topicCounts
select new TopicAndCount
{
Topic = topicCounts.Key,
Count = topicCounts.Count()
};
}
Note the select new TopicAndCount statement and the return value of the enclosing method.
That will solve your problem.

Related

Hungarian method - 3 choices assignment

I have a question about the hungarian method for assigment problems.
In the examples I found from the hungarian method you have 1 to n prefereces.
So at the moment, we have in the school the task to create a programm where you have a school class (1 to n students). The students take exactly ONE gift to the class. So (1 to amount of students )
After that every student hast exactly three CHOICES(to pick one gift, e.g. gift 1, gift 5, gift 9) and our programm is supossed to output the best assignment for the class.
But as mentioned before the examples we found about the hungarian methods have 1- to n preferences. And we need exactly three.
How would we solve this specific task ?
Is the hungarian method still the best way to solve this task or should we look at another algorithm ?
I just saw your last question get closed without an answer.
I've implemented a practical way to solve this problem in C#, assuming the wishes are given in a csv file of the following format:
2,10,6
2,7,3
4,7,1
...
The number of columns, i.e. the number of preferences, doesn't matter for my implementation.
Obviously, there's room for performance improvements, but I chose to keep it more readable for the sake of the answer.
Here are the classes used in the code below:
public class Student
{
public int StudentNumber { get; set; }
public List<WishVote> WishVotes { get; set; }
}
public class WishVote
{
public int WishNumber { get; set; }
public int Order { get; set; }
public string Id { get; set; }
}
public class WishVoteResult
{
public int WishNumber { get; set; }
// wish order
// vote count for that order
// wish number
public List<Tuple<int, int, int>> Assignments { get; set; }
public int TotalVoteCount { get; set; }
}
And here is the code you could run in Main to output the wishes' numbers alongside the total number of votes in descending order:
var lines = File.ReadAllLines("wishes.csv").ToList();
int studentNumber = lines.Count;
var students = new List<Student>();
int currentLine = 0;
lines.ForEach(l =>
{
currentLine++;
var wishVotes = new List<WishVote>();
int wishOrder = 0;
l.Split(',').ToList().ForEach(w =>
{
wishOrder++;
wishVotes.Add(new WishVote
{
Id = Guid.NewGuid().ToString(),
Order = wishOrder,
WishNumber = Convert.ToInt32(w)
});
});
students.Add(new Student
{
StudentNumber = currentLine,
WishVotes = wishVotes
});
});
var allWishVotes = students.SelectMany(s => s.WishVotes).ToList();
List<int> uniqueWishes = allWishVotes.Select(w => w.WishNumber).Distinct().ToList();
var wishVoteResults = new List<WishVoteResult>();
// assuming every row in the file has the same number of columns
int orderCount = students.First().WishVotes.Max(w => w.Order);
uniqueWishes.ForEach(uw =>
{
var wishVoteResult = new WishVoteResult
{
WishNumber = uw,
TotalVoteCount = allWishVotes.Where(w => w.WishNumber == uw).Count(),
Assignments = new List<Tuple<int, int, int>>()
};
for(int i = 1; i <= orderCount; i++)
{
wishVoteResult.Assignments.Add(new Tuple<int, int, int>(i, allWishVotes.Where(w => w.Order == i && w.WishNumber == uw).Count(), uw));
}
wishVoteResults.Add(wishVoteResult);
});
var assignments = wishVoteResults.SelectMany(w => w.Assignments).OrderByDescending(a => a.Item2).ThenBy(a => a.Item1).ToList();
Console.WriteLine("Wish {wishNumber}: {voteCount} {wishOrder}");
foreach (var assignment in assignments)
{
Console.WriteLine($"Wish number {assignment.Item3}: {assignment.Item2} {assignment.Item1}");
}
Console.WriteLine("Finished.");
Effectively, what we're doing is creating all the possible assignments and then just sorting accordingly.

Entity Framework Core Select Outer Join

is there an easy way to include a nullable navigation inside a select expression for EF Core?
My model looks like this
public class RootVO
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Description { get; set; }
public int? RelationId { get; set; }
[ForeignKey(nameof(RelationId))]
public RelationVO Relation { get; set; }
}
public class RelationVO
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Property1 { get; set; }
[Required]
[StringLength(200)]
public string Property2 { get; set; }
public ICollection<RootVO> RootRelations { get; set; }
}
When I load the data I just want to select certain kind of properties. Currently my expression looks like this:
Expression<Func<RootVO, RootVO>> selectExpr = m => new RootVO
{
Id = m.Id,
Description = m.Description,
Relation = m.Relation != null ? new RelationVO
{
Id = m.Relation.Id,
Property1 = m.Relation.Property1
} : null
};
var result = context.Roots.Select(selectExpr).ToList();
Is there an easier way to handle the relation select?
Edit
Maybe some background here will help:
I have a huge object with a lot of columns and relations, some with inner, some with outer joins. This query gets accessed by a datagrid on UI which can have dynamic columns depending on the user selection. To increase the performance I've written a class that will build the select expression dynamicly depending on the selected columns. For now it is working, but I'm having trouble when an outer join is null due to null-reference excepction.
The debug view on the expression could look like this:
.New IVA.Core.Data.Models.StockMovementLogVO(){
SequenceNo = $m.SequenceNo,
PostingPeriodId = $m.PostingPeriodId,
TransactionDate = $m.TransactionDate,
FinancialYear = $m.FinancialYear,
FinancialYearPeriod = $m.FinancialYearPeriod,
VoucherDate = $m.VoucherDate,
ItemQuantity = $m.ItemQuantity,
BuCode = $m.BuCode,
LocationStructure = .New IVA.Core.Data.Models.LocationStructureVO(){
Id = ($m.LocationStructure).Id,
Description = ($m.LocationStructure).Description
},
BookingType = .New IVA.Core.Data.Models.BookingTypeVO(){
Id = ($m.BookingType).Id,
Description = ($m.BookingType).Description
},
PartnerStockLocationType = .New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.PartnerStockLocationType).Id,
Description = ($m.PartnerStockLocationType).Description
},
StockLocationType = .New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.StockLocationType).Id,
Description = ($m.StockLocationType).Description
}
}
StockLocationType and PartnerStockLocationType are outer joins and if those are null the query fails to execute.
I've now changed my expression builder that it will take care of the outer joins by including a null reference check. The expression now looks like this:
.New IVA.Core.Data.Models.StockMovementLogVO(){
SequenceNo = $m.SequenceNo,
PostingPeriodId = $m.PostingPeriodId,
TransactionDate = $m.TransactionDate,
FinancialYear = $m.FinancialYear,
FinancialYearPeriod = $m.FinancialYearPeriod,
VoucherDate = $m.VoucherDate,
ItemQuantity = $m.ItemQuantity,
BuCode = $m.BuCode,
LocationStructure = .New IVA.Core.Data.Models.LocationStructureVO(){
Id = ($m.LocationStructure).Id,
Description = ($m.LocationStructure).Description
},
BookingType = .New IVA.Core.Data.Models.BookingTypeVO(){
Id = ($m.BookingType).Id,
Description = ($m.BookingType).Description
},
PartnerStockLocationType = .If ($m.PartnerStockLocationType != null) {
.New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.PartnerStockLocationType).Id,
Description = ($m.PartnerStockLocationType).Description
}
} .Else {
null
},
StockLocationType = .If ($m.StockLocationType != null) {
.New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.StockLocationType).Id,
Description = ($m.StockLocationType).Description
}
} .Else {
null
}
}
Edit
If anyone is interessted how it looks, I've created a repository where I use the class.
https://github.com/NQuirmbach/DynamicQueryBuilder

Project into a (list of) concrete object instead of an (list of) anonymous object

The problem simplified is as follows: in Entity Framework i am doing a join involving 3 tables, and returning the joined result set, which involves (some) fields from the 3 tables.
var query = (
from t1 in dbCtx.TB_Entity1
from t2 in dbCtx.TB_Entity2
.Where(p => p.someCol == t1.someCol && t.IsActive == true)
.DefaultIfEmpty() //LEFT JOIN
from t3 in dbCtx.TB_Entity3
.Where(q => q.someCol == t2.someCol)
where t1.IsLatest == true
&& (t1.istatus == 2
|| t1.istatus == 3
)
select new {
t1.col100,
t1.col101,
t2.col200,
t2.col201,
t3.col300,
t3.col301
}).OrderByDescending(t1 => t1.ID);
var anonObjList = query.ToList();
Thus, at the end of the query I write a projection to select the fields i want.
Finally i run the query with .ToList() and get a list of Anonymous objects.
How do i modify the query to project into a List of MyConcreteClass
i.e. i want to be able to write something similar to
List<MyConcreteClass> myObjList = query.ToList();
You may assume my concrete class looks like
public class MyConcreteClass
{
public string Col100 { get; set; }
public string Col101 { get; set; }
public string Col200 { get; set; }
public string Col201 { get; set; }
public string Col300 { get; set; }
public string Col301 { get; set; }
}
You just use the object initializer syntax:
new MyConcreteClass
{
Col100 = t1.col100,
Col101 = t1.col101,
Col200 = t2.col200,
Col201 = t2.col201,
Col300 = t3.col300,
Col301 = t3.col301
}

SQLite.net Extensions loads the same entity multiple times rather than returning the same reference

I'm using the PCL version of SQLite.Net Extensions in a Windows 10 universal app. This is my first time using it. It generally seems to be working, but it appears to be loading an entity multiple times rather than reusing a reference to the same object.
According to the SQLite.Net extensions documentation:
SQLite-Net Extensions will ensure that any object is loaded only once from the database and will resolve circular dependencies and inverse relationships while maintaining integral reference. This means that any returned object of the same class with the same identifier will be a reference to exactly the same object.
That does not appear to be happening for me. Here is my code:
public class Group {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
public string GroupName { get; set; }
public override string ToString() {
return string.Format("Group [ID: {0}, HashCode: {1}] GroupName={2}", Id.ToString().Last(4), GetHashCode(), GroupName);
}
[ManyToMany(typeof(GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
public List<Member> Members { get; set; }
public void DebugIt() {
Debug.WriteLine(this);
foreach (var member in Members) Debug.WriteLine(" " + member);
}
}
public class Member {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
public string Name { get; set; }
public override string ToString() {
return string.Format("Member [ID: {0}, HashCode: {1}] Name={2}", Id.ToString().Last(4), GetHashCode(), Name);
}
[ManyToMany(typeof (GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
public List<Group> Groups { get; set; }
public void DebugIt() {
Debug.WriteLine(this);
foreach (var group in Groups) Debug.WriteLine(" " + group);
}
}
public class GroupMember {
[PrimaryKey, AutoIncrement]
public Guid Id { get; set; }
[ForeignKey(typeof(Group))]
public Guid GroupID { get; set; }
[ForeignKey(typeof(Member))]
public Guid MemberId { get; set; }
}
public class DatabaseGroups {
private const string FileName = "db.sqlite";
private SQLiteConnection _db;
public async Task<bool> LoadAsync() {
var exists = await FileHelper.DoesFileExistAsync(FileName);
_db = new SQLiteConnection(new SQLitePlatformWinRT(), DatabaseFullPath,
exists ? SQLiteOpenFlags.ReadWrite : SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite);
if (!exists) InitializeWithDefaults();
return await FileHelper.DoesFileExistAsync(FileName);
}
private void InitializeWithDefaults() {
_db.CreateTable<Group>();
_db.CreateTable<Member>();
_db.CreateTable<GroupMember>();
var group1 = new Group {GroupName = "Group 1"};
var group2 = new Group {GroupName = "Group 2"};
var member1 = new Member {Name = "Bob"};
var member2 = new Member {Name = "Jane"};
_db.Insert(group1);
_db.Insert(group2);
_db.Insert(member1);
_db.Insert(member2);
group1.Members = new List<Member> {member1, member2};
_db.UpdateWithChildren(group1);
group2.Members = new List<Member> {member1, member2};
_db.UpdateWithChildren(group2);
}
private static StorageFolder DatabaseFolder {
get { return ApplicationData.Current.LocalFolder; }
}
private static string DatabaseFullPath {
get { return Path.Combine(DatabaseFolder.Path, FileName); }
}
public void DebugIt() {
foreach (var groupId in _db.Table<Group>().Select(g => g.Id)) {
var group = _db.GetWithChildren<Group>(groupId);
group.DebugIt();
}
foreach (var memberId in _db.Table<Member>().Select(m => m.Id)) {
var member = _db.GetWithChildren<Member>(memberId);
member.DebugIt();
}
}
}
protected override async void OnLaunched(LaunchActivatedEventArgs e) {
_db = new DatabaseGroups();
await _db.LoadAsync();
_db.DebugIt();
When it runs, I create some initial data. I then load those objects using GetWithChildren and debug it. Here are the results:
Group[ID: 4858, HashCode: 51192825] GroupName = Group 1
Member[ID: dbfa, HashCode: 64971671] Name = Jane
Member[ID: b047, HashCode: 30776584] Name = Bob
Group[ID: 30f0, HashCode: 53439890] GroupName = Group 2
Member[ID: dbfa, HashCode: 36062904] Name = Jane
Member[ID: b047, HashCode: 9089598] Name = Bob
Member[ID: b047, HashCode: 20305449] Name = Bob
Group[ID: 30f0, HashCode: 9648315] GroupName = Group 2
Group[ID: 4858, HashCode: 29803642] GroupName = Group 1
Member[ID: dbfa, HashCode: 36899882] Name = Jane
Group[ID: 30f0, HashCode: 23318221] GroupName = Group 2
Group[ID: 4858, HashCode: 60865449] GroupName = Group 1
As you can see, the objects appear to be loading correctly, but the object references for Group 1 (for example) are different (see the hash code).
Am I perhaps misinterpreting the way SQLite.Net Extensions handles object references? Perhaps it handles reusing object references within a single call to GetWithChildren, but not across multiple calls on the same SQLiteConnection?
If that's the case, how are you supposed to load a more complex object graph with these sorts of relationships?
You are correct, SQLite-Net Extensions caches the objects for recursive calls to avoid reference loops and handle inverse relationships, but it doesn't cache the objects between calls.
SQLite-Net Extensions is just a thin layer over SQLite.Net, if integral reference is important for you, you can go back to manual queries for more complexes operations.
If you have any suggestion or pull request, they are always welcome ;)

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