简体   繁体   English

ASP.NET MVC中的多对多关系错误

[英]Many-to-many relationship error in ASP.NET MVC

I am new to ASP.NET MVC, and I'm creating an app for attending music events.我是 ASP.NET MVC 的新手,我正在创建一个用于参加音乐活动的应用程序。 There is a button for user to follow an artist to get all upcoming events.有一个按钮供用户关注艺术家以获取所有即将发生的事件。

When clicking on that button I get an error:单击该按钮时,出现错误:

Entities in 'ApplicationDbContext.Followings' participate in the 'ApplicationUser_Followees' relationship. “ApplicationDbContext.Followings”中的实体参与“ApplicationUser_Followees”关系。 0 related 'ApplicationUser_Followees_Source' were found.找到 0 个相关的“ApplicationUser_Followees_Source”。 1 1

This is the code of the model:这是 model 的代码:

public class Following
{
    public ApplicationUser Followers { get; set; }
    public ApplicationUser Followees { get; set; }

    [Key]
    [Column(Order = 1)]
    public string FollowerId { get; set; }

    [Key]
    [Column(Order = 2)]
    public string FolloweeId { get; set; }
}

and this is code of the fluent api to build the data:这是用于构建数据的流利 api 的代码:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public DbSet<Following> Followings { get; set; }

    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ApplicationUser>()
                    .HasMany(u => u.Followers)
                    .WithRequired(f => f.Followees)
                    .WillCascadeOnDelete(false);

        modelBuilder.Entity<ApplicationUser>()
                    .HasMany(u => u.Followees)
                    .WithRequired(f => f.Followers)
                    .WillCascadeOnDelete(false);

        base.OnModelCreating(modelBuilder);
    }
}

This is the controller for the following api:这是以下 api 的 controller:

public class FollowingsController : ApiController
{
    private readonly ApplicationDbContext _context;

    public FollowingsController()
    {
        _context = new ApplicationDbContext();
    }

    [HttpPost]
    public IHttpActionResult Follow(FollowingDto dto)
    {
        var userId = User.Identity.GetUserId<string>();
        Console.WriteLine(userId);

        var exists = _context.Followings.Any(f => f.FollowerId == userId && f.FolloweeId == dto.FolloweeId);

        if (exists) 
             return BadRequest("The following already exists");

        var following = new Following
                            {
                                FollowerId = userId,
                                FolloweeId = dto.FolloweeId
                            };

        _context.Followings.Add(following);
        _context.SaveChanges();

        return Ok();
    }
}

This is the dto object:这是 dto object:

public class FollowingDto
{
    public string FolloweeId { get; set; }
}

This is the frontend code for the homepage where the following button is located:这是以下按钮所在的主页的前端代码:

@model Gighub.ViewModels.GigsViewModel

@{
    ViewBag.Title = "Home Page";
}

<ul class="gigs">
    @foreach (var gig in Model.UpComingGigs)
    {
    <li>
     
        <div class="details">
            <span class="artist">
                @gig.Artist.Name @if (Model.ShowActions)   {
                     <button class="btn btn-link btn-sm js-toggle-following" data-user-id="@gig.ArtistId">Follow?</button>
                }
            </span>
        </div>
    </li>
    }
</ul>

@section scripts {
    <script>
        $(document).ready(function () {

                $(".js-toggle-following").click(function (e) {
                    var btn = $(e.target);
                    $.post("/api/followings", { "FolloweeId": btn.attr("data-user-id") })
                    .done(function () {
                        btn.text("following")
                    })
                    .fail(function () {
                        alert("something failed");
                    });
                });
            });
    </script>
    }

and here is applicationuser class:这是应用程序用户 class:

    public class ApplicationUser : IdentityUser
    {
        public ICollection<Following> Followers { get; set; }
        public ICollection<Following> Followees { get; set; }
        public ApplicationUser()
        {
            Followers = new Collection<Following>();
            Followees = new Collection<Following>();
        }
        [Required]
        [StringLength(100)]
        public string Name { get; set; }
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }
    }

Replace your db context with this:用这个替换你的数据库上下文:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
       modelBuilder.Entity<Following>(entity =>
            {
               
                entity.HasOne(d => d.Followee )
                    .WithMany(p => p.Followees )
                    .HasForeignKey(d => d.FolloweeId)
                    .OnDelete(DeleteBehavior.ClientSetNull);

                entity.HasOne(d => d.Follower )
                    .WithMany(p => p.Followers )
                    .HasForeignKey(d => d.FollowerId );
            });
        base.OnModelCreating(modelBuilder);
    }

and check your db.并检查您的数据库。 Maybe you will need to re-migrate.也许您需要重新迁移。

