Get related entites of child items using Linq? - c#

I have these entities:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CustomerOrder
{
public int Id { get; set; }
public string Name { get; set; }
//Some other properties here...
public IEnumerable<Order> Orders { get; set; }
}
I'm using Linq to get a customer and his orders as follows:
var customers = dataContext.Set<Customer>();
return (from customer in customers
where customer.Id == id
select new CustomerOrder
{
Id = id,
Name = customer.Name,
Orders = customer.Orders
});
The above projects as expected. However, I need to retrieve the Category for each Order.
How can I do this by expanding the above Linq statement?

Related

EntityFramework Join with MVC C# not displaying anything

I am using EntityFramework which creates a local database and I would like to perform Join operation to show the Purchases done for each Customer, but I can't see to make it work.
Here is my Customer Model
public class Customer
{
[Key]
public int custId { get; set; }
public string firstName { get; set; }
public string surname { get; set; }
public string addresss { get; set; }
}
Here is my Purchase Model
public class Purchase
{
public int purchaseId { get; set; }
public int custId { get; set; }
public int productId { get; set; }
public double price { get; set; }
public int quantity { get; set; }
public double deliveryFees { get; set; }
public double finalAmount { get; set; }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime dateOfPurchase { get; set; }
}
I have tried to create a ViewModel which should keep each Model in the class, so that I could perform join and it is shown down below:
public class ViewModel
{
public Customer customer { get; set; }
public Purchase purchase { get; set; }
public List<Customer> customers;
public List<Purchase> purchases;
}
And I have created a Controller for ViewModel to try to
public ActionResult Index()
{
using (db)
{
var cust = (from p in db.PurchaseList
join c in db.Customers on p.custId equals c.custId
select new
{
id = p.custId,
firstName = c.firstName,
surname = c.surname,
total = p.finalAmount.ToString()
}
).ToList();
return View(cust);
}
}
The db variable is the context of the database that I am using.
public Context() : base("name=Context")
{
}
public void CreateDatabase()
{
}
public System.Data.Entity.DbSet<Models.Offer> Offers { get; set; }
public System.Data.Entity.DbSet<Models.Employee> Employees { get; set; }
public System.Data.Entity.DbSet<Models.Customer> Customers { get; set; }
public System.Data.Entity.DbSet<Models.User> Users { get; set; }
public System.Data.Entity.DbSet<Models.Purchase> PurchaseList { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
In my View page I am only doing a foreach to retrieve the data but it is always empty. Do you have any suggestion? Should I use a different approach to the above?
Have you given correct relationships in your dbContext class?
Refer my sample application
(https://github.com/AvanthaSiriwardana/TheProjector-V1.0/blob/master/TheProjector.Domain/Entities/TheProjectorContext.cs)

Code First : Unable to create 1 to many relationship. Creates 0...1 to many instead

Customer
public class Customer
{
public Customer()
{
Addresses = new List<Address>();
Reviews = new List<Review>();
Products = new List<Product>();
}
[Key]
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public Address DefaultAddress { get; set; }
public int DefaultAddressId { get; set; }
public List<Address> Addresses { get; set; }
public List<Review> Reviews { get; set; }
public List<Product> Products { get; set; }
}
Product
public class Product
{
public Product()
{
Reviews = new List < Review >();
}
public int Id { get; set; }
public Category Category { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Specification { get; set; }
public List<Review> Reviews { get; set; }
public List<Customer> Customers { get; set; }
}
Review
public class Review
{
public int Id { get; set; }
public string Text { get; set; }
public int Stars { get; set; }
[Required]
public int ProductId { get; set; }
[Required]
public string CustomerId { get; set; }
}
}
Generated model
I want the relationship between Review and Customer to 1 to many not 0..1 to many. Each review must belong to one customer. I don't understand how the relationship is mapped properly for Review - Product but not for the customer.
i only used Customer and Review models since those are the once you wanted an answer.
Following is the your model classes should be.
public class Customer
{
public Customer()
{
Reviews = new HashSet<Review>();
}
[Key]
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public ICollection<Review> Reviews { get; set; }
}
public class Review
{
public int Id { get; set; }
public string Text { get; set; }
public int Stars { get; set; }
[Required]
public int ProductId { get; set; }
[Required]
public string CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
this way you can have customers Review collection and cast it in to a list if you want.use Include() method in your linq query to lazy load the customers review collection.
for an example:
var customer = dbContext.Customer.Include("Reviews").where(x => x.Email == "john#gmail.com").FirstOrDefault();

Querying object with list of lists of lists

I want to query the database and get a sport object containing all inner lists, but for some reason I'm missing, the select only goes 2 levels deep in lists, any deepers and the lists property have a value of null,
example of the structure
public class Sports
{
public int id { get; set; }
public string name { get; set; }
public List<League> leagues { get; set; }
}
public class League
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public List<Team> teams { get; set; }
}
public class Team
{
public int id { get; set; }
public string name { get; set; }
public string successrate { get; set; }
public List<Player> players { get; set; }
}
public class Player
{
public int id { get; set; }
public string name { get; set; }
public int age { get; set; }
}
I created property in MyAppContext file as this
public DbSet<Sports> sports { get; set; }
now when I call an item using select or linq or any other way I tried, the sport object is always 2 Dimensional, meaning it doesn't go deeper than two levels in nested lists! Example of result using var sport=db.Sports.First() the result is {"id":1,"name":"Football","leagues":null} or if I used select()
var sportQuery = db.sports.Select(
s=>new Sports(){
id=s.id,
leagues=s.leagues,
name=s.name
}).First();
I still don't get full information {"id":1,"name":"Football","leagues":[{"id":1,"name":"fc","description":"Some Leauge","teams":null},{"id":2,"name":"al","description":"League","teams":null}]}
why is that! and how to get full object like this
{"id":1,"name":"Football","leagues":[{"id":1,"name":"fc","description":"Some Leauge","teams":[{"id":1,"name":"real madrid","successrate":null,"players":[{"id":1,"name":"Cristiano Ronaldo","age":21},{"id":2,"name":"Iniesta","age":38}]},{"id":2,"name":"Barcelona","successrate":null,"players":[{"id":1,"name":"Cristiano Ronaldo","age":21},{"id":2,"name":"Iniesta","age":38}]}]},{"id":2,"name":"al","description":"League","teams":[{"id":1,"name":"real madrid","successrate":null,"players":[{"id":1,"name":"Cristiano Ronaldo","age":21},{"id":2,"name":"Iniesta","age":38}]},{"id":2,"name":"Barcelona","successrate":null,"players":[{"id":1,"name":"Cristiano Ronaldo","age":21},{"id":2,"name":"Iniesta","age":38}]}]}]}
I've been stuck for days, Please any help would be much appreciated
Try following :
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataBase db = new DataBase();
var sportQuery = db.sports.Select(s=> s.leagues.Select(l => l.teams.Select(t => t.players.Select(p => new {
playerid = p.id,
playerName = p.name,
playerAge = p.age,
teamId = t.id,
teamName = t.name,
teamSucessrate = t.successrate,
leagueId= l.id,
leagueName= l.name,
leagueDescription = l.description,
sportId = s.id,
sportName = s.name
}))
.SelectMany(p => p))
.SelectMany(t => t))
.SelectMany(l => l)
.ToList();
}
}
public class DataBase
{
public List<Sports> sports { get; set;}
}
public class Sports
{
public int id { get; set; }
public string name { get; set; }
public List<League> leagues { get; set; }
}
public class League
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public List<Team> teams { get; set; }
}
public class Team
{
public int id { get; set; }
public string name { get; set; }
public string successrate { get; set; }
public List<Player> players { get; set; }
}
public class Player
{
public int id { get; set; }
public string name { get; set; }
public int age { get; set; }
}
}
problem solved using Include() and thenInclude()
https://learn.microsoft.com/en-us/ef/core/querying/related-data
however, that doesn't explain why Select() only loads one list property deep, also it seems like i should be able to load object with select only or linq

