Lambda, get properties from nested list - c#

I'm trying to retrieve a list of UserProfile-objects of a given CustomerId. I Have theese pocos:
public class UserRole
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserRoleId { get; set; }
public Role Role { get; set; }
public Customer Customer { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
public class Customer
{
public Customer()
{
this.UserRoles = new Collection<UserRole>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int CustomerId { get; set; }
[Display(Name = "Gruppens namn")]
public string Name { get; set; }
[InverseProperty("Customers")]
public virtual ICollection<UserRole> UserRoles { get; set; }
}
I can't seem to find a way to actually get the UserProfiles. I can get a list of UserRoles by doing:
dbContext.Customers.First(c => c.CustomerId == customer.CustomerId).UserRoles
but cannot access UserProfiles of of that object. I also tried
dbContext.UserRoles.Where(c => c.Customer.CustomerId == customerId)
but same result. Ideas?

To get the list of profiles for a specific customer:
var result = dbContext.UserRoles
.Where(ur => ur.Customer.CustomerId == customer.CustomerId)
.Select(ur => ur.UserProfile);

Related

EF Core join on sub table

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

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

Query through multiple navigation properties

I am getting confused with my LINQ query and wondering if there is a way I can achieve the following:
A user has a list of liked Categories:
public class ApplicationUser : IdentityUser
{
[PersonalData]
public string Name { get; set; }
[PersonalData]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[PersonalData]
public PersonGender Gender { get; set; }
[PersonalData]
[ForeignKey("Suburb")]
public int SuburbId { get; set; }
//Information about user preferences
public ICollection<UserCategory> LikedCategories { get; set; }
public virtual Suburb Suburb { get; set; }
}
Where UserCategory is defined as follows:
public class UserCategory
{
[Key]
public int Id { get; set; }
public ApplicationUser applicationUser { get; set; }
public Category Category { get; set; }
[ForeignKey("ApplicationUser")]
public string applicationUserId { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
}
And Category is:
public class Category
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int CategoryFollowers { get; set; }
public ICollection<EventCategory> EventCategories { get; set; }
public ICollection<UserCategory> UserCategories { get; set; }
}
}
Finally, in my Events class I have the following:
public class Event
{
[Key]
public int ID { get; set; }
[Required]
public string Name { get; set; }
[Display(Name = "Is Featured?")]
public bool isFeatured { get; set; }
public byte[] EventImage1 { get; set; }
public byte[] EventImage2 { get; set; }
public virtual ICollection<EventCategory> EventCategories { get; set; }
public virtual ICollection<UserEvent> UserEvents { get; set; }
My view requires an object of type Event, so I am trying to return a list of Events where the EventCategory is contained in the UserCategory table. In other words I just want to show events that the user has liked the category for.
MORE CLARIFICATION
I am able to filter my events by category from a different function that takes in a hardcoded category Id from the view and this works fine:
//GET:// Browse event by category
public async Task<IActionResult> BrowseByCategory(int? id, int? alt_id)
{
if (id == null)
{
return NotFound();
}
var eventsContext = _context.Events
.Include(m => m.Venue)
.ThenInclude(mf => mf.Suburb)
.ThenInclude(mc => mc.Constituency)
.ThenInclude(md => md.City)
.Where(e => e.EventCategories.Any(c => c.Category.ID == id || c.Category.ID == alt_id))
.Take(15)
.OrderByDescending(o => o.StartDate);
return View("Browse",await eventsContext.ToListAsync());
}
I would like to do the exact same as above, but rather than pass in the hardcoded ID queries from the form, I want the query to check the categoryIDs that are saved in the UserCategory table. There is no set number of how many UserCategory items there are.

Linq Entity Framework: select table data without repeated data into IEnumerable

I am working with the following technologies: C#, SQL Server, ASP.NET and Entity Framework and Linq.
I have a many-to-many relation in my database:
The model classes:
public class Courses
{
[Required]
public int Id { get; set; }
//more properties here
public student stud { get; set; }
}
public class inscribe
{
[Key]
public intId { get; set; }
//properties here
public student student{ get; set; }
[Required]
[ForeignKey("student")]
public string StudentId{ get; set; }
public Courses Courses{ get; set; }
}
Given a student Id, I would like to return a list of courses where he/she is inscribed.
This is what I have tried so far:
public IEnumerable<CursoDto> GetCursosAlumno(Int studentId)
{
//some code here to validate
var x = _dbContext
.Inscribe.Include(c => c.Courses)
.Where(c => c.StudentId == studentId).toList();
// x variable is a list<inscribe>
}
My problem is that I do not know how to access to the courses entity and return it as a list, for instance:
var result = X.Courses;
return result; //result is a list<courses>
How can I do it? If possible, not using a foreach block please.
Thanks
In Code First approach you don't need to add "link table" (inscribe in OP) into your models (it will be created transparently).
//Models
public class Course
{
[Key]
public int Id { get; set; }
//more properties here
public virtual /*important*/ ICollection<Student> studs { get; set; }
}
public class Student
{
[Key]
public int Id { get; set; }
//more properties here
public virtual /*important*/ ICollection<Course> courses { get; set; }
}
//Controller
var stud = _dbContext.studs.Where(s => s.Id == /*param*/id).FirstOrDefault();
var courses = stud.courses.ToList();//Real courses with id, name, etc. No Include required
Update
If you do need the "link table" (for example to add some properties like sortOrder or enrollmentDate) then the models will be a little different.
//Models
public class Course
{
[Key]
public int Id { get; set; }
//more properties here
public virtual /*important*/ ICollection<StudentCourse> studs { get; set; }
}
public class Student
{
[Key]
public int Id { get; set; }
//more properties here
public virtual /*important*/ ICollection<StudentCourse> courses { get; set; }
}
[Table("inscribe")]
public class StudentCourse
{
[Key, Column(Order = 0)]
public int StudentId {get; set'}
[Key, Column(Order = 1)]
public int CourseId {get; set'}
//extra properties
[ForeignKey("StudentId")]
public virtual Student stud { get; set; }
[ForeignKey("CourseId")]
public virtual Course course { get; set; }
}
//Controller
var courses = _dbContext.courses.Where(c/*c is "link"*/ => c.Student/*StudentCourse prop*/.Any(s/*link again*/ => s.StudentId == someId/*param*/));//again courses
As you see Include is not required.
var result = _dbContext
.Inscribe.Include(c => c.Courses)
.Where(c => c.StudentId == studentId)
.SelectMany(c => c.Courses).ToList();

Recursive Linq query

Currently I'm working with WebApi and Entity Framework, So I have 3 entities: Products, Categories and ProductCategory; their relationships are:
My problem is that Category entity has a Category Parent property, so it's recursive, my Category Controller looks like this:
public async Task<IHttpActionResult> GetCategory()
{
var category = await db.Category.Select(x=>new {
x.categoryDesc,
x.CategoryId,
x.categoryImage,
x.categoryName,
x.categoryParent
}).ToListAsync();
return Ok(category);
}
I'm returning an anonymous object, the propierty categoryParent its the same object as category so its recursive; when I fill the database with mock data in the Category table and call the get method, everything runs OK because I dont have any data en ProductCategory, but when I fill it(the ProductCategory table) the program crashes.
MY entity classes are:
public class Category {
public int CategoryId { set; get; }
public string categoryName { set; get; }
public string categoryDesc { set; get; }
public string categoryImage { set; get; }
public int? categoryParentId { set; get; }
public virtual ICollection<ProductCategory> ProductCategories { set; get; }
public virtual Category categoryParent { set; get; }
}
public class Product{
public int ProductId { set; get; }
public string productName { set; get; }
public string productDesc { set; get; }
public double productPrice { set; get; }
public string productUrl { set; get; }
public DateTime productPublishDate { set; get; }
public DateTime productModifyDate { set; get; }
public bool productStatus { set; get; }
public int productStock { set; get; }
public virtual ICollection<ProductCategory> ProductCategories { set; get; }
}
public class ProductCategory : IProductCategory {
[Required]
[Key]
[ForeignKey("Category")]
[Column(Order = 1)]
public int CategoryId { set; get; }
[Required]
[Key]
[ForeignKey("Product")]
[Column(Order = 2)]
public int ProductId { set; get; }
public virtual Product Product { set; get; }
public virtual Category Category { set; get; }
}
Can you help me to fix it?, So when I return categoryParent return it recursively, Thanks
I'm guessing you might have better luck if you explicitly state how you want the information organized, and remove the virtual property
IQueryable<Category> category = db.Category;
var result = category.Where(w => w.categoryParentId != null)
.Join(category,
child => (int)child.categoryParentId,
parent => parent.CategoryId,
(child, parent) => new {
child.categoryDesc,
child.CategoryId,
child.categoryImage,
child.categoryName,
parent
}
);
return Ok(await result.ToListAsync());
That should get you the same result as your query above, then you could remove:
public virtual Category categoryParent { set; get; }
Thank you very much but I found the solution: https://practiceaspnet.wordpress.com/2015/11/09/many-to-many-relationships-with-additional-fields/
I used fluent API to resolve the navigation recursive problem I had:
modelBuilder.Entity<Category>()
.HasMany(x => x.ProductCategories)
.WithRequired(x => x.Category)
.HasForeignKey(x=>x.CategoryId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductCategories)
.WithRequired(x => x.Product)
.HasForeignKey(x => x.ProductId);
Basically the WithRequired method prevents a navigation property on the other side of the relationship so it stops the recursion

Categories