1

最初に Entity Framework Core 2.2 コードを使用しています。論理的な削除は、オーバーライドされた Context クラスに実装されています。コンストラクターでコンテキストを引数として取りながら、特定のエンティティのリポジトリを初期化する UnitOfWork クラスがあります。概要を明確にするために、このサンプルでは一部のアプリケーション エンティティが省略されています。チーム、メンバー、プロジェクト、および従業員のみを使用します。

テスト中に、次のことに気付きました: オブジェクト (たとえば、プロジェクト) を削除すると、SaveChanges() の後で、その "Deleted" プロパティが "true" に正常に設定されます。ただし、ソリューション プロジェクトがまだ実行されている場合、このオブジェクト (プロジェクト) に親オブジェクト (チーム) がある場合、「削除済み」プロパティが明らかに「true」であっても、チームのリスト プロジェクトに表示されることに気付きました。プロセスがまだ実行されている間に、すべてのプロジェクトを返すリポジトリの Get() メソッドを呼び出すと、削除されたプロジェクトは返されたプロジェクト リストには表示されず、チームのリスト プロジェクトにのみ表示されます。ソリューション プロジェクトを再実行すると、これらの子オブジェクトが親のリスト プロジェクトに表示されなくなります。

問題を分析するために作成した NUnit テストを次に示します。

[Test, Order(2)]
public void GetTeamAfterDeletingProject()
{
    //There are initially 11 projects
    var projects = unit.Projects.Get().ToList();
    Assert.AreEqual(11, projects.Count());

    Project project = unit.Projects.Get(8);
    unit.Projects.Delete(project);
    unit.Save();

    //now there are 10 projects
    projects = unit.Projects.Get().ToList();
    Assert.AreEqual(10, projects.Count());

    //this team had initially 2 projects, and one of them was deleted
    Team team = unit.Teams.Get(3);

    //only one project should be left, but there are actually two showing up - this test fails
    Assert.AreEqual(1, team.Projects.Count);

    project.Deleted = false;
    unit.Save();
}

これは、リストに子オブジェクトがある場合、オブジェクトの削除を許可しない、TeamsRepository に実装した Delete メソッドに干渉します。つまり、以前のテストでチームを削除したい場合、彼のプロジェクトをすべて削除したとしても、アプリを再起動しないと削除できません (リポジトリ全体は以下にあります)。

        public override void Delete(int id)
        {
            Team old = Get(id);

            if (old.Projects.Count != 0 || old.Members.Count != 0)
            {
                Services.ThrowChildrenPresentException();
            }

            Delete(old); 
        }

これが遅延読み込みを使用している場合の通常の動作なのか、それとも構成に何かが欠けていたのか疑問に思っています。

protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder)
        {
            if(_conStr != null)
            {
                optionBuilder.UseNpgsql(_conStr);
            }
            optionBuilder.UseLazyLoadingProxies(true);
            base.OnConfiguring(optionBuilder);
        }
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            builder.Entity<Member>().HasQueryFilter(x => !x.Deleted);
            builder.Entity<Employee>().HasQueryFilter(x => !x.Deleted);
            builder.Entity<Project>().HasQueryFilter(x => !x.Deleted);
            builder.Entity<Team>().HasQueryFilter(x => !x.Deleted);
        }

        public override int SaveChanges()
        {
            foreach (var entry in ChangeTracker.Entries().Where(x => x.State == EntityState.Deleted && x.Entity is BaseClass))
            {
                entry.State = EntityState.Modified;
                entry.CurrentValues["Deleted"] = true;
            }
            return base.SaveChanges();
        }

これはチーム エンティティです。

public class Team: BaseClass
    {
        public Team()
        {
            Members = new List<Member>();
            Projects = new List<Project>();
        }
        public string Name { get; set; }
        public string Description { get; set; }
        public bool StatusActive { get; set; }
        public virtual IList<Member> Members { get; set; }
        public virtual IList<Project> Projects { get; set; }
    }

    public class Member: BaseClass
    {
        public  virtual Team Team { get; set; }
        public virtual Employee Employee { get; set; }        
        public virtual Role Role { get; set; }
        public virtual MemberStatus Status { get; set; }
        public decimal HoursWeekly { get; set; }
    }

    public class BaseClass
    {
        public BaseClass()
        {
            Created = DateTime.Now;
            Creator = 0;
            Deleted = false;
        }
        [Key]
        public int Id { get; set; }
        public DateTime Created { get; set; }
        public int Creator { get; set; }
        public bool Deleted { get; set; }
    }

オーバーライドされたメソッドを持つ汎用リポジトリと特定の TeamsRepository。論理的に削除されたプロジェクトは old.Projects.Count に表示されるため、この方法でチームを削除することはできません (たとえそれらが取得されなかったとしても)

        public class Repository<Entity>: IRepository<Entity> where Entity : class
    {
        protected AppContext _context;
        protected DbSet<Entity> _dbSet;

        public Repository(AppContext context)
        {
            _context = context;
            _dbSet = _context.Set<Entity>();
        }

        public void ValidateUpdate(Entity newEntity, int id)
        {
            if (id != (newEntity as BaseClass).Id)
                throw new ArgumentException($"Error! Id of the sent object: {(newEntity as BaseClass).Id} and id in url: {id} do not match");
        }

        public virtual IQueryable<Entity> Get() => _dbSet;

        public virtual IList<Entity> Get(Func<Entity, bool> where) => Get().Where(where).ToList();

        public virtual Entity Get(int id)
        {
            Entity entity = _dbSet.Find(id);
            if (entity == null)
                throw new ArgumentException($"There is no object with id: {id} in the database");
            return entity;
        }

        public virtual void Insert(Entity entity)
        {
            entity.Build(_context);
            _dbSet.Add(entity);
        }

        public virtual void Update(Entity entity, int id)
        {
            entity.Build(_context);
            Entity old = Get(id);
            ValidateUpdate(entity, id);
            _context.Entry(old).CurrentValues.SetValues(entity);
            old.Relate(entity);
        }

        public void Delete(Entity entity) => _dbSet.Remove(entity);

        public virtual void Delete(int id)
        {
            Entity old = Get(id);
            Delete(old);
        }
    }

public class TeamsRepository: Repository<Team>
    {
        public TeamsRepository(AppContext context) : base(context) { }

        public override void Delete(int id)
        {
            Team old = Get(id);

            if (old.Projects.Count != 0 || old.Members.Count != 0)
            {
                Services.ThrowChildrenPresentException();
            }

            Delete(old); 
        }
    }
4

0 に答える 0