Getting related data through navigation in linq

I want to display a list of category names associated with a product. In my system, a product can be linked to any number of categories. These are my entity models:
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public List<ProductInCategory> InCategories { get; set; }
}
public class ProductInCategory
{
public int Id { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
public int ProductCategoryId { get; set; }
public Product Product { get; set; } // Nav.prop. to product
public ProductCategory ProductCategory { get; set; } // Nav.prop. to category
}
public class ProductCategory
{
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } //nav. prop to children
public List<ProductInCategory> ProductInCategory { get; set; }
}
EDIT
This is my UPDATED viewmodel:
public class ViewModelProduct
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public int SortOrder { get; set; }
public List<ProductInCategory> InCategories { get; set; }
public IEnumerable<ProductCategory> Categories { get; set; }
}
At present, I have this query, which retrieves the product with a list of category Ids in InCategories:
var product = _context.Products
.Select(p => new ViewModelProduct
{
Id = p.Id,
Title = p.Title,
Info = p.Info,
Price = p.Price,
InCategories = p.InCategories
})
.SingleOrDefault(m => m.Id == id);
What needs to be added in order to also get Title from ProductCategory for each ProductCategoryId in ProductInCategory?
Also, I'm not sure if I like the method-syntax. In order to do joins, I think query syntax is better, because of readability. I have tried to make a query syntax version of my query, but I don't know how to put it into ViewModelProduct, so it just fails when sent to the view:
var product = from p in _context.Products
where p.Id == id
select new
{
p.Id,
p.Info,
p.Title,
p.Price,
p.InCategories
};
I think this is solution for you:
EDIT:
public class ViewModelCategoryWithTitle
{
public int Id { get; set; }
public string Title { get; set; }
}
public class ViewModelProduct
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public int SortOrder { get; set; }
public IEnumerable<ViewModelCategoryWithTitle> Categories { get; set; }
}
EDIT This is for IEnumerable ViewModelCategoryWithTitle Categories (my ViewModel):
var product = (from p in _context.Products
where p.Id == id
select new ViewModelProduct
{
Id = p.Id,
Title = p.Title,
Info = p.Info,
Price = p.Price,
Categories = p.InCategories.Select(
inCategory=> new ViewModelCategoryWithTitle
{
CategoryId = inCategory.ProductCategory.Id,
Title = inCategory.ProductCategory.Title
})
}).SingleOrDefaultAsync();
EDIT This is for your updated ViewModel:
var product = (from p in _context.Products
where p.Id == id
select new ViewModelProduct
{
Id = p.Id,
Title = p.Title,
Info = p.Info,
Price = p.Price,
Categories = p.InCategories.Select(
inCategory=> inCategory.ProductCategory)
}).SingleOrDefaultAsync();

