One to one relationship - code first - sql

I'm trying to make one to one relationship. I don't want to use fluent API if it is not necessary. This is what I tried so far:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Person
{
public int Id { get; set; }
[ForeignKey("UserId")]
public UserProfile UserProfile { get; set; }
}
Yes, I know there are few similar questions outhere, but none of them were short and clear. Lot of them also did not work.

It depends a little on what type of table structure you want to achieve. There are various ways of doing this, and there is a good walkthrough for all the options, from Shared Primary Key Assocations to One-to-One Foreign Key Associations in those links. Unfortunately those links make more use of Fluent than Annotations. The samples below use Annotations, as you need.
Shared Primary Key
In theory the Shared Primary Key (horizontal table partitioning, in database terms) is the "correct way". It is also the smallest change you need to do to be able to generate a migration (which will use a Shared Primary Key Association). Note that I would change Person.Id to Person.UserId to better show your intent:
// tested in EF 5 and MVC 4.5.
[Table("UserProfile")]
public class UserProfile {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
[Table("Person")] // not required, added for clarity in sample code
public class Person {
// Note the change of property name to reflect that this is a shared primary key,
// using the UserId column in UserProfile as the Primary Key
[Key]
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }
}
// The generated migration:
public partial class AddTable_Person : DbMigration
{
public override void Up() {
CreateTable(
"dbo.Person",
c => new {
UserId = c.Int(nullable: false),
})
.PrimaryKey(t => t.UserId)
.ForeignKey("dbo.UserProfile", t => t.UserId)
.Index(t => t.UserId);
}
public override void Down(){
DropIndex("dbo.Person", new[] { "UserId" });
DropForeignKey("dbo.Person", "UserId", "dbo.UserProfile");
DropTable("dbo.Person");
}
}
this then gives you, in effect a 1:0-1 relationship between UserProfile (which is mandatory) and People (which is optional, but can have one per person at the most.
If you want to use Id in Person then do the following (the migration will change accordingly):
public class Person {
public int Id { get; set; }
[ForeignKey("Id")]
public UserProfile UserProfile { get; set; }
}
Shared Primary Key with two-way navigation
If you want to navigate from UserProfile to Person you have more work to do. Simply adding public virtual Person Person { get; set; } to UserProfile will give you an error:
Unable to determine the principal end of an association between the types 'Test.Models.UserProfile' and 'Test.Models.Person'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
So, we fix it with [Required] on the Person.UserProfile property (Person requires UserProfile). This gives the same migration as before.
// tested in EF 5 and MVC 4.5.
[Table("UserProfile")]
public class UserProfile {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
[ForeignKey("UserId")]
public virtual Person Person { get; set; }
}
[Table("Person")] // not required, added for clarity in sample code
public class Person {
[Key]
public int UserId { get; set; }
[ForeignKey("UserId")]
[Required]
public virtual UserProfile UserProfile { get; set; }
}
Again, this works if you use Id for Person instead of UserId:
public class Person {
[Key]
public int Id { get; set; }
[ForeignKey("Id")]
[Required]
public virtual UserProfile UserProfile { get; set; }
}

[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual Person Person {get;set;}
}
public class Person
{
public int Id { get; set; }
public int UserProfileUserId { get; set; } //Foreign Key
public virtual UserProfile UserProfile { get; set; }
}

If you want to create a one-to-one relationship the first thing you must clarify is what is the principal and what the dependent entity in this relationship. Can a Person exist without UserProfile or can a UserProfile exist without Person?
Because you have started to apply a [ForeignKey] attribute in Person I am assuming now that Person is the dependent, i.e. it cannot exist without UserProfile.
Correct application of the [ForeignKey] attribute would then be:
public class Person
{
[ForeignKey("UserProfile")]
public int Id { get; set; }
public UserProfile UserProfile { get; set; }
}
I am not sure if that is sufficient because you don't have a Person navigation property in UserProfile. If it doesn't work add this property to UserProfile:
public Person Person { get; set; }
If you don't want to have such a navigation property you can't avoid Fluent API:
modelBuilder.Entity<Person>()
.HasRequired(p => p.UserProfile)
.WithOptional();

Related

How to set up relationship using entity framework core

I have Two Model
public class User: Entity
{
public string Gender { get; set; }
public string Name { get; set; }
}
And
public class CognitoUser : Entity
{
public string UserId { get; set; }
public User User{ get; set; }
public string CognitoName { get; set; }
}
I want to set Cognito.UserId as User.Id . I have written the following which is not working can you please correct me as i dont want to create a model CognitoUser into user model.
modelBuilder.Entity<CognitoUser>(e =>
{
e.ToTable("CognitoUser");
e.HasKey(p => p.UserId);
e.HasOne(x => x.User)
.HasForeignKey<User>(c => c.Id);
});
Primary keys are required in each Entity which is missing in your User Entity.
Using Fluent API is optional. If you set your classes right, Entity Framework will understand what you want to achieve.
Hints:
Use [Key] attribute to specify a property as primary key
Prefered primary key format would be {ClassName}{Id}
Use [DatabaseGenerated(DatabaseGeneratedOption.Identity)] to force database
to automatically generate primary key for you.
You can use Guid as primary key type, it is always unique and hassle-free
Additionally, check out the code below to see how to create a relation.
public class User: Entity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId { get; set; }
public string Gender { get; set; }
public string Name { get; set; }
}
public class CognitoUser: Entity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid CognitoUserId { get; set; }
public string CognitoName { get; set; }
// relation
public Guid UserId { get; set; }
public User User { get; set; }
}
Visualization:

Link two tables with a one-to-one relationship, using the same unique key

I have two tables that need to be linked one to one by the key field email.
When I try to do this, I get an error like this:
Cannot use table 'UserSettings' for entity type 'UserSettings' since it is being used for entity type 'UserSettings' and potentially other entity types, but there is no linking relationship. Add a foreign key to 'UserSettings' on the primary key properties and pointing to the primary key on another entity type mapped to 'UserSettings'.
how I tried to implement it:
public class UserSettingsConfiguration : IEntityTypeConfiguration<UserSettings>
{
public void Configure(EntityTypeBuilder<UserSettings> builder)
{
builder.HasKey(n => n.Email);
builder.HasOne(n => n.User)
.WithOne(u => u.UserSettings)
.HasForeignKey<UserSettings>(k => k.Email)
.HasPrincipalKey<UserSettings>(k => k.Email);
}
}
UserSettings and User entities:
public class User
{
public string Email { get; set; }
public DateTime RegistrationDate { get; set; }
public string Image { get; set; }
public UserSettings UserSettings { get; set; }
}
public class UserSettings
{
public string Email { get; set; }
public int LanguageId { get; set; }
public User User { get; set; }
}

Extend Asp Net Core Identity Relation

In Asp net core (3.1) Identity i want add a many to many relationship between user and TourOperators.
(The concept is that many user can follow many tour operators).
I have the tour operators class:
public class TourOperator
{
public int Id { get; set; }
public ICollection<Follow> Follows { get; set; }
}
I have extended the UserIdentity class:
public class ApplicationUser: IdentityUser
{
public ICollection<Follow> Follow { get; set; }
}
I have the Follow class:
public class Follow
{
public long Id { get; set; }
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int TourOperatorId { get; set; }
public TourOperator TourOperator{ get; set; }
}
After execute the migration, why in the Follow table i have 4 field instead of 3?
I have the following field:
I think that ApplicationUserId couldn't be present
Entity Framework has no way to link the UserId and ApplicationUser properties. So you either need to follow convention, whereby EF can make an educated guess. The simplest option is to rename your string property:
public string ApplicationUserId { get; set; }
Alternatively, you can configure it, for example using an attribute:
[ForeignKey("ApplicationUser"]
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
Or in the OnModelCreating method, for example:
modelBuilder.Entity<Follow>()
.WithOne(f => f.ApplicationUser)
.HasForeignKey("UserId");

How to retrieve the objects participating in a many-to-many relationship?

I have a many-to-many relationship between User(Contributor) and TaskType. I want to assign to a variable only TaskTypes that have the current user among their contributors. Obviously, I can somehow do this using the functionality of the Entity Framework. But how? I use asp.net core 3.
Below I try unsuccessfully to do it:
public IQueryable<TaskType> ContributedTaskTypes
{
get
{
// This code doesn't work!
return _dbContext.TaskTypes.Where(t => t.Contributors.Contains(c => c.UserId == CurrentUserId));
}
}
Below are definitions of all models involved in this relationship:
public class TaskType
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public virtual List<Contribution> Contributors { get; set; }
}
public class Contribution
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int TaskTypeId { get; set; }
public TaskType TaskType { get; set; }
}
public class ApplicationUser : IdentityUser
{
public virtual List<Contribution> ContributedToTaskTypes { get; set; }
}
For those queries it is always easiest to do queries where you can dot to the result.
Here is the query with sql-like syntax
from row in _dbContext.Contribution
where row.UserId == CurrentUserId
select row.TaskType
By selecting row.TaskType instead of just row you get it correct entity.
Is that Contributors property retrieved correctly from DB? if it is not you must call Include() method to load/refer relational referenced entities
_dbContext.TaskTypes.Include(p=>p.Contributors).Where(..
more: https://learn.microsoft.com/en-us/ef/core/querying/related-data
In Addition, if EF Core Table Relation is not correctly defined, you should follow
this instruction: https://www.entityframeworktutorial.net/efcore/configure-many-to-many-relationship-in-ef-core.aspx

EF Core only creates one self-referencing foreign key instead of 2

I am trying to create a model to represent missions in a game. When a mission is completed, another mission is played depending on which team won the mission. For example Mission1, if TeamA wins then you play Mission2, if TeamB wins then you play Mission3.
For each mission I want two self-referencing columns, TeamAWinMission and TeamBWinMission, which hold the foreign key to another mission.
The migrations only seem to recognise a single self-reference in the model.
This will create a column for TeamAWinMission:
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public Mission TeamAWinMission{ get; set; }
}
This will only create a column for TeamBWinMission:
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public Mission TeamAWinMission{ get; set; }
public Mission TeamBWinMission{ get; set; }
}
How can I make the migration generate columns/foreign keys for both of these?
Edit: Was able to resolve this with InverseProperty from https://stackoverflow.com/a/46832490/11575271
It seems that there is one-to-one self-reference relationship in Mission model , you could try to define the [ForeignKey]in your model like below :
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("TeamAWinMission")]
public int? TeamAWinMissionId { get; set; }
public Mission TeamAWinMission { get; set; }
[ForeignKey("TeamBWinMission")]
public int? TeamBWinMissionId { get; set; }
public Mission TeamBWinMission { get; set; }
}
Then use Include attribute to load related data in the controller :
public IActionResult Mission()
{
var result = _context.Missions
.Include(m => m.TeamAWinMission)
.Include(m => m.TeamBWinMission)
.ToList();
return View(result);
}
Reference : https://learn.microsoft.com/en-us/ef/core/querying/related-data