Inserir / atualizar estrutura de entidades de muitos para muitos. Como eu faço isso?

104

Estou usando EF4 e sou novato. Tenho muitos para muitos em meu projeto e não consigo descobrir como inserir ou atualizar. Eu construí um pequeno projeto apenas para ver como ele deve ser codificado.

Suponha que eu tenha 3 mesas

  1. Classe: ClassID-ClassName
  2. Aluno: StudentID-FirstName-Sobrenome
  3. StudentClass: StudentID-ClassID

Depois de adicionar todo o relacionamento e atualizar o modelo através do navegador de modelos, percebi que StudentClass não aparece, esse parece ser o comportamento padrão.

Agora preciso fazer uma inserção e uma atualização. Como você faz isso? Qualquer amostra de código ou link onde eu possa baixar um exemplo, ou você pode poupar 5 minutos?

usuário9969
fonte

Respostas:

139

Em termos de entidades (ou objetos), você tem um Classobjeto que possui uma coleção de Studentse um Studentobjeto que possui uma coleção de Classes. Como sua StudentClasstabela contém apenas os Ids e nenhuma informação extra, o EF não gera uma entidade para a tabela de junção. Esse é o comportamento correto e é o que você espera.

Agora, ao fazer inserções ou atualizações, tente pensar em termos de objetos. Por exemplo, se você deseja inserir uma classe com dois alunos, crie o Classobjeto, os Studentobjetos, adicione os alunos à Studentscoleção da classe, adicione o Classobjeto ao contexto e chame SaveChanges:

using (var context = new YourContext())
{
    var mathClass = new Class { Name = "Math" };
    mathClass.Students.Add(new Student { Name = "Alice" });
    mathClass.Students.Add(new Student { Name = "Bob" });

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

Isso criará uma entrada na Classtabela, duas entradas na Studenttabela e duas entradas na StudentClasstabela ligando-as.

Você basicamente faz o mesmo para atualizações. Basta buscar os dados, modificar o gráfico adicionando e removendo objetos de coleções, chamar SaveChanges. Verifique esta questão semelhante para obter detalhes.

Editar :

De acordo com seu comentário, você precisa inserir um novo Classe adicionar dois existentes Studentsa ele:

using (var context = new YourContext())
{
    var mathClass= new Class { Name = "Math" };
    Student student1 = context.Students.FirstOrDefault(s => s.Name == "Alice");
    Student student2 = context.Students.FirstOrDefault(s => s.Name == "Bob");
    mathClass.Students.Add(student1);
    mathClass.Students.Add(student2);

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

Como os dois alunos já estão no banco de dados, eles não serão inseridos, mas como agora estão na Studentscoleção de Class, duas entradas serão inseridas na StudentClasstabela.

Yakimych
fonte
Olá, Obrigado pela sua resposta. Meu cenário é que eu preciso inserir 1 entrada na classe e como x seu exemplo 2 entradas na StudentClass e nenhuma entrada na StudentClass. Estou confuso como faço isso
user9969
ISSO FUNCIONOU UM TRATAMENTO. Em relação à atualização. Há algo especial que eu preciso fazer? EX .: apenas atualizar o className, por exemplo.
user9969
3
Isso adicionará uma sobrecarga de buscar no banco de dados os itens que precisam ser adicionados. O método Attach pode ser usado para adicionar apenas uma relação. Veja msdn.microsoft.com/en-us/data/jj592676.aspx e também stackoverflow.com/questions/11355019/…
Gnomo
3
AddToClasses é o DbSet for Class?
Jo Smo
1
Não se esqueça de inicializar suas coleções nos construtores para Class e Student. Por exemplo: public Class () {this.Students = new HashSet <Student> (); }
user1040323
40

Experimente este para atualizar:

[HttpPost]
public ActionResult Edit(Models.MathClass mathClassModel)
{
    //get current entry from db (db is context)
    var item = db.Entry<Models.MathClass>(mathClassModel);

    //change item state to modified
    item.State = System.Data.Entity.EntityState.Modified;

    //load existing items for ManyToMany collection
    item.Collection(i => i.Students).Load();

    //clear Student items          
    mathClassModel.Students.Clear();

    //add Toner items
    foreach (var studentId in mathClassModel.SelectedStudents)
    {
        var student = db.Student.Find(int.Parse(studentId));
        mathClassModel.Students.Add(student);
    }                

    if (ModelState.IsValid)
    {
       db.SaveChanges();
       return RedirectToAction("Index");
    }

    return View(mathClassModel);
}
Stritof
fonte
isso salvou meu dia, obrigado !!! eles são parte chave sendo o item.Collection (i => i.Students) .Load (); parte
Gerrie Pretorius
Apenas uma observação, eu tive uma InvalidOperationException ao fazer isso, algo como minha entidade não existia no contexto. Acabei de chamar context.MyEntityCollection.Attach (myEntity) para lidar com isso.
Kilazur 02 de
Nunca teria percebido isso. Muito obrigado.
Praia Jared de
1
Permita-me adicionar meus agradecimentos aqui. Escrevi e testei mais de 130 linhas de código nos últimos 4 dias tentando realizar essa tarefa, mas não consegui. Tentei isso e as coisas funcionaram perfeitamente. Embora eu não entenda o propósito de estabelecer a variável de item e não a use de forma alguma, estou feliz que funcione! Graças a um milhão de bajillion !!!! :)
Dude-Dastic
Considerando que a questão é Inserir E Atualizar, esta resposta completa a questão e a resposta aceita. Muito obrigado!
Vahx
5

Eu queria adicionar minha experiência nisso. Na verdade, EF, quando você adiciona um objeto ao contexto, ele altera o estado de todos os filhos e entidades relacionadas para Adicionado. Embora haja uma pequena exceção na regra aqui: se as entidades filhas / relacionadas estão sendo rastreadas pelo mesmo contexto, EF entende que essas entidades existem e não as adiciona. O problema acontece quando, por exemplo, você carrega os filhos / entidades relacionadas de algum outro contexto ou uma interface de usuário da web etc e então sim, EF não sabe nada sobre essas entidades e vai e adiciona todas elas. Para evitar isso, basta pegar as chaves das entidades e localizá-las (por exemplo, context.Students.FirstOrDefault(s => s.Name == "Alice"))no mesmo contexto em que deseja fazer a adição.

Athina
fonte
3

Eu uso a seguinte maneira para lidar com o relacionamento muitos-para-muitos onde apenas as chaves estrangeiras estão envolvidas.

Portanto, para inserir :

public void InsertStudentClass (long studentId, long classId)
{
    using (var context = new DatabaseContext())
    {
        Student student = new Student { StudentID = studentId };
        context.Students.Add(student);
        context.Students.Attach(student);

        Class class = new Class { ClassID = classId };
        context.Classes.Add(class);
        context.Classes.Attach(class);

        student.Classes = new List<Class>();
        student.Classes.Add(class);

        context.SaveChanges();
    }
}

Para deletar ,

public void DeleteStudentClass(long studentId, long classId)
{
    Student student = context.Students.Include(x => x.Classes).Single(x => x.StudentID == studentId);

    using (var context = new DatabaseContext())
    {
        context.Students.Attach(student);
        Class classToDelete = student.Classes.Find(x => x.ClassID == classId);
        if (classToDelete != null)
        {
            student.Classes.Remove(classToDelete);
            context.SaveChanges();
        }
    }
}
Lenglei
fonte
1

Na estrutura da entidade, quando o objeto é adicionado ao contexto, seu estado muda para Adicionado. EF também altera o estado de cada objeto para adicionado na árvore de objetos e, portanto, você está obtendo um erro de violação de chave primária ou registros duplicados são adicionados à tabela.

Nilesh Hirapra
fonte
2
Por favor edite a dar detalhes suficientes para que um usuário não tem que visitar o seu blog para obter a resposta.