admin管理员组

文章数量:1355570

I have works, users and likes. A like is a connection between a user and a work. The work was created by a user.

I would like to have tables like this:

public class DbUser
{
    public Guid Id { get; set; }

    public ICollection<DbWork> Works { get; set; } = new List<DbWork>();
    public ICollection<DbLike> Likes { get; set; } = new List<DbLike>();
}
public class DbWork
{
    public Guid Id { get; set; }

    public Guid UserId { get; set; }
    //public DbUser User { get; set; } this doesn't work

    public ICollection<DbLike> Likes { get; set; } = new List<DbLike>();
}
public class DbLike
{
    public Guid Id { get; set; }

    public Guid UserId { get; set; }
    public DbUser User { get; set; }

    public Guid WorkId { get; set; }
    public DbWork Work { get; set; }
}

I can't set up all the connections.

My current Fluent API is:

public class DbUserConfiguration : IEntityTypeConfiguration<DbUser>
{
    public void Configure(EntityTypeBuilder<DbUser> builder)
    {
        builder
            .HasKey(u => u.Id);

        //builder
        //    .HasMany(u => u.Works)
        //    .WithOne(w => w.User)
        //    .OnDelete(DeleteBehavior.NoAction);

        builder
          .HasMany(u => u.Likes)
          .WithOne(w => w.User)
          .OnDelete(DeleteBehavior.NoAction);
    }
}
public class DbWorksConfiguration : IEntityTypeConfiguration<DbWork>
{
    public void Configure(EntityTypeBuilder<DbWork> builder)
    {
        builder.HasKey(w => w.Id);

        builder.HasMany(w => w.Likes)
            .WithOne(l => l.Work)
            .HasForeignKey(l => l.WorkId)
            .OnDelete(DeleteBehavior.NoAction);
    }
}
public class DbLikeConfiguration : IEntityTypeConfiguration<DbLike>
{
    public void Configure(EntityTypeBuilder<DbLike> builder)
    {
        builder.HasKey(u => u.Id);
    }
}

There are two keys in the work table: UserId and DbUserId

error if you uncomment comments:

Annotations are simple for configuration like Keys, FKs where fluent is suited to more complex mappings like the one-to-many/many-to-many. Some general tips are to protect setters for PKs and FKs to avoid code from accidentally trying to change these. Also remove setters for collection navigation properties. Setting these inappropriately can cause issues. When it comes to EF and resolving relationships by convention an important detail that may have tripped up your configuration is that by convention when linking FKs to their navigation property, EF defaults to resolving these by type name, not property name. If your entity type is "DbUser" EF will look for "DbUserId" or "DbUser_Id", not "UserId" so you need to explicitly set these mappings. (Annotations or fluent config)

You will need to be very careful with tracking references to these entities as the NoAction delete behavior will result in exceptions being raised if EF interprets your code as removing entities or setting navigation properties to #null. Often errors occurring when updating or creating rows involving navigation properties are due to bad assumptions around tracked/untracked references rather than mapping.

Database with UserId and DbUserId with commented code:

I have works, users and likes. A like is a connection between a user and a work. The work was created by a user.

I would like to have tables like this:

public class DbUser
{
    public Guid Id { get; set; }

    public ICollection<DbWork> Works { get; set; } = new List<DbWork>();
    public ICollection<DbLike> Likes { get; set; } = new List<DbLike>();
}
public class DbWork
{
    public Guid Id { get; set; }

    public Guid UserId { get; set; }
    //public DbUser User { get; set; } this doesn't work

    public ICollection<DbLike> Likes { get; set; } = new List<DbLike>();
}
public class DbLike
{
    public Guid Id { get; set; }

    public Guid UserId { get; set; }
    public DbUser User { get; set; }

    public Guid WorkId { get; set; }
    public DbWork Work { get; set; }
}

I can't set up all the connections.

My current Fluent API is:

public class DbUserConfiguration : IEntityTypeConfiguration<DbUser>
{
    public void Configure(EntityTypeBuilder<DbUser> builder)
    {
        builder
            .HasKey(u => u.Id);

        //builder
        //    .HasMany(u => u.Works)
        //    .WithOne(w => w.User)
        //    .OnDelete(DeleteBehavior.NoAction);

        builder
          .HasMany(u => u.Likes)
          .WithOne(w => w.User)
          .OnDelete(DeleteBehavior.NoAction);
    }
}
public class DbWorksConfiguration : IEntityTypeConfiguration<DbWork>
{
    public void Configure(EntityTypeBuilder<DbWork> builder)
    {
        builder.HasKey(w => w.Id);

        builder.HasMany(w => w.Likes)
            .WithOne(l => l.Work)
            .HasForeignKey(l => l.WorkId)
            .OnDelete(DeleteBehavior.NoAction);
    }
}
public class DbLikeConfiguration : IEntityTypeConfiguration<DbLike>
{
    public void Configure(EntityTypeBuilder<DbLike> builder)
    {
        builder.HasKey(u => u.Id);
    }
}

There are two keys in the work table: UserId and DbUserId

