I have 2 models:
public class Text
{
public long Id { get; set; }
public string Text { get; set; }
}
public class User
{
public int Id { get; set; }
public ICollection<Text> Texts { get; set; }
}
My model build on user is that
e.HasMany(o => o.Texts).WithOne().HasForeignKey(d => d.Id).IsRequired();
When I try to run:
dotnet ef migrations add
I get this error:
with foreign key properties {'Id' : long} cannot target the primary
key {'Id' : int} because it is not compatible. Configure a principal
key or a set of compatible foreign key properties for this
relationship.
UPDATE:
It should be able for new models to have a collection of the table Texts like:
public class Customer
{
public int Id { get; set; }
public ICollection<Text> Texts { get; set; }
}
....
e.HasMany(o => o.Texts).WithOne().HasForeignKey(d => d.Id).IsRequired();
Had similar problem using EF Core but didn't want to include the (equivalent in my class) UserId on the dependent entity Text, just to make happy EF. Finally found that you can replace the primary key used in the relationship (UserId)
using HasPrincipalKey()
modelBuilder.Entity<User>()
.HasMany(t => t.Texts)
.WithOne()
.HasPrincipalKey(u => u.Text);
Firstly, change your Model naming please,
public class Text
{
public long Id { get; set; }
public int UserId { get; set; }// add a foreign key that could point to User.Id
public string Body { get; set; }//you cannot have a string property called "Text".
public virtual User Owner { get; set; }
}
public class User
{
public int Id { get; set; }
public virtual ICollection<Text> Texts { get; set; } = new HashSet<Text>();
}
builder.Entity<Text>(table =>
{
table.HasKey(x => x.Id);
table.HasOne(x => x.User)
.WithMany(x => x.Texts)
.HasForeignKey(x => x.UserId)
.HasPrincipalKey(x => x.Id)//<<== here is core code to let foreign key userId point to User.Id.
.OnDelete(DeleteBehavior.Cascade);
});
the reason we have to figure out which key is referred is because of multiple primary keys. I saw it once in MSDN, but cannot find it back.
You can use shadow properties for foreign keys, it looks popular now.
public class Text
{
public long Id { get; set; }
public string Body { get; set; }
public virtual User Owner { get; set; }
}
public class User
{
public int Id { get; set; }
public virtual ICollection<Text> Texts { get; set; } = new HashSet<Text>();
}
builder.Entity<Text>(table =>
{
table.HasKey(x => x.Id);
// Add the shadow property to the model
table.Property<int>("UserId");
table.HasOne(x => x.User)
.WithMany(x => x.Texts)
.HasForeignKey("UserId")//<<== Use shadow property
.HasPrincipalKey(x => x.Id)//<<==point to User.Id.
.OnDelete(DeleteBehavior.Cascade);
});
In the EF context configuration, specifically in the HasForeignKey() you are supposed to specify Which property on the Text model should be the foreign key that points to the User model?
Since User model's primary key is an int, the foreign key pointing from Text to User should naturally also be an int.
I think the mistake you've made is that you are configuring the PK of Textto also be the FK for the relationship Text -> User. Try to change your Text model to :
public class Text
{
public long Id { get; set; }
public string Text{ get; set; }
public int UserId { get; set; }
}
And your configuration to:
e.HasMany(o => o.Texts).WithOne().HasForeignKey(d => d.UserId).IsRequired();
You can simply drop all the migrations or the migration that made that Id, drop the database (if it is small or has no data) and add a clean migration
I was facing the same issue in one-to-one relationship. If you are facing the issue in one-one relationship. Then try this:
public partial class document
{
public document()
{
groups = new group();
}
public int? group_id { get; set; }
public virtual group groups { get; set; }
}
[Table("group")]
public class group
{
[Key]
[Column("group_id")]
public int group_id { get; set; }
[ForeignKey(nameof(group_id))]
public virtual document document { get; set; }
}
Each document has single group. So, we can consider these settings.
modelBuilder.Entity<group>().HasOne(a => a.document)
.WithOne(y => y.groups).HasForeignKey<document>(b => b.group_id);
Related
I have a program written with a database-first approach; I have a table ServicePlan and another ServicePlanDetails. They are not mapped to each other, but they have a common column PlanId; a servicePlan can contain multiple ServicePlanDetails like a list of it.
I don't want to make any change to the database, but I want to map them as well. How can I do this? Does doing this within the method of on model creating will do the work for me and will not change anything in the database? I have tried this but could get the result.
For simplicity, I have just added few columns and their mapping and not all of them:
public partial class ServicePlan
{
public ServicePlan()
{
ServicePlanDetails = new HashSet<ServicePlanDetail>();
}
public long PlanId { get; set; }
public decimal PhoneId { get; set; }
public byte? NLines { get; set; }
public DateTime? DateStart { get; set; }
public DateTime? DateEnd { get; set; }
public virtual ICollection<ServicePlanDetail> ServicePlanDetails { get; set; }
}
public partial class ServicePlanDetail
{
public long PlanId { get; set; }
public string? ServCode { get; set; }
public string? CountryCode { get; set; }
public bool? IsPlan { get; set; }
public decimal? Cost { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ServicePlan>(entity =>
{
entity.HasKey(e => e.PlanId).HasName("PK_UsersPlan");
entity.ToTable("ServicePlan");
entity.HasIndex(e => e.VideoTronId, "IDX_VTID").HasFillFactor(80);
entity.HasIndex(e => new { e.PhoneId, e.IsApproved }, "Ix_SrvcPlan").HasFillFactor(80);
entity.Property(e => e.Zone).HasMaxLength(50);
entity.HasMany(p => p.ServicePlanDetails)
.WithOne()
.HasPrincipalKey(p => p.PlanId)
.HasForeignKey(p => p.PlanId);
});
}
The error I get is :
Unable to determine the relationship represented by navigation 'ServicePlan.ServicePlanDetails' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.'
I want to get the serviceplandetails with the same planid as the serviceplan into a list in the serviceplan.
PlanId cannot be both foreign and principal key for one to many.
public partial class ServicePlanDetail
{
public long Id { get; set; }
public long PlanId { get; set; }
public string? ServCode { get; set; }
public string? CountryCode { get; set; }
public bool? IsPlan { get; set; }
public decimal? Cost { get; set; }
}
Configuration
entity.HasMany(p => p.ServicePlanDetails)
.WithOne()
.HasPrincipalKey(p => p.PlanId)
.HasForeignKey(p => p.PlanId);
If in the database a Plan can have many ServicePlanDetails, and you link them by Plan ID, how do you differentiate one ServicePlanDetail against that Plan from another? What makes two ServicePlanDetail records unique? That is the crux of your problem. Your FK mapping is correct, but it won't work if PlanId is the PK on ServicePlanDetail. PKs must uniquely identify a single record. For instance if your plan is associated to service plan details applying to various users where multiple users reference the same plan and there is a UserID on ServicePlanId, the PK should be a composite of PlanId + UserId.
As a DB-First approach the database should already have the PKs and constraints set up. You just set up EF keys and relationship types to match that.
Now if the ServicePlanDetail's PK is declared as just PlanId, then the answer is that the relationship between Plan and ServicePlanDetail is 1-to-1, not 1-to-many. This becomes a .HasOne(p => p.ServicePlanDetail).WithOne(sp => sp.Plan) and there's really nothing you can do about that without altering the data relationships. You cannot magically change the relationship that EF will use if the underlying database schema cannot support that relationship.
I have a class which represent a connection of a page and a tag and it looks more or less like this:
public class TagLink {
[Key]
public int Id { get; set; }
public int PageId { get; set; }
public int TagId { get; set; }
public string TagName { get; set; }
}
In my database I would like to have 2 tables: TagLinks and TagNames. First one with Id, PageId and TagId and the second one with TagId and TagName.
I would like the tag id to be a foreign key so many tag links can be assigned to a single tag name.
I gave it a try with EntityTypeConfiguration but I don't know how to configure it properly. It gives me wrong foreign keys which are built like this:
ALTER TABLE [dbo].[TagNames] WITH CHECK ADD CONSTRAINT [FK_dbo.TagNames_dbo.TagLinks_TagId] FOREIGN KEY([TagId])
REFERENCES [dbo].[TagLinks] ([TagId]);
ALTER TABLE [dbo].[TagNames] CHECK CONSTRAINT [FK_dbo.TagNames_dbo.TagLinks_TagId]
I started off with this:
public class TagLinkEntityConfiguration : EntityTypeConfiguration<TagLink>
{
public TagLinkEntityConfiguration()
{
HasKey(e => e.Id);
HasKey(e => e.TagId);
Property(e => e.Id).HasColumnName(nameof(TagLink.Id));
Property(e => e.PageId).HasColumnName(nameof(TagLink.PageId));
Property(e => e.TagId).HasColumnName(nameof(TagLink.TagId));
Property(e => e.TagName).HasColumnName(nameof(TagLink.TagName));
Map(m =>
{
m.Properties(e => new
{
e.Id,
e.PageId,
e.TagId
});
m.ToTable("TagLinks");
});
Map(m =>
{
m.Properties(e => new
{
e.TagId,
e.TagName
});
m.ToTable("TagNames");
});
}
}
How do I make it work with many to one relation? I guess the foreign key should be added on TagLinks table to reference TagId in TagNames table
Give this a try:
public class TagLink {
[Key]
public int Id { get; set; }
public int PageId { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
public class Tag {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<TagLink> TagLinks { get; set; }
}
Skip the TagLinkEntityConfiguration definitions and let EF's code-first conventions to take over and solve the problem for you.
I have a class:
public class FormTemplate
{
public Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Code { get; set; }
public virtual string Shifr { get; set; }
public virtual FormType FormType { get; set; }
public virtual DateTime ActivationDate { get; set; }
}
and i need to split it into to tables in db. I have a fluent mapping wich do this for me:
en.Map(m =>
{
m.Properties(_ => new {_.Code, _.Name, _.Shifr});
m.ToTable("Table1");
})
.Map(m =>
{
m.Properties(_ => new {_.ActivationDate});
m.ToTable("Table2");
}).HasRequired(t => t.FormType).WithMany().Map(m =>
{
m.MapKey("FormType");
m.ToTable("Table2");
});
It works fine, but creates two tables with same primary key column "Id". Is it possible to map first table PK to column "Id" and second table PK to column "Form" ?
I have an entity that excludes entities of the same type under certain conditions. In order to achieve this, I have an entity class like:
public class Entity
{
public int ID { get; set; }
public virtual ICollection<EntityExcludedEntity> ExcludedEntities { get; set; }
}
public class ExcludedEntity
{
public int ID { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public int EntityID { get; set; }
public virtual Entity Entity { get; set; }
public int ExcludedEntityID { get; set; }
public virtual Entity ExcludedEntity { get; set; }
}
//declared in the ExcludedEntity mapping class.
public ExcludedEntityMapping()
{
HasRequired(t => t.Entity).WithMany(t => t.ExcludedEntity).HasForeignKey(t => t.EntityID)
HasRequired(t => t.ExcludedEntity).WithMany(t => t.ExcludedEntity).HasForeignKey(t => t.ExcludedEntityID);
}
This causes in EF creating a third column and foreign key field called Entity_ID in my model. Seems like it thinks I have another relationship defined here but I don't understand why.
Here is the part related to foreign keys in the tables created:
.ForeignKey("dbo.Entities", t => t.EntityID)
.ForeignKey("dbo.Entities", t => t.ExcludedEntityID)
.ForeignKey("dbo.Entities", t => t.Entity_ID)
This post helped me find the answer.
Basically, EF cannot have two foreign keys to the same entity field. If you need to create two foreign key to the same entity you should bind them to different fields. So in this example:
public class Entity
{
public int ID { get; set; }
public virtual ICollection<EntityExcludedEntity> ExcludingEntities { get; set; }
public virtual ICollection<EntityExcludedEntity> ExcludedFromEntities { get; set; }
}
and this configuration:
public DBConceptAnswerExcludedAnswerMapping()
{
HasRequired(t => t.Entity).WithMany(t => t.ExcludingEntities).HasForeignKey(t => t.EntityID);
HasRequired(t => t.ExcludedEntity).WithMany(t => t.ExcludedFromEntities).HasForeignKey(t => t.ExcludedEntityID);
}
would solve the problem.
I am using the entity framework (code first).
I would like to know if I really need to use a property with Id for relationship with another entity as in the code below.
public class User
{
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public int ProfileId { get; set; }
public Profile Profile{ get; set; }
}
public class Profile
{
public int Id { get; set; }
public string Description{ get; set; }
}
For this way when I insert a user by setting the profileid property performs perfectly.
But when I don't use the profileid property in the Profile class,
public class User
{
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public Profile Profile{ get; set; }
}
public class Profile
{
public int Id { get; set; }
public string Description{ get; set; }
}
the execution the insert method adds another profile record. Why?
My mapping:
public class EntityMapping<Entity> : EntityTypeConfiguration<Entity> where Entity : EntityBase
{
public EntityMapping()
{
HasKey(e => e.Id);
Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class UserMapping : EntityMapping<User>
{
public UserMapping() : base()
{
ToTable("USER");
Property(p => p.Id).HasColumnName("USER_CD_USER");
Property(p => p.Login).HasColumnName("USER_TX_LOGIN").HasMaxLength(10).IsRequired();
Property(p => p.Password).HasColumnName("USUA_TX_PASSWORD").HasMaxLength(8).IsRequired();
HasRequired(e => e.Profile).WithMany(p => p.Users).Map(p => p.MapKey("PROF_CD_PROFILE"));
}
}
public class ProfilelMapping : EntityMapping<Profile>
{
public ProfileMapping()
: base()
{
ToTable("PROFILE");
Property(p => p.Id).HasColumnName("PROF_CD_PROFILE");
Property(p => p.Description).HasColumnName("PROFILE_DS_PROFILE").HasMaxLength(20).IsRequired();
HasMany(e => e.Users).WithRequired(p => p.Profile);
}
}
You are asking two questions.
Do I need to use FK property?
No you don't but EF behavior changes if you use it or not. More about it is in separate answer and linked blog article.
Why EF inserts Profile again?
Creating relations with existing entities requires special care. EF doesn't check if your entity exists in the database - you must tell it to EF. Here is one of many ways how to achieve that (without loading profile from the database):
var user = GetNewUserSomewhere();
context.Users.Add(user);
// Dummy profile representing existing one.
var profile = new Profile() { Id = 1 };
// Informing context about existing profile.
context.Profiles.Attach(profile);
// Creating relation between new user and existing profile
user.Profile = profile;
context.SaveChanges();
Short answer: Yes. It's the way EF work. It needs to store the foreign key in a dedicated property. Have you ever generated the class structure from a database? It always adds that key property. There are cases you don't need the Profile property loaded, but later you might want to retrieve it. That's what the dedicated ProfileId property is used, it will read the key value from there and load the object.