foreign key assignments using code first - c#

I am creating a MVC application and want to generate the database using the Code First approach. I've done this with simple tables, but now that I am introducing Foreign Key relationships, things are getting muddled for me.
I have three classes, each of which will have their own table in the DataBase:
public class Device
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int InternalDeviceID { get; set; }
public int DeviceID { get; set; }
public string DeviceName { get; set; }
public virtual EquipmentDevice EquipmentDevice { get; set; }
}
public class Equipment
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int InternalEquipmentID { get; set; }
public int EquipmentID { get; set; }
public string EquipmentName { get; set; }
public virtual EquipmentDevice EquipmentDevice { get; set; }
}
public class EquipmentDevice
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int InternalEquipmentDeviceID { get; set; }
public virtual ICollection<Equipment> EquipmentID { get; set; }
public virtual ICollection<Device> DeviceID { get; set; }
}
The relationship is that a Equipment may have many Devices, and a Device may belong to many Equipments (a many to many if I'm not mistaken). To organize which Equipments have what Devices associated with them, I am using the EquipmentDevice table, which will just pair their IDs together along with an internal ID for that table.
Based on other examples I have found, I had believed this to be the way in which to code such a situation, where the Many side of a relationship will have an ICollection<> object of the Single side, and the Single side of a relationship will have a virtual object of the Many side (both classes having a ICollection<> of the other for a N:N, or both having a virtual for 1:1)
However, when I create the database, my tables look like this:
Device Table
Equipment Table
EquipmentDevice Table
Am I simply backwards in the way in which I am setting up foreign keys for my tables, or is there another problem I am missing altogether for this type of situation? I couldn't find an exceptionally helpful source of info on this subject. I'm also attaching what the diagram of these tables would look like as well, just to help be more clear.

You're close- the properties on Equipment and Device need to be ICollections. You don't need to define a class for the join table, EF will take care of that for you. Here's what your classes should look like (here's a good reference:)
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
public class Device
{
public Device()
{ this.Equipments = new HashSet<Equipment>();}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int InternalDeviceID { get; set; }
public int DeviceID { get; set; }
public string DeviceName { get; set; }
public virtual ICollection<Equipment> Equipments { get; set; }
}
public class Equipment
{
public Equipment()
{ this.Devices = new HashSet<Device>(); }
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int InternalEquipmentID { get; set; }
public int EquipmentID { get; set; }
public string EquipmentName { get; set; }
public virtual ICollection<Devices> Devices { get; set; }
}

To generate foreign keys, the first thing to do is to create the models.
public class Device
{
[Key]
public int DeviceID { get; set; }
public string DeviceName { get; set; }
public virtual ICollection<Equipment> Equipments { get; set; }
}
public class Equipment
{
[Key]
public int EquipmentID { get; set; }
public string EquipmentName { get; set; }
public virtual ICollection<Device> Devices { get; set; }
}
Then you generate and compile the driver for each of the models.
By generating the first model, creates the "Context".
After loading the project and check if you have access to the drivers created.
Now checks the database. The table has two foreign keys in the new table is automatically created.

Related

Retrieve Entity Framework Foreign Key Relationship Models automatically

I am working on a Restaurant Application. I have a restaurant model and a table model.
namespace Restaurant.Models
{
[Table("Restaurant")]
public class RestaurantModel
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("telephone_number")]
public int TelephoneNumber { get; set; }
[NotMapped]
public List<TableModel> Tables;
public RestaurantModel()
{
Tables = new List<TableModel>();
}
}
}
namespace Restaurant.Models
{
[Table("Table")]
public class TableModel
{
[Key]
[Column("id")]
public int Id { get; set; }
[ForeignKey("restaurant_id")]
[Required] [NotNull]
public int RestaurantId { get; set; }
[Column("available_seats")]
public int AvailableSeats { get; set; }
[Column("is_indoors")]
public bool IsIndoors { get; set; }
}
}
I have a dependency between Restaurant and Table:
Here are the columns and keys that Entity Framework has created for me via my context:
Lastly, here's my Context class:
namespace Restaurant.Data
{
public class RestaurantContext : DbContext
{
public RestaurantContext(DbContextOptions<RestaurantContext> options) : base(options)
{
}
public DbSet<RestaurantModel> Restaurants { get; set; }
public DbSet<TableModel> Tables { get; set; }
public DbSet<GuestModel> Guests { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RestaurantModel>().ToTable("Restaurant");
modelBuilder.Entity<TableModel>().ToTable("Table");
modelBuilder.Entity<GuestModel>().ToTable("Guest");
modelBuilder.Entity<TableModel>()
.HasOne<RestaurantModel>();
}
}
}
When I retrieve a restaurant, I want the corresponding tables to be retrieved inside of the TableModel List. Currently, when I retrieve a Restaurant, it will not retrieve any corresponding Tables. This makes sense to me, as I have not properly connected the relationship for EntityFramework to recognize it. I have tried to look online how to do it, consulting guides on setting up Foreign Key relationships and such. I cannot find the information I am looking for, due to a lack of basic knowledge. The answers I can find do not make sense to me because I do not understand what they are doing or how they are doing it.
Could anyone point me in the right direction or tell me what I am doing wrong?
add relations to your classes
[Table("Restaurant")]
public class Restaurant
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("telephone_number")]
public int TelephoneNumber { get; set; }
public virtual ICollection<Table> Tables { get; set; }
}
}
[Table("Table")]
public class Table
{
[Key]
[Column("id")]
public int Id { get; set; }
public int? RestaurantId { get; set; }
public virtual Restourant Restaurant { get; set; }
[Column("available_seats")]
public int AvailableSeats { get; set; }
[Column("is_indoors")]
public bool IsIndoors { get; set; }
}
}
and since you are using Net core 5+ I don' t think that you any navigation attributes or fluent APIs
Delete old migration folde and make a clean migration to db
after this you can try this code for test
var restourant= context.Restourants.Include(r=> r.Tables).FirstOrDefault(r=>r.Id==id);
it should return a restourant with a list of tables