The specified type member 'Product' is not supported in LINQ to Entities

I'm getting the error 'Product' is not supported in LINQ to Entities whilst using cartItems.Product.UnitCost when trying to get the total of the basket.
public decimal GetTotal()
{
//Multiply price by count of item to get price for each item in cart and them sum all prices up
decimal? total = (from cartItems in db.ShoppingBaskets where cartItems.BasketID == ShoppingCartID select (int?)cartItems.BasketQuantity * cartItems.Product.UnitCost).Sum();
return total ?? decimal.Zero;
}
I then tried splitting the query up to see if this would fix the problem
int? quantity = (from cartItems in db.ShoppingBaskets where cartItems.BasketID == ShoppingCartID select (int?)cartItems.BasketQuantity).Sum();
decimal? price = (from cartItems in db.ShoppingBaskets where cartItems.BasketID == ShoppingCartID select cartItems.Product.UnitCost).Sum();
I get the same problem with the second separate query with 'Product'.
Product Class
public partial class Product
{
public Product()
{
this.OrderCheckouts = new HashSet<OrderCheckout>();
this.ProductReviews = new HashSet<ProductReview>();
}
public int ProductID { get; set; }
public int CategoryID { get; set; }
public int ManufacturerID { get; set; }
public string ProductName { get; set; }
public string ProductCode { get; set; }
public decimal UnitCost { get; set; }
public int UnitQuantity { get; set; }
public string ProductDescription { get; set; }
public string ProductImage { get; set; }
public virtual Category Category { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public virtual ICollection<OrderCheckout> OrderCheckouts { get; set; }
public virtual ICollection<ProductReview> ProductReviews { get; set; }
}
ShoppingBasket Class
public partial class ShoppingBasket
{
public int CartID { get; set; }
public string BasketID { get; set; }
public int BasketQuantity { get; set; }
public int ProductID { get; set; }
public virtual Product Product { get; set; }
}
Could someone explain the problem more clearly and how I can solve this? Thanks!
I think it's because Entity Framework hasn't fully created the relationship between ShoppingBasket and Product. Try adding:
public virtual ICollection<ShoppingBasket> ShoppingBaskets { get; set; }
to the Product class.

Categories

Resources