Entity Framework 5 using multiple relationships between two POCOs - entity-framework

I'm having issues applying multiple relationships (or possibly foreignkey) on two POCO objects. I've got the first relationship many-to-many working and when the database is created it creates the three tables (Projects, Users and ProjectsUsers) needed for the relationship.
Code so far:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? CompletionDate { get; set; }
public bool Deleted { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public User()
{
Name = new Name();
}
public int UserId { get; set; }
public string LoginId { get; set; }
public string Password { get; set; }
public Name Name { get; set; }
public ICollection<Project> ManagedProjects { get; set; }
}
public class ProjectConfiguration : EntityTypeConfiguration<Project>
{
public ProjectConfiguration()
{
HasMany(x => x.Users)
.WithMany(x => x.ManagedProjects);
}
}
public UserConfiguration()
{
HasMany(x => x.ManagedProjects)
.WithMany(x => x.Users);
}
Now I want to add an optional one-to-one relationship of Project.ManagingUser -> User. However, I can't seem to figure out how to indicate this in the configuration.
Code for what I think is needed:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? CompletionDate { get; set; }
public bool Deleted { get; set; }
public int? ManagingUserId { get; set; }
public User ManagingUser { get; set; }
public ICollection<User> Users { get; set; }
}
I don't think the User object needs to change.
This shows my last attempt on mapping the new relationship:
public ProjectConfiguration()
{
HasMany(p => p.Users)
.WithMany(u => u.Projects);
this.HasOptional(p => p.ManagingUser)
.WithOptionalDependent()
.Map(m=>m.MapKey("ManagingUserId"))
.WillCascadeOnDelete(false);
}
What is happening when the database is created, I now end up with only two tables (Projects and Users). And it looks like it is only trying to setup the one-to-one relationship.
Can someone tell me what I'm missing?
Richard I've not changed the UserConfiguration and below is the DbContext:
public class MyDbContext : DbContext
{
public MyDbContext() : base(Properties.Settings.Default.ConnectionString)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Project> Projects { get; set; }
}

You probably want WithMany instead of WithOptionalDependent - it's a one:many relationship, not a one:one.
HasOptional(p => p.ManagingUser)
.WithMany()
.HasForeignKey(m => m.ManagingUserId)
.WillCascadeOnDelete(false);
EDIT
I think you're missing the OnModelCreating override from the DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ProjectConfiguration());
modelBuilder.Configurations.Add(new UserConfiguration());
}

Related

Entity Framework database-first : how to add property to a model that will reference itself?

I am trying to create a Product table that will have a list of SubstitutionProducts that should reference other Product from the same table.
Model example :
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsMissing { get; set; }
public ICollection<Product> SubstitutionProducts { get; set; }
}
It is better to include the parent ID in your model:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsMissing { get; set; }
public int? ParentId { get; set; }
public Product Parent { get; set; }
public ICollection<Product> SubStitutionProducts { get; set; }
}
This is how to configure DB schema via overriding OnModelCreatingmethod of your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasOne(x => x.Parent)
.WithMany(x => x.SubStitutionProducts)
.HasForeignKey(x => x.ParentId).IsRequired(false);
}

EF Core Many-to-Many Relation Table Naming [duplicate]