Foreign keys with Code First in a Web API

Very simple question but it looks like I'm trying to implement a simple one-to-many relationship between two models.
So far, what I have is this :
A product class :
public class Products
{
public long Id { get; set; }
public long Code { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public Boolean Reviewed { get; set; }
[ForeignKey("BundleId")]
public int BundleId { get; set; }
public virtual Bundles Bundle { get; set; }
}
And the Defects class looks like this:
public class Defects
{
public long Id { get; set; }
public String Description { get; set; }
public String Picture { get; set; }
public DateTime DateCreated { get; set; }
[ForeignKey("ProductId")]
public int ProductId { get; set; }
public Products Product { get; set; }
[ForeignKey("UserId")]
public int UserId { get; set; }
public virtual Users User { get; set; }
}
I thought that I did not need to add an ICollection of Defects to the Products class because it's a "simple" one-to-many relationship and this code would be enought to be able to get the ID of a Product in the Defects class (I don't need more).
But, of course I get an exception :
The property 'ProductId' 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
How may I solve that issue ?
I might be doing someting wrong with my two foreign keys but since I declared the name of the foreign keys, I assumed it would have been enought.
Thanks for your attention.
This is what your relationship can be distilled to.
Please note that ForeignKey annotation is applied to navigation property with the name of the key property.
If you build one-to-many relationship - then ICollection is absolutely necessary. Otherwise where's the "many"
public class Products
{
public int Id { get; set; }
public virtual List<Defects> Bundle { get; set; }
}
public class Defects
{
public long Id { get; set; }
public int ProductId { get; set; }
[ForeignKey("ProductId")]
public Products Product { get; set; }
}
FK can also be applied to the key property. But in that case you have to put the name of the instance of related class there
public class Defects
{
public long Id { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
public Products Product { get; set; }
}

EF relation one-to-two

I'm having some troubles when saving in the database a model with a little complex relationship.
The UML of the classes is:
The classes definition are:
public abstract class EntityBase
{
public virtual Guid Id { get; set; }
}
public class LoanRequest : EntityBase
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public virtual Applicant Applicant1 { get; set; }
public virtual Applicant Applicant2 { get; set; }
}
public class Applicant
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid LoanRequestId { get; set; }
[ForeignKey("LoanRequestId")]
public virtual LoanRequest LoanRequest { get; set; }
public virtual ICollection<MonthlyIncome> Incomes { get; set; }
}
public class MonthlyIncome
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid ApplicantId { get; set; }
[ForeignKey("ApplicantId")]
public virtual Applicant Applicant { get; set; }
}
I'm able to run a migration and looking into the database the tables and columns created by the framework seems fine to me. But, when saving an exception happens. The exception is:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values
I've been searching for a solution on the internet and I can't see where my problem is. Any suggestions? Thanks!
After several tryings I was able to find a solution. Changing the applicant definition to:
public class Applicant
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public virtual ICollection<MonthlyIncome> Incomes { get; set; }
}
was all I needed

ef modelBuilder force foreignkey a or foreignkey b

