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