2014-01-20 14 views
2

Я довольно новичок в платформе Entity Framework, и это также моя первая публикация на Stack Overflow, так голая со мной, пожалуйста.Entity Framework сохраняет слишком много дочерних объектов

Я работаю над проектом, который состоит из нескольких объектов, которые я хочу сохранить в моей базе данных. Я использую один контекст и ряд классов репозитория для доступа к ним.

Когда я пытаюсь сохранить объект, он сохраняет все связанные с ним виртуальные объекты, даже если этот объект уже существует в базе данных.

Это мои классы:

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

Когда Я запускаю этот метод, я ожидаю, что он добавит несколько новых требований в базу данных с идентификатором проекта 1 и стандартный идентификатор любого стандарта, в котором он включен. Вместо этого он создает совершенно новый проект и совершенно новый стандарт для каждого добавляемого требования, а затем присваивает эти идентификаторы этому требованию.

Как я, так и мой наставник (который также является новичком в Entity Framework) очень смущены, и я не могу найти ничего об этом в другом месте. Я надеялся, что кто-то здесь сможет помочь.

Заранее благодарен!

+1

Возможно, ваша проблема сохраняется в другом контексте, чем тот, который использовался для извлечения вашего проекта и стандартных экземпляров. – B2K

+0

@ B2K вы говорите, что он должен удалить операторы 'using' и сделать' db' переменной класса? – Jonesopolis

+0

@ Jonesy не совсем. Проблема сложнее, просто переместив контекст в переменную класса. Он извлекает объекты из разных контекстов, а затем пытается сохранить их в новый контекст. Все они должны быть восстановлены и сохранены с использованием одного и того же. См. Принятый ответ ниже. – B2K

ответ

0

Каждый контекст отслеживает сущности, загруженные, измененные и добавленные сущности.

Ваши Хранилища должны выглядеть примерно так ....

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

Это работало превосходно. Сохранение всех объектов в одном экземпляре контекста - это то, что нам нужно было сделать. Я очень ценю помощь! Благодаря! – Drayvock

0

В моем понимании, вы не должны вручную установить состояние объекта, который вы изменили, если вы не отдельные его от его контекст. 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); 
} 

а затем делать то, что вам нужно. Контекст отображается через интерфейс IEntityRepositoryBase, если вам нужно выполнить модификации вне репозитория, а затем SaveChanges(), а также любые конкретные типы типов CRUD в вашем репо. После выхода из области действия объект и базовое соединение будут закрыты.

Смежные вопросы