OK.
Background.
I was initially trying to make EF models along the lines of:
public class Person
{
[Key]
public Guid ID { get; set; }
public string Name { get; set; }
public Guid PhoneID { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
}
public class Org
{
[Key]
public Guid ID { get; set; }
public string Name { get; set; }
public Guid PhoneID { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
}
public class Phone
{
[Key]
public Guid ID { get; set; }
public string Number { get; set; }
public Guid EntityID { get; set; }
[ForeignKey("EntityID")]
public virtual User User { get; set; }
[ForeignKey("EntityID")]
public virtual Org Org { get; set; }
}
But I now (mostly) realize that this causes an issue with Foreign Key relationship integrity in SQL Server. So to correct this, I altered the Phone class to:
public class Phone
{
[Key]
public Guid ID { get; set; }
public string Number { get; set; }
public Guid? PersonID { get; set; }
public Guid? OrgID { get; set; }
[ForeignKey("PersonID")]
public virtual User User { get; set; }
[ForeignKey("OrgID")]
public virtual Org Org { get; set; }
}
Question.
How can I enforce / map a rule using modelbuilder / Fluent API to ensure a Phone object has either a PersonID or an OrgID?
Edit:
I do realise that this is creating a data integrity rule that I would be unable to do if I was designing the database in SQL Server, but to me it seems that EF has the potential flexibility to take database design to the next level.
I see EF (Code-First especially) as Microsoft's next big leap in their software development strategy. IMHO this is as big a leap as the introduction of .Net (Now there is a statement that should generate some debate!), that being moving the database design away from the database itself & integrating it in with the managed code.

Entity Framework Code First associations/FK issues and assumptions/defaults

I am very confused by the way Entity Framework is able to pick up on relationships between entities. I have a few questions on this.
In a simple test application, I have a table of people, and a table of notes, and a table of picture assets.
There are many pictures, each is owned by a person (a person can own more than one).
There are many notes, each is owned by a person (a person can own more than one).
and finally a person has a logo which is a picture.
.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
When I try to run, I get the error "Unable to determine the principal end of an association between the types ....".
(If I drop the Person.logo field, compile/run, then manually add it in to SQL, along with the FK relationship, it works 100% as expected... I just can't seem to work out how to set this from EF itself).
Can you help with the error? I have read quite a few answers here, but, I just can't seem to adapt it to fix my error.
However, now I have a one to many and a many to one (I don't think this is classed as many to many?), I just don't understand how to create people objects, where a FK is non nullable and the FK doesn't exist yet.
A solution I found, which I really don't like is to make the person.picture column nullable, then create a person, followed by creating a picture, then assign a picture to the person object... but, ideally I don't want it nullable. There should always be a picture.
Old answer assuming your 1:1 relation:
Scroll down for an answer on your clarification.
Here are two ways to achieve this, one way is to remove the cross reference navigation by applying 1:N.
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Picture Logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
}
This is a really cheap solution, you probably don't want more notes or pictures than persons...
So, use Data Annotations to instruct what the foreign key is, this will keep navigation and 1:1.
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Picture Logo { get; set; }
}
public class Note
{
[Key, ForeignKey("Person")]
public int OwnerID { get; set; }
public string Text { get; set; }
public virtual Person Owner { get; set; }
}
public class Picture
{
[Key, ForeignKey("Person")]
public virtual int OwnerId { get; set; }
public string Path { get; set; }
public virtual Person Owner { get; set; }
}
As you can see, because of the 1:1 relationship the picture and note will use the same ID. If your key is not named ID, you need to add a KeyAttribute, in this case we also add the ForeignKeyAttribute.
Please note that you should use virtual so that things load only when you request them, you most likely don't want the database to query the Picture information if you only want the name of the Person.
Association properties that are marked as virtual will by default be lazy-loaded. What this means is that if you retrieve a Product entity, its Category information will not be retrieved from the database until you access its Category property (or unless you explicitly indicate that the Category data should be retrieved when you write your LINQ query to retrieve the Product object).
— Scott Gu - Using EF Code First with an existing database
New answer regarding your clarification:
Bulding further on the Foreign Keys by going back to a 1:N structure with ICollection<T>; once you obtain your person like var boss = Person.Find(BossID) you can then access boss.Pictures which will have the various pictures. You can also assign boss.Logo to be one of those pictures.
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Picture Logo { get; set; }
public ICollection<Picture> Pictures { get; set; }
public ICollection<Note> Notes { get; set; }
}
public class Note
{
public int ID { get; set; }
[ForeignKey("Person")]
public int OwnerID { get; set; }
public string Text { get; set; }
public virtual Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
[ForeignKey("Person")]
public virtual int OwnerId { get; set; }
public string Path { get; set; }
public virtual Person Owner { get; set; }
}
You might be interested in hinting your data members using DataAnnotations as well as taking a look to the current Conventions for Code First.
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Picture Logo { get; set; }
public ICollection<Picture> Pictures { get; set; }
public ICollection<Note> Notes { get; set; }
}
public class Note
{
public int ID { get; set; }
public int OwnerID { get; set; }
public string Text { get; set; }
[ForeignKey("OwnerId")]
public virtual Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public virtual int OwnerId { get; set; }
public string Path { get; set; }
[ForeignKey("OwnerId")]
public virtual Person Owner { get; set; }
}
The above code is right, after trying the wrong code several times, I got the right code.
Hope it is usefull for you.

Categories