I use EF Code First approach in an ASP.NET MVC project and I have PK-FK relations on several entities as shown below:
public class Staff
{
public int Id { get; set; }
//Foreign key for Project
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
}
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Staff> Staffs { get; set; }
}
On the other hand, sometimes there is a need to use nullable FK values and in that case I create dummy record as N/A as FK is required property that seems to em ugly :( I know I can easily use nullable value for the related FK property, but I am not sure if it is a good approach or not. And what is the pros and cons using this approach (I know a pros of required FK : Data integrity :)
Secondly, should I use 0 or null value for the nullable FK? Why?
Adding a dummy record is not right, the correct approach here is to use an int? for the foreign key relation, see here:
If the data type of GradeId is nullable integer, then it will create a
null foreign key.
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int? GradeId { get; set; }
public Grade Grade { get; set; }
}
The above code snippet will create a nullable GradeId column in the
database because we have used Nullable<int> type (? is a shortcut for
Nullable<int>)
An alternative approach would be removing ProjectId from the staff (Convention 3 in the above document):
public class Staff
{
public int Id { get; set; }
public virtual Project Project { get; set; }
}
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Staff> Staffs { get; set; }
}
Related
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.
I made a .Net MVC project. First I have created a ClassLibrary with all models and Db Contexts and i used this library in working with database. Now i want to use this class library in another project from another solution. I made a reference to class library but this new project does not recognize nullable Foreign keys (such as "strings" or nullable "Int"). Error ocuurs on runtime.
Class example
[Table("PORT_CALLS")]
public class PORT_CALLS
{
[Key]
public Int64 SID { get; set; }
[ForeignKey("SHIP_CALLS")]
public Int64 SHIP_CALL_SID { get; set; }
public virtual SHIP_CALLS SHIP_CALLS{ get; set; }
[ForeignKey("MNG_OPERATORS")]
public Int64 ? OPERATOR_SID { get; set; }
[JsonIgnore]
public virtual MNG_OPERATORS MNG_OPERATORS { get; set; }
public Decimal ? FORE_DRAUGHT { get; set; }
public Decimal ? MID_SHIP_DRAUGHT { get; set; }
public Decimal ? AFT_DRAUGHT { get; set; }
public Decimal ? AIR_DRAUGHT { get; set; }
public Int16 ? IS_TANKER { get; set; }
}
Error: Entity Type has no key defined Define the key for this EntityType (this error occurs for all nullable foreign keys)
You should apply the [ForeignKey] attribute to the SHIP_CALLS property with the name of the property that offers the foreign key:
[ForeignKey("SHIP_CALL_SID")]
public virtual SHIP_CALLS SHIP_CALLS{ get; set; }
The Application I am trying to make is Hospital Management System. In the EnterPatientDiagnosis Form(screenshot is given at the end), I need to Add The Patient's Diagnosis Information 1st then I need to Add its associated Billing Information. Here,Both of tables Primary Key Column is an Identity Column.
This is a common step in many Systems .But I still couldn't find details on how to achieve it.
One solution I thought of is to insert all the Diagnosis Information keeping the FK_billId attribute Null using Stored Procedure and get the DiagnosisId as output parameter form the Stored Procedure. Then when user will Submit Bill information I will use the BillId and DiagnosisId to update the previously inserted row in the Diagnosis Table. But I do not like this approach for 2 reason:
Firstly, Because it has an extra update query. Since, If I used the DiagnosisId as a foreign key between this 2 Database Tables rather than BillId then there would not be any need for this update query. But I haven't found anywhere giving any rules/precedence on which Key you should use as FK in One-to-One relation.
Secondly, It conflicts with the Entity Class that I have created. I have manually created 2 classes for this 2 tables in my Entity Layer. So, If I want to insert row through entity layer then I would have to give The Billing class a new Property named DiagnosisId which is contradictory with my Database Table Schema.
Here is the 2 classes in Entity Layer:
public class EntityPatientDiagnosis
{
//Diagnosis Id is automatically assigned
public int DiagnosisId { get; set; }
public int PatientId { get; set; }
public string Symptoms { get; set; }
public string DiagnosisProvided { get; set;}
public string AdministeredBy { get; set; }
public DateTime DateofDiagnosis { get; set; }
public string FollowUpRequired { get; set; }
public DateTime DateOfFollowUp { get; set; }
public int BillId { get; set; } //BillId -> Foreign Key
}
public class EntityBilling
{
//BillId -> Primary Key ->set automatically
public int BillId { get; set; }
public int BillAmount { get; set; }
public string CardNumber { get; set; }
public string ModeOfPayment { get; set; }
}
Here is the picture of the ERD of the tables and Web Forms:
You can try a different approach. You can change the relationship between entities PatientDiagnosis and Billing. So, at first you can create a PatientDiagnosis entry and then create a Billing entry for it.
public class EntityPatientDiagnosis
{
//Diagnosis Id is automatically assigned
public int DiagnosisId { get; set; }
public int PatientId { get; set; }
public string Symptoms { get; set; }
public string DiagnosisProvided { get; set;}
public string AdministeredBy { get; set; }
public DateTime DateofDiagnosis { get; set; }
public string FollowUpRequired { get; set; }
public DateTime DateOfFollowUp { get; set; }
}
public class EntityBilling
{
//BillId -> Primary Key ->set automatically
public int BillId { get; set; }
//DiagnosisId -> Foreign Key unique
public int DiagnosisId { get; set; }
public int BillAmount { get; set; }
public string CardNumber { get; set; }
public string ModeOfPayment { get; set; }
}
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; }
}
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool IsInStock { get; set; }
public string ImageUrl { get; set; }
public List<ProductOption> ProductOptions { get; set; }
public virtual Category Category { get; set; }
}
public class ProductOption
{
[Key]
public int Id { get; set; }
public string ProductOptionName { get; set; }
public string ProductOptionDescription { get; set; }
}
Now I know when your using Code First EF, so that the tables are created correctly. You need to do something like this.
modelBuilder.Entity<Product>().HasMany(p => p.ProductOptions).WithMany().Map(m =>
{
m.MapLeftKey("ProductId").MapRightKey("ProductOptionId").ToTable("SelectedProductOptionsInOrderedItem");
});
So....
Does this mean that if I do something like Product.ProductOptions I will be able to access all associated productoptions.
Is this the best way to set it up, or is there another way?
To enable lazy load and EF can create derived proxy types for your collection, that property should be declared this way:
public virtual ICollection<ProductOptions> ProductOptions { get; set; }
That should be enought. Other aspect is the mapping approach that you use. You choose fluent api, i prefer mapping by convention, but that is a matter of personal taste anyway.
Ok, Mapping by Conventions:
Is the ability of EF that from the name of entities and their properties along with their types, to map our model with the underlying data without providing any other information.
for example
public class Customer {
public long CustomerID {get; September;}
public string CustomerName {get; September;}
public Employee AssignedTo {get; September;}
}
With the previous model EF will map database with a table named Customer with:
. CustomerID bigint primary key column
. CustomerName nvarchar column
. Customer_EmployeeID foreign key to Employee table, with the datatype Corresponding to EmployeeID in that table.
You can read more Here