Entity Framework Constraints with Oracle DB - c#

pulling my hair out on this...I have a code-first entity model for oracle, and I am having problems getting around this foreign key issue:
"The property 'aidYearCode' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type."
Here are the relevant code snippets:
1) in "OnModelCreating(DbModelBuilder modelBuilder)":
modelBuilder.Entity<INSTITUTIONAL>()
.HasMany(e => e.applicantBudgetComponents)
.WithRequired(e => e.institutional) // so this exists in APPLICANT_BUDGET_COMPONENTS: public virtual INSTITUTIONAL institutional { get; set; }
.HasForeignKey(e => e.aidYrCode)
.WillCascadeOnDelete(false);
2) the table APPLICANT_BUDGET_COMPONENTS with the foreign key has:
[Key]
[Column(Order = 0)]
[StringLength(4)]
public string aidYrCode { get; set; }
3) lastly the INSTITUTIONAL table has the primary key:
[Key]
[StringLength(4)]
public string aidYearCode { get; set; }
This was all generated by the Code-First wizard in VS2015, here is what I have tried:
putting the annotation
[ForeignKey("aidYrCode")] in 2)
putting the annotation from oracle's schema in 2)
[ForeignKey("FK1_RBRACMP_INV_ROBINST_CODE")]
adding "Id" to Foreign Key Names so as to follow EF6 conventions.
it should be noted that APPLICANT_BUDGET_COMPONENTS has a composite primary key.

It turns out that the error message was spurious; the configuration of "aidYearcode" was correct, however, after digging deeper I found some missing model configurations in other tables (indirectly related to the ones mentioned here) and after inserting the proper fluent commands and attributes for those properties the "error" was resolved.

Related

.NET EF Core context querying Postgresql tries to query unwanted shadow FK column

