EF核心多对多关系违反主键约束实体

时间:2021-02-02 15:47:01

标签: entity-framework asp.net-core

我想将项目添加到测试数据库以进行集成测试。 这些表具有多对多关系(我在数据库上下文中配置它们)我想向监视列表表添加新记录但 AddAsync 抛出 DbUpdate 异常

<块引用>

违反 PRIMARY KEY 约束“PK_Movies”。无法在对象“dbo.Movies”中插入重复键。重复的键值为 (f48bd131-79ad-47bc-8cf2-1ece8ba648c2)。 声明已终止。

EF 认为我想更新电影实体,但我想将项目添加到监视列表和链接表(电影监视列表)

我的项目基于 CleanArchitecture

   public class Movie
   {
        [Key]
        public Guid Id { get; set; } = Guid.NewGuid();
        public string PrimaryTitle { get; set; }
        public string OriginalTitle { get; set; }
        public ICollection<Watchlist> Watchlists { get; set; }
   }

    public class Watchlist 
    {
        [Key]
        public Guid Id { get; set; } = Guid.NewGuid();
        public bool IsPrivate { get; set; }
        public Guid UserId { get; set; }
        public ICollection<Movie> Movies { get; set; }
    }

    [Test]
    public static async Task ShouldRemoveMovieFromWatchlist<TEntity>()
    {
        var userId = Guid.NewGuid();
        var movieList = await GetAsync();
        var watchlist = new Watchlist { Id = Guid.NewGuid(), UserId = userId, Movies = moviesList};
        await AddAsync(watchlist);

        // Act
        var command = new RemoveMovieFromWatchlistCommand { MovieId = movieId, WatchlistId = watchlist.Id };
        var id = await SendAsync(command);
        var result = Include<Watchlist>(id,e => e.Movies);

        // Assert
        result.Should().NotBeNull();
        result.Movies.Should().HaveCount(0);
    }
    private static IServiceScopeFactory _scopeFactory;

    public static async Task AddAsync<TEntity>(TEntity entity)
        where TEntity : class
    {
        using var scope = _scopeFactory.CreateScope();
        var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
        var dbSet =  context.Set<TEntity>();
        dbSet.Add(entity);
        await context.SaveChangesAsync();
    }
    

    public static async Task<List<TEntity>> GetAsync<TEntity>()
        where TEntity : class
    {
        using var scope = _scopeFactory.CreateScope();

        var context = scope.ServiceProvider.GetService<ApplicationDbContext>();

        return await context.Set<TEntity>().ToListAsync();
    }

那我该怎么办?

1 个答案:

答案 0 :(得分:0)

我相信您的问题是 EF Core 的更改跟踪器没有跟踪给定的实体,因为当您调用 AddAsync() 时,它会创建一个新的范围和一个新的 DbContext

我最近开始使用相同的集成测试方法,它很棒,但是如果不对其进行大量扩展,测试辅助方法将无法让您走得更远。处理 DbContext 的更好方法可能是为每个测试用例实例化一个,而不是每个操作一个。

无论如何,一种简单的解决方案是将给定的实体附加到与插入新 DbContext 相同的范围内的当前 Watchlist。对执行此更改的附加方法进行粗略修改:

public static async Task AddAsync<TEntity>(TEntity entity, IEnumerable<object> attachThese = null)
where TEntity : class
{
    using var scope = _scopeFactory.CreateScope();
    var context = scope.ServiceProvider.GetService<ApplicationDbContext>();

    if (attachThese != null)
    {
        context.AttachRange(attachThese);
    }

    var dbSet = context.Set<TEntity>();
    dbSet.Add(entity);
    await context.SaveChangesAsync();
}

记得将电影列表添加到观看列表:

var watchlist = new Watchlist { Id = Guid.NewGuid(), UserId = userId, Movies = moviesList};

然后在你的测试中这样调用它:

await AddAsync(watchlist, movieList);

让我知道它是否有效/无效。