Add a column to a many to many relation table code first entity framework - ef-code-first

I have 2 classes which have a many to many relation.
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public bool AvailableOffline { get; set; }
public string URL { get; set; }
public virtual ICollection<Profile> Profiles { get; set; }
}
public class Profile
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Document> Documents { get; set; }
}
On each profile I wish to have a SortOrder field for each document. So I made the joined table explicit in another class
public class ProfileDocuments
{
[Key, Column(Order = 0)]
public int DocumentId { get; set; }
[Key, Column(Order = 1)]
public int ProfileId { get; set; }
public int SortOrder { get; set; }
[ForeignKey("DocumentId")]
public virtual Document Document { get; set; }
[ForeignKey("ProfileId")]
public virtual Profile Profile { get; set; }
}
But when I update the database the table for this last class will not have a column for SortOrder. It only holds the 2 foreign keys. How can I tell EF to generate this table with my column?

When a junction table in a many-to-many association should contain more information than just the two foreign keys, it's no longer possible to map the association as a 'pure' many-to-many (with hidden junction class).
You need an explicit class in the class model to address the extra information (as you already found out), but this also changes the association into 1-n-1:
class Document
{
...
public virtual ICollection<ProfileDocument> ProfileDocuments { get; set; }
}
class Profile
{
...
public virtual ICollection<ProfileDocument> ProfileDocuments { get; set; }
}

Related

How can I set up two navigation properties of the same type in Entity Framework without use Fluent API

i'm trying create DB using codefirst. i want to create two ForeingKey from same table. But when i set up two navigation properties of the same type, get error like :
The foreign key name 'FollowedUser' was not found on the dependent type Models.UserUserWatchListItem'. The Name value should be a comma separated list of foreign key property names.
public class UserUserWatchListItem
{
public int Id { get; set; }
[Key,ForeignKey("FollowedUser")]
public virtual User FollowedUser { get; set; }
public int FollowedUserId { get; set; }
[Key,ForeignKey("FolloweeUser")]
public int FolloweeUserId { get; set; }
public virtual User FolloweeUser { get; set; }
}
Use this :
public class UserUserWatchListItem
{
public int Id { get; set; }
public int FollowedUserId { get; set; }
public int FolloweeUserId { get; set; }
[ForeignKey("FollowedUser")]
[InverseProperty("FollowedUsers")]
public virtual User FollowedUser { get; set; }
[ForeignKey("FolloweeUser")]
[InverseProperty("FolloweeUsers")]
public virtual User FolloweeUser { get; set; }
}
public class User
{
...
[InverseProperty("FollowedUser")]
public virtual ICollection<UserUserWatchListItem> FollowedUsers { get; set; }
[InverseProperty("FolloweeUser")]
public virtual ICollection<UserUserWatchListItem> FolloweeUsers { get; set; }
}

How to retrieve book related to a specific tag

