Getting related data through navigation in linq - c#

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

Related

Selection for Linq Query

How can I select the List of ExtrasName and ExtrasId in the following query.
The query contains some mathematical operations aswell.
var query =
from a in _context.Cities
from b in a.CityExtras
where a.CityId == CityId && extraIds.Contains(b.ExtrasId)
group new { a, b } by new { a.PricePerSqM , a.Name, a.CityId , } into g
select new
{
City = g.Key.Name,
PricePerSqM = g.Key.PricePerSqM,
TotalPrice = g.Sum(x => x.b.Price) + g.Key.PricePerSqM * squareMeter
};
My Models are:
public class Extras
{
public int ExtrasId { get; set; }
[Required]
public string ExtrasName { get; set; }
public ICollection<CityExtras> CityExtras { get; set; }
}
public class City
{
public int CityId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int PricePerSqM { get; set; }
public ICollection<CityExtras> CityExtras { get; set; }
}
public class CityExtras
{
public int CityId { get; set; }
public City City { get; set; }
public Extras Extras { get; set; }
public int ExtrasId { get; set; }
public int Price { get; set; }
}
I need ExtrasNames and ExtrasId in the query
As they stand, your models are not going to allow you to do this easily. You should add navigation properties to your models, then your Linq will be much cleaner (no need for the double select), and you will be able to navigate upwards to the Extra object and get the data you want.

How to join list of ids with another table many to many in EFcore3.1?