error if you uncomment comments:

Annotations are simple for configuration like Keys, FKs where fluent is suited to more complex mappings like the one-to-many/many-to-many. Some general tips are to protect setters for PKs and FKs to avoid code from accidentally trying to change these. Also remove setters for collection navigation properties. Setting these inappropriately can cause issues. When it comes to EF and resolving relationships by convention an important detail that may have tripped up your configuration is that by convention when linking FKs to their navigation property, EF defaults to resolving these by type name, not property name. If your entity type is "DbUser" EF will look for "DbUserId" or "DbUser_Id", not "UserId" so you need to explicitly set these mappings. (Annotations or fluent config)

You will need to be very careful with tracking references to these entities as the NoAction delete behavior will result in exceptions being raised if EF interprets your code as removing entities or setting navigation properties to #null. Often errors occurring when updating or creating rows involving navigation properties are due to bad assumptions around tracked/untracked references rather than mapping.

Database with UserId and DbUserId with commented code:

Share Improve this question edited Apr 2 at 7:34 Pavel Kostin asked Mar 30 at 2:01 Pavel KostinPavel Kostin 111 silver badge3 bronze badges 4
  • Please edit your question to include the full complete error messages you get when you try to setup the navigation properties. Also add the SQL table structures you have in the database (as CREATE TABLE statements) to your question to make it more clear what exact columns you have (and what the foreign keys are). – Progman Commented Mar 30 at 6:57
  • Putting public ICollection<DbWork> Works { get; set; } = new List<DbWork>(); on DbUser doesn't make a huge amount of sense. Just keep Likes. – Charlieface Commented Mar 30 at 9:52
  • I suspect your problem is skipping .HasForeignKey on some navigations. But I can't be certain without seeing how it fails. – Jeremy Lakeman Commented Mar 31 at 0:58
  • Thanks for the answers! I've added an error message and a database diagram to the question. – Pavel Kostin Commented Apr 2 at 7:40
Add a comment  | 

1 Answer 1

Reset to default 2

There are two separate mappings to set up on the user. Works and Likes. Works is a regular one-to-many relationship, where Likes is effectively a many-to-many. You can approach this two ways, either a standard many-to-many if a "Like" is just a joining table between User and Work, or though set up as a one-to-many-to-one using the joining entity if you want to expose additional columns on the Like table. (I.e. comments, rating, etc.)

Assuming a one-to-many-to-one:


public class DbUser
{
    [Key]
    public Guid Id { get; protected set; }

    public ICollection<DbWork> Works { get; } = [];
    public ICollection<DbLike> Likes { get; } = [];
}
public class DbWork
{
    [Key]
    public Guid Id { get; protected set; }

    public Guid UserId { get; protected set; }
    [ForeignKey(nameof(UserId))]
    public DbUser User { get; set; } 

    public ICollection<DbLike> Likes { get; } = [];
}
public class DbLike
{
    [Key]
    public Guid Id { get; protected set; }

    public Guid UserId { get; protected set; }
    [ForeignKey(nameof(UserId))]
    public DbUser User { get; set; }

    public Guid WorkId { get; protected set; }
    [ForeignKey(nameof(WorkId))]
    public DbWork Work { get; set; }
}

public class DbUserConfiguration : IEntityTypeConfiguration<DbUser>
{
    public void Configure(EntityTypeBuilder<DbUser> builder)
    {
        builder
            .HasMany(u => u.Works)
            .WithOne(w => w.User)
            .HasForeignKey(w => w.UserId)
            .OnDelete(DeleteBehavior.NoAction); // Be prepared for exceptions if deleting user entities.

        builder
          .HasMany(u => u.Likes)
          .WithOne(l => l.User)
          .OnDelete(DeleteBehavior.NoAction);
    }
}
public class DbWorksConfiguration : IEntityTypeConfiguration<DbWork>
{
    public void Configure(EntityTypeBuilder<DbWork> builder)
    {
        builder.HasKey(w => w.Id);

        builder.HasMany(w => w.Likes)
            .WithOne(l => l.Work)
            .OnDelete(DeleteBehavior.NoAction);
    }
}

Annotations are simple for configuration like Keys, FKs where fluent is suited to more complex mappings like the one-to-many/many-to-many. Some general tips are to protect setters for PKs and FKs to avoid code from accidentally trying to change these. Also remove setters for collection navigation properties. Setting these inappropriately can cause issues. When it comes to EF and resolving relationships by convention an important detail that may have tripped up your configuration is that by convention when linking FKs to their navigation property, EF defaults to resolving these by type name, not property name. If your entity type is "DbUser" EF will look for "DbUserId" or "DbUser_Id", not "UserId" so you need to explicitly set these mappings. (Annotations or fluent config)

You will need to be very careful with tracking references to these entities as the NoAction delete behavior will result in exceptions being raised if EF interprets your code as removing entities or setting navigation properties to #null. Often errors occurring when updating or creating rows involving navigation properties are due to bad assumptions around tracked/untracked references rather than mapping.

本文标签: cHow to set up a cyclic connection in EF CoreStack Overflow