Não é possível criar um relacionamento muitos-para-muitos com uma tabela de junção personalizada. Em um relacionamento muitos para muitos, a EF gerencia a tabela de junção internamente e oculta. É uma tabela sem uma classe Entity no seu modelo. Para trabalhar com uma tabela de junção com propriedades adicionais, você precisará criar dois relacionamentos um para muitos. Pode ficar assim:
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class MemberComment
{
[Key, Column(Order = 0)]
public int MemberID { get; set; }
[Key, Column(Order = 1)]
public int CommentID { get; set; }
public virtual Member Member { get; set; }
public virtual Comment Comment { get; set; }
public int Something { get; set; }
public string SomethingElse { get; set; }
}
Se agora você deseja encontrar todos os comentários de membros com LastName
= "Smith", por exemplo, você pode escrever uma consulta como esta:
var commentsOfMembers = context.Members
.Where(m => m.LastName == "Smith")
.SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
.ToList();
... ou ...
var commentsOfMembers = context.MemberComments
.Where(mc => mc.Member.LastName == "Smith")
.Select(mc => mc.Comment)
.ToList();
Ou, para criar uma lista de membros com o nome "Smith" (presumimos que exista mais de um), juntamente com seus comentários, você pode usar uma projeção:
var membersWithComments = context.Members
.Where(m => m.LastName == "Smith")
.Select(m => new
{
Member = m,
Comments = m.MemberComments.Select(mc => mc.Comment)
})
.ToList();
Se você deseja encontrar todos os comentários de um membro com MemberId
= 1:
var commentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1)
.Select(mc => mc.Comment)
.ToList();
Agora você também pode filtrar pelas propriedades em sua tabela de junção (o que não seria possível em um relacionamento muitos para muitos), por exemplo: Filtre todos os comentários do membro 1 que possui uma propriedade 99 Something
:
var filteredCommentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1 && mc.Something == 99)
.Select(mc => mc.Comment)
.ToList();
Devido ao carregamento lento, as coisas podem ficar mais fáceis. Se você tiver um carregamento Member
, poderá obter os comentários sem uma consulta explícita:
var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);
Eu acho que esse carregamento lento buscará os comentários automaticamente nos bastidores.
Editar
Apenas por diversão, alguns exemplos mais sobre como adicionar entidades e relacionamentos e como excluí-los neste modelo:
1) Crie um membro e dois comentários deste membro:
var member1 = new Member { FirstName = "Pete" };
var comment1 = new Comment { Message = "Good morning!" };
var comment2 = new Comment { Message = "Good evening!" };
var memberComment1 = new MemberComment { Member = member1, Comment = comment1,
Something = 101 };
var memberComment2 = new MemberComment { Member = member1, Comment = comment2,
Something = 102 };
context.MemberComments.Add(memberComment1); // will also add member1 and comment1
context.MemberComments.Add(memberComment2); // will also add comment2
context.SaveChanges();
2) Adicione um terceiro comentário de member1:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
var comment3 = new Comment { Message = "Good night!" };
var memberComment3 = new MemberComment { Member = member1,
Comment = comment3,
Something = 103 };
context.MemberComments.Add(memberComment3); // will also add comment3
context.SaveChanges();
}
3) Crie um novo membro e relacione-o com o comentário existente2:
var comment2 = context.Comments.Where(c => c.Message == "Good evening!")
.SingleOrDefault();
if (comment2 != null)
{
var member2 = new Member { FirstName = "Paul" };
var memberComment4 = new MemberComment { Member = member2,
Comment = comment2,
Something = 201 };
context.MemberComments.Add(memberComment4);
context.SaveChanges();
}
4) Crie um relacionamento entre o membro2 existente e o comentário3:
var member2 = context.Members.Where(m => m.FirstName == "Paul")
.SingleOrDefault();
var comment3 = context.Comments.Where(c => c.Message == "Good night!")
.SingleOrDefault();
if (member2 != null && comment3 != null)
{
var memberComment5 = new MemberComment { Member = member2,
Comment = comment3,
Something = 202 };
context.MemberComments.Add(memberComment5);
context.SaveChanges();
}
5) Exclua esse relacionamento novamente:
var memberComment5 = context.MemberComments
.Where(mc => mc.Member.FirstName == "Paul"
&& mc.Comment.Message == "Good night!")
.SingleOrDefault();
if (memberComment5 != null)
{
context.MemberComments.Remove(memberComment5);
context.SaveChanges();
}
6) Exclua member1 e todos os seus relacionamentos com os comentários:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
context.Members.Remove(member1);
context.SaveChanges();
}
Isso exclui as relações em MemberComments
demasiado porque as relações um-para-muitos entre Member
e MemberComments
e entre Comment
e MemberComments
são configurados com eliminação em cascata por convenção. E esse é o caso, porque MemberId
e CommentId
in MemberComment
são detectados como propriedades de chave estrangeira para as propriedades Member
e de Comment
navegação e, como as propriedades FK são do tipo não nulo, int
é necessário o relacionamento que finalmente causa a configuração de exclusão em cascata. Faz sentido nesse modelo, eu acho.
OnModelCreating
. O exemplo depende apenas de convenções de mapeamento e anotações de dados.MemberId
eCommentId
colunas e não uma terceira coluna adicionalMember_CommentId
(ou algo parecido) - o que significa que você não tem nomes de correspondência exata objetos através de suas chavesExcelente resposta de Slauma.
Vou postar o código para fazer isso usando o mapeamento de API fluente .
Na sua
DbContext
classe derivada, você pode fazer o seguinte:Tem o mesmo efeito que a resposta aceita, com uma abordagem diferente, que não é nem melhor nem pior.
Edição:
Eu mudei CreatedDate de bool para DateTime.EDIÇÃO 2: Devido à falta de tempo, coloquei um exemplo de um aplicativo no qual estou trabalhando para garantir que isso funcione.
fonte
In your classes you can easily describe a many to many relationship with properties that point to each other.
retirado de: msdn.microsoft.com/en-us/data/hh134698.aspx . Julie Lerman não pode estar errada.Comments
propriedadeMember
. E você não pode simplesmente corrigir isso renomeando aHasMany
chamada para,MemberComments
porque aMemberComment
entidade não possui uma coleção inversaWithMany
. Na verdade, você precisa configurar dois relacionamentos um para muitos para obter o mapeamento correto.@Esteban, o código que você forneceu está correto, obrigado, mas incompleto, eu testei. Faltam propriedades na classe "UserEmail":
Publico o código que testei se alguém estiver interessado. Saudações
fonte
Quero propor uma solução em que ambos os sabores de uma configuração muitos-para-muitos possam ser alcançados.
O "problema" é que precisamos criar uma exibição que tenha como alvo a tabela de junção, pois o EF valida que a tabela de um esquema pode ser mapeada no máximo uma vez por
EntitySet
.Essa resposta se soma ao que já foi dito nas respostas anteriores e não substitui nenhuma dessas abordagens, ela se baseia nelas.
O modelo:
A configuração:
O contexto:
A partir de Salumã (@Saluma) resposta
Isso ainda funciona ...
... mas agora também pode ser ...
Isso ainda funciona ...
... mas agora também pode ser ...
Se você deseja remover um comentário de um membro
Se você quiser
Include()
comentar os membrosTudo isso parece açúcar sintático, no entanto, você recebe algumas vantagens se você estiver disposto a passar pela configuração adicional. De qualquer maneira, você parece conseguir o melhor de ambas as abordagens.
fonte
EntityTypeConfiguration<EntityType>
a chave e as propriedades do tipo de entidade. Por exemplo,Property(x => x.MemberID).HasColumnType("int").IsRequired();
parece ser redundante compublic int MemberID { get; set; }
. Você poderia esclarecer meu entendimento confuso, por favor?TLDR; (semi-relacionado a um erro do editor EF no EF6 / VS2012U5) se você gerar o modelo a partir do DB e não puder ver a tabela m: m atribuída: exclua as duas tabelas relacionadas -> Salvar .edmx -> Gerar / adicionar do banco de dados - > Salvar.
Para aqueles que vieram aqui se perguntando como obter um relacionamento muitos-para-muitos com colunas de atributo para serem mostrados no arquivo .edmx EF (como atualmente não seria exibido e tratado como um conjunto de propriedades de navegação), E você gerou essas classes da sua tabela de banco de dados (ou banco de dados primeiro no jargão da Microsoft, acredito).
Exclua as 2 tabelas em questão (para pegar o exemplo do OP, Membro e Comentário) no seu .edmx e adicione-as novamente através de 'Gerar modelo do banco de dados'. (ou seja, não tente permitir que o Visual Studio os atualize - exclua, salve, adicione, salve)
Em seguida, ele criará uma terceira tabela de acordo com o que é sugerido aqui.
Isso é relevante nos casos em que um relacionamento entre muitos e muitos é adicionado inicialmente e os atributos são projetados no banco de dados posteriormente.
Isso não ficou claro imediatamente neste tópico / no Google. Então, basta colocá-lo lá fora, já que este é o link 1 no Google, procurando o problema, mas vindo primeiro do lado do banco de dados.
fonte
Uma maneira de resolver esse erro é colocar o
ForeignKey
atributo em cima da propriedade que você deseja como chave estrangeira e adicionar a propriedade de navegação.Nota: No
ForeignKey
atributo, entre parênteses e aspas duplas, coloque o nome da classe referida dessa maneira.fonte