Maybe one of the problems is that you immediately start to think in tables and IDs and Foreign Keys and so on. It is usually much easier if you think in objects and their relation towards each other.
It is a bit difficult to read your picture. From the text I gather you have the notion of an account, and apparently there are three types of accounts: normal, trainers and gym owners. Is there a difference between those accounts? Do gym owners have properties that normal users don't have? or is it just some kind of identification.
From your drawing it seems that a normal user has a user name, a password and an access level. Do trainers also have these properties?
If so, then in normal object oriented design, you would say it is an aggregation: trainers, normal people and gym owners all HAVE a user name, a password and an access level. aggregation is also quite often called composition. There is a small difference, but for this discussion it is not important.
Instead of aggregation/composition (the object HAs an account) you could also think of inheritance: the object IS an account: trainer accounts, gym owner accounts and normal accounts are objects that are all different kinds of Accounts, with a user name, a password and an access level.
In this example there is not a big difference between aggregation (a trainer has an account), or inheritance (a trainer account is an account). Usually the advise is to favor aggregation over inheritance enter link description here
Be aware: if the only difference between your three accounts is a value of a property, then consider making them all the same type, with a property AccountType which gives you information on whether it is a normal account, a gym owners account or a trainer account.
If the only difference between those three is the access level, then don't create an account type. The access level would serve as account type.
So far, I'm only talking object oriented design. Once you've designed your objects you might think of putting them in a database.
Let's assume a trainer, a gym owner and normal people really are different things, with different properties. Your class would look similar to:
public enum AccessLevel
{
None,
...,
FullAccess,
}
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public .. some trainer properties {get; set;}
// a trainer has an account
public Account Account {get; set;}
}
public class GymOwner
{
public ... some gym owner properties {get; set;}
public Account Account {get; set;}
}
If your design would be like this, you'd see that at least you'd have a gym owner table and a trainer table. But what to do with the Account.
One solution would be to put an account in a separate table and add something to the trainer and the gym owner, so that if you have a trainer you know which item in the account table belongs to this specific trainer.
This method is usually not used in this design. If object A and object B have a one to one relation: one A belongs to one B, and one B belongs to one A, then in fact they can be put into one table. If you use entity framework and you had defined the classes above, the account properties would be added as columns to the trainer table and as columns to the gym owner table. The advantage is that fetching the information about a trainer is faster, as this would involve accessing only one table.
About Primary Keys. Every element in every table should have exactly one primary key. This is the property that identifies the object. If you have the key, you have very fast access to all other properties. To make it easier for you to understand the role of Account I've left out the Id in my original code.
But now that we've decided that it would be best to let Trainers and Gym owners HAVE an account, there would be two tabled: Trainers and GymOwners. Those are the only elements that have a primary key. The classes would be like:
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public Account Account {get; set;}
}
public class GymOwner
{
public int Id {get; set;}
public ... some gym owner properties {get; set;}
public Account Account {get; set;}
}
Note that there is no table for Account, so Account does not need a key.
So when do you need a foreign key
If a Trainer would not have one account, but several accounts, maybe a lot of accounts, usually called a collection of accounts, then we can't save the accounts as columns inside the Trainer table anymore. We will have to put all accounts of the Trainer in a separate table and tell each account to which trainer it belongs. The account gets a foreign key to the primary key of the trainer to whom he belongs.
In database terms, this is called a one-to-many relation: one trainer has many accounts.
For entity framework the classes would be like:
public class Account
{
public int Id {Get; set;}
public int TrainerId {get; set;}
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public virtual ICollection<Account> Accounts {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Trainer> Trainers {get; set;}
public DbSet<Account> Accounts {get; set;}
}
Note that because Account has its own table it has gotten a primary key.
Beside it has also gotten a foreign key in property TrainerId.
I've also added a class derived from DbContext to access the tables. Until know I have only tables with Trainers and tables with Accounts. Each table is called a DbSet, the type of DbSet informs entity framework about the columns in the table.
Because Trainer has a virtual ICollection of Accounts, entity framework knows that there is a one-to-many relation between Trainer and Account, and because of the namer TrainerId, entity framework knows that TrainerId is the foreign key to the primary key of the Trainer that the account belongs to.
So if you have the id of a trainer, you get all accounts of this trainer using the following Linq statements:
int trainerId = GetMyTrainerId();
IEnumerable<Account> accountsOfTrainer = dbContext.Accounts
.Where(account => account.TrainerId == trainerId);
From the collection of Accounts, take all records where property TrainerId equals trainerId
So now you know how to design a primary key and let a foreign key point to it.
But what about gym owners? If a gym owner only has one account, just let it HAVE the one account (composition). But what if your gym owner also has a collection of accounts.
If you just add the gym owner accounts to the accounts table, then you get in trouble. To which Id does the foreign key point? To primary key in the Trainer table or in the Gym Owners table?
The safest way would be to create GymOwnersAccount and a TrainersAccount. They will each have their own table with a foreign key. A GymOwnersAccount will have a foreign key to the GymOwners table and a TrainersAccount will have a foreign key to the trainers table.
Here you could also decide to let the GymOwners account HAVE an account, but it seems more natural to say the a GymOwners account IS a special type of Account, and thus derives from Account.
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class GymOwnerAccount : Account
{
public int Id {get; set;}
public int GymOwnerId {get; set;}
}
public class TrainerAccount : Account
{
public int Id {get; set;}
public int TrainerId {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public virtual ICollection<Account> Accounts {get; set;}
}
public class GymOwner
{
public int Id {get; set;}
public ... some gym owner properties {get; set;}
public virtual ICollection<Account> Accounts {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Trainer> Trainers {get; set;}
public DbSet<Account> GymOwnerAccounts {get; set;}
public DbSet<Account> TrainerAccounts {get; set;}
}
There are other solutions possible, for instance give each account two foreign keys, one to gym owners and one to trainers, where you always need to set one foreign key to 0. It's easy to see that this might lead to maintenance problems, while it doesn't give you any benefits, so I would advice to stick with separate tables for GymOwnerAccounts and TrainerAccounts.
Now that I've entered the realm of inheritance in database there is a wealth of items you can configure. An article that helped me a lot to understand entity framework, and how classes, inheritance, composition is transferred to databases is Entity Framework Code First