public class Message
{
[Key]
public int MeesageId { get; set; }
public int SenderId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Sender { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Receiver { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public bool Seen { get; set; }
}
public class Person
{
public string Username { get; set; }
[Key]
public int PersonId { get; set; }
}
I'm getting this error:
The ForeignKeyAttribute on property 'Receiver' on type 'Finder.Models.Message' is not valid. The foreign key name 'PersonId' was not found on the dependent type 'Finder.Models.Message'. The Name value should be a comma-separated list of foreign key property names.
What I think I should do is rename ReceiverId to PersonId, so it matches the foreign key, but then the property names would be too messy. Any help would be appreciated
The ForeignKey attribute specifies which int property is the foreign key for the specified navigation property. So
public class Message
{
[Key]
public int MeesageId { get; set; }
public int SenderId { get; set; }
[ForeignKey("SenderId")]
public virtual Person Sender { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("ReceiverId")]
public virtual Person Receiver { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public bool Seen { get; set; }
}
It is better to use fluent api.
for example :
public class Message
{
public int MeesageId { get; set; }
public int SenderId { get; set; }
public virtual Person Sender { get; set; }
public int ReceiverId { get; set; }
public virtual Person Receiver { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
public bool Seen { get; set; }
}
public class Person
{
public string Username { get; set; }
public int PersonId { get; set; }
public List<Message> SenderMessages { get; set; }
public List<Message> RecieverMessages { get; set; }
}
public class MessageConfigurations : IEntityTypeConfiguration<Message>
{
public void Configure(EntityTypeBuilder<Message> builder)
{
builder.HasKey(x => x.MeesageId);
builder.HasOne(x => x.Sender)
.WithMany(x => x.SenderMessages)
.HasForeignKey(x => x.SenderId)
.OnDelete(DeleteBehavior.NoAction);
builder.HasOne(x => x.Receiver)
.WithMany(x => x.RecieverMessages)
.HasForeignKey(x => x.ReceiverId)
.OnDelete(DeleteBehavior.NoAction);
}
}
public class PersonConfigurations : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.HasKey(x => x.PersonId);
}
}
Related
I have two tables Person and Round, a person can participate in many rounds and a round can only contain one person.
How can I treat this case and add rounds to the database in an ASP.NET Core Web API?
Should it be in the person controller or round controller? And how to do it using Entity Framework? And should I include a PersonId attribute in the Round Post Dto class
Round class:
public class Round
{
[Key] public int RoundId { get; set; }
[Required] public int RoundNumber { get; set; }
[Required] public int HostScore { get; set; }
public Person Person { get; set; }
}
Person class:
public class Person
{
[Key] public int PersonId { get; set; }
[Required] public string Username { get; set; } = default!;
[Required] public string Email { get; set; } = default!;
[Required] public string Password { get; set; } = default!;
public List<Round> Rounds { get; set; }
}
You can do it like this:
public class Round
{
[Key] public int RoundId { get; set; }
[Required] public int RoundNumber { get; set; }
[Required] public int HostScore { get; set; }
[Required]
[ForeignKey("Person")]
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
public class Person
{
[Key] public int PersonId { get; set; }
[Required] public string Username { get; set; } = default!;
[Required] public string Email { get; set; } = default!;
[Required] public string Password { get; set; } = default!;
public virtual ICollection<Round> Rounds { get; set; }
}
If you don't have em you might need these usings also
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
In ef, to identify the relationship you should use the Id word only in the key name of the parent class.
for example:
public class Round
{
[Key] public int RoundId { get; set; }
[Required] public int RoundNumber { get; set; }
[Required] public int HostScore { get; set; }
public Person Person { get; set; }
public int PersonId { get; set; }
}
public class Person
{
[Key] public int Id { get; set; }
[Required] public string Username { get; set; } = default!;
[Required] public string Email { get; set; } = default!;
[Required] public string Password { get; set; } = default!;
public List<Round> Rounds { get; set; }
}
If you don't want to use this name, you can use fluent api
for example:
public class Round
{
public int RoundId { get; set; }
public int RoundNumber { get; set; }
public int HostScore { get; set; }
public Person Person { get; set; }
public int PersonId { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string Username { get; set; } = default!;
public string Email { get; set; } = default!;
public string Password { get; set; } = default!;
public List<Round> Rounds { get; set; }
}
public class RoundConfigurations : IEntityTypeConfiguration<Round>
{
public void Configure(EntityTypeBuilder<Round> builder)
{
builder.HasOne(x => x.Person)
.WithMany(x => x.Rounds)
.HasForeignKey(x => x.PersonId);
builder.HasKey(x => x.RoundId);
builder.Property(x => x.HostScore).IsRequired();
builder.Property(x => x.RoundNumber).IsRequired();
}
}
public class PersonConfigurations : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.HasKey(x => x.PersonId);
builder.Property(x => x.Username).IsRequired();
builder.Property(x => x.Email).IsRequired();
builder.Property(x => x.Password).IsRequired();
}
}
I have 4 classes in dbcontext,it's EventRemind.cs Event.cs House.cs Customer.cs,the code like this:
public class EventRemind
{
[Key]
public Guid Id { get; set; }
public Guid CustomerEventId { get; set; }
public DateTime RemindTime { get; set; }
public bool HasRead { get; set; }
public DateTime CreatedTime { get; set; }
[ForeignKey("CustomerEventId")]
public virtual Event CustomerEvent { get; set; }
}
public class Event
{
[Key]
public Guid Id { get; set; }
public string Title { get; set; }
public int UserId { get; set; }
public int HouseId { get; set; }
[MaxLength(800)]
public string Content { get; set; }
public DateTime CreatedTime { get; set; }
[ForeignKey("HouseId")]
public virtual House House { get; set; }
[ForeignKey("UserId")]
public virtual Customer Customer { get; set; }
public virtual ICollection<EventRemind> EventReminds { get; set; }
}
public class House
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Event> HouseEvents { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Event> CustomerEvents { get; set; }
}
and my dbcontext is this:
public class DataContext:DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<EventRemind> EventReminds { get; set; }
public DbSet<Event> Events { get; set; }
public DbSet<House> Houses { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
that means an eventRemind include an event,an event include a house and a customer,now what puzzles me is that what should I do to get the House and Customer at the same time from EventReminds,what I want is this:
var query = _dataContext.EventReminds.Include(c => c.CustomerEvent)
.ThenInclude(c => c.Customer).ThenInclude(c => c.House //this get a compile error);
why dis this happen? Can somebody help me? Thanks in advance.
I think your last operator should be just Include. Try this:
var query = _dataContext.EventReminds
.Include(c => c.CustomerEvent)
.ThenInclude(c => c.Customer)
.Include(c => c.House);
You have to write code following way.
Way1
var query =
_dataContext.EventReminds.Include(c => c.CustomerEvent).ThenInclude(c => c.Customer)
.Include(c=> c.CustomerEvent).ThenInclude(c => c.House);
Way2 (If property is not collection then it is usefull)
var query =
_dataContext.EventReminds.Include(c=> c.CustomerEvent).
.Include(c=> c.CustomerEvent.Customer)
.Include(c=> c.CustomerEvent.House);
I have User table and I'd like to add connection called UserFriend between 2 users. I've searched a lot and basicly tried many different solutions and none of them worked. Everytime I get same error:
Introducing FOREIGN KEY constraint 'FK_UserFriends_Users_Friend2Id' on table 'UserFriends' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Here are my models:
public class User
{
[Key]
public Guid Id { get; set; }
public string Username { get; set; }
public string EmailAddress { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public virtual ICollection<UserFriend> Friends { get; set; }
public virtual ICollection<UserFriend> FriendOf { get; set; }
}
public class UserFriend
{
public User Friend1 { get; set; }
public Guid Friend1Id { get; set; }
public User Friend2 { get; set; }
public Guid Friend2Id { get; set; }
public bool Confirmed { get; set; }
public DateTime Added { get; set; }
}
And here's code in DataContext:
modelBuilder.Entity<UserFriend>().HasKey(sc => new { sc.Friend1Id, sc.Friend2Id });
modelBuilder.Entity<UserFriend>()
.HasOne(c => c.Friend1)
.WithMany(c => c.FriendOf)
.HasForeignKey(f => f.Friend1Id);
modelBuilder.Entity<UserFriend>()
.HasOne(c => c.Friend2)
.WithMany(c => c.Friends)
.HasForeignKey(f => f.Friend2Id)
.OnDelete(DeleteBehavior.Restrict);
Change your code to below and remove the other lines you have posted.
public class User
{
[Key]
public Guid Id { get; set; }
public string Username { get; set; }
public string EmailAddress { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public virtual ICollection<UserFriend> Friends { get; set; }
public virtual ICollection<UserFriend> FriendOf { get; set; }
}
public class UserFriend
{
public User Friend1 { get; set; }
[ForeignKey("Friend1")]
public Guid? Friend1Id { get; set; }
public User Friend2 { get; set; }
[ForeignKey("Friend2")]
public Guid? Friend2Id { get; set; }
public bool Confirmed { get; set; }
public DateTime Added { get; set; }
}
modelBuilder.Entity<User>();
modelBuilder.Entity<UserFriend>();
I have two classes:
One is User
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<Subscription> Subscriptions { get; set; }
}
Other is Subscription:
public class Subscription
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
As you can see that User has a list of Subscriptions.
Now when using the entity framework code first approach I am getting a table for User which doesn't contain Subscriptions but a new column for User Id is being added to Subscription table. I was expecting to have a third table which contains two columns one with User ID and the other with subscription ID.
How can I achieve this?
From documentation:
Many-to-many relationships without an entity class to represent the join table are not yet supported. However, you can represent a many-to-many relationship by including an entity class for the join table and mapping two separate one-to-many relationships.
So this answer is correct.
I just corrected code a little bit:
class MyContext : DbContext
{
public DbSet<Use> Users { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserSubscription>()
.HasKey(t => new { t.UserId, t.SubscriptionId });
modelBuilder.Entity<UserSubscription>()
.HasOne(pt => pt.User)
.WithMany(p => p.UserSubscription)
.HasForeignKey(pt => pt.UserId);
modelBuilder.Entity<UserSubscription>()
.HasOne(pt => pt.Subscription)
.WithMany(t => t.UserSubscription)
.HasForeignKey(pt => pt.SubscriptionId);
}
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<UserSubscription> UserSubscriptions{ get; set; }
}
public class Subscription
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public List<UserSubscription> UserSubscriptions{ get; set; }
}
public class UserSubscription
{
public int UserId { get; set; }
public User User { get; set; }
public int SubscriptionId { get; set; }
public Subscription Subscription { get; set; }
}
PS. You don't need use virtual in navigation property, because lazy loading still not available in EF Core.
Create a third middle table named: UserSubscriptions for example.
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public virtual ICollection<UserSubscription> Subscriptions { get; set; }
}
public class Subscription
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class UserSubscription
{
public int ID { get; set; }
public int SubscriptionID { get; set; }
public decimal Price { get; set; }
public int UserID { get; set; }
public virtual User { get; set; }
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
}
Second Solution:
Add reference for Subscription to User and name it CurrentSubscription for example.
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int CurrentSubscriptionID { get; set; }
public virtual Subscription Subscription { get; set; }
}
public class Subscription
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
I am using Entity framework 4.1 in MVC 3 application. I have an entity where I have primary key consists of two columns ( composite key). And this is being used in another entity as foreign key. How to create the relationship ? In normal scnerios we use :
public class Category
{
public string CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
but what if category has two columns key ?
You can use either fluent API:
public class Category
{
public int CategoryId1 { get; set; }
public int CategoryId2 { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId1 { get; set; }
public int CategoryId2 { get; set; }
public virtual Category Category { get; set; }
}
public class Context : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Category>()
.HasKey(c => new {c.CategoryId1, c.CategoryId2});
modelBuilder.Entity<Product>()
.HasRequired(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => new {p.CategoryId1, p.CategoryId2});
}
}
Or data annotations:
public class Category
{
[Key, Column(Order = 0)]
public int CategoryId2 { get; set; }
[Key, Column(Order = 1)]
public int CategoryId3 { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
[ForeignKey("Category"), Column(Order = 0)]
public int CategoryId2 { get; set; }
[ForeignKey("Category"), Column(Order = 1)]
public int CategoryId3 { get; set; }
public virtual Category Category { get; set; }
}
I believe the easiest way is to use Data Annotation on the Navigation property like this:
[ForeignKey("CategoryId1, CategoryId2")]
public class Category
{
[Key, Column(Order = 0)]
public int CategoryId1 { get; set; }
[Key, Column(Order = 1)]
public int CategoryId2 { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId1 { get; set; }
public int CategoryId2 { get; set; }
[ForeignKey("CategoryId1, CategoryId2")]
public virtual Category Category { get; set; }
}
In .NET Core and .NET 5 < the documentation only shows Data annotations (simple key).
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-composite-key%2Csimple-key#foreign-key
However using the example from #LadislavMrnka you will get a error message like this:
System.InvalidOperationException: There are multiple properties with
the [ForeignKey] attribute pointing to navigation ''. To define a
composite foreign key using data annotations, use the [ForeignKey]
attribute on the navigation.
Using that error message you can write the code like this:
public class Product
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId2 { get; set; }
public int CategoryId3 { get; set; }
[ForeignKey("CategoryId2,CategoryId3")]
public virtual Category Category { get; set; }
}
Fluent API (composite key) example from Microsoft:
internal class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasKey(c => new { c.State, c.LicensePlate });
modelBuilder.Entity<RecordOfSale>()
.HasOne(s => s.Car)
.WithMany(c => c.SaleHistory)
.HasForeignKey(s => new { s.CarState, s.CarLicensePlate });
}
}
public class Car
{
public string State { get; set; }
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public List<RecordOfSale> SaleHistory { get; set; }
}
public class RecordOfSale
{
public int RecordOfSaleId { get; set; }
public DateTime DateSold { get; set; }
public decimal Price { get; set; }
public string CarState { get; set; }
public string CarLicensePlate { get; set; }
public Car Car { get; set; }
}