I have been trying to work this out for a while now and can't find an answer that makes sense to me. The concept is very common, so I must be totally misunderstanding a basic concept.
If I have a recipe class that can be found in many recipe categories then I have;
public class Recipe
{
public int ID { get; set; }
public string Title { get; set; }
public int CategoryID { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Description { get; set; }
public int RecipeID {get; set;
public virtual void Recipe Recipe {get; set;}
But I also need a join table that relates a recipe to a Category. I want to display this;
Recipe Title | Category
Mac-N-Cheese | Pasta
| Easy
Pot Roast | Beef
| Slow cooker
The Category is a table of available categories. So the join table has
RecipeID | CategoryID
I tried setting up the models using the Entity Framework format of foreign keys and navigation properties.
So I set up the join table like this;
public class RecipeCategories
{
public int ID { get; set; }
public int RecipeID { get; set; }
public int CategoryID { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
So a recipe can have many categories. The join table can have many recipes and many categories. The category table is just a simple list of categories. What am I missing? When I try to run the view there is no list of categories for the given recipe. The best I have achieved is the CategoryID.
Sorry for the long post, but you need all the details.
The problem is your Category model. The way you've currently defined it you have a one-to-many relationship between Category and Recipe (a Recipe can have many Categories but a Category one has a single Recipe). What you want is a many-to-many relationship so put a collection of Recipes on the Category and EF should automatically generate the join table.
public class Recipe
{
public int ID { get; set; }
public string Title { get; set; }
public int CategoryID { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Description { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
I am no getting actual scenario as you trying to achieve.
as you mention your model and then what you expected both contradict. as per you mention you have recipe and category (one to many) but later you change your model and mention you want (many to many) so you need join table. as a relation model you can handle 2 ways in EF. 1st without creating separate table and keep collection map to each model and 2nd way creating separate table and explicitly map that in your model. you need to explicitly specify while model building.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Recipe>()
.HasMany(c => c.Categories).WithMany(i => i.Recipes)
.Map(t => t.MapLeftKey("RecipeID")
.MapRightKey("CategoryID")
.ToTable("ReceipeCategory")
);
you can define your class model (and third table will generate but don't required specific model )
class Recipe {.... public virtual ICollection<Category> Categories { get; set; }
class Category {... public virtual ICollection<Recipe> Recipes { get; set; } }
Related
I have two tables with one-to-one relationship.
public class Name
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Category { get; set; }
public int? NameId { get; set; }
[ForeignKey("NameId ")]
public virtual Name Name { get; set; }
}
I already have data in those tables.
I know the database relations are not supported to be changed.
Is it possible to change one-to-one relationships to many-to-many relationships?
What is the most suitable approach to overcome this requirement?
Yes, you can still change that, using migrations.
Step 1 is to create a linking table, like NameCategories, which looks something like this:
public class NameCategories
{
public int Id { get; set; }
public int NameId { get; set; }
public Name Name { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
Step 2 is to reference this table in the tables you already have. In Name it would look like this
public class Name
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<NameCategory> Categories { get; set; }
}
Step 3 is to add a migration. You'll have some AddColumn() and some DropColumn() statements. In between them, when all the add stuff was executed but the drops not yet, you can add SQL code to carry over all the existing relations into the newly created table. After that, the old data will be deleted by the DropColumn() code. In your example, this would look something like this
INSERT INTO NameCategories (NameId, CategoryId)
SELECT (n.Id, c.Id) FROM Names n
JOIN Categories c on c.NameId = n.Id
WHERE ..
You can execute the SQL in the migration like this:
var sql = #"...";
Sql(sql);
I hope this helps you out!
I have a DbContext which I via the developer command prompt and creating a migrations schema turn in to my database. But if you look at the product object I have a dictionary object named Parts. That property does not get added to the Product table when the database is updated in the command prompt. I don't even know if it is possible what I am trying to do.
I want to add a table in the database named Parts and then add a foreign key to the Product table which connects the Parts dictionary object in the Product table, and the the new Parts table. Is this possible with Entity Framework Core?
public class ShoppingDbContext : IdentityDbContext<User>
{
public ShoppingDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
public int CategoryId { get; set; }
Dictionary<string, Part> Parts { get; set; }
}
EF Core can't currently map a dictionary property directly. If you want to create an association between Products and Parts, then define each of them as an entity. You can then create navigation properties between them--a reference from Part to the Product which it belongs, and a collection of Parts on Product. For example:
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
public int CategoryId { get; set; }
public ICollection<Part> Parts { get; set; }
}
public class Part
{
public int PartId { get; set; }
public int ProductId { get; set; }
public Product Product { get; set;}
}
Part also defines a property ProductId that acts as the FK to the Product entity. You don't need to add that property--EF will simulate it for you if you don't want it, but usually it is easier to deal with entities if the FK is mapped to a property.
Relationships are tracked through object references instead of foreign key properties. This type of association is called an independent association.
More Details Here:
https://msdn.microsoft.com/en-us/data/jj713564.aspx
Sample code:
public partial class Product
{
public Product()
{
this.Parts = new HashSet<Part>();
}
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
public int CategoryId { get; set; }
public virtual ICollection<Part> Parts { get; set; }
}
Basically like what Arthur said, EF Core does not support it yet.
However, another way is to create a composite table should you want to or if it's viable for your use.
Here's a simple example:
// -------------- Defining BrandsOfCategories Entity --------------- //
modelBuilder.Entity<BrandCategory>()
.HasKey(input => new { input.BrandId, input.CatId })
.HasName("BrandsOfCategories_CompositeKey");
modelBuilder.Entity<BrandCategory>()
.Property(input => input.DeletedAt)
.IsRequired(false);
// -------------- Defining BrandsOfCategories Entity --------------- //
public class BrandCategory
{
public int CatId { get; set; }
public int BrandId { get; set; }
public DateTime? DeletedAt { get; set; }
public Category Category { get; set; }
public Brands Brand { get; set; }
}
The DeletedAt is optional of course. This handles M-M Relationships.
I had the same issue, I resolved it by removing the keyword virtual on the navigation properties and with in the ApplicatinDbContext
I have two simple classes:
class Student
{
public int StudentId { get; set; }
public int IndeksNo { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public virtual Group Group { get; set; }
}
And
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual List <Student > Students { get; set; }
}
Database is created correctly (using Code First). I have added couple items to both tables, but all the time Students list and Group property in Student are null. I have no idea why. I have searched solution for about an hour and i came up with something like this:
modelBuilder.Entity<Student>()
.HasRequired(st => st.Group)
.WithMany(gr => gr.Students)
.WillCascadeOnDelete(false);
But it doesn't help. I have no idea what may went wrong or why Group.Students and Student.Group are always null. List of groups and list of students are selected from db successfully - i mean all params except those connections.
In order to use the Lazy Loading feature of EntityFramework. Your navigation property must be virtual. In your Student class in the the case. The problem is with your Group class. The navigational property is a virtual List<Student> it must be a virtual ICollection<Student>
You can simply change your Group to
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual ICollection<Student > Students { get; set; }
}
Here's my problem:
I have 3 tables in my database
Movies (list of movies)
ID
OriginalTitle
...
Genres (table with all possible genres)
ID
Name
RelatedGenres (those genres that belog to a specific movie and point a specific genre, since movie can have more than 1 genre)
ID
MovieID
GenreID
The relationships are as folows:
Movies.ID -> RelatedGenres.MovieID -> Genres.ID
I have a model with assosiations (navigation properties).
What I get:
class Movie
{
public int ID { get; set; }
public string OriginalTitle { get; set; }
public ObjectCollection<RelatedGenre> RelatedGenres { get; set; }
}
where
class RelatedGenre
{
public int ID { get; set; }
public int MovieID { get; set; }
public ObjectCollection<Genre> Genres { get; set; }
}
What I want:
class Movie
{
public int ID { get; set; }
public string OriginalTitle { get; set; }
public ObjectCollection<Genre> Genres { get; set; }
}
As you can see, i want to skip data from this array of RelatedGenres & just get array of concrete Genres...
How can I achive this?
Thanks in advance =)
You need to remove th ID column of the RelatedGenres table. The join table of many-to-many relationship should only contain the keys of the participating entities.
EF will automatically model the relationship as you have shown in the final code sample.
I'm trying to set up a many to many relationship in EF code first but the default conventions is getting it wrong. The following classes describes the relationship:
class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
class Account
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
One Account can have many Products.
However the EF conventions will create the DB tables as:
Products Table
--------------
Id
Name
Account_Id <- What is this?
Accounts Table
--------------
Id
Name
This doesn't look like a many-to-many table structure? How can i get configure the fluent API to reflect the relationship and create an intermediary table:
AccountProducts Table
---------------------
Account_Id
Product_Id
modelBuilder.Entity<Account>()
.HasMany(a => a.Products)
.WithMany()
.Map(x =>
{
x.MapLeftKey("Account_Id");
x.MapRightKey("Product_Id");
x.ToTable("AccountProducts");
});
What EF has suggested is a one-to-many relationship.
One Account can have many Products, i.e. each Product has an Account_Id
If you want a many to many relationship (and create an intermediary table), the following should work
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
class Account
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Code first is creating tables in right relational way. When
One Account can have many Products.
Products table should store key for its Account, as it actually does now.
What you are trying to see in db, is many-to-many relationship, not one to many. If you want to achieve that with code first, you should redesign your entities
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
class Account
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
In this case, one product can have many accounts, and one account can have many products.
public AccountProductsMap()
{
this.ToTable("AccountProducts");
this.HasKey(cr => cr.Id);
this.HasMany(cr => cr.Account)
.WithMany(c => c.Products)
.Map(m => m.ToTable("AccountProducts_Mapping"));
}