You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core MVC上传视频时出现PK_User主键冲突问题求助

上传视频时的主键冲突问题

错误信息

SqlException: Violation of PRIMARY KEY constraint 'PK_User'. Cannot insert duplicate key in object 'dbo.User'. The duplicate key value is (831769c6-fc01-4203-a345-0aa12702b95e)

上传视频时遇到矛盾:不为Video模型传入User对象,Video.User属性会为null;传入User对象后,EF会尝试创建新用户,触发主键冲突错误。


相关代码

控制器Upload方法

public async Task<IActionResult> Upload(VideoUploadViewModel model)
{
    if (ModelState.IsValid)
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        var user = await _userRepository.GetUserByIdAsync(userId);

        var video = new Video
        {
            Title = model.Title,
            Description = model.Description,
            DurationSeconds = model.DurationSeconds,
            CategoryId = model.CategoryId,
            User = user,
            UserId = userId
        };

        await _videoRepository.AddVideoAsync(video, model.VideoFile);

        return RedirectToAction("Index");
    }

    var categories = await _categoryRepository.GetAllCategoriesAsync();
    ViewBag.Categories = categories;

    return View(model);
}

VideoRepository的AddVideoAsync方法

public async Task AddVideoAsync(Video video, IFormFile videoFile)
{
    try
    {
        if (videoFile != null && videoFile.Length > 0)
        {
            var fileName = Guid.NewGuid().ToString() + Path.GetExtension(videoFile.FileName);
            var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "videos", fileName);

            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await videoFile.CopyToAsync(stream);
            }

            video.FilePath = fileName;
        }

        _context.Videos.Add(video);
        await _context.SaveChangesAsync();
    }
    catch (Exception)
    {
        if (!string.IsNullOrEmpty(video.FilePath))
        {
            var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "videos", video.FilePath);

            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
        }
        throw;
    }
}

Video实体类

public class Video
{
    [Key]
    public int VideoId { get; set; }

    [Required(ErrorMessage = "Поле 'Название видео' обязательно для заполнения")]
    [Display(Name = "Название видео")]
    [StringLength(100, ErrorMessage = "Длина 'Названия' не должна превышать 100 символов")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Поле 'Описание видео' обязательно для заполнения")]
    [Display(Name = "Описание видео")]
    [StringLength(500, ErrorMessage = "Длина 'Описания' не должна превышать 500 символов")]
    public string Description { get; set; }

    [Required(ErrorMessage = "Поле 'Продолжительность видео' обязательно для заполнения")]
    [Display(Name = "Продолжительность видео")]
    [Range(0, int.MaxValue, ErrorMessage = "Значение 'Продолжительность' должно быть неотрицательным")]
    public int DurationSeconds { get; set; }

    [ScaffoldColumn(false)]
    public string FilePath { get; set; }

    [NotMapped]
    [Display(Name = "Файл видео")]
    [Required(ErrorMessage = "Выберите видеофайл")]
    public IFormFile VideoFile { get; set; }

    [ScaffoldColumn(false)]
    public DateTime UploadDate { get; set; }

    [Display(Name = "Категория видео")]
    [Required(ErrorMessage = "Поле 'Категория видео' обязательно для заполнения")]
    public int CategoryId { get; set; }
    [ForeignKey("CategoryId")]
    public Category Category { get; set; }

    public string UserId { get; set; }
    [ForeignKey("UserId")]
    public User User { get; set; }

    public ICollection<Comment> Comments { get; set; }
    public ICollection<Like> Likes { get; set; }
    public ICollection<View> Views { get; set; }

    public Video()
    {
        UploadDate = DateTime.UtcNow;
        Comments = new List<Comment>();
        Likes = new List<Like>();
        Views = new List<View>();
    }
}

User实体类

public class User : IdentityUser
{
    [Key]
    public override string Id { get; set; }

    [Required(ErrorMessage = "Поле 'Email' обязательно для заполнения")]
    [EmailAddress(ErrorMessage = "Некорректный формат 'Email'")]
    public override string Email { get; set; }

    [Required(ErrorMessage = "Поле 'Логин' обязательно для заполнения")]
    [StringLength(50, MinimumLength = 3, ErrorMessage = "Длина 'Логина' должна быть от 3 до 50 символов")]
    public override string UserName { get; set; }

    [ScaffoldColumn(false)]
    [Display(Name = "Дата регистрации")]
    public DateTime RegistrationDate { get; set; }

    public ICollection<Video> Videos { get; set; }
    public ICollection<SearchHistory> SearchHistory { get; set; }
    public ICollection<WatchHistory> WatchHistory { get; set; }
    public ICollection<WatchLater> WatchLaterList { get; set; }
    public ICollection<Notification> Notifications { get; set; }
    public ICollection<View> Views { get; set; }
    public ICollection<Comment> Comments { get; set; }
    public ICollection<Like> Likes { get; set; }

    public User()
    {
        RegistrationDate = DateTime.UtcNow;
        Videos = new List<Video>();
        SearchHistory = new List<SearchHistory>();
        WatchHistory = new List<WatchHistory>();
        WatchLaterList = new List<WatchLater>();
        Notifications = new List<Notification>();
        Views = new List<View>();
        Comments = new List<Comment>();
        Likes = new List<Like>();
    }
}

UserRepository类

public class UserRepository : IUserRepository
{
    private readonly UserManager<User> _userManager;

    public UserRepository(UserManager<User> userManager)
    {
        _userManager = userManager;
    }

    public async Task<User> GetUserByIdAsync(string id)
    {
        return await _userManager.FindByIdAsync(id);
    }
}

解决方案

方案1:仅设置UserId,不关联User对象

EF只需外键UserId就能建立关联,无需传入完整的User对象。修改Video初始化代码:

var video = new Video
{
    Title = model.Title,
    Description = model.Description,
    DurationSeconds = model.DurationSeconds,
    CategoryId = model.CategoryId,
    UserId = userId // 只设置外键,移除User = user
};

此方式下EF不会尝试创建新用户,仅关联已存在的用户记录。

方案2:将User对象附加到DbContext

若必须保留User属性赋值,需告知EF该User是已存在实体,避免被标记为新增。在AddVideoAsync方法中,_context.Videos.Add(video);之前添加:

if (video.User != null)
{
    _context.Attach(video.User).State = EntityState.Unchanged;
}

该操作会让EF将User视为已存在实体,不会执行插入操作。

方案3:确保User对象被当前DbContext跟踪

UserManager.FindByIdAsync返回的User可能不在当前_context的跟踪范围内。若你的DbContext继承自IdentityDbContext,可修改UserRepository的查询方式:

public async Task<User> GetUserByIdAsync(string id)
{
    return await _context.Users.FindAsync(id);
}

此方式获取的User会被DbContext跟踪,添加Video时EF不会将其识别为新实体。

内容的提问来源于stack exchange,提问作者user23356758

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 19:18:12