After changing entity, adding or removing properties, a "random" column was added to query and it's causing problems.
Npgsql.PostgresException (0x80004005): 42703: column u.UserOldId does not exist
I am querying this entity
[Table("user_visuals", Schema = "present")]
public class UserVisual
{
#region Columns
[Key]
[Column("user_visual_id")]
public long Id { get; set; }
[Column("user_id")]
public long UserId { get; set; }
[Column("visual_id")]
public long VisualId { get; set; }
[Column("render_position")]
public int RenderPosition { get; set; }
#endregion
#region Relations
public Visual Visual { get; set; } = default!;
//public UserOld User { get; set; } = default!;
#endregion
}
After some investigation I've found out that context somehow contains shadow FK and tries to query it though it doesn't exist
Seems like context has this kind of information about it
EntityType: UserVisual
Properties:
Id (long) Required PK AfterSave:Throw ValueGenerated.OnAdd
Annotations:
Npgsql:ValueGenerationStrategy: IdentityByDefaultColumn
Relational:ColumnName: user_visual_id
RenderPosition (int) Required
Annotations:
Npgsql:ValueGenerationStrategy: None
Relational:ColumnName: render_position
UserId (long) Required FK Index
Annotations:
Npgsql:ValueGenerationStrategy: None
Relational:ColumnName: user_id
UserOldId (no field, long?) Shadow FK Index
Annotations:
Npgsql:ValueGenerationStrategy: None
VisualId (long) Required FK Index
Annotations:
Npgsql:ValueGenerationStrategy: None
Relational:ColumnName: visual_id
Navigations:
Visual (Visual) ToPrincipal Visual Inverse: UserVisuals
Keys:
Id PK
Foreign keys:
UserVisual {'UserId'} -> User {'Id'} ToDependent: Visuals Cascade
UserVisual {'UserOldId'} -> UserOld {'Id'} ToDependent: Visuals ClientSetNull
UserVisual {'VisualId'} -> Visual {'Id'} ToDependent: UserVisuals ToPrincipal: Visual Cascade
Indexes:
UserId
UserOldId
VisualId
Annotations:
DiscriminatorProperty:
Relational:FunctionName:
Relational:Schema: present
Relational:SqlQuery:
Relational:TableName: user_visuals
Relational:ViewName:
Relational:ViewSchema:
Context configuration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserIdentity>()
.HasKey(x => x.Id);
modelBuilder.Entity<UserRole>()
.HasKey(x => x.Id);
modelBuilder.Entity<IdentityUserRole<long>>().HasKey(p => new { p.UserId, p.RoleId });
modelBuilder.Entity<UserVisual>().HasOne<Visual>(x => x.Visual).WithMany(x => x.UserVisuals).HasForeignKey(x => x.VisualId);
modelBuilder.Entity<UserDevice>().HasOne<Device>(x => x.Device).WithMany(x => x.UserDevices).HasForeignKey(x => x.DeviceId);
modelBuilder.Entity<Device>().HasOne<DeviceType>(x => x.DeviceType).WithMany(x => x.Devices).HasForeignKey(x => x.TypeId);
}
Out of ideas, what should I do?
Phantom shadow FKs usually indicate some relationship misconfiguration via fluent API, or like in this case, relationship created by EF Core default conventions.
Note that Single navigation property in one of the entities is enough to imply relationship in the model. So even if you removed the reference navigation property from the dependent entity (UserOld User in UserVisual), if the principal type (UserOld in this case) is still included in the model, and has collection navigation property referring to the dependent entity type (ICollection<UserVisual or similar), EF Core still considers a relationship with conventional shadow FK.
So, always check the usage (find all references) of a entity class inside other entities navigation properties, and remove the ones which are not intended to create relationship. Also (even though it's not the case here) the correct pairing of Has / With` calls to not leave existing navigation property out of the fluent configuration, thus creating additional conventional relationship.

Composite Key EF Core getting error when using Fluent Api

So I have the following class in Entity Framework Core. I am trying to do a code first migration and can't for the life of me figure out how to make the fluent API for this work.
public class Participants
{
public Activity Activity { get; set; } //Class with Id and Name of Activity
public ApplicationUser Participant { get; set; }
[Key]
[Column(Order = 1)]
public int ActivityId { get; set; }
[Key]
[Column(Order = 2)]
public string ParticipantId { get; set; }
}
In EF6 I was able to do this in OnModelCreating to get it to work fine.
modelBuilder.Entity<Attendance>()
.HasRequired(a => a.Activity)
.WithMany()
.WillCascadeOnDelete(false);
But in EF Core I get
" Entity type 'Participants' has composite primary key defined with data annotations. To set composite primary key, use fluent API."
I have tried using
modelBuilder.Entity<Participants>().HasKey(p => new {p.Activity, p.Participant});
But, that just leads to
Introducing FOREIGN KEY constraint 'FK_Participants_AspNetUsers_ParticipantId' on table 'Participants' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
If there is a better way to do the whole thing I'm open to suggestions. If you have pluralsight subscription, I'm basically trying to get "Become a Full Stack Developer" by Mosh Hamedani to work in EF core. The example is in "13-full-stack-fundamentals" folder.
UPDATE: Also tried
modelBuilder.Entity<Participants>()
.HasOne(p => p.Activity)
.WithMany()
.OnDelete(DeleteBehavior.Cascade);
Still got
"Entity type 'Participants' has composite primary key defined with data annotations. To set composite primary key, use fluent API."
UPDATE 2: After trying Roy's suggestion this is what I'm getting
Introducing FOREIGN KEY constraint 'FK_Participants_AspNetUsers_ParticipantId' on table 'Participants' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
UPDATE 3: In the Migration
I removed one of the OneDelete: ReferntialAction.Cascade and it worked. I removed the one off of FK_Participants_AspNetUsers_ParticipantId.
I also changed to this in my OnModelCreating
modelBuilder.Entity<Participants>()
.HasKey(p => new { p.ActivityId, p.ParticipantId });
base.OnModelCreating(modelBuilder);
//Added this ( Not sure if it's needed if anyone knows let me know)
modelBuilder.Entity<Participants>()
.HasOne(p => p.Activity)
.WithMany()
.OnDelete(DeleteBehavior.Cascade);
What you are trying to do is create a relationship between Activity and Participant which is a little different in EFCore.
To do it, you would need to reference the ForeignKey Properties instead of NavigationProperties in the modelbuilder as follows:
modelBuilder.Entity<Participants>()
.HasKey(p => new { p.ActivityId , p.ParticipantId });

How do I specify the foreign key in a one-to-one/zero relationship?

I have a parent entity and a child entity.
In the DB the primary key for parent is p_p_id
and the foreign key in the child is the same p_p_id
There is no foreign key constraint in the database.
The entities have the properties set up in their respective classes pointing at each other.
Parent Class
public virtual ChildProject ThisChildProject { get; set; }
Child Class
public virtual ParentProject ThisParentProjection { get; set; }
There are no annotations on these properties nor on the Ids of either class.
In the config, I tried to do the mapping in the child.
HasRequired(i => i.ThisParentProject).WithOptional(o => o.ThisChildProject );
What happens is EF tries to map using the primary key of the child and the primary key of the parent.
But I want to use a defined FK in the child and the primary key of the parent
By default EF uses so called Shared Primary Key Association which uses the dependent entity PK as FK to the principal entity.
You can override that behavior by specifying the hidden (shadow) FK name through Map -> MapKey fluent configuration:
HasRequired(e => e.ThisParentProject)
.WithOptional(e => e.ThisChildProject)
.Map(m => m.MapKey("p_p_id"));
Update: Please note the hidden (shadow) word. EF does not support explicit FK property for this type of relationship - there is no HasForeignKey fluent method and putting ForeignKey attribute leads to error. So if you have something like this in your ChildProject class:
[Column("p_p_id")]
public int ThisParentProjectId { get; set; }
I'm afraid the only option is to remove it and work only with navigation properties.
Depending on your property names, you may need to add an ForeignKey attribute, but the standard collection declaration is fine:
http://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx
And here is One-One as requested by your updated question:
http://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx

MVC 4 Entity Framework Optional Foreign Key On User Profile

In my User model I need an optional constraint to an active playlist.
public class User
{
//.. Properties
public int? ActivePlaylistID { get; set; }
public virtual Playlist ActivePlaylist { get; set; }
}
In my database i have ActivePlaylistID set to nullable with a relationship established as: 'ActivePlaylistID' is a foreign key on table 'Playlists', column 'ID'.
In my Playlist model i have:
public class Playlist : BaseModel
{
//.. Properties
public int CreatedByUserID { get; set; }
public virtual User CreatedByUser { get; set; }
}
The CreatedByUserID relationship is established in my database and the model property is set in my controller before saving a new Playlist.
I get the following error at this point in the setup:
Unable to determine the principal end of an association between the types 'SyncFinder.Models.Playlist' and 'SyncFinder.Models.User'.
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
So in my DbEntity i added the following to my model builder:
modelBuilder.Entity<User>()
.HasOptional(x => x.ActivePlaylist)
.WithRequired(x => x.CreatedByUser);
At this point the view loads, but when trying to add a new playlist to my database i get:
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'ID'.
I am not sure how to resolve this constraint issue and the stacktrace isn't providing much detail except that the operation fails when trying to save the playlists repository.
Only one half of the relation was bound in the model binder. I am uncertain of the full support for one-to-one relations in Entity Framework, but for your example completing the relation should work
modelBuilder.Entity<Playlist>().HasKey(e => e.CreatedByUserID);
modelBuilder.Entity<User>()
.HasOptional(x => x.ActivePlaylist)
.WithRequired(x => x.CreatedByUser);

How to model look-up tables with EF DbContext

I am using Entity Framwork DbContexts with a legacy database. I have 2 different properties of an entity that both need to reference the same lookup table, like so:
public class Address
{
public virtual AddressType AnAddressType {get; set;}
public virtual AddressType AnotherAddressType {get; set;}
}
// now here's a LINQ query that just flat won't work:
from a in Addresses select a;
The exception indicates that we tried to include a completely fictional field in the select list -- the field doesn't appear in my POCO or the table -- it looks like it was expected by convention, it's named AnAddressType_AddressType or something close to that
The AddressType entity does not have a corresponding navigation property on it. I cannot seem to get this to work. When I attempt to select data with my LINQ query, I get runtime errors.
Edit
I have other relations that are working (this code is generated from the "stock" DbContext generator). The thing about this one relation that is different is that the lookup table does not have a navigation property back to the main table (the lookup table is used all over the place, so I don't really want to add nav properties from it to everything that uses it). EF seems to be having a problem with that. It's probably a configuration vs. convention thing, and I have inadvertently tripped on some kind of convention problem.
You have a foreign key column name in your legacy database which doesn't follow the EF conventions, for example: The foreign key column in your Addresses table to the AddressTypes table for the AnAddressType relationship has the name MyCrazyAnAddressTypeNumberCodeKeyId.
But EF will assume by convention that the FK column name is: [Nav.property]_[KeyColumn]. For example: If AddressType has a PK with name AddressTypeId EF will assume the FK column has the name AnAddressType_AddressTypeId. Because this doesn't match you get the exception you describe. You must specify the FK column name explicitely to fix this problem:
modelBuilder.Entity<Address>()
.HasRequired(a => a.AnAddressType)
.WithMany()
.Map(c => c.MapKey("MyCrazyAnAddressTypeNumberCodeKeyId"))
.WillCascadeOnDelete(false);
(code snippet partially stolen from Ladislav's answer for convenience)
That's my hypothesis.
Edit
Alternatively you can introduce a foreign key property into your model and tell EF by data annotations that this property is a FK to the corresponding navigation property:
public class Address
{
[ForeignKey("AnAddressType")]
public int MyCrazyAnAddressTypeNumberCodeKeyId {get; set;}
public virtual AddressType AnAddressType {get; set;}
}
If you don't want navigation properties on both sides of the relation you should help EF with fluent mapping so that model is represented correctly:
public class YourContext : DbContext
{
public DbSet<Address> Addresses { get; set; }
public DbSet<AddressType> AddresTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Address>()
.HasRequired(a => a.AnAddressType)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Address>()
.HasRequired(a => a.AnotherAddressType)
.WithMany()
.WillCascadeOnDelete(false);
}
}

Categories

Resources