I have 3 tables (User, Tags, Book).
In the User table I have an attribute Interesting, it has a tag_id (I saving inside it a tagid). I also have a Tags table that has many tags with many to many to book table relation.
public class Tag
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string UrlSlug { get; set; }
public virtual string Description { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
and here is the book table :
public partial class Book
{
[Key]
public int Book_id { get; set; }
[Required]
[Display(Name ="User Name")]
[StringLength(128)]
public string User_ID { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual AspNetUser AspNetUser { get; set; }
}
and here is the user table :
public partial class AspNetUser
{
public string Id { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
[Display(Name = "Interesting")]
public int Interesting { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
What is the SQL statement here to retrieve the book that match the Interesting attribute?
For example in Interesting attribute I have 17 , so I want to retrieve the book that related to the tag id 17 ..
Note: my many-to-many table generated from the relation name is TagBooks
This should give you all the posts where it is associated with the "Swift" tag.
var books = dbContext.Books.Where(s => s.Tags.Any(f => f.Name == "Swift")).ToList();
If you want to get the result based on the TagId, change the condition in the predicate.
var books = dbContext.Books.Where(s => s.Tags.Any(f => f.Id== 17)).ToList();

EF codefirst relationships

Could someone show me how to create a relationship in my EF codefirst example - I want a relationship on the Products class that has a many relationship to the Product_Spec class so when I compile the code it will have relationships when the database is generated, and also a relationship for the Specification class related to the Product_Spec
Data Context class
classes:
namespace MvcApplication1.Models
{
public class Department
{
[Key]
public long Id { get; set; }
[Required(ErrorMessage="Please enter a name for the departments.")]
[DataType(DataType.Text)]
public string Name { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please enter a valid url for the department.")]
public string Url { get; set; }
public virtual List<Product> Products { get; set; }
}
public class Product
{
[Key]
public long Id { get; set; }
[ForeignKey("FK_Department_Id")]
public long DepartmentId { get; set; }
[DataType(DataType.Text)]
public string Title { get; set; }
[DataType(DataType.Currency)]
public decimal UnitPrice { get; set; }
[DataType(DataType.Currency)]
public decimal SellPrice { get; set; }
}
public class Product_Spec
{
[ForeignKey("FK_Spec_ProductId")]
public long ProductId { get; set; }
[ForeignKey("FK_Spec_SpecId")]
public long SpecId { get; set; }
}
public class Specification
{
[Key]
public long SpecId { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please enter a product specification type.")]
public string Type { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please enter a product specification value.")]
public string Value { get; set; }
}
}
namespace MvcApplication1
{
public class DataContext : DbContext
{
public DbSet<Department> Department { get; set; }
public DbSet<Product> Product { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().HasRequired(x => x.Products)
.WithMany().HasForeignKey(x => x.Id).WillCascadeOnDelete(true);
modelBuilder.Entity<Product>().HasOptional(x => x.Product_Specs)
.WithMany().HasForeignKey(x =>x.ProductId) // this lines doesn't work
base.OnModelCreating(modelBuilder);
}
}
}
I think you should set column names in ForeignKey attribute, not constraint names:
public class Product
{
[Key]
public long Id { get; set; }
public long DepartmentId { get; set; }
[DataType(DataType.Text)]
public string Title { get; set; }
[DataType(DataType.Currency)]
public decimal UnitPrice { get; set; }
[DataType(DataType.Currency)]
public decimal SellPrice { get; set; }
[ForeignKey("DepartmentId")]
public virtual Department Department { get; set; }
public ICollection<Product_Spec> ProductSpecs { get; set; }
}
public class Product_Spec
{
public long ProductId { get; set; }
public long SpecId { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product {get; set;}
}
It looks like you're trying to create a Many-Many relationship between Products and Specifications. If that's the case, you don't need to define Product_Spec, using the default conventions, Entity Framework will create your required junction table for you provided you make some alterations to your entities (to define the relationship).
In your case, you could make the following alterations:
public class Product
{
// Your other code
// [ForeignKey("FK_Department_Id")] - Not required, EF will configure the key using conventions
public long DepartmentId { get; set; }
public virtual ICollection<Specification> Specifications { get; set; } // Navigation property for one end for your Product *..* Specification relationship.
}
public class Specification
{
// Your other code
public virtual ICollection<Product> Products { get; set; }
}
When your tables are created, you should see a table with a name like SpecificationProducts, which is the junction table used to hold your many..many Product/Specification relationship.
If you needed to explicitly define this mapping (for example if you had an existing tables), you should be able to do something like this:
modelBuilder.Entity<Product>().
HasMany(s => s.Specifications).
WithMany(p => p.Products).
Map(
m =>
{
m.MapLeftKey("ProductId");
m.MapRightKey("SpecId");
m.ToTable("SpecificationProducts");
});

Code first one to one foreign key

I have two model class, where I want to make one-to-one relation. When I make migration, I get an error:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint
"FK_dbo.Uzytkownik_dbo.UserProfile_UserId". The conflict occurred in
database "db_wydarzenia", table "dbo.UserProfile", column 'UserId'.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
[Table("Uzytkownik")]
public class Uzytkownik
{
[Key]
public int UzytkownikID { get; set; }
public int UserId { get; set; }
public string Imie { get; set; }
public string Nazwisko { get; set; }
public string Telefon { get; set; }
public string Email { get; set; }
[ForeignKey("UserId")]
public UserProfile UserProfile { get; set; }
}
EDIT:
Problem solved :)
I remove all data from uzytkownik table and it's go.
If you want one-to-one - you cannot have both the primary-key and the foreign-key specified. One-to-one is modeled via primary-keys (pk == pk), otherwise it becomes 'multiplicity' (and just typical one-to-many).
To get what you want just remove your other PK - and user UserId as both primary and fk...
[Table("Uzytkownik")]
public class Uzytkownik
{
// [Key] public int UzytkownikID { get; set; }
[Key]
public int UserId { get; set; }
[ForeignKey("UserId")]
public UserProfile UserProfile { get; set; }
}

Why foreign key is added to my table

I have a model:
public class QuestionRevision
{
[Key]
public int Id { get; set; }
public int IdEditor { get; set; }
public List<Tag> Tags { get; set; }
}
public class Tag
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
The problem is, I have additional column in Tags table, called QuestionRevision_Id.
Of course one tag could be assigned to many questions, so it's not what I need.
What annotation I need to add to get the desired result?
I've added at Tag class:
public virtual List<QuestionRevision> QuestionRevisions { get; set; }
and at QuestionRevision:
public virtual List<Tag> Tags { get; set; }
and now I have table TagQuestionRevisions, so now it's work how I want.

Resources