EF Core join on sub table - c#

I have three classes set up like this:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserItem> UserItems { get; set; }
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserItem> UserItems { get; set; }
}
public class UserItem
{
public int ID { get; set; }
public int UserID { get; set; }
public int ItemID { get; set; }
}
Entity Framework generates the tables in the SQL Server database with the correct foreign keys. I can query the Item and User to get the UserItem collections within like this:
var a = context.DBUser.Include(s => s.UserItems);
var b = context.DBItem.Include(s => s.UserItems);
What I am not happy about is how I get from the User to the Item. In SQL I would do something like this
SELECT * FROM Users U
LEFT JOIN UserItems UI
ON U.ID = UI.UserID
LEFT JOIN Items I
ON UI.ItemID = I.ID
In C# I've needed to resort to this:
var c = from user in _context.DBUsers
join userItem in _context.DBUserItems
on user.ID equals userItem.UserID into ui
from userItem in ui.DefaultIfEmpty()
join item in _context.DBItems
on userItem.ItemID equals item.ID into i
from item in i.DefaultIfEmpty()
select new
{
user,
userItem,
item
};
Which given there should already be relationships between the tables in EF doesn't feel right

You need to add navigations to UserItem also:
public class UserItem
{
public int ID { get; set; }
public int UserID { get; set; }
public int ItemID { get; set; }
public User User { get; set; }
public Item Item { get; set; }
}
Then your query can be like this:
var users = context.DBUser
.Include(x => x.UserItems)
.ThenInclude(x => x.Item);
Also note that if you are using .NET 5, the UserItem "join entity" can be omitted altogether:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
Which would simplify your query:
var users = context.DBUser.Include(x => x.Items);

Related

Order by repeated times LINQ ASP.NET MVC

