Entity Framework relations questions - c#

I have three classes
public class SPR
{
public int ID { get; set; }
public string SubmittedBy { get; set; }
public virtual ICollection<SPRItem> AllItems { get; set; }
}
public class SPRItem
{
[Key]
public int ID { get; set; }
public string manufacturer { get; set; }
[ForeignKey("SPRItemDetails")]
public virtual SPRItemDetails ItemDetails { get; set; }
public string requestedMinimumQuantity { get; set; }
public virtual SPR SPR { get; set; }
}
public class SPRItemDetails
{
public int ID { get; set; }
public string ItemNumber { get; set; }
public virtual SPRItem SPRItem { get; set; }
}
So the SPR class has a collection of SPRItem and which has the ItemDetails object.
I have a web API method which maps the data to the SPR object and fills in the SPRItem list and ItemDetails object. But whenever I am trying to save it using Entity Framework code first I am getting this error
{"Message":"An error has occurred.","ExceptionMessage":"Unable to determine the principal end of an association between the types 'SharePoint.MultiSPR.Service.Models.SPRItemDetails' and 'SharePoint.MultiSPR.Service.Models.SPRItem'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
This is my Context
public System.Data.Entity.DbSet<SharePoint.MultiSPR.Service.Models.SPR> SPRs { get; set; }
public System.Data.Entity.DbSet<SharePoint.MultiSPR.Service.Models.SPRItem> SPRItem { get; set; }
public System.Data.Entity.DbSet<SharePoint.MultiSPR.Service.Models.SPRItemDetails> SPRItemDetails { get; set; }
Can someone please tell me how to configure the relations correctly.
Thanks

In a 1:1 relation you always have to indicate the principal and the dependent entity. The principal entity is the one that is most independent of the other, in this case SPRItem, presumably.
Next thing to decide is whether the relationship should be optional or required. I think, judging by the entity names, an SPRItemDetails will never exist without an SPRItem, so the relationship is 1:0..1 (not 0..1:0..1). Here's how to configure that:
modelBuilder.Entity<SPRItem>()
.HasOptional(si => si.ItemDetails)
.WithRequired(id => id.SPRItem);
This creates (or requires) an SPRItemDetails table having a primary key that's also a foreign key to SPRItem.

Related

EF Core 2.2.6: Unable to map 2 foreign keys to the same table

I am having issues trying to map two fields that are foreign keys into the same table. The use case is for a modifier and creator. My class already has the Ids, and then I wanted to add the full User object as virtual.
I am using a base class so that each of my tables have the same audit fields:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
[ForeignKey("CreatedById")]
public virtual User CreatedByUser { get; set; }
[ForeignKey("ModifiedById")]
public virtual User ModifiedByUser { get; set; }
}
The child class is very simple:
public class CircleUserSubscription : Entity
{
[Required]
public long Id { get; set; }
public long SponsorUserId { get; set; }
[ForeignKey("SponsorUserId")]
public virtual User User { get; set; }
public long TestId { get; set; }
[ForeignKey("TestId")]
public virtual User Test { get; set; }
}
This is a standard junction table.
When I try to generate the migration, I am getting errors that I don't understand fully.
Unable to determine the relationship represented by navigation property 'CircleUserSubscription.User' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I tried what this answer had, but the code is basically the same: https://entityframeworkcore.com/knowledge-base/54418186/ef-core-2-2---two-foreign-keys-to-same-table
An inverse property doesn't make sense since every table will have a reference to the user table.
For reference, here is the User entity:
public class User : Entity
{
public long Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I am hoping you all can help me out, TIA :)
EDIT: One thing to note, all of this worked fine when the entity class was as follows:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
}
It was only after I added the entity that things went awry.

EF Core - Shared Primary Key Associations via Fluent API

I have an 'User'class contains two 'Address' properties reference to 'Address' entity, and there is another class - 'Shipment' also associate with 'Address'.
How i can use fluent api on ef core to build a correct relation between entities.
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
...
}
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual Address DefaultAddress { get; set; }
public virtual Address BillingAddress { get; set; }
}
public class Shipment
{
public int ShipmentId { get; set; }
public virtual Address DeliveryAddress { get; set; }
}
There is not need to declare explicit configuration, the EF will do everything without any help.
I prepared a working example with and without fluent configuration, you can check it out here. Just switch between commits to see the difference.
As you can notice, there is no differences in generated migration.

Foreign Key creation in EF Core code first

I have 2 models:
public class GaOrgOrders
{
public virtual Guid Id { get; set; }
[ForeignKey("GaOrganizations")]
public virtual Guid OrgId { get; set; }
[ForeignKey("GaApps")]
public virtual Guid AppId { get; set; }
[ForeignKey("GaOrgUserOrganizations")]
public virtual Guid OrgUserId { get; set; }
[ForeignKey("GaServicesTariffs")]
public virtual Guid ServiceTariffId { get; set; }
public virtual bool IsTemporary { get; set; }
public virtual Apps GaApps { get; set; }
public virtual Organizations GaOrganizations { get; set; }
public virtual OrgUserOrganizations GaOrgUserOrganizations { get; set; }
public virtual GaServicesTariffs GaServicesTariffs { get; set; }
}
and
public class GaOrganizations
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual bool IsDeleted { get; set; }
}
When I try to update database and add these tables I get an error:
Introducing FOREIGN KEY constraint 'FK_GaOrgOrders_GaOrganizations_OrgId' on table 'GaOrgOrders' 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 or index. See previous errors.
If I remove "ForeignKey" attribute from OrgId property - error disappears, but it creates in database a second field(GaOrganizationsId) and makes it a FK. Other Keys work fine. What's wrong with OrgId?
EF doesn't understand the relationship between the two entities so you might need to clarify using fluent API in your "OnModelCreating" method.
Using something like
modelBuilder.Entity<ClassB>().HasRequired(x => x.ClassA).WithOptional(x => x.ClassB);
where ClassA and ClassB are my entities having a 1 to 0 or 1 relationship between them.

