I am using code first to generate tables.
I have User object:
public class ApplicationUser
{
public int? ImageId { get; set; }
public virtual Image Image { get; set; }
public virtual ICollection<Image> Images { get; set; }
and class Image:
public class Image
{
public int ImageId { get; set; }
public int CreatedBy { get; set; }
public virtual ApplicationUser CreatedByUser { get; set; }
I map objects via fluent api:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// User can create many images
modelBuilder.Entity<Image>()
.HasRequired(e => e.CreatedByUser)
.WithMany(e => e.Images)
.HasForeignKey(e => e.CreatedBy)
.WillCascadeOnDelete(false);
// User can and doesn't have to have image
modelBuilder.Entity<ApplicationUser>()
.HasRequired(e => e.Image)
.WithOptional()
.WillCascadeOnDelete(false);
The first part create me one relation UserId > CreatedBy fine.
But second relation is 1:1 and related fields are UserId > ImageId which is not what I am trying to make.
I have tried to use HasOptional instead HasRequired but then I get additional keys in tables.
What should I do to map this two tables?
UPDATE 1
Based on answer. I leave User and Image classes the same.
User have One image (for profile) and list of images (all other images that user created).
And I use fluent api to connect tables:
But EF generate me additional key and doesn't use User > ImageId as key I can't understand why?
modelBuilder.Entity<ApplicationUser>()
.HasOptional(e => e.Image)
.WithOptionalDependent()
.WillCascadeOnDelete(false);
Unfortunately, EF does not support 1:1 mappings in this way. If you think about it, the reason should be obvious. How would you model this in a database? You can't. The best you can do is create dual 1:many and many:1 connections.
ie, if you have two ApplicationUser rows, they could both have the same ImageId. There's no way to guarantee that there is only one row (at least not without constraints, which EF doesn't support).
EF only supports 1:1 when using a shared primary key. That means both entities have to use the same Key name, and they both have to be primary keys, and one has to also make the other a Foreign key.
Related
Odd issue that I've been looking at all day. I am working with Entity Framework 6. The issue I have is that I have three entities:
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
// other properties and relationships here
public int? OrderProfileId { get; set; }
public int OrderTemplateId { get; set; }
public virtual OrderProfile Profile { get; set; } // optional property
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderProfile
{
public int Id { get; set; }
// other properties
// added here 6/15/2021
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderTemplate : EntityMetaData
{
public int Id { get; set; }
// other properties
public int? OrderProfileId{ get; set; }
public OrderProfile OrderProfile { get; set; }
}
In our model builder, we have these definitions:
modelBuilder.Entity<Order>()
.HasOptional(x => x.OrderProfile)
.WithMany(x => x.Orders)
.HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
But even with the above fluent api model, we get the error
Invalid column name 'OrderProfile_Id'
Throughout various testing I was unable to find why this issue was occurring, so I looked at our logs and found when this error started popping it's head up and then was able to find the changes associated to OrderProfile and found that the only change that was made was adding the relationship from OrderProfile to OrderTemplate.
When I removed that fluent api relationship OrderProfile to OrderTemplate, it worked as expected... I don't need that relationship to OrderTemplate, but would like it to be there, how can I establish a optional 1 to optional 1 relationship without breaking other relationships? Also, why would additional relationships be effected by this?
UPDATE 6/15/2021
So I found I had a reverse navigation property in the OrderProfile model:
public virtual OrderTemplate OrderTemplate{ get; set; }
removing that and the associated fluent relationship
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
Doing the above resolved the issue, but for some reason, the issue seems to have cascaded down to another relationship that has a circular reference like the above. The Order class is involved with this cascaded issue. I guess this is a pretty big cause for concern since this application worked fine for the last 4 years and for these relationships to be decaying like this is worrisome. Does anyone know why this is happening?
if you use the right naming convention, EF will do magic. in this sample, you don't need fluent API to relate entities.
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
public int? OrderProfileId { get; set; } //means HasOptional (nullable) and ForeignKey
//variable name must be OrderProfile not Profile
public virtual OrderProfile OrderProfile { get; set; }
}
public class OrderProfile
{
public OrderProfile()
{
Orders = new HashSet<Order>();
}
public int Id { get; set; }
//be aware circular reference at any conversion or mapping
public virtual ICollection<Order> Orders {get; set;} //means WithMany
}
I've got an error like this too. It's caused by unmatching OrderProfileId property in OrderTemplate class with the fluent api model
If I'm not wrong, you want the OrderProfile model a many to many relation between Order and OrderTemplate. Then if it was the case, add the nvaigation property in OrderProfile.
public class OrderProfile
{
public int Id { get; set; }
// other properties
public virtual ICollection<Order> Orders { get; set; }
public virtual OrderTemplate OrderTemplate { get; set; }
}
Then change the fluent api model to be like this
// the EF has modelled the relation for normal 1 to many relation
// modelBuilder.Entity<Order>()
// .HasOptional(x => x.OrderProfile)
// .WithMany(x => x.Orders)
// .HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderTemplate>()
.HasOptional(x => x.OrderProfile)
.WithOptional(x => x.OrderTemplate);
You're working database-first, which always leaves room for a mismatch between the actual database model and the model EF infers from class and property names and mapping code (= conceptual model). If this happens, it may help to make EF generate a database from the conceptual model and see where it creates the column it expects, OrderProfile_Id.
This is what you'll see when logging the SQL statements:
CREATE TABLE [dbo].[OrderTemplates] (
[Id] [int] NOT NULL IDENTITY,
[OrderProfileId] [int],
[OrderProfile_Id] [int],
CONSTRAINT [PK_dbo.OrderTemplates] PRIMARY KEY ([Id])
)
...
ALTER TABLE [dbo].[OrderTemplates]
ADD CONSTRAINT [FK_dbo.OrderTemplates_dbo.OrderProfiles_OrderProfile_Id]
FOREIGN KEY ([OrderProfile_Id]) REFERENCES [dbo].[OrderProfiles] ([Id])
There you see the expected nullable column OrderProfile_Id which is the FK to OrderProfiles. It's noteworthy to see that EF does not use OrderProfileId as a foreign key field. It's just a field that could be used for anything.
That's because EF6 doesn't support 1:1 associations as foreign key associations (reference property and primitive FK property).
Knowing this, the remedy is simple: remove the property OrderTemplate.OrderProfileId and tell EF to use the field OrderTemplate.OrderProfileId in the database:
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile)
.Map(m => m.MapKey("OrderProfileId"));
That said, I wonder why Order has a foreign key to OrderProfile. Isn't its OrderProfile determined by its OrderTemplate? If it's a redundant relationship it may be better to remove it.
trying to implement two instances of a 1 to 1 or 1 to zero navigation property in ef core using either data annotations or fluent api.
formation table is just id and name.
public class Formation {
public long Id { get; set; }
public string Name {get; set;}
}
the well class can have but does not need to have a deepestformation of type formation
the well class can have but does not need to have a producingformation of type formation
public class Well
{
public long Id { get; set; }
public long? DeepestFormationId { get; set; }
public Formation DeepestFormation { get; set; }
public long? ProducingFormationId { get; set; }
public Formation ProducingFormation { get; set; }
}
I am struggling how to set this up using either data annotations or the fluid api
I attempted
modelBuilder.Entity<Well>()
.HasOne(c => c.DeepestFormation)
.WithOne()
.HasForeignKey<Formation>(c => c.Id);
but where do I tell it that the foreign key is DeepestFormationId?
Firstly, I'd recommend you read ef core Definition of terms.
Principal key: The properties that uniquely identify the principal entity. This may be the primary key or an alternate key.
Foreign key: The properties in the dependent entity that are used to store the principal key values for the related entity.
Then you can try to change your mapping:
modelBuilder.Entity<Well>()
.HasOne(c => c.DeepestFormation)
.WithOne()
.HasForeignKey<Well>(c => c.DeepestFormationId);
modelBuilder.Entity<Well>()
.HasOne(c => c.ProducingFormation)
.WithOne()
.HasForeignKey<Well>(c => c.ProducingFormationId);
You optionally can specify the principle key - .HasPrincipalKey(item => item.Id), but Ef core smart enough to resolve it.
Then you need to consider OnDelete behavior. Be careful with it, if you set Cascade, in your case you might get Multiple cascade path exception
I'm trying to create a 'one to many' relation between the classes 'ApplicationUser' and one recently created called 'Issue'.
So, in Models / IdentityModels.cs / ApplicationUser i added this property:
public ICollection<Issue> Issues { get; set; }
And Issue.cs has this code:
namespace Test.Models
{
public class Issue
{
public int Id { get; set; }
public ApplicationUser Courier { get; set; }
public ApplicationUser Customer { get; set; }
}
}
I'm using automatic migrations. So, after building and running 'update-database', the Issues table was created with these fields:
Id
ApplicationUser_Id
CourierId
CustomerId
My question is why was the field 'ApplicationUser_Id ' created and how can i prevent it?
The problem is that EF thinks you actually want three one-to-many relationships between Issue and ApplicationUser:
one for ICollection<Issue> Issues on ApplicationUser (ApplicationUser_Id)
one for ApplicationUser Courier on Issue (CourierId)
one for ApplicationUser Customer on Issue (CustomerId)
(Note that EF allows to define relationships from either side.)
If you want that ApplicationUser.Issues contains all Issues of this User, whether he is a Courier or Customer, you will need the additional ApplicationUser_Id key. Configuring EF so that this works will be quite a pain.
Maybe a simpler solution will do: introduce two collections on ApplicationUser.
public ICollection<Issue> CourierIssues { get; set; }
public ICollection<Issue> CustomerIssues { get; set; }
And then configure the backlinks in the ModelBuilder using the fluent API to eliminate the ApplicationUser_Id key:
modelBuilder.Entity<ApplicationUser>().HasMany(au => au.CourierIssues).WithOptional(i => i.Courier);
modelBuilder.Entity<ApplicationUser>().HasMany(au => au.CustomerIssues).WithOptional(i => i.Customer);
Because you have two foreign keys to ApplicationUser, Courier and Customer, but only one collection referencing Issue on ApplicationUser. EF has no way of know which foreign key it should line up with, so it just created a new one. To handle this properly, you need to utilize fluent config:
public class ApplicationUser
{
...
public class Mapping : EntityTypeConfiguration<ApplicationUser>
{
HasMany(m => m.Issues).WithRequired(m => m.Customer);
}
}
Then, in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ApplicationUser.Mapping());
}
The problem here of course, is that you are likely wanting to track collections for both the Customer and Courier collections. For that, you need two collections:
public virtual ICollection<Issue> CustomerIssues { get; set; }
public virtual ICollection<Issue> CourierIssues { get; set; }
Then, the following fluent config:
HasMany(m => m.CustomerIssues).WithRequired(m => m.Customer);
HasMany(m => m.CourierIssues).WithRequired(m => m.Courier);
I am having trouble setting up my database to properly for 0 to many (not 1 to many). I have WordModel objects which represent a given word. Within each are ICollection<WordModel> representing the synonyms of that word as a noun, adverb, ect. The database is slowly filled out with queries to an online thesaurus API. The API is only queried when the synonyms of a given word are requested. My WordModel class:
[Key]
public int Id { get; set; }
public string Word { get; set; }
public WordType Traits { get; set; }
public bool SynonymsQueried { get; set; }
public virtual ICollection<WordModel> NounSynonyms { get; set; }
public virtual ICollection<WordModel> VerbSynonyms { get; set; }
public virtual ICollection<WordModel> AdjectiveSynonyms { get; set; }
public virtual ICollection<WordModel> AdverbSynonyms { get; set; }
I use the SynonymsQueried to check if this word has been queried or not. This is so I can populate the synonym collections with WordModel objects while keeping track of whether or not those word's themselves have been queried (or else I would query recursively for potentially a very long time). There is no reason for any WordModel to have any knowledge of any synonym lists it might be included in, I only want to keep track of its own personal synonyms (which will remain null or empty until specifically queried).
The database that is generated from this model is a single table with a 1 to many relationship. Every WordModel has an Id referencing to a parent WordModel which wont make sense if multiple WordModel objects include another WordModel in their synonym collection.
I could make the relationship Many-To-Many but I don't know how to force this using Code First. It is also important to note that I have no experience with Model First or Database First EF, I am hoping this is possible with Code First.
What you want are four many-to-many self-referencing relationships without reverse navigation properties. Please try the following:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<WordModel>()
.HasMany(x => x.NounSynonyms).WithMany()
.Map(x => x.ToTable("WordModelNounSynonym")
.MapLeftKey("WordModelId").MapRightKey("SynonymId"));
modelBuilder.Entity<WordModel>()
.HasMany(x => x.VerbSynonyms).WithMany()
.Map(x => x.ToTable("WordModelVerbSynonym")
.MapLeftKey("WordModelId").MapRightKey("SynonymId"));
modelBuilder.Entity<WordModel>()
.HasMany(x => x.AdjectiveSynonyms).WithMany()
.Map(x => x.ToTable("WordModelAdjectiveSynonym")
.MapLeftKey("WordModelId").MapRightKey("SynonymId"));
modelBuilder.Entity<WordModel>()
.HasMany(x => x.AdverbSynonyms).WithMany()
.Map(x => x.ToTable("WordModelAdverbSynonym")
.MapLeftKey("WordModelId").MapRightKey("SynonymId"));
}
Because I don't like the naming convention the entityFramework (Code first) uses I usually use a foreignKey property to assign my own property:
public class User {
public virtual Customer Customer { get; set; }
[ForeignKey("Customer")]
public Guid CustomerId { get; set; }
}
Works just fine, a column "CustomerId" is created inside the "User"-Table.
But how do I achieve the same when I need a many-to-many relationship?
public class User {
public virtual ICollection<Role> Roles { get; set; }
}
public class Role {
public virtual ICollection<User> Users { get;set; }
}
This results in a Table "UserRole" which contains "User_id" and "Role_Id".
Still, I prefer "UserId" and "RoleId". I could just create a UserRole-Class myself containing the references to User and Role but there should be a more elegant way. Is there any?
You could configure that many-to-many relationship on your context using Fluent Api as I show below:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany<Role>(u => u.Roles)
.WithMany(c => c.Users)
.Map(cs =>
{
cs.MapLeftKey("UserId");
cs.MapRightKey("RoleId");
cs.ToTable("UserRoles");
});
}
This way you could name the junction table and the FK columns with the names that you want.
Another way is create an entity that represent the junction table and stablish two one-to-many relationships with User and Role, but if you don't need to add extra columns to the junction table, It's recommended use the first variant, but you can find some advantages in map this table explicitly.