持久化实体框架嵌套/相关实体

时间:2015-09-17 10:14:46

标签: c# entity-framework

我有以下实体:

public class ModuleCriteria
{
    public int ModuleCriteriaId { get; set; }

    public string Criteria { get; set; }

    public List<ModuleCriteriaLookup> ModuleCriteriaLookups { get; set; 
}
}

public class ModuleCriteriaLookup
{
    public int ModuleCriteriaLookupId { get; set; }

    public int ModuleCriteriaId { get; set; } // ** (Foreign Key) **

    public int SiteId { get; set; }

    public string CategoryId { get; set; }

    public ModuleCriteria ModuleCriteria { get; set; }
}

我的Context课程(为简洁起见编辑)中有以下EF配置:

protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
    base.OnModelCreating( modelBuilder );

    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    modelBuilder.Entity<ModuleCriteriaLookup>().HasRequired( mc => mc.ModuleCriteria );

    // I tried adding the below line but it made no difference.
    //modelBuilder.Entity<ModuleCriteria>().HasMany( mc => mc.ModuleCriteriaLookups );
}

...我在DbSet类中定义了以下Context属性:

public DbSet<ModuleCriteria> ModuleCriteria { get; set; }

public DbSet<ModuleCriteriaLookup> ModuleCriteriaLookup { get; set; }

我有一个CriteriaRepository类,它有一个Save方法,用于对ModuleCriteria个实体进行持久更改:

public void Save( ModuleCriteria moduleCriteria )
{
    using ( var ctx = new MyAppContext() )
    {
        ctx.Entry( moduleCriteria ).State = moduleCriteria.ModuleCriteriaId == 0 ? EntityState.Added : EntityState.Modified;

        ctx.SaveChanges();
    }
}

ModuleCriteria对象可以在没有ModuleCriteriaLookup对象的情况下存在,但ModuleCriteriaLookup对象必须与现有的ModuleCriteria对象相关(在ModuleCriteriaId上相关)。

您可以拥有多个ModuleCriteraLookup个对象,这些对象都与同一个ModuleCriteria对象相关。

我希望和EF期望的行为是:

1)如果我创建一个新的ModuleCriteria对象(没有任何ModuleCriteriaLookups),请调用我的存储库中的Save方法,我希望看到新的ModuleCriteria记录在数据库中,数据库中没有相关的ModuleCriteriaLookup记录。

2)如果我创建一个新的ModuleCriteria对象并为其分配List<ModuleCriteriaLookup>,请调用我的存储库中的Save方法,我希望在db中看到新的ModuleCriteria记录数据库中的x个新ModuleCriteriaLookup行与该特定ModuleCriteria相关。

3)如果我添加/编辑/删除与我的ModuleCriteriaLookup个对象之一相关的任何ModuleCriteria个对象,那么调用我的存储库中的Save方法,我希望要查看要从数据库中删除的ModuleCriteria个已删除ModuleCriteriaLookup个对象,添加任何新对象以及任何已编辑的对象只是为了更新。

所以我需要担心的是,无论ModuleCriteria.ModuleCriteriaLookups属性包含给定ModuleCriteria的内容,只需调用Save方法即可在数据库的2个表中反映出来。我的存储库中的ModuleCriteria对象。

不幸的是,目前,如果我添加一个关联ModuleCriteria的新List<ModuleCriteriaLookup>对象,它会很好地在db中添加ModuleCriteria和x ModuleCriteriaLookup行。但是当我想编辑或删除ModuleCriteria.ModuleCriteriaLookups属性中的条目时,这不会反映在数据库中。 ModuleCriteriaLookups行没有任何结果。

我不确定问题究竟在哪里,无论是EF映射配置还是与存储库如何工作有关?

1 个答案:

答案 0 :(得分:1)

问题位于存储库中。 DbContext需要了解实体的存在。因此,在编辑和/或删除实体时,需要首先从数据库中提取实体。

description明确指出:

  

.Entry属性返回正在进行的上下文中的对象   跟踪背景。

因为您在创建上下文后立即直接使用此属性,所以上下文不跟踪这些实体,因此不知道某些内容已更改。因此无法生成正确的SQL语句。

根据您的其他设计,有几种方法可以解决这个问题。

删除它的一种方法是:

    public void DeleteModuleCriteriaLookup(ModuleCriteriaLookup[] lookups)
    {
        using (var ctx = new MyAppContext())
        {
            var moduleCriteriaId = lookups.First().ModuleCriteriaId;

            var moduleCritria = (
                from criteria in ctx.ModuleCriteria
                where criteria.ModuleCriteriaId == moduleCriteriaId
                select criteria
                ).Single();

            var lookupIdsToDelete = lookups.Select(l => l.ModuleCriteriaLookupId);

            var lookupsToDelete = (
                from lookup in moduleCritria.ModuleCriteriaLookups
                where lookupIdsToDelete.Contains(lookup.ModuleCriteriaLookupId)
                select lookup
                ).ToArray();

            foreach (var lookup in lookupsToDelete)
            {
                moduleCritria.ModuleCriteriaLookups.Remove(lookup);
            }

            ctx.SaveChanges();
        }
    }