EF 6 how to set two foreign keys to same table

I have a table UserForms that has two foreign keys to a Countries table, but on creating my controller and create view (for the UserForms model) the two fields linking to the foreign keys do not appear. What should I do to sort this problem? Below are the two models:
public class UserForms
{
public int Id { get; set; }
public string FullNames { get; set; }
public Countries IndividualsCountry { get; set; }
public Countries BusinessCountry { get; set; }
}
public class Countries
{
public Countries()
{
this.STRBusinessCountry = new HashSet<UserForms>();
this.STRIndividualsCountry = new HashSet<UserForms>();
}
public int Id { get; set; }
public string NameOfCountry { get; set; }
[InverseProperty("IndividualsCountry")]
public virtual ICollection<UserForm> STRIndividualsCountry { get; set; }
[InverseProperty("BusinessCountry")]
public virtual ICollection<UserForm> STRBusinessCountry { get; set; }
}
The comment left by #T.Glatzer is correct. You should expose foreign key properties on your dependent entities:
public class UserForms
{
public int Id { get; set; }
public string FullNames { get; set; }
public int IndividualsCountryId { get; set; }
[ForeignKey("IndividualsCountryId")]
public virtual Countries IndividualsCountry { get; set; }
public int BusinessCountryId { get; set; }
[ForeignKey("BusinessCountryId")]
public virtual Countries BusinessCountry { get; set; }
}
Here I used int, but if either of these navigation properties are optional, you would just substitute int? or System.Nullable<int> instead (which will create an int NULL column in the database rather than an int NOT NULL).
Although EF does not require you to expose navigation properties, it is generally a good practice to. Trust me. It will help you avoid unexpected exceptions later on. In fact, some EF exception messages actually recommend exposing foreign key properties on the entity classes to help EF better figure out how to map relationships. Here is an example of one such exception. Note "Additional Information" section:
{"The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.DependentTable_dbo.PrincipalTable_Id". The conflict
occurred in database "DatabaseName", table "dbo.PrincipalTable", column
'Id'. The statement has been terminated."}
Additional information: An error occurred while saving entities that
do not expose foreign key properties for their relationships. The
EntityEntries property will return null because a single entity cannot
be identified as the source of the exception. Handling of exceptions
while saving can be made easier by exposing foreign key properties in
your entity types. See the InnerException for details.
#danludwig thanks for expounding #T.Glatzer answer this has worked for me! thank you. my final code that is now working is
public class UserForms
{
public int Id { get; set; }
public string FullNames { get; set; }
[ForeignKey("IndividualsCountry")]
public int? IndividualsCountryId { get; set; }
[ForeignKey("BusinessCountry")]
public int? BusinessCountryId { get; set; }
public virtual Countries IndividualsCountry { get; set; }
public virtual Countries BusinessCountry { get; set; }
}
public class Countries
{
public Countries()
{
this.STRBusinessCountry = new HashSet<UserForms>();
this.STRIndividualsCountry = new HashSet<UserForms>();
}
public int Id { get; set; }
public string NameOfCountry { get; set; }
[InverseProperty("IndividualsCountry")]
public virtual ICollection<UserForms> STRIndividualsCountry { get; set; }
[InverseProperty("BusinessCountry")]
public virtual ICollection<UserForms> STRBusinessCountry { get; set; }
}

Entity Framework --Unable to determine the principal end of an association between the types?

I am creating entities using code first schema but when run the application its generating exception
Unable to determine the principal end of an association between the types 'WebApplication1.Models.DateOfProject' and 'WebApplication1.Models.Projects'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
My scenario is to implement 1.1 relation between Projects and DateOfProjects such that 1 project has 1 dateOfProject.
My code is
public class Projects
{
[Key()]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ProjectId { get; set; }
public string ProjectTitle { get; set; }
public string ProjectDescriptions { get; set; }
public DateOfProject DateOfProject { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<TaskSheetManagement> TaskSheetManagement { get; set; }
}
public class DateOfProject
{
public int DateOfProjectId { get; set; }
[ForeignKey("ProjectId")]
public Projects Projects { get; set; }
public DateTime DateOfProjectCreation { get; set; }
public Nullable<DateTime> ExpectedCompletionDate { get; set; }
public Nullable<DateTime> ProjectCompletionDate { get; set; }
}
and inside DbContextClass inOnModelCreating function
modelBuilder.Entity<Projects>().HasKey(pk => pk.ProjectId).ToTable("Projects");
modelBuilder.Entity<DateOfProject>().HasKey(pk => pk.DateOfProjectId).ToTable("DateOfProject");
modelBuilder.Entity<Projects>().HasRequired(p => p.DateOfProject).WithRequiredPrincipal(c => c.Projects);
I could not just resolve that problem.
If you want a 1 : 1 relationship you have to remove the [ForeignKey("ProjectId")] Attribute. For 1: 1 relationships the Primary Key is used. If you want a separate foreign Key column it is a 1 : * relationship.

Categories