From the exception, the issue looks to be on your ApplicationUser class and mapping the followings.从例外情况来看,问题似乎出在您的 ApplicationUser class 并映射以下内容。 If this is EF6 or EF Core 5 you can have something like:如果这是 EF6 或 EF Core 5,您可以使用以下内容:

public class ApplicationUser
{
   // ...

   public virtual ICollection<ApplicationUser> Followers { get; set; } = new List<ApplicationUser>();
   public virtual ICollection<ApplicationUser> Followees { get; set; } = new List<ApplicationUser>();
}

This relationship has to be explicitly mapped: (EF6)这种关系必须明确映射:(EF6)

modelBuilder.Entity<ApplicationUser>()
    .HasMany(u => u.Followers)
    .WithMany(u => u.Followees)
    .Map(u => u.ToTable("Followings")
        .MapLeftKey("FollowerId")
        .MapRightKey("FolloweeId"))
    .WillCascadeOnDelete(false);

EF Core's mapping will be slightly different but should be able to follow the same gist, just need to check up on EF Core 5's many-to-many setup supporting non-declared linking entities. EF Core 的映射会略有不同,但应该能够遵循相同的要点,只需要检查 EF Core 5 的多对多设置支持未声明的链接实体。

Alternatively you can map it out with a "Following" entity, (required for EF Core < 5) but this changes the relationship somewhat because it acts more like a Many-to-One-to-Many rather than Many-to-Many:或者,您可以使用“Following”实体将其 map 输出(EF Core < 5 需要),但这会在一定程度上改变关系,因为它更像是多对多而不是多对多:

public class ApplicationUser
{
   // ...

   public virtual ICollection<Following> FollowingsAsFollower { get; set; } = new List<Following>();
   public virtual ICollection<Following> FollowingsAsFollowee { get; set; } = new List<Following>();
}

public class Following
{
    [Key, Column(Order=0), ForeignKey("Follower")]
    public int FollowerId {get; set;}
    [Key, Column(Order=1), ForeignKey("Followee")]
    public int FolloweeId {get; set;}

    public virtual ApplicationUser Follower { get; set; }
    public virtual ApplicationUser Followee { get; set; }
}

The mapping then looks like:然后映射如下所示:

modelBuilder.Entity<Following>()
    .HasRequired(u => u.Follower)
    .WithMany(u => u.FollowingsAsFollower)
    .WillCascadeOnDelete(false);

modelBuilder.Entity<Following>()
    .HasRequired(u => u.Followee)
    .WithMany(u => u.FollowingsAsFollowee)
    .WillCascadeOnDelete(false);

This looks rather odd with the linking table since we are mapping a many-to-many self-referencing.这对于链接表来说看起来很奇怪,因为我们正在映射一个多对多的自引用。 I mixed the configuration between attribute and modelBuilder but you will need at least some explicit modelBuilder/EntityTypeConfiguration mapping for the relationships because of the self-referencing types.我在属性和模型构建器之间混合了配置,但是由于自引用类型,您至少需要一些显式的模型构建器/实体类型配置映射来映射关系。 EF's conventions for automatically mapping relationships are based on the navigation property type rather than the property names, so setting up multiple relationships for the same type need to be explicit. EF 自动映射关系的约定基于导航属性类型而不是属性名称,因此需要明确为同一类型设置多个关系。

The issue will likely be trying to set up a single Following in the user for Followers/Followees.该问题可能是尝试Following用户中为关注者/关注者设置单个关注。 There needs to be two collections for this self-referencing relationship because the "Following" table is expecting a direct relationship between the FollowerID on it's end to marry to the Follower IDs in a collection on the other end.这种自引用关系需要两个 collections,因为“Following”表期望其一端的 FollowerID 与另一端集合中的 Follower ID 之间存在直接关系。 A many to many relationship set up between two tables will have collections on each table so that a Student will list all Courses and a Course will list all Students.在两个表之间建立的多对多关系将在每个表上具有 collections,以便学生将列出所有课程,而课程将列出所有学生。 With a self-referencing table the same thing applies.对于自引用表,同样适用。 A Follower needs to list all Followees, and a Followee needs to list all Followers. Follower 需要列出所有 Followee,Followee 需要列出所有 Followers。 It won't work if it's expecting to resolve a collection where I might be a Followee or Follower.如果它期望解决我可能是关注者或关注者的集合,它将不起作用。 Alternatively you can always do away with the collection on Application user and just have a 1-way many-to-one on Following to go to and resolve the applicable users.或者,您始终可以取消对应用程序用户的收集,而只需在关注 go 上进行 1 路多对一以解决适用的用户。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM