2

次のコードとの関係を削除しようとしています:

                var serverVideo = InternalGetVideo(db, videoId);

                // get owning user
                var owner = InternalGetUser(db, serverVideo.UserId);

                // Add/remove video to user's upvotedVideos
                if (owner.UpvotedVideos.Contains(serverVideo))
                {
                    serverVideo.UpVotes--;
                    SaveChanges(db); // Works
                    owner.UpVotes--;
                    SaveChanges(db); // Works
                    owner.UpvotedVideos.Remove(serverVideo);
                    SaveChanges(db); // Breaks
                }

                SaveChanges(db);

しかし、私はいつもこのエラーを受け取ります:

操作に失敗しました: 1 つ以上の外部キー プロパティが null 非許容であるため、リレーションシップを変更できませんでした。リレーションシップに変更が加えられると、関連する外部キー プロパティが null 値に設定されます。外部キーが null 値をサポートしていない場合は、新しい関係を定義するか、外部キー プロパティに別の非 null 値を割り当てるか、関連のないオブジェクトを削除する必要があります。

ビデオを削除しようとしているのではなく、ユーザーの UpvotedVideos プロパティから削除しようとしているだけです (そこにあります):

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string Username { get; set; }
    public int UpVotes { get; set; }
    public virtual List<Video> UpvotedVideos { get; set; }
    public string Email { get; set; }
}

Videos には UserProfiles への参照が含まれていません。これは 1 対多の関係です。私が間違っていることは何か分かりますか?

動画クラス:

[DataContract]
public class Video
{
    [Key]
    [DataMember(IsRequired = false)]
    public int VideoId { get; set; }

    [DataMember(IsRequired = false)]
    public int UserId { get; set; }  // user that created the video, unrelated

    [Required]
    [DataMember]
    public virtual IList<Tag> Tags { get; set; }

}
4

2 に答える 2

1

ソースコードでのコメント:

public int UserId { get; set; }  // user that created the video, unrelated

無関係?ほとんどの場合、これは問題の 1 対多の関係の外部キーです。これは、 の主キー プロパティと同じ名前を持っているため、マッピング規則によって外部キーとして検出されるためUserIdですUserProfile。また、プロパティには null 非許容型があるintため、EF は必要に応じて関係を検出します。つまり、ビデオに賛成票を投じたユーザーがいなければ、ビデオは存在できません。

これは、コレクションからビデオを削除するときに例外が示すことです。これはUpvotedVideos、ビデオに賛成票を投じた現在のユーザーとの関係が削除されることを意味します。これを行うことはできますが、すべての動画には賛成票を投じたユーザーが必要であるため、その動画を別のユーザーに割り当てるか、動画を完全に削除する必要があります。

どうやら、ビデオに賛成票を投じたユーザーがいる必要はないようですが、代わりに、ビデオに賛成票がなくてもかまいません。この場合、関係はオプションでなければなりません。これはいくつかの方法で実現できます。

  • UserIdたとえば、プロパティの名前を変更して、従来のマッピングを破ります。

    public int CreatorId { get; set; }  // user that created the video, unrelated
    

    さて、このプロパティは実際には無関係であり、まったく関係のない単なるスカラー プロパティです。デフォルトでは、UpvotedVideosコレクションによって導入された関係は、反対側に必要な外部キー プロパティがないため、現在はオプションです。

  • Fluent API でマッピング規則をオーバーライドします。

    modelBuilder.Entity<UserProfile>()
        .HasMany(u => u.UpvotedVidoes)
        .WithOptional()
        .Map(m => m.MapKey("UpvoterId")); // <- FK column name
    
  • 2 番目の (null 許容) プロパティを導入し、データ注釈を使用して外部キーとして定義します。

    [DataContract]
    public class Video
    {
        [Key]
        [DataMember(IsRequired = false)]
        public int VideoId { get; set; }
    
        [DataMember(IsRequired = false)]
        public int UserId { get; set; } // user that created the video, unrelated
    
        public int? UpvoterId { get; set; }
    
        [Required]
        [DataMember]
        public virtual IList<Tag> Tags { get; set; }
    }
    

    そしてでUserProfile

    [ForeignKey("UpvoterId")]
    public virtual List<Video> UpvotedVideos { get; set; }
    

などにナビゲーション プロパティを追加するバリアントはおそらく他にもありますVideo。ただし、すべての場合において、コードを機能させるには、関係をオプションとして定義する必要があります。

補足:関係は多対多であるべきではありませんか? ユーザーは多くの動画に賛成票を投じることができ、動画は多くのユーザーに賛成票を投じることができますか? しかし、それは別の質問です...

于 2013-08-23T20:34:45.250 に答える