Returning Related Entites in EF6 with Linq - c#

I am having a problem understanding how Linq and EF are working to return data. I have three simple classes
Products,
Materials,
Documents
Products are made up of materials and materials have documents. When I load a product, I want to return all the documents for the materials that product is made up from.
Here are my classes:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
...
public ICollection<ProductMaterials> ProductMaterial { get; set; }
}
public class ProductMaterials
{
public int Id { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
public int MaterialId { get; set; }
public Materials Material { get; set; }
...
}
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
...
public ICollection<MaterialDocument> MaterialDocument { get; set; }
}
public class MaterialDocument
{
public int Id { get; set; }
public int MaterialId { get; set; }
public Materials Material { get; set; }
public int DocumentId { get; set; }
public Document Document { get; set; }
}
I am not having any issues when loading a material and its related documents. I use this query:
var materialDocuments = db.MaterialDocuments
.Include("Document")
.Where(i => i.MaterialId == id)
.ToList();
How can I load Product with related Materials and the Material's documents? Do I need additional Navigation properties on the MaterialDocument class pointing back to ProductMaterials?

To return all of the Document records for a specific Product that you're loading (given an ID we'll call myProductID), just do the following:
var product = db.Products.Find(myProductID); //The product that you're loading
var documents = product.ProductMaterials.SelectMany(pm =>
pm.Material.SelectMany(mat =>
mat.MaterialDocuments.Select(matdoc => matdoc.Document)));

Related

C# Entity Framework - Entity only relating to one entry at a time

So I am have difficulty understanding why CompaniesAccepted is only allowing one Company to be related at a time.
So for example if I have 2 entries of File in my Database. The company then accepts one of these files and gets added to CompaniesAccepted list. If I try to add this same company to the other File entry, the Company will be removed from the CompaniesAccepted list from the first file and added to the list of the new one which doesn't make sense to me as I wish to have the Company in both of those lists.
public class File
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual Company Company { get; set; }
public string DisplayName { get; set; }
public virtual List<Company> CompaniesAccepted { get; set; }
public bool isDeleted { get; set; }
}
Company Entity
public class Company
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public string ParentCompany { get; set; }
public string CompanyName { get; set; }
public string LegalName { get; set; }
public string DUNS { get; set; }
[JsonIgnore]
public virtual Company CustomerCompany { get; set; }
public bool isActivated { get; set; }
public bool isDeleted { get; set; }
}
Code used to add the relationship.
if(accepted)
{
var company = file.CompaniesAccepted.FirstOrDefault(x => x.Id == account.Company.Id);
if(company == null)
{
file.CompaniesAccepted.Add(account.Company);
}
} else
{
file.CompaniesAccepted.Remove(account.Company);
}
Db.SaveChanges();
Any suggestions? If I haven't provided enough code, please do ask.

Which one is the correct one-to-many relation in EF

