【问题标题】:Entity Framework saves too many child entitiesEntity Framework 保存了太多子实体
【发布时间】:2014-01-20 21:17:59
【问题描述】:

我正在处理一个包含多个要保存到数据库中的对象的项目。我正在使用单个上下文和一系列存储库类来访问它们。

当我尝试保存一个实体时,它似乎保存了与其关联的所有虚拟实体,即使该实体已经存在于数据库中。

这些是我的课程:

public class Requirement
{
    public int ID { get; set; }
    public DateTime DateDue { get; set; }
    public DateTime DateCompleted { get; set; }

    public virtual Standard Standard { get; set; }
    public virtual Project Project { get; set; }
}

public class Standard
{
    public int ID { get; set; }
    public int AgencyID { get; set; }
    public string Name { get; set; }

    public virtual Agency Agency { get; set; }
}


public class Project
{
    public int ID { get; set; }
    public bool Active { get; set; }

    public virtual Agency Agency { get; set; }
    public virtual Department Department { get; set; }
}

这是我创建一些数据的方法:

public class RequirementRepository
{
    public static string CreateMockData()
    {
        StandardRepository stdRep = new StandardRepository();
        ProjectRepository projRep = new ProjectRepository();
        RequirementRepository reqRep = new RequirementRepository();
        Project project = projRep.Find(1);
        StringBuilder sb = new StringBuilder()
        foreach (Standard s in stdRep.FindByAgencyID(project.Agency.ID))
        {
            Requirement r = new Requirement();
            r.Project = project;
            r.Standard = s;
            r.DateCompleted = (DateTime)SqlDateTime.MaxValue;
            r.DateDue = DateTime.Now.AddDays(90);

            r = reqRep.Save(r);

            sb.AppendLine(String.Format("Saved Requirement ID {0} with Project ID {1}<br>", r.ID, r.Project.ID));
        }

        return sb.ToString();
    }
}

这里是相关的存储库代码:

public class ProjectRepository
{
    public Project Find(int id)
    {
        using (var db = new MyContext())
        {
            return db.Projects
                .Include(p => p.Agency)
                .Include(p => p.Department)
                .First(p => p.ID.Equals(id));
        }
    }
}


public class StandardRepository
{
    public List<Standard> FindByAgencyID(int agencyID)
    {
        using (var db = new MyContext())
        {
            return db.Standards.Where(r => r.AgencyID == agencyID).ToList();
        }
    }
}



public class RequirementRepository
{
    public Requirement Save(Requirement requirement)
    {
        using (var db = new MyContext())
        {
            Requirement retVal = requirement;
            if (requirement.ID.Equals(0))
            {
                retVal = db.Requirements.Add(requirement);
            }
            else
            {
                db.Entry(requirement).State = EntityState.Modified;
            }

            db.SaveChanges();
            return retVal;
        }
    }
}

当我运行这个方法时,我希望它会在数据库中插入一些新的需求,项目 ID 为 1,标准 ID 为它所在的任何标准。相反,它会为它添加的每个需求创建一个全新的项目和一个全新的标准,然后将这些 ID 分配给需求。

【问题讨论】:

  • 您的问题可能是您保存到与用于检索项目和标准实例的上下文不同的上下文中。
  • @B2K 你是说他应该删除using 语句并将db 设为类变量吗?
  • @Jonesy 不完全是。这个问题比简单地将上下文移动到类变量中更复杂。他正在从不同的上下文中检索对象,然后尝试将它们保存到新的上下文中。它们都需要使用同一个进行检索和保存。请参阅下面接受的答案。

标签: c# asp.net .net entity-framework


【解决方案1】:

每个上下文都会跟踪加载、修改和添加的实体。

您的存储库需要看起来像这样......

public class StandardRepository
{
    MyContext _context;

    public StandardRepository(MyContext context)
    {
        _context = context;
    }

    public List<Standard> FindByAgencyID(int agencyID)
    {
        return _context.Standards.Where(r => r.AgencyID == agencyID).ToList();
    }
}

public class RequirementRepository
{
    MyContext _context;

    public RequirementRepository(MyContext context)
    {
        _context = context;
    }

    public Requirement Save(Requirement requirement)
    {
    Requirement retVal = requirement;
    if (requirement.ID.Equals(0))
    {
        retVal = _context.Requirements.Add(requirement);
    }
    else
    {
        _context.Entry(requirement).State = EntityState.Modified;
    }

    _context.SaveChanges();
    return retVal;
    }
}

public class RequirementRepository
{
    public static string CreateMockData()
    {
        using(MyContext context = new MyContext())
        {
                StandardRepository stdRep = new StandardRepository(context);
                ProjectRepository projRep = new ProjectRepository(context);
                RequirementRepository reqRep = new RequirementRepository(context);
                Project project = projRep.Find(1);
                StringBuilder sb = new StringBuilder()
                foreach (Standard s in stdRep.FindByAgencyID(project.Agency.ID))
                {
                Requirement r = new Requirement();
                r.Project = project;
                r.Standard = s;
                r.DateCompleted = (DateTime)SqlDateTime.MaxValue;
                r.DateDue = DateTime.Now.AddDays(90);

                r = reqRep.Save(r);

                sb.AppendLine(String.Format("Saved Requirement ID {0} with Project ID {1}<br>", r.ID, r.Project.ID));
            }
        }
        return sb.ToString();
    }
}

【讨论】:

  • 这非常有效。将所有实体保持在同一个上下文实例中是我们需要做的。我真的很感激帮助!谢谢!
【解决方案2】:

据我了解,您不必手动设置已修改对象的状态,除非您已将其从上下文中分离出来。 EF 跟踪对象状态。

我喜欢用这样的东西:

    public abstract class EntityRepositoryBase<TEntity> : IDisposable, IEntityRepositoryBase<TEntity> where TEntity : class , IEntityWithId
    {
        protected EntityRepositoryBase()
        {
            Context = new SomeEntities();
        } 

        public abstract ObjectSet<TEntity> EntityCollection { get; }
        public SomeEntities Context { get; set; }

        public TEntity GetById(int id)
        {
            return EntityCollection
                .FirstOrDefault(x => x.Id == id);
        }

    public void Dispose()
    {
        Context.Dispose();
    }
}

然后在派生存储库中:

public class AnswerRepository : EntityRepositoryBase<AnswerEntity>, IAnswerRepository
{
    public override ObjectSet<AnswerEntity> EntityCollection
    {
        get { return Context.AnswerEntities; }
    }
}

我使用 ninject 将存储库注入到相关类中,但您应该能够获得类似的结果:

using (var repo = new AnswerRepository()) 
{ 
// modifying via Context
var someEntity = repo.GetById(someId);
someEntity.Value = "1";
repo.Context.SaveChanges();

//modifying via repo
repo.Delete(anotherEntity);
}

然后做你需要做的事情。如果您需要执行存储库外修改,然后执行 SaveChanges() 以及存储库中的任何特定 CRUD 类型方法,则上下文通过接口 IEntityRepositoryBase 公开。一旦超出范围,对象和底层连接将被关闭。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-25
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 2020-02-06
    • 2017-04-03
    • 1970-01-01
    相关资源
    最近更新 更多