Olá Rafaela,
Se você tentar filtrar uma venda apenas chamando o FirstOrDefault, assim:
Venda venda = contexto.Vendas.FirstOrDefault(v2 => v2.ID == 1);
mesmo que você tenha o atributo IList<ProdutoVenda> ProdutoVenda na Venda , ele não virá preenchido no objeto Venda retornado. Você precisa dar o comando Include(v => v.ProdutoVenda) para dizer ao Entity Framework que quando ele filtrar a Venda, tem que trazer junto a sua lista de ProdutoVenda.
Venda venda = contexto.Vendas
                        .Include(v => v.ProdutoVenda)
                        .FirstOrDefault(v2 => v2.ID == 1);
Agora sim conseguimos fazer um foreach por exemplo dentro dessa lista dado que ela vem preenchida. 
foreach(ProdutoVenda pv in venda.ProdutoVenda) {
    ...
}
Só que agora, se você tentar fazer um foreach por essa lista para pegar o nome de cada Produto do ProdutoVenda
foreach(ProdutoVenda pv in venda.ProdutoVenda) {
    string nome = pv.Produto.Nome;
}
Ele vai dará uma exception na linha pv.Produto.Nome informando que o Produto do pv está null. Ou seja, mesmo incluindo a  IList<ProdutoVenda> , cada ProdutoVenda veio sem o seu Produto.  Para trazer este item junto com o ProdutoVenda, ai sim usamos o ThenInclude( pv => pv.Produto), dizendo que para cada pv (ou seja, cada ProdutoVenda que será colocado na lista) é para incluir também o seu pv.Produto.
Venda venda = contexto.Vendas
                        .Include(v => v.ProdutoVenda) //traz na Venda a List<ProdutoVenda> preenchida
                        .ThenInclude( pv => pv.Produto) //para cada ProdutoVenda traz também o Produto
                        .FirstOrDefault(v2 => v2.ID == 1);