简体   繁体   English

实体框架保存了太多子实体

[英]Entity Framework saves too many child entities

I'm working on a project that consists of multiple objects that I want to save to my database. 我正在开发一个由多个对象组成的项目,我希望将其保存到我的数据库中。 I'm using a single context and a series of repository classes to access them. 我正在使用单个上下文和一系列存储库类来访问它们。

When I try to save an entity, it seems to save all the virtual entities associated with it, even if that entity exists in the database already. 当我尝试保存实体时,它似乎保存与其关联的所有虚拟实体,即使该实体已存在于数据库中。

These are my classes: 这些是我的课程:

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; }
}

And this is the method I have for creating some data: 这是我创建一些数据的方法:

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();
    }
}

And here is associated repository code: 这里是相关的存储库代码:

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;
        }
    }
}

When I run this method, I expect it to insert a number of new Requirements into the database with the project ID of 1 and a standard ID of whatever standard it's on. 当我运行这个方法时,我希望它将一些新的需求插入到数据库中,项目ID为1,标准ID为其标准ID。 Instead, it creates a whole new project and a whole new standard for every requirement it adds, then assigns those IDs to the requirement. 相反,它为它添加的每个需求创建一个全新的项目和一个全新的标准,然后将这些ID分配给需求。

Each context keeps track of the entities loaded, modified and the entities added. 每个上下文都跟踪加载,修改的实体和添加的实体。

Your repositories need to look something like this.... 你的存储库需要看起来像这样....

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();
    }
}

From my understanding, you shouldn't have to manually set the state of the object you have modified unless you have detached it from its context. 根据我的理解,您不必手动设置已修改对象的状态,除非您已将其与上下文分离。 EF keeps track of the object state. EF跟踪对象状态。

I like to use something like this: 我喜欢用这样的东西:

    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();
    }
}

Then in the deriving repositories: 然后在派生存储库中:

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

I inject the Repositories into the relevant class using ninject but you should be able to get similar with: 我使用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);
}

and then doing what you need to do. 然后做你需要做的事。 The context is exposed via the interface IEntityRepositoryBase should you need to perform out-of-repository modifications and then SaveChanges() as well as any specific CRUD type methods in your repo. 如果需要执行存储库外修改,然后在存储库中执行SaveChanges()以及任何特定的CRUD类型方法,则通过接口IEntityRepositoryBase公开上下文。 Once out of scope the object and the underlying connection will be closed. 一旦超出范围,对象和底层连接将被关闭。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM