EF 4.1 Entity mappings for complex relations - join

i have the following tables which i want to map with EF 4.1 Code First:
Items - An item which can have a number of categories
ItemCategories - The relation table to map the many-to-many relationship of Items<->Categories
Category - An Category, categories are a tree of categories, ParentId is self referencing
CategoryLink - This table contains the edges of the tree
The question is how can I create the EF code first mapping for my entity classes so that I can use the Categories collection property on Item while being able to use all my tables inside a query (especially a join over the relation table) like this:
//searchIds contains a list of category guids to be searched for inside the category tree
var query = from item in db.Items
join itemCategory in db.ItemCategories on item.Id equals itemCategory.ItemId
join category in db.Categories on itemCategory.CategoryId equals category.Id
join categoryLink in db.CategoryLinks on category.Id equals categoryLink.ChildId
where searchIds.Contains(categoryLink.ParentId)
select item;
Here are the entity classes with DbContext(without the mappings)
class TestContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Item> Items { get; set; }
public DbSet<ItemCategory> ItemCategories { get; set; }
public DbSet<CategoryLink> CategoryLinks { get; set; }
}
public class Item
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class ItemCategory
{
public Guid ItemId { get; set; }
public Guid CategoryId { get; set; }
}
public class Category
{
public Guid Id { get; set; }
public Guid? ParentId { get; set; }
public virtual Category Parent { get; set; }
public string Name { get; set; }
}
public class CategoryLink
{
public Guid ParentId { get; set; }
public Guid ChildId { get; set; }
public virtual Category Parent { get; set; }
public virtual Category Child { get; set; }
}
Thanks

I've only played around a bit with code first not sure if this may help. but the below posts show how to create bridge tables and work around for many-to-many issue with EF4.1 code first.
How to perform CRUD with Entity Framework Code-First?
also
Forcing a bridge/join table to become a many to many relationship in EF4

Related

Entity Framework database mapping relationships (Duplicate creation using Seed() method)

I created a post with an issue and another issue.
These can be looked at for references but i consider them as handled.
My question arising from these issues and the action i (need or not need) to apply bothers me because i don't quite understand EF its behavior and expectations.
I have a Product, PurchasePrice and SalesPrice entity where my initial thought was that 1 Product can have multiple PurchasePrices but that 1 PurchasePrice only can exist in 1 Product (same for SalesPrice).
Therefore these relations:
// NOTE that BaseEntity just has a "int ID" prop and datetimes/stamps
public class Product : BaseEntity
{
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<PurchasePrice> SalesPrices { get; set; }
}
public class PurchasePrice:BaseEntity
{
public Product Product { get; set; }
}
public class SalesPrice:BaseEntity
{
public Product Product { get; set; }
}
Now, lets add a Supplier Entity to it because that is why i seperate Sales & Purchase apart and don't create an Enum out of it, because 1 Product (in database) can have multiple suppliers, each having their own Sales/Purchase prices AND another Productnumber value.
So above becomes:
public class Product : BaseEntity
{
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<PurchasePrice> SalesPrices { get; set; }
// added
public ICollection<Supplier> Suppliers { get; set; }
}
public class PurchasePrice:BaseEntity
{
public Product Product { get; set; }
// added
public Supplier Supplier { get; set; }
}
public class SalesPrice:BaseEntity
{
public Product Product { get; set; }
// added
public Supplier Supplier { get; set; }
}
// added Entity Supplier into the party
public class Supplier : BaseEntity
{
public ICollection<Product> Products { get; set; }
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<SalesPrice> SalesPrices { get; set; }
}
Lets continue a little furhter because it doesn't stop there, i want to keep track of these Product-Supplier-Prices relations so i created a Entity called 'ProductSupplierForContract' which would have the following structure:
public class ProductSupplierForContract:BaseEntity
{
public string ProductnumberValue { get; set; }
public int Product_Id { get; set; }
public int Supplier_Id { get; set; }
public int? Contract_Id { get; set; }
public virtual Product Product { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual Contract Contract { get; set; }
}
Finally i have a Contract Entity which has the following structure:
public class Contract:BaseEntity
{
[Required]
public ICollection<Product> Products { get; set; }
public ICollection<ProductSupplierForContract> ProductSupplierForContracts { get; set; }
}
So Product becomes:
public class Product : BaseEntity
{
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<PurchasePrice> SalesPrices { get; set; }
public ICollection<Supplier> Suppliers { get; set; }
// added
public ICollection<Contract> Contracts { get; set; }
}
Custom Seeding (inherits from DropCreateDatabaseAlways):
protected override void Seed(ApplicationDbContext context)
{
PurchasePrice purchaseprice = new PurchasePrice((decimal)17.70);
ctx.PurchasePrices.Add(purchaseprice);
Product product1 = new Product("test product 1",purchaseprice);
ctx.Products.Add(product1);
base.Seed(ctx);
}
I also have mappings defined in Fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// setting the Product FK relation required + related entity
modelBuilder.Entity<Entity.ProductSupplierForContract>().HasRequired(psfc => psfc.Product)
.WithMany(p => p.ProductSupplierForContracts)
.HasForeignKey(psfc => psfc.Product_Id);
// setting the Supplier FK relation required + related entity
modelBuilder.Entity<Entity.ProductSupplierForContract>().HasRequired(psfc => psfc.Supplier)
.WithMany(s => s.ProductSupplierForContracts)
.HasForeignKey(psfc => psfc.Supplier_Id);
// setting the Contract FK relation required + related entity
modelBuilder.Entity<Entity.ProductSupplierForContract>().HasOptional(psfc => psfc.Contract)
.WithMany(c => c.ProductSupplierForContracts)
.HasForeignKey(psfc => psfc.Contract_Id);
}
Now, initially i didn't had any issues and i really really don't understand what has brought up this sudden change that i now got duplicates Products when i seed my database. I can strip it down to just adding a simple PurchasePrice with a value and a Product having a reference to this PurchasePrice and there is my duplicate.
Changing the relation inside the PurchasePrice class of the Entity Product, to a ICollection doesn't create a duplicate but i don't want this collection because it is not a Many to Many relation ...
I have tried enormous amounts of things but nothing that "resolved" this (if this is a problem to start with, for me yes but maybe not for EF) like removing inhertance BaseEntity, changinge Mapping (Fluent AND annotations), changed the way i seeded and initialized everthing, defining ID's myself, you name it ...
Mind that the purpose is not to optimize the way i seed in anyway but to have a decent working Model AND to understand what EF does and what it wants.
My questions:
Why is this duplicate occuring/appearing ?
If i want to be able to have 1 instance holding the relation of
Price-Supplier-Product-Contract, how should i do this? Answer is here
I fixed my problem by redesigning the model. I have added a additional Entity ProductForSupplier which holds the relation of a Product & Supplier and a Productnumber.
public class ProductForSupplier:BaseEntity
{
public string ProductnumberValue { get; set; }
[Required]
public Product Product { get; set; }
[Required]
public Supplier Supplier { get; set; }
}
Added a Entity ProductsForContract which will hold the amount of a Product-Supplier relation for 1 contract:
public class ProductsForContract
{
public int ProductsForContractId { get; set; }
public int Amount { get; set; }
public ProductForSupplier ProductForSupplier { get; set; }
public Contract Contract { get; set; }
}
And the Existing Entity ProductSupplierForContract becomes:
public class ProductSupplierForContract:BaseEntity
{
public ICollection<ProductsForContract> ProductsForContract { get; set; }
[Required]
public Contract Contract { get; set; }
}
This gives me the flexibility to keep relations of any kind between the entities and also has taken care of the duplicate (which i still don't know the cause of).

Multiple tables update MVC .net

I am new to MVC and this is my function. There are three tables (Order, OrderNotes, Notes), ID is their primary key. One Order can have many Notes, the table OrderNotes has foreign key OrderID(from Booking table) and NotesID(from Notes table). I want to have a Order Edit page to display individual Order (FirstName, LastName), also display a list of its Notes. Here is my DB structure:
Booking table:
{ID,
FirstName,
LastName
}
BookingNotes table:
{ID,
BookingID,
NotesID
}
Notes table:
{ID,
NoteName,
StatusID
}
So how can I implement the list of Notes since it's from multiple tables? It will be able to Create New Note, Delete existing Note in the list row record, not Edit. Linq used in DB query. Thanks.
It would be a better idea to have only 2 tables:
public class Book
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Navigational properties
public virtual List<Note> Notes { get; set; }
}
public class Note
{
public int ID { get; set; }
public int BookID { get; set; }
public string NoteName { get; set; }
public int StatusID { get; set; }
// Navigational properties
public virtual Book Book { get; set; }
public virtual Status Status { get; set; }
}
A third table is useful when you want to reuse the same Note for a different booking. However i think this is not the case.
So to retrieve data for your context make sure you have the DbSet<Book>
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Book> Bookings { get; set; }
}
In your controller (or better in a repository class):
var BookingID = 10; // this is parameter passed to the function
var myBooking = this.dbContext.Bookings
.Include(p => p.Notes)
.ThenInclude(p => p.Status)
.FirstOrDefault(p => p.ID == BookingID);
Map the retrieved booking to a ViewModel, pass it to the View and you're good to go.

EF Code First - How do I code OnModelCreating with an entity that has property which is collection of itself?

I have an entity that has a property which is a collection of it's self. How do I program my classes and the OnModelCreating to correctly create the database tables?
public class Category{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Category> PriorCategories { get; set; }
}
So, for the end result, I want my database mapping table (PriorCategories) to look like this:
Id - PK
ParentCategoryId - FK to Category table
ChildCategoryId - FK to Category table
How do I set up my classes and/or program the overridden OnModelCreating function?
Thanks!
You don't need to do anything in OnModelCreating - the following model class will set up the relationship to itself perfectly:
public class Category
{
public int Id { get; set; }
public int? ParentCategoryId { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Category> Children { get; set; }
}

LINQ to entities against EF in many to many relationship

I'm using ASP.NET MVC4 EF CodeFirst.
Need help to write LINQ (to entities) code in Index action to get collection of Courses which are attended by selected student. The relationship is many to many with join table with payload.
//StudentController
//-----------------------
public ActionResult Index(int? id)
{
var viewModel = new StudentIndexViewModel();
viewModel.Students = db.Students;
if (id != null)
{
ViewBag.StudentId = id.Value;
// *************PROBLEM IN LINE DOWN. HOW TO MAKE COURSES COLLECTION?
viewModel.Courses = db.Courses
.Include(i => i.StudentsToCourses.Where(t => t.ObjStudent.FkStudentId == id.Value));
}
return View(viewModel);
}
The error I got is:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
I have modeles (the third one is for join table with payload):
//MODEL CLASSES
//-------------
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentToCourse> StudentsToCourses { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public virtual ICollection<StudentToCourse> StudentsToCourses { get; set; }
}
public class StudentToCourse
{
public int StudentToCourseId { get; set; }
public int FkStudentId { get; set; }
public int FkCourseId { get; set; }
public string Classroom { get; set; }
public virtual Student ObjStudent { get; set; }
public virtual Course ObjCourse { get; set; }
}
Then, here is modelview I need to pass to view
//VIEWMODEL CLASS
//---------------
public class StudentIndexViewModel
{
public IEnumerable<Student> Students { get; set; }
public IEnumerable<Course> Courses { get; set; }
public IEnumerable<StudentToCourse> StudentsToCourses { get; set; }
}
EF does not support conditional include's. You'll need to include all or nothing (ie no Whereinside the Include)
If you need to get the data for just certain relations, you can select it into an anonymous type, something like (the obviously untested);
var intermediary = (from course in db.Courses
from stc in course.StudentsToCourses
where stc.ObjStudent.FkStudentId == id.Value
select new {item, stc}).AsEnumerable();
Obviously, this will require some code changes, since it's no longer a straight forward Course with a StudentsToCourses collection.

How to model a pure join table with 2 primary keys to another pure join table with 3 primary keys in Entity Framework code first

How do I model a relationship where there is a pure join to another pure join table with three primary keys using code first?
Example code:
public class ItemA
{
public int ItemAId { get; set; }
public string Name { get; set; }
public ICollection<ItemB> ItemBs { get; set; }
}
public class ItemB
{
public int ItemBId { get; set; }
public string Name { get; set; }
public ICollection<ItemA> ItemAs { get; set; }
public ICollection<ItemC> ItemCs { get; set; }
}
public class ItemC
{
public int ItemCId { get; set; }
public String Name { get; set; }
public ICollection<ItemB> ItemBs { get; set; }
}
public class TestDbContext : DbContext
{
public DbSet<ItemA> ItemA { get; set; }
public DbSet<ItemB> ItemB { get; set; }
public DbSet<ItemC> ItemC { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
EF creates a join table for (ItemC and ItemB) and another join table for (ItemB and ItemA) and I want to join (create a relationship) to another join table which has something like (ItemA, ItemB, ItemC) together, i.e., link ItemA, ItemB, and ItemC together.
How do you create a relationship/mapping join to another table with three primary keys (ItemA, Itemb, ItemC)?
Also, is this table really an entity that needs a class of its own so that Entity Framework can create this third table? (I wanted to avoid creating classes for things that only have primary keys in them, e.g., creating a class called ItemAItemBItemC that only has primary keys.)

Resources