i am designing a system and one of my entity has one to many relation as shown below.
public class Product
{
public int Id { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public Product Product { get; set; }
}
competitorProduct indicates that product has a equivalent which is sold by different store. should i define one-to-many relation as shown above or below? which one is correct?
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts{ get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
}
Assuming it is a one to many relationship (what would happen if a competitor product was competing with more than one of your products for example) you can do both and add in a foreign key as well.
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
You can then set up your relationship using fluent API as so:
modelBuilder.Entity<CompetitorProduct>(entity =>
{
entity.HasOne(e => e.Product)
.WithMany(e => e.CompetitorProducts)
.HasForeignKey(e => e.ProductId)
.HasConstraintName("FK_ComptetitorProduct_Product");
});
This way you can access the competitor products from the product and the product from the competitor products.
Here is a quick example of a ecommerce site I have worked on and how we did table relations.
I removed a bunch of the fields so you can see what you really need. Once to make relations and run Add-Migration EF will handle the FK constraints for you as long as you identified them in models like how I have below.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Active = true;
CreateDateTimeUtc = DateTime.UtcNow;
ModifiedDateTimeUtc = DateTime.UtcNow;
}
[StringLength(500)]
public string FirstName { get; set; }
[StringLength(500)]
public string LastName { get; set; }
[StringLength(1000)]
public string Address { get; set; }
[StringLength(100)]
public string Unit { get; set; }
[StringLength(250)]
public string City { get; set; }
[StringLength(25)]
public string State { get; set; }
[StringLength(20)]
public string ZipCode { get; set; }
//This will give access to a list of child carts a user could have
[Index]
public bool Active { get; set; }
public virtual ICollection<Cart> Carts { get; set; }
// Account Profile Image
public byte[] ProfileImage { get; set; }
[StringLength(500)]
public string ProfileFilename { get; set; }
[StringLength(100)]
public string ProfileMimeType { get; set; }
}
[Table("Cart", Schema = "dbo")]
public class Cart : AbstractTable
{
public Cart()
{
IsComplete = false;
}
//This create relation to user table where I can get one unique user.
[StringLength(128)]
[ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
//These link us to child tables of Cart where we can get a LIST of the items below
public virtual ICollection<CartCategory> CartCategories { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
// Marked when a payment/receipt is generated based off of this cart
public bool IsComplete { get; set; }
}
[Table("CartItem", Schema = "dbo")]
public class CartItem : AbstractTable
{
//This will return one unique cart id and let us access it as the parent record
[ForeignKey("Cart")]
public Guid CartId { get; set; }
public virtual Cart Cart { get; set; }
// Signifies if this was paid for in a receipt
public bool IsComplete { get; set; }
public virtual ICollection<CartItemCustomField> CustomFields { get; set; }
}

Call ICollection in ICollection with Lambda Expression

I want a lambda expression, that when i call a Company from database with id 1030 to bring me Companys info, a list with all the cars that company has and a list of all images that related to each car (each car has 4 images).
Structure of my classes:
public partial class Companies
{
public int CompanyId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Tel { get; set; }
public string Logo { get; set; }
public int? Owner { get; set; }
public int? Address { get; set; }
public int? Publish { get; set; }
public ICollection<Cars> Cars { get; set; }
}
public partial class Cars
{
public int CarId { get; set; }
public string Manufacturer { get; set; }
public string Model { get; set; }
public string Year { get; set; }
public string Description { get; set; }
public decimal? Price { get; set; }
public int? CarCatId { get; set; }
public int? CompanyId { get; set; }
public int? CharacteristicId { get; set; }
public ICollection<CarImages> CarImages { get; set; }
}
public partial class CarImages
{
public int CarImagesId { get; set; }
public string Image { get; set; }
public int? CarId { get; set; }
public Cars Car { get; set; }
}
As you can see in Company class, I have a reference to Cars class as ICollection, and the same thing in Cars class with CarImages.
I've try lot of things including those:
var compInfo = _context.Companies.SelectMany(cr => cr.Cars.SelectMany(i =>i.CarImages.Where(car => car.CarId == car.Car.CarId))).ToList();
var compInfo2 = _context.Companies.Include(a => a.AddressNavigation).Include(cr =>cr.Cars.).FirstOrDefault(c => c.CompanyId == id);
The 2nd brings me the info of company (and it's address) and the list of the cars (which is the half success) but not the images..
The 1st just brings me a list all the info of images table.
I found the solution.
//Get the Company with id == 1030.
var x = _context.Companies.Include(a => a.AddressNavigation).Include(c => c.Cars).Where(c => c.CompanyId == id).FirstOrDefault();
//Get the Car ICollection and include the CarImages ICollection depending on CompanyID.
var y = _context.Cars.Include(img => img.CarImages).Where(cr => cr.CompanyId == x.CompanyId).ToList();
//Now each Car in our Cars list has a list of CarImages related to each car. Finaly pass our data to our Company var(x) and send it to View.
x.Cars = y;
return View(x);

Entity Framework Code First creates classes from two tables and relationships one to many

I create an application and as an example for testing I take a table of orders. I have questions about class modeling.
I have 3 classes:
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
public class Part
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
class Order
{
public Order()
{
Cars = new List<Car>();
Parts = new List<Part>();
}
public int OrderId { get; set; }
public int CarId { get; set; }
public int PartId { get; set; }
public ICollection<Car> Cars { get; set; }
public ICollection<Part> Parts { get; set; }
}
I do not know if this model is ok. What do you think? Because something does not go here: / In the application:
I can not add cars or parts to the order that I do not have in the database.
In the table of orders I would like to see only the order Id, the value of the order, and the Id of the car and Id of the part that was bought.
 
I would like the Car and Part tables to have no data about orders. I would like to only add parts or cars in the application, later only be able to select from them in the order section.
Let's start with the physical tables you will need:
Part { Id, Name, Price }
Car { Id, Name, Price }
Order { Id }
OrderPart* { OrderId, PartId }
OrderCar* { OrderId, CarId }
The last two tables are called "join tables" because you need them to be able to store multiple parts and multiple cars on the same order, but are not really tables you think of as being part of your model.
Entity Framework will automatically make these join tables if you set up your classes as follows:
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Part
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
class Order
{
public Order()
{
Cars = new List<Car>();
Parts = new List<Part>();
}
public int OrderId { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public virtual ICollection<Part> Parts { get; set; }
}
Note that the ICollection<> properties on the Car and Part table will be the clue to EF that it needs to make the join table. Also, remember that you need "virtual" on your navigation properties.
It is good model ?
One Pizza may have a few idgredience
One Pizza may have one sauce under the cheese
One Order may have a few idgredience and a few sauces.
It is my classes :
public class Suace
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Pizza
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public ICollection<Idgredient> Idgredients { get; set; }
public Sauce Sauce {get;set;}
public virtual ICollection<Order> Orders { get; set; }
}
class Order
{
public Order()
{
Cars = new List<Car>();
Parts = new List<Part>();
}
public int OrderId { get; set; }
public virtual ICollection<Car> Suace { get; set; }
public virtual ICollection<Part> Pizza { get; set; }
}
public class Idgredient
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Pizza> Pizzas { get; set; }
}

Recipe - ingredients database in Entity Framework ASP.NET MVC

This will create two tables "Ingredient" and "Recipe" and an additional table for many-to-many mapping.
public class DC : DbContext {
public DbSet<Ingredient> Ingredients { get; set; }
public DbSet<Recipe> Recipes { get; set; }
}
public class Ingredient {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Ingredient> Ingredients { get; set; }
}
Question: I want to include additional column "quantity" in the third mapping table that will be created by Entity Framework. How to make that possible? Thanks in advance.
When you've got some extra information, I suspect it won't really count as a mapping table any more - it's not just a many-to-many mapping. I think you should just model it as another table:
public class Ingredient {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipePart> RecipeParts { get; set; }
}
public class RecipePart {
public int Id { get; set; }
public Ingredient { get; set; }
public Recipe { get; set; }
// You'll want to think what unit this is meant to be in... another field?
public decimal Quantity { get; set; }
}
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipePart> Parts { get; set; }
}
So now you don't really have a many-to-many mapping - you have two ordinary many-to-one mappings. Do you definitely need to "ingredient to recipes" mapping exposed in your model at all? If you want to find out all the recipes which use a particular ingredient, you could always do a query such as:
var recipies = DB.Recipies.Where(r => r.Parts
.Any(p => p.Ingredient == ingredient));

Categories