I have the following two entities:
public class Company
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public string Town { get; set; }
public string Country { get; set; }
}
This corresponds to two DB tables:
Company:
Id uniqueidentifier
Name varchar
Address:
Id uniqueidentifier
Town varchar
Country varchar
RelationId uniqueidentifier
RelationId is the foreign key which stores the link back to CompanyId.
I can not change either the classes or the tables.
I am trying to figure out how to represent this construct in code first EFCore.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>().ToTable("Companies");
modelBuilder.Entity<Address>().ToTable("Addresses");
modelBuilder.Entity<Company>().HasKey(c => c.Id);
modelBuilder.Entity<Address>().HasKey(c => c.Id);
???????????????????????
}
What am I missing in the code above to prevent the creation of a CompanyId foreign key and use the 'RelationId' instead.
Create a Guid RelationId and a Company Company properties in Address, as purposed by #TanvirArjel and use the following fluent configuration:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
.....
modelBuilder.Entity<Company>()
.HasOne(c => c.Address)
.WithOne(a => a.Company)
.HasForeignKey(a => a.RelationId);
}
If an address can have more than one company, change the WithOne by WithMany and the Company property type to ICollection<Company>
Here is the solution for your requirement :
public class Company
{
public Guid Id { get; set; }
public string Name { get; set; }
//remove virtual keyword as there is no lazy loading in entityframework core
public ICollection<Address> Addresses { get; set; }
}
public class Address
{
public Guid Id { get; set; }
[ForeignKey("Company ")]
public Guid RelationId { get; set; }
public string Town { get; set; }
public string Country { get; set; }
//remove virtual keyword as there is no lazy loading in entityframework core
public Company Company {get; set;}
}
Using Fluent API:
public class YourDbContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Address>()
.HasOne(p => p.Company)
.WithMany(b => b.Addresses);
}
}
Related
I have the following:
public class Event : IEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("date")]
public DateTimeOffset Date { get; set; }
[JsonProperty("distance")]
public int Distance { get; set; }
[JsonProperty("verticalAscend")]
public int VerticalAscend { get; set; }
[ForeignKey("User")]
[JsonProperty("userId")]
public Guid UserId { get; set; }
//attending
public DateTimeOffset DateCreated { get; set; }
public DateTimeOffset DateModified { get; set; }
[JsonProperty("user")]
public virtual User User { get; set; }
[JsonProperty("comments")]
public virtual ICollection<Comment> Comments { get; set; }
[JsonProperty("attending")]
public virtual ICollection<User> AttendingList { get; set; }
}
And:
public class User : IEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("profilePicUrl")]
public string ProfilePicUrl { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("surname")]
public string LastName { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
public DateTimeOffset DateCreated { get; set; }
public DateTimeOffset DateModified { get; set; }
public virtual ICollection<Event> AttendingEvents { get; set; }
[InverseProperty("User")]
public virtual ICollection<Event> Events { get; set; }
}
Relationships:
Event:
Many Users attending (AttendingList)
User:
Can attend many events (AttendingEvents)
Can create multiple events (Events)
There exists a many-many relationship between the Event.AttendingList and User.AttendingEvents.
There exists 0-many relationship between Event.User and User.Events, with ForeignKey as UserId.
I am trying to configure these with Fluent API, and using the InverseProperty to configure the other side of the relationship, mapping back to Event.User, but getting the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.UserEvents_dbo.Events_Event_Id' on table 'UserEvents' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I am unsure on how to solve this relationship on one table. What am I doing wrong?
In the DbContext configure your models as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Event>().HasRequired(e => e.User)
.WithMany(u => u.Events)
.HasForeignKey(e => e.UserId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany<Event>(s => s.AttendingEvents)
.WithMany(c => c.AttendingList)
.Map(cs =>
{
cs.MapLeftKey("UserId");
cs.MapRightKey("EventId");
cs.ToTable("UserEvents");
});
}
So I'm following this answer in trying to get two foreign keys to get to a single table and it works.
public class Team
{
public int TeamId { get; set;}
public string Name { get; set; }
public virtual ICollection<Match> HomeMatches { get; set; }
public virtual ICollection<Match> AwayMatches { get; set; }
}
public class Match
{
public int MatchId { get; set; }
public int HomeTeamId { get; set; }
public int GuestTeamId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public virtual Team HomeTeam { get; set; }
public virtual Team GuestTeam { get; set; }
}
public class Context : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Match>()
.HasRequired(m => m.HomeTeam)
.WithMany(t => t.HomeMatches)
.HasForeignKey(m => m.HomeTeamId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Match>()
.HasRequired(m => m.GuestTeam)
.WithMany(t => t.AwayMatches)
.HasForeignKey(m => m.GuestTeamId)
.WillCascadeOnDelete(false);
}
}
However, in my solution I don't want Team to have HomeMatches and AwayMatches collections as it doesn't make sense to be able to navigate to Match from that entity.
Is it possible to have two foreign keys pointing to the same table when the entity for the parent table doesn't have collections for the child tables.
I would like my Team entity to be like below.
public class Team
{
public int TeamId { get; set;}
public string Name { get; set; }
// HomeMatches and AwayMatches collection is no longer here
}
How could I use the modelBuilder to articulate to EntityFramework that I want to HomeTeamID and GuestTeamID to be foreign keys of Team?
Just remove collections & leave empty parameters for .WithMany().
Suppose in the data model there is a many-to-many relationship. How can I set it up so that the list of id's is eager loaded and navigation properties are lazy loaded?
In a one-to-one relationship, I can easily use the ForeignKey attribute to link the id and the navigation property, but I'm not sure if there's a way to link collections. How can I make sure the two are consistent?
public class User {
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<User> FollwerIds { get; set; }
public ICollection<User> FollwingIds { get; set; }
public virtual ICollection<User> Followers { get; set; }
public virtual ICollection<User> Following { get; set; }
}
public class UserContext: DbContext {
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuiler.Entity<User>()
.HasMany(u => u.Followers)
.WithMany(u => u.Following)
.Map(m => m.MapLeftKey("FollowingUserId")
.MapRightKey("FollowerUserId")
.ToTable("UserFollowUser")
);
}
}
I'd like to have the controllers in ASP.NET always return User objects with the two arrays of User Ids.
Thank you.
In many-to-many you don't need this:
public ICollection<User> FollwerIds { get; set; }
public ICollection<User> FollwingIds { get; set; }
To make the many-to-many relationship work, you'll need :
public class User {
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Followers { get; set; }
public virtual ICollection<User> Following { get; set; }
}
public class UserContext: DbContext {
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuiler.Entity<User>()
.HasMany(u => u.Followers)
.WithMany(u => u.Following)
.Map(m => m.MapLeftKey("FollowingUserId")
.MapRightKey("FollowerUserId")
.ToTable("UserFollowUser")
);
}
}
I believe this would work as you wish.
Here's my code:
base class:
public class BaseEnt
{
[Key]
public int ID { get;set; }
public DateTime InsertDate { get; set; }
public int InsertUserID { get; set; }
public DateTime UpdateDate { get; set; }
public int UpdateUserID { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
public virtual User InsertUser { get; set; }
public virtual User UpdateUser { get; set; }
}
user entity:
public class User:BaseEnt
{
public string Username { get; set; }
public string Fullname { get; set; }
public virtual ICollection<User> InsertedUsers { get; set; }
public virtual ICollection<User> UpdatedUsers { get; set; }
}
model creating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(t => t.InsertUser)
.WithMany(t=>t.InsertedUsers)
.HasForeignKey(t => t.InsertUserID).WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasRequired(t => t.UpdateUser)
.WithMany(t=>t.UpdatedUsers)
.HasForeignKey(t => t.UpdateUserID).WillCascadeOnDelete(false);
}
Seed:
protected override void Seed(MCFDataContext context)
{
context.Users.Add(new Entities.User {ID=1,Fullname="Rusty Boi ",Username="jhaskdhaksdhk",InsertUserID=1,UpdateUserID=1,UpdateDate=DateTime.Now,InsertDate=DateTime.Now });
}
and here's the error i encountered in the seed part:
{"Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values."}
what you have here is some sort of "chicken and egg" problem. Possible solutions:
Make the foreign keys InsertUserID and UpdateUserID nullable
Do a design change: Maybe the user table shouldn't have InsertUserID, UpdateUserID. this could be solved by introducing another base class which doesn't have the InsertUserID and UpdateUserID properties or something similar
I have aproject with codefirst migrations wiht the following classes:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Chat> Chats { get; set; }
}
and
public class Chat
{
[Key, Column(Order = 0)]
public int User1Id { get; set; }
public virtual User User1 { get; set; }
[Key, Column(Order = 1)]
public int User2Id { get; set; }
public virtual User User2 { get; set; }
public string Channel { get; set; }
}
The Idea is A chat has two users, and the chat Primary key is a composite key of the two user Ids, which are foreign keys.
A user has also a list of chats, in which he participates.
now when I try to run this, I get the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.Chat_dbo.User_User2Id' on table 'Chat' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint. See previous errors.
I kind of understand what's happening, but I don't know how to fix it. Tried this (from a topic here somewhere):
public DbSet<User> Users { get; set; }
public DbSet<Chat> Chats { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User1)
.WithMany()
.WillCascadeOnDelete(true);
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User2)
.WithMany()
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
but with no result, i still get the error. Can anyone shine some light on this matter?
try
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Chat> InitiatedChats { get; set; }
public virtual ICollection<Chat> ParticipantInChats { get; set; }
}
public class Chat
{
[Key, Column(Order = 0)]
public int User1Id { get; set; }
public virtual User User1 { get; set; }
[Key, Column(Order = 1)]
public int User2Id { get; set; }
public virtual User User2 { get; set; }
public string Channel { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User1)
.WithMany(u => u.InitiatedChats)
.HasForeignKey(s => s.User1Id)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User2)
.WithMany(u => u.ParticipantInChats)
.HasForeignKey(s => s.User2Id)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
The anwser comes from this post