I have this Report model:
public class Report
{
[Key]
public int Id { get; set; }
public string ReporterId { get; set; }
[ForeignKey("ReporterId")]
public virtual ApplicationUser Reporter { get; set; }
public int ProductId { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
public ReportType ReportType { get; set; }
}
Now, I want to return a List<Report> ordered by the most repeated element based on ProductId to the least repeated element.
Is there any way on how to do this with linq?
Try this:
var newList = List.GroupBy(p=> p.ProductId )
.OrderByDescending(x=> x.Count())
.SelectMany(x=> x).ToList();

Linq Group by relative entites

My scenario: Users will be able to create lists and add items to these lists. What I want to do is to find the items in the lists created by the users at most.
Item Entity
public class Item:BaseEntity
{
public string Name { get; set; }
public decimal Price { get; set; }
public decimal DiscountedPrice{ get; set; }
public virtual ICollection<ItemList> ItemLists { get; set; }
}
Item List Entity
public class ItemList:BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public int UserId { get; set; }
public ICollection<Item> Items { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
}
User Entity
public class User:BaseEntity
{
public string Name { get; set; }
public string Surname { get; set; }
public string Gsm { get; set; }
public string Email { get; set; }
public virtual ICollection<ItemList> ItemLists{ get; set; }
}
my DTO
public class TopItemsForUsers
{
[BsonRepresentation(BsonType.ObjectId)]
[BsonId]
public string ItemId { get; set; }
public string UserId { get; set; }
public int Quantity { get; set; }
}
My Item repository
var query = _context.Items.Include(l => l.ItemLists)
.GroupBy(g => g.ItemLists)
.Select(z => new TopItemsInLists { ItemId = z.Key.ToString(), Quantity = z.Count() })
.OrderByDescending(z => z.Quantity)
.Take(10);
I want to get products that are very present in users' lists
Where am I doing wrong? If anyone has any other suggestions
Try this query. I hope I understand question correctly.
var query =
from u in _context.Users
from il in u.ItemLists
from i in il.Items
group i by new { UserId = u.Id, ItemId = i.Id } into g
select new TopItemsInLists
{
UserId = g.Key.UserId.ToString(),
ItemId = g.Key.ItemId.ToString(),
Quantity = g.Count()
};
query = query
.OrderByDescending(z => z.Quantity)
.Take(10);

Linq2Couchbase joins on List<> to id

I have two objects
public class Card : IEntity
{
public Guid Id { get; set; }
public string Title { get; set; }
public string BackgroundColour { get; set; }
public string Texture { get; set; }
public List<Guid> Category { get; set; }
public string Notes { get; set; }
}
and
public class Category : IEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid Parent { get; set; }
}
What I would like to do is, using the Couchbase SDK, join these two objects together (Card.Category => Category.Id) in one database hit (Like a classic SQL Stored Procedure that brings me back all the data to populate both objects), but all of the (examples I found have it from id => id rather than a List of Ids (Many) to (one) id.
An example of something I tried to do...
var query = from card in context.Query<Card>()
from categoryId in card.Category.Ids
join category in context.Query<Category>()
on categoryId equals category.Id
select new CardView { Id = card.Id, Categories = category};

Linq c# query to join two many to many relationship

here i have a group containing many users and assets ,i want list of assets linked to a single user.
What is the best way of using linq.
public class Asset
{
public Guid Id { get; set; }
public virtual ICollection<GroupToAssets> GroupToAssets { get; set; }
}
public class User: IdentityUser
{
public virtual ICollection<GroupToUsers> GroupToUsers { get; set; }
}
public class GroupToAssets
{
public Guid GroupId { get; set; }
public virtual Group Group { get; set; }
public Guid AssetId { get; set; }
public virtual Asset Asset { get; set; }
}
public class GroupToUsers
{
public Guid GroupId { get; set; }
public virtual Group Group { get; set; }
public string UserId { get; set; }
public virtual User User { get; set; }
}
I think the following linq satisfy my case don't know if there is a better method
var Groups =
await _Context.Groups.Where(x => x.GroupToUsers.Any(d => d.UserId == UserId && d.GroupId == x.Id)).ToListAsync();
var assets =
await _Context.GroupToAssets.Where(x => Groups.Contains(x.Group)).Select(x => new { x.Asset }).Distinct().ToListAsync();

How to avoid to load collection property in Entity Framework

When I select artile, it select user, but user has a collection of article, so article select user again. May be recursive cause out of memory ,
The calling processing is :
article=>user=>article=>user...
ef entities is :
public partial class article
{
public int id { get; set; }
public string title { get; set; }
public string cont { get; set; }
public Nullable<int> uid { get; set; }
public System.DateTime addtime { get; set; }
public Nullable<int> colid { get; set; }
public virtual user user { get; set; }
public virtual column column { get; set; }
}
public partial class user
{
public user()
{
this.roleusers = new HashSet<roleuser>();
this.articles = new HashSet<article>();
}
public int id { get; set; }
public string email { get; set; }
public string uname { get; set; }
public string upass { get; set; }
public virtual ICollection<roleuser> roleusers { get; set; }
public virtual ICollection<article> articles { get; set; }
}
mysql EF operation class is :
public class ArtDao
{
readonly crmEntities _ent = new crmEntities();
public List<article> PageArts(int start, int limit, out int total)
{
var ll =
_ent.articles.OrderByDescending(o => o.id)
.Skip(start)
.Take(limit)
.ToList();
total = _ent.articles.Count();
return ll;
}
}
How to avoid to eager load the collection property roleusers and articles ?
You need to set LazyLoad on you edmx properties, and manually load only first level childs with Include() method when selecting:
public List<article> PageArts(int start, int limit, out int total)
{
var ll =
_ent.articles.OrderByDescending(o => o.id)
.Skip(start)
.Take(limit)
.Include(o => o.user)
.ToList();
total = _ent.articles.Count();
return ll;
}
You need to implement it in another class, wich can be a partial class.

Categories