This question already has answers here:
Change name of generated Join table ( Many to Many ) - EF Core 5
(2 answers)
Closed 1 year ago.
Does EF Core provide a way of naming the many-to-many relations mapping to database tables ?
In a code-first pattern, I have the following 2 Entities:
[Table("Prefix.Users")]
public class User
{
public int ID { get; set; }
public IEnumerable<Role> Roles { get; set; }
}
[Table("Prefix.Roles")]
public class Role
{
public int ID { get; set; }
public IEnumerable<User> Users { get; set; }
}
I've skipped the detailed Entity structure here. The ID properties in User & Role are keys (Database generated Identity)
User and Role entities share a many-to-many relationship.
EF Core generates a third table in Database with Table name UsersRoles
Is there a way I can add a prefix to the 3rd table name so it becomes Prefix.UsersRoles without manually adding a third Entity UserRoles that maps User and Role and giving it the desired name with Prefix
Use fluent API instead of using data annotations
Your model classes should be like this.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
}
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
}
public class UserRole
{
public int UserId { get; set; }
public int RoleId { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
Your fluent api configuration classes like be this
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("User");
builder.HasKey(x => x.Id);
}
}
public class RoleConfiguration : IEntityTypeConfiguration<Role>
{
public void Configure(EntityTypeBuilder<Role> builder)
{
builder.ToTable("Role");
builder.HasKey(x => x.Id);
}
}
public class UserRoleConfiguration : IEntityTypeConfiguration<UserRole>
{
public void Configure(EntityTypeBuilder<UserRole> builder)
{
builder.ToTable("UserRole");
builder.HasKey(x => new { x.UserId, x.RoleId });
builder
.HasOne<Role>(s => s.Role)
.WithMany(r => r.UserRoles)
.HasForeignKey(s => s.RoleId).OnDelete(DeleteBehavior.Restrict);
builder
.HasOne<User>(s => s.User)
.WithMany(r => r.UserRoles)
.HasForeignKey(s => s.UserId).OnDelete(DeleteBehavior.Restrict);
}
}
Your DbContext class should be like this
public class MyDbContext : DbContext
{
public EEGDbContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(#"Server=xxxx;Database=DB;User Id=sa;Password=xxxxx;");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new RoleConfiguration());
modelBuilder.ApplyConfiguration(new UserRoleConfiguration());
base.OnModelCreating(modelBuilder);
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
}

Integrating SimpleMembership with Entity Framework

I'm trying to integrate the SimpleMembership tables with the rest of my Object Model - to manage all the entities from a single database and context.
Up to now the best recipe I've found for manually spinning up the SM tables (the entry point to combine SimpleMember with the rest of my Object Model) is found here. But, as cited in the comments section there are a couple mistakes in the code sample provided. The comments attempt to provide corrections but, due to formatted, really hard to follow.
I'm 80% the way there but getting stuck with the Foreign Key generation for the Membership table. Does the code within OnModelCreating block belong in the MyDbContext class? I'm getting a compile error on the .WithMany(u => u.Members) line.
Membership.cs
[Table("webpages_Membership")]
public class Membership
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public DateTime? CreateDate { get; set; }
[StringLength(128)]
public string ConfirmationToken { get; set; }
public bool? IsConfirmed { get; set; }
public DateTime? LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
[Required, StringLength(128)]
public string Password { get; set; }
public DateTime? PasswordChangedDate { get; set; }
[Required, StringLength(128)]
public string PasswordSalt { get; set; }
[StringLength(128)]
public string PasswordVerificationToken { get; set; }
public DateTime? PasswordVerificationTokenExpirationDate { get; set; }
<strike>public virtual ICollection<Role> Roles { get; set; }</strike>
EDIT: Originally I added the line above to remove a compiler complaint in the extraneous code block below. Removing this attempt to create a FK to Roles will align the rest of this code so that these model classes create a Migration that generates tables for SM.
OAuthMembership.cs
[Table("webpages_OAuthMembership")]
public class OAuthMembership
{
[Key, Column(Order = 0), StringLength(30)]
public string Provider { get; set; }
[Key, Column(Order = 1), StringLength(100)]
public string ProviderUserId { get; set; }
public int UserId { get; set; }
}
Role.cs
[Table("webpages_Roles")]
public class Role
{
[Key]
public int RoleId { get; set; }
[StringLength(256)]
public string RoleName { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
UserProfile.cs
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
MyDbContext.cs
public MyDbContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Membership> Membership { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<OAuthMembership> OAuthMembership { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserProfile>()
.HasMany<Role>(r => r.Roles)
.WithMany(u => u.UserProfiles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
EDIT: The block below was included in one of the article's comments but seems not to be needed.
//modelBuilder.Entity<Membership>()
// .HasMany<Role>(r => r.Roles)
// .WithMany(u => u.Members)
// .Map(m =>
// {
// m.ToTable("webpages_UsersInRoles");
// m.MapLeftKey("UserId");
// m.MapRightKey("RoleId");
// });
}
}
I followed the instructions in the article, and I also took into account the the comments that suggested the article was wrong in a few ways.
I ended up with the following classes:
UserProfile.cs
[Table("UserProfile")]
public class UserProfile
{
[Key, ForeignKey("Membership")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public ICollection<WebSecurity.Role> Roles { get; set; }
public WebSecurity.Membership Membership { get; set; }
}
You should notice right away the "ForeignKey" attribute I use on the UserId column. Since the user is first created in the Membership table, my UserProfile table is the dependent table.
Membership.cs
[Table("webpages_Membership")]
public class Membership
{
//public Membership()
//{
// Roles = new List<Role>();
// OAuthMemberships = new List<OAuthMembership>();
//}
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public DateTime? CreateDate { get; set; }
[StringLength(128)]
public string ConfirmationToken { get; set; }
public bool? IsConfirmed { get; set; }
public DateTime? LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
[Required, StringLength(128)]
public string Password { get; set; }
public DateTime? PasswordChangedDate { get; set; }
[Required, StringLength(128)]
public string PasswordSalt { get; set; }
[StringLength(128)]
public string PasswordVerificationToken { get; set; }
public DateTime? PasswordVerificationTokenExpirationDate { get; set; }
public UserProfile UserProfile { get; set; }
}
Per Richard's comments in the article, I commented out the constructor. I also created a reference back to the UserProfile, but not to roles.
OAuthMembership.cs
[Table("webpages_OAuthMembership")]
public class OAuthMembership
{
[Key, Column(Order = 0), StringLength(30)]
public string Provider { get; set; }
[Key, Column(Order = 1), StringLength(100)]
public string ProviderUserId { get; set; }
public int UserId { get; set; }
//[Column("UserId"), InverseProperty("OAuthMemberships")]
//public Membership User { get; set; }
}
My OAuthMembership class remained basically the same; I commented out only the User attribute, per Richard's comment in the article.
AccountModel.cs+UsersContext
Finally, the UserContext class, where I create the association for the UsersInRoles table.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<InternetApplication.Models.WebSecurity.Role>()
.HasMany<InternetApplication.Models.UserProfile>(r => r.UserProfiles)
.WithMany(u => u.Roles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
}
public DbSet<WebSecurity.Membership> Membership { get; set; }
public DbSet<WebSecurity.OAuthMembership> OAuthMembership { get; set; }
public DbSet<WebSecurity.Role> Roles { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
}
In addition to adding the UsersInRoles mapping, I added DbSet entries for each table.
Now that everything has been created, I can use my Add-Migration and Update-Database commands and use the following code snippet that combines the Membership, UserProfile, and Roles tables:
using (var db = new UsersContext())
{
var memberships = db.Membership
.Include("UserProfile")
.Include("UserProfile.Roles")
.ToList();
foreach (var member in memberships)
{
member.IsConfirmed = true;
}
db.SaveChanges();
}
This was a long post, but I hope that helps.
I used the answer to this question to automatically generate the models from the existing 'webpage_' tables in my database. This ensures that the models are created in the exact same way that SimpleMembership creates them. This resulted in the following code:
Models:
public partial class webpages_Membership
{
public int UserId { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public string ConfirmationToken { get; set; }
public Nullable<bool> IsConfirmed { get; set; }
public Nullable<System.DateTime> LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
public string Password { get; set; }
public Nullable<System.DateTime> PasswordChangedDate { get; set; }
public string PasswordSalt { get; set; }
public string PasswordVerificationToken { get; set; }
public Nullable<System.DateTime> PasswordVerificationTokenExpirationDate { get; set; }
}
public partial class webpages_Roles
{
public webpages_Roles()
{
this.webpages_UsersInRoles = new HashSet<webpages_UsersInRoles>();
}
public int RoleId { get; set; }
public string RoleName { get; set; }
public virtual ICollection<webpages_UsersInRoles> webpages_UsersInRoles { get; set; }
}
public partial class webpages_UsersInRoles
{
public int UserId { get; set; }
public int RoleId { get; set; }
public virtual webpages_Roles webpages_Roles { get; set; }
}
Fluent Mappings:
internal partial class MembershipMapping : EntityTypeConfiguration<webpages_Membership>
{
public MembershipMapping()
{
this.HasKey(t => t.UserId);
this.ToTable("webpages_Membership");
this.Property(t => t.UserId).HasColumnName("UserId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.Property(t => t.CreateDate).HasColumnName("CreateDate");
this.Property(t => t.ConfirmationToken).HasColumnName("ConfirmationToken").HasMaxLength(128);
this.Property(t => t.IsConfirmed).HasColumnName("IsConfirmed");
this.Property(t => t.LastPasswordFailureDate).HasColumnName("LastPasswordFailureDate");
this.Property(t => t.PasswordFailuresSinceLastSuccess).HasColumnName("PasswordFailuresSinceLastSuccess");
this.Property(t => t.Password).HasColumnName("Password").IsRequired().HasMaxLength(128);
this.Property(t => t.PasswordChangedDate).HasColumnName("PasswordChangedDate");
this.Property(t => t.PasswordSalt).HasColumnName("PasswordSalt").IsRequired().HasMaxLength(128);
this.Property(t => t.PasswordVerificationToken).HasColumnName("PasswordVerificationToken").HasMaxLength(128);
this.Property(t => t.PasswordVerificationTokenExpirationDate).HasColumnName("PasswordVerificationTokenExpirationDate");
}
}
internal partial class RolesMapping : EntityTypeConfiguration<webpages_Roles>
{
public RolesMapping()
{
this.HasKey(t => t.RoleId);
this.ToTable("webpages_Roles");
this.Property(t => t.RoleId).HasColumnName("RoleId");
this.Property(t => t.RoleName).HasColumnName("RoleName").IsRequired().HasMaxLength(256);
}
}
internal partial class UsersInRolesMapping : EntityTypeConfiguration<webpages_UsersInRoles>
{
public UsersInRolesMapping()
{
this.HasKey(t => new { t.UserId, t.RoleId });
this.ToTable("webpages_UsersInRoles");
this.Property(t => t.UserId).HasColumnName("UserId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.Property(t => t.RoleId).HasColumnName("RoleId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.HasRequired(t => t.webpages_Roles).WithMany(t => t.webpages_UsersInRoles).HasForeignKey(d => d.RoleId);
}
}
Database Context:
public class MembershipContext : DbContext, IDisposable
{
public DbSet<webpages_Membership> Membership { get; set; }
public DbSet<webpages_Roles> Roles { get; set; }
public DbSet<webpages_UsersInRoles> UsersInRoles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MembershipMapping());
modelBuilder.Configurations.Add(new RolesMapping());
modelBuilder.Configurations.Add(new UsersInRolesMapping());
base.OnModelCreating(modelBuilder);
}
}
Note that I have excluded the OAuthMembership table, because I didn't need it for my solution. But if you follow the steps in the link I provided above you can easily include that table as well.
Starting from a blank MVC4 Internet Template I ran the project so as to create the SimpleMembership tables in a fresh db - then used EF's Reverse Engineer tool to create POCOs from those tables. Stepped thru it line by line to find the error and edited the code block in the OP.
With that code in place I used Package Manager to 'Add-Migration' and 'Update-Database'. Initial tests confirm everything works - I suppose I'll have to revisit if I find edge-cases that expose any deeper problems.

Entity Framework Code First Membership Provider Relationship Issue

I'm trying to code first on asp.net membership provider. When code first creates the table, it generates all tables and appropriate relationship to the bridge table but it also create an additional relationship from the AspNet_User(one) to Aspnet_Role(many). Do you know why it doing this? There shouldn't be any relationship between user and role table.
public class Aspnet_Role
{
public Aspnet_Role()
{
Aspnet_Users = new HashSet<Aspnet_Users>();
}
[Key]
public Guid RoleId { get; set; }
public string RoleName { get; set; }
public string LoweredRoleName { get; set; }
public string Description { get; set; }
public virtual ICollection<Aspnet_Users> Aspnet_Users { get; set; }
}
public class Aspnet_Users
{
public Aspnet_Users()
{
Aspnet_Roles = new HashSet<Aspnet_Role>();
}
[Key]
public Guid UserId { get; set; }
public string UserName { get; set; }
public string LoweredUserName { get; set; }
public string MobileAlias { get; set; }
public bool IsAnonymous { get; set; }
public DateTime LastActivityDate { get; set; }
public virtual Aspnet_Membership Aspnet_Membership { get; set; }
public virtual ICollection<Aspnet_Role> Aspnet_Roles { get; set; }
}
public class StagingContext : DbContext
{
public DbSet<Aspnet_Role> Aspnet_Roles { get; set; }
public DbSet<Aspnet_Users> Aspnet_Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Aspnet_Users>()
.HasMany(r => r.Aspnet_Roles)
.WithMany()
.Map(m => m.ToTable("aspnet_UsersInRoles")
.MapRightKey("RoleId")
.MapLeftKey("UserId"));
}
}
I added the navigation property on the WithMany extension.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Aspnet_Users>()
.HasMany(r => r.Aspnet_Roles)
.WithMany(u => u.Aspnet_Users)
.Map(m => m.ToTable("aspnet_UsersInRoles")
.MapRightKey("RoleId")
.MapLeftKey("UserId"));
}

Entity Framework 4.1 Code First Self-Referencing One-to-Many and Many-to-Many Associations

I have a User that can have collection of users he likes...
Another user can have collection of users he likes....
If User A likes User B and if User B likes User A, then they get to hang out. I need to send each other their contact info. How do we represent such a model in Entity Framework Code First?
public class User
{
public int UserId { get; set; }
public int? UserLikeId { get; set; }
public virtual UserLike UserLike { get; set; }
}
public class UserLike
{
public int UserLikeId { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<User> LikeUsers { get; set; }
}
Is this model correct? I can't get this to work.
I've tried another way but that doesn't work too...
I tried to add collection of user to user table.
For ex :
public virtual ICollection<User> userlike { get; set; }
public class User
{
public int UserId { get; set; }
public virtual ICollection<UserLike> UserLikes { get; set; }
}
public class UserLike
{
public int UserLikeId { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public int LikeUserId { get; set; }
public virtual User LikeUser { get; set; }
}
I get this error when I try to add user and who they like:
Conflicting changes to the role 'UserLike_LikeUser_Target' of the relationship 'UserLike_LikeUser' have been detected.
What's the best way to represent such a model?
You don't really need a separate entity to describe the relationship, the object model below will do the trick:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public int? ThisUserLikesId { get; set; }
public virtual User ThisUserLikes { get; set; }
public virtual ICollection<User> LikeThisUser { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(u => u.ThisUserLikes)
.WithMany(u => u.LikeThisUser)
.HasForeignKey(u => u.ThisUserLikesId);
}
}
Now let's say you have a UserId in your hand and want to find the other User who likes this user which this user also like him:
using (var context = new Context())
{
// For a given user id = 1
var friends = (from u in context.Users
where u.UserId == 1
from v in u.LikeThisUser
where v.UserId == u.ThisUserLikesId
select new
{
OurUser = u,
HerFriend = v
})
.SingleOrDefault();
ExchangeContactInfo(friends.OurUser, friends.HerFriend);
}
Update 1:
A self referencing many-to-many association will be mapped to database using a join table which require a different object model and fluent API altogether:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<User> ThisUserLikes { get; set; }
public virtual ICollection<User> UsersLikeThisUser { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(u => u.ThisUserLikes)
.WithMany(u => u.UsersLikeThisUser)
.Map(c =>
{
c.MapLeftKey("UserId");
c.MapRightKey("OtherUserId");
c.ToTable("UserLikes");
});
}
}
Update 2:
As I explained in this post, a many-to-many association cannot have a payload (e.g EventId), and if that’s the case then we have to break it down to two one-to-many associations to an intervening class and I can see you’ve correctly created this class (UserLike) to represent the extra information attached to your self-referencing many-to-many association but the associations from this intermediate class are not correct as we need to define exactly 2 many-to-one association from UserLike to User like I showed in the following object model:
public class User
{
public int UserId { get; set; }
public string Email { get; set; }
public virtual ICollection ThisUserLikes { get; set; }
public virtual ICollection UsersLikeThisUser { get; set; }
}
public class UserLike
{
public int UserLikeId { get; set; }
public int LikerId { get; set; }
public int LikeeId { get; set; }
public int EventId { get; set; }
public User Liker { get; set; }
public User Likee { get; set; }
public virtual Event Event { get; set; }
}
public class Event
{
public int EventId { get; set; }
public string Name { get; set; }
}
public class Context : DbContext
{
public DbSet Users { get; set; }
public DbSet Events { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasMany(u => u.ThisUserLikes)
.WithRequired(ul => ul.Liker)
.HasForeignKey(ul => ul.LikerId);
modelBuilder.Entity()
.HasMany(u => u.UsersLikeThisUser)
.WithRequired(ul => ul.Likee)
.HasForeignKey(ul => ul.LikeeId)
.WillCascadeOnDelete(false);
}
}
Now you can use the following LINQ query to retrieve all the users who like each other:
using (var context = new Context())
{
var friends = (from u1 in context.Users
from likers in u1.UsersLikeThisUser
from u2 in u1.ThisUserLikes
where u2.LikeeId == likers.LikerId
select new
{
OurUser = u1.UserId,
HerFriend = u2.LikeeId
})
.ToList();
}
Hope this helps.