Hi everyone i have many to many relation between invoice and product and the join class is customer invoice i want to insert invoice and add many product to it.
one of the answer when i ask in other place told me you make list of product while your join class take only one product but if you make it list of product inside the customer invoice it will work and i make it still not work anyone can explain and how the code should be
this is the function.
try
{
Invoice invoice = new Invoice
{
InvoiceNote = addCustomerInvoice.InvoiceNote,
AdminId = addCustomerInvoice.AdminId,
CustomerId = addCustomerInvoice.CustomerId,
CompanyStoresId = addCustomerInvoice.CompanyStoresId,
InvoiceDate = addCustomerInvoice.InvoiceDate.Date,
};
List<int>result= await _drugDbContext.Products.Select(p => p.ProductId).ToListAsync();
CustomerInvoice customerInvoice = new CustomerInvoice
{
Products =result,
Invoice = invoice
};
await _drugDbContext.CustomerInvoices.AddAsync(customerInvoice);
await _drugDbContext.SaveChangesAsync();
servicesResponse.Data = _mapper.Map<InvoiceForGet>(invoice);
}
public class Invoice
{
public int InvoiceId { get; set; }
public DateTimeOffset InvoiceDate { get; set; } = DateTimeOffset.UtcNow;
public string InvoiceNote { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public int AdminId { get; set; }
public Admin Admin { get; set; }
public int CompanyStoresId { get; set; }
public CompanyStore CompanyStores { get; set; }
public ICollection<CustomerInvoice> CustomerInvoices { get; set; }
= new List<CustomerInvoice>();
public ICollection<ProductsReturn> ProductsReturn { get; set; }
= new List<ProductsReturn>();
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public string Company { get; set; }
public float Price { get; set; }
public int Quantity { get; set; }
public string Description { get; set; }
public string ProductImage { get; set; }
public string BarCode { get; set; }
public float BuyPrice { get; set; }
public ICollection<ProductAndCategory> ProductCategory { get; set; }
= new List<ProductAndCategory>();
public ICollection<TransportInvoice> transportInvoices { get; set; }
= new List<TransportInvoice>();
public ICollection<CustomerInvoice> CustomerInvoices { get; set; }
= new List<CustomerInvoice>();
public ICollection<ProductsReturn> ProductsReturn { get; set; }
= new List<ProductsReturn>();
public int CompanyStoresId { get; set; }
public CompanyStore CompanyStores { get; set; }
public ICollection<OfficeInvoice> OfficeInvoices { get; set; }
= new List<OfficeInvoice>();
public ICollection<OfficeReturn> OfficeReturns { get; set; }
= new List<OfficeReturn>();
}
modelBuilder.Entity<CustomerInvoice>(entity =>
{
modelBuilder.Entity<CustomerInvoice>().HasKey(ci => new { ci.ProductId, ci.InvoiceId });
entity.HasKey(c => c.CustomerInvoiceId);
entity.Property(c => c.Quantity).IsRequired();
entity.Property(c => c.TotalPrice).IsRequired();
});
modelBuilder.Entity<CustomerInvoice>(entity =>
{
entity.HasOne(c => c.Products)
.WithMany(p => p.CustomerInvoices)
.HasForeignKey(c => c.ProductId)
.IsRequired(false)
.OnDelete(DeleteBehavior.NoAction);
});
modelBuilder.Entity<CustomerInvoice>(entity =>
{
entity.HasOne(c => c.Invoice)
.WithMany(i => i.CustomerInvoices)
.HasForeignKey(c => c.InvoiceId)
.IsRequired(false)
.OnDelete(DeleteBehavior.NoAction);
});
public class CustomerInvoice
{
public int CustomerInvoiceId { get; set; }
public int Quantity { get; set; }
public float Discount { get; set; }
public float TotalPrice { get; set; }
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
public int ProductId { get; set; }
public Product Products { get; set; }
public ICollection<ProductsReturn> ProductsReturn { get; set; }
= new List<ProductsReturn>();
}
this is the dto class
public class AddCustomerInvoice
{
public ICollection<Product> products { get; set; }
= new List<Product>();
public DateTime InvoiceDate { get; set; }
public string InvoiceNote { get; set; }
public int CustomerId { get; set; }
public int AdminId { get; set; }
public int CompanyStoresId { get; set; }
}
I made a simple demo based on your codes and requirement for how to insert a record with many-to-many relationship.
Below is the code:
Model:
public class Invoice
{
public int InvoiceId { get; set; }
public string InvoiceNote { get; set; }
public ICollection<CustomerInvoice> CustomerInvoices { get; set; }
= new List<CustomerInvoice>();
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public float Price { get; set; }
public ICollection<CustomerInvoice> CustomerInvoices { get; set; }
= new List<CustomerInvoice>();
}
public class CustomerInvoice
{
public int CustomerInvoiceId { get; set; }
public int Quantity { get; set; }
public float TotalPrice { get; set; }
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
public int ProductId { get; set; }
public Product Products { get; set; }
}
public class AddCustomerInvoice
{
public ICollection<Product> products { get; set; }
= new List<Product>();
public string InvoiceNote { get; set; }
}
Controller:
public IActionResult Index()
{
AddCustomerInvoice addCustomerInvoice = new AddCustomerInvoice
{
products = new List<Product>
{
new Product { ProductId = 1, Price = 3, Quantity = 10},
new Product { ProductId = 2, Price = 5, Quantity = 10},
new Product { ProductId = 3, Price = 6, Quantity = 10},
new Product { ProductId = 4, Price = 2, Quantity = 10},
},
InvoiceNote = "ddddd"
};
Invoice invoice = new Invoice
{
InvoiceNote = addCustomerInvoice.InvoiceNote,
};
_drugDbContext.Invoices.Add(invoice);
_drugDbContext.SaveChanges();
int invoiceId = invoice.InvoiceId;
foreach (var item in addCustomerInvoice.products)
{
_drugDbContext.CustomerInvoices.Add(
new CustomerInvoice
{
InvoiceId = invoiceId,
ProductId = item.ProductId,
Quantity = item.Quantity,
TotalPrice = item.Quantity * item.Price
});
_drugDbContext.SaveChanges();
}
return View();
}
Result:

why does not work OrderBy method in EF core?

I want to get sorted products(table products) by 'value'(table Product Parameters) according to "ParamertomId"(table Parameters) from my model.
How to sort related data correctly?
These are my model classes:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Number { get; set; }
public double Amount { get; set; }
public double PrimeCostEUR { get; set; }
[ForeignKey("ProductTypeId")]
public int ProductTypeId { get; set; }
public virtual ProductType ProductType { get; set; }
public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}
public class ProductType //: BaseObject
{
public int Id { get; set; }
public string NameType { get; set; }
public ICollection<Parameter> Parameters { get; set; } = new List<Parameter>();
public ICollection<Product> Products { get; set; } = new List<Product>();
}
public class Parameter
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("ProductType")]
public int ProductTypeId { get; set; }
public virtual ProductType ProductType { get; set; }
public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}
public class ProductParameter
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int ParameterId { get; set; }
public virtual Parameter Parameter { get; set; }
public string Value { get; set; }
}
These are my DTO classes needed to display the data I need.:
public class ProductDTO{
public int ProductId { get; set; }
public string Number { get; set; }
public double Amount { get; set; }
public double PrimeCostEUR { get; set; }
public int ProductTypeId { get; set; }
public string NameType { get; set; }
public ICollection<ParameterDTO> Parameters { get; set; } = new
List<ParameterDTO>();}
public class ParameterDTO {
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
I receive related data from three table. This my method GetSortedProducts:
public async Task<IEnumerable<ProductDTO>> GetProducts(int id)
{
var typeParams = _context.Parameters
.Where(t => t.ProductTypeId == id)
.ToList();
var products = _context.Products
.Include(t => t.ProductParameters)
.Where(t => t.ProductTypeId == id)
.ToList();
var items = new List<ProductDTO>();
foreach (var product in products)
{
var productDTO = new ProductDTO()
{
ProductId = product.Id,
Number = product.Number,
Amount = product.Amount,
PrimeCostEUR = product.PrimeCostEUR,
ProductTypeId = product.ProductTypeId
};
foreach (var typeParam in typeParams)
{
var paramDTO = new ParameterDTO();
var value = product.ProductParameters.FirstOrDefault(t => t.ParameterId == typeParam.Id);
if (value != null)
{
paramDTO.Id = value.Id;
paramDTO.Value = value.Value;
}
paramDTO.ParameterId = typeParam.Id;
paramDTO.Name = typeParam.Name;
productDTO.Parameters.Add(paramDTO);
}
// sort products by value
productDTO.Parameters.Where(y => y.ParameterId == 4)
.OrderBy(t => t.Value).ToList();
items.Add(productDTO);
}
return items;
}
It doesn't work because LINQ never affects the original collection, so this doesn't do anything at all:
productDTO.Parameters
.Where(y => y.ParameterId == 4)
.OrderBy(t => t.Value)
.ToList();
Overall, anyway, that code is not even close to good code. Don't create Product (and others) objects just to convert them to their DTO version. Use Entity Framework properly:
var products = await _context.Products
.Where(prod => prod.ProductTypeId == id)
.Select(prod => new ProductDTO
{
ProductId = prod.ProductId,
// rest of properties...
// now get the parameters
Parameters = prod.Parameters
// assuming you want to order by value, the question isn't very clear
.OrderBy(par => par.Value)
.Select(par => new ParameterDTO
{
Id = par.Id,
// etc
})
.ToList()
})
.ToListAsync();
Compare that single database query to the multiple calls that the question's code is doing when the data is already in memory.

Get related entites of child items using Linq?

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?

Linq query to get filtred main collection and each list in result in that collection

These are my classes:
public class Restaurant
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public City City { get; set; }
public List<Meal> Meals { get; set; }
}
public class Meal
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
public int Price { get; set; }
public int Number { get; set; }
public int Kind { get; set; }
}
and now I want to create a query which give me list of restaurants in city with list of meals which has today date so I started like this:
return db.Restaurants.Where(rest => rest.City.Name == city).Include(rest => rest.Meals);
But I am not sure how to connected with this part:
.Where(meal => meal.Date == DateTime.Today)
so I would get results which I want. So how is possible to do this? Thanks
Edit:
My result:
return db.Restaurants
.Include(rest => rest.Meals)
.Where(rest => rest.City.Name == city)
.AsEnumerable()
.Select(r => new Restaurant()
{
City = r.City,
Id = r.Id,
Name = r.Name,
Meals = r.Meals.Where(meal => meal.Date == DateTime.Today).ToList()
});
This should be done in View Model (or Dto) layer, first, define your view models:
public class RestaurantVM
{
public int Id { get; set; }
public string Name { get; set; }
public City City { get; set; }
public List<MealVM> Meals { get; set; }
}
public class MealVM
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
public int Price { get; set; }
public int Number { get; set; }
public int Kind { get; set; }
}
Then you can write LINQ like below:
var restaurantVMs = db.Restaurants
.Include(rest => rest.Meals)
.Where(rest => rest.City.Name == city)
.AsEnumerable()
.Select(r => new RestaurantVM(){
Id = r.Id,
Name = r.Name,
City = r.City,
Meals = r.Meals.Where(meal => meal.Date == DateTime.Today)
.Select(m => new MealVM(){
...
}).ToList()
}).Where(r => r.Meals.Count > 0);

Categories

Resources