Q:

Update de Many-To-Many EntityFramework não funciona

Tenho um relacionamento N:N entre Atividade e Projeto, onde uma atividade tem muitos projetos:
public class Atividade : ObjetoPersistente
{

    public Atividade()
    {
        StatusAtividade = EStatusAtividade.NaoIniciado;
        TipoAtividade = ETipoAtividade.NovaImplementacao;
        Usuario = new Usuario();
        Projetos = new List<Projeto>();
    }

    [JsonConverter(typeof(FormatoDataHoraMinutoNullableConverter))]
    public DateTime? DataHoraFim { get; set; }

    [JsonConverter(typeof(FormatoDataHoraMinutoNullableConverter))]
    public DateTime? DataHoraInicio { get; set; }

    public string DescricaoAtividade { get; set; }
    public string EstimativaInicialAtividade { get; set; }      

    public Usuario Usuario { get; set; }

    public string LoginUsuario
    {
        get { return Usuario.Login; }
    }      

    public EStatusAtividade StatusAtividade { get; set; }

    public string DescricaoStatusAtividade
    {
        get { return StatusAtividade.Descricao; }
    }           

    public string DescricaoTipoAtividade
    {
        get { return TipoAtividade.Descricao; }
    }

    public ETipoAtividade TipoAtividade { get; set; }
    public string TituloAtividade { get; set; }

    public long CodigoUsuario
    {
        get { return Usuario.Codigo; }
        set { Usuario.Codigo = value; }
    }

    public List<Projeto> Projetos { get; set; }

    public List<long> CodigoProjetos
    {
        get { return ObtenhaListaDeCodigosPorListaDeObjetos(Projetos); }

        set { Projetos = ObtenhaListaDeObjetoPorListaDeCodigos<Projeto>(value); }
    }

    public override bool Equals(object obj)
    {
        return (obj is Atividade) && (obj as Atividade).Codigo.Equals(Codigo);
    }

    public override int GetHashCode()
    {
        return Codigo.GetHashCode();
    }
}

Projeto:
public class Projeto : ObjetoPersistente, IObjetoElementoOption
{       
    public string Nome { get; set; }

    public override bool Equals(object obj)
    {
        return (obj is Projeto) && (obj as Projeto).Codigo.Equals(Codigo);
    }

    public override int GetHashCode()
    {
        return Codigo.GetHashCode();
    }

    public string Valor
    {
        get { return Codigo.ToString(); }
    }

    public string Descricao {
        get { return Nome; }
    }
}

Os mapeamentos estão desta forma:
    public AtividadeMap()
    {
        HasKey(a => a.Codigo);

        ToTable("tb_atividade");

        Property(a => a.TituloAtividade).HasColumnName("titulo_atividade");
        Property(a => a.DescricaoAtividade).HasColumnName("descricao_atividade");
        Property(a => a.DataHoraInicio).HasColumnName("data_hora_inicio");
        Property(a => a.DataHoraFim).HasColumnName("data_hora_fim");
        Property(a => a.Codigo).HasColumnName("pk_atividade");
        Property(a => a.StatusAtividade.Identificador).HasColumnName("status_atividade");
        Property(a => a.EstimativaInicialAtividade).HasColumnName("estimativa_inicial_atividade");
        Property(a => a.TipoAtividade.Identificador).HasColumnName("tipo_atividade");
        Property(a => a.CodigoUsuario).HasColumnName("fk_usuario");

        HasRequired(a => a.Usuario)
            .WithMany()
            .HasForeignKey(a => a.CodigoUsuario);

        HasMany(a => a.Projetos)
            .WithMany()
            .Map(pa =>
            {
                pa.MapLeftKey("fk_atividade");
                pa.MapRightKey("fk_projeto");
                pa.ToTable("tb_atividade_projeto");
            });
    }

public class ProjetoMap : EntityTypeConfiguration<Projeto>
{
    public ProjetoMap()
    {            
        HasKey(a => a.Codigo);

        ToTable("tb_projeto");

        Property(p => p.Codigo).HasColumnName("pk_projeto");
        Property(a => a.Nome).HasColumnName("nome");
    }

}

O processo de consulta está OK, porém, inclusões e alterações não funcionam.
O que está faltando?
Após pesquisar sobre o assunto, achei várias respostas como essa, que em resumo, na minha situação, eu teria que carregar cada Projeto da lista do objeto Atividade para realizar um Attach na classe de contexto:
https://stackoverflow.com/questions/14631309/updating-many-to-many-navigation-property-in-entity-framework-6-changes-not-bei
Esta é a unica forma de realizar um update na situação de relacionamentos many-to-many? Poderia tornar genérica a persistência destes relacionamentos?

A:

Bom, a priori você não precisa carregar as entidades relacionadas sempre. Especialmente quando for fazer alterações.
Agora, sempre que for usar uma referência a uma entidade que existe no banco de dados, você precisa dar attach ou recuperar as entities. Nem sempre buscar no banco de dados, mas precisa dar attach no dbcontext.
Curiosamente, eu tenho uma pergunta exatamente assim no SO, que não obteve resposta (e eu tive que voltar e responder): Pergunta SO.
No começo parece estranho: ter que recuperar as entidades que já estão lá. Mas funciona assim. Digamos que você já tem os projetos, digamos retornados da interface de usuários em um aplicativo web. Digamos também que você confia que essses objetos estão ok. Você poderia criar uma nova atividade e inserir esses projetos, certo? Não. Se fizer isso assim, o DBContext vai, na verdade, criar novos projetos, pois esses que você está usando não está attached.
Uma forma alternativa de fazer é dar attach e mudar o state das entidades pra unchanged. Assim o DBContext não altera esses projetos, mas também não os modifica.
Mas no fundo, a prática de buscá-los novamente no banco é correta. Não imagino um cenário onde você possa ter objetos dettached que sejam seguros, nem que seja pelo fato que foram recuperados anteriormente (possivelmente BEM antes) possam ter sido removidos do BD.

Portanto, sim, essa lógica com attach é o único jeito de atualizar entidades com relações N:N ou 1:N.
Você pode encapsular isso em seus repositórios, mas não de forma genérica. É exatamente o fato que cada entidade tem relações especiais que faz com que sejam necessárias essas lógicas.

A:

Acho que entendi o que você está tentando fazer. Seu relacionamento está errado. Se um Projeto tem N Atividades, e uma Atividade pertence a N Projetos, você nunca poderia usar algo assim: 
public class Atividade : ObjetoPersistente
{

    public Atividade()
    {
        ...
        Projetos = new List<Projeto>();
    }

    ...
    public List<Projeto> Projetos { get; set; }    
    ...
}

Com isso você está dizendo que uma Atividade tem N Projetos, mas que um Projeto pertence a apenas uma Atividade. 
O correto seria:
[Table("tb_atividade")]
public class Atividade : ObjetoPersistente
{

    public Atividade()
    {
        ...
        // Retire isso
        // Projetos = new List<Projeto>();
    }

    ...
    // Não use Projetos diretamente. Use uma tabela associativa.
    public virtual ICollection<ProjetoAtividade> ProjetoAtividades { get; set; }    
    ...
}

Projeto também recebe ProjetoAtividades, pois é uma associação:
[Table("tb_projeto")]
public class Projeto : ObjetoPersistente, IObjetoElementoOption
{       
    ...
    public virtual ICollection<ProjetoAtividade> ProjetoAtividades { get; set; }  
}

Agora você precisa mapear também ProjetoAtividade:
[Table("tb_atividade_projeto")]
public class ProjetoAtividade
{
    [Key]
    [Column("fk_atividade")]
    public int AtividadeId { get; set; }

    [Key]
    [Column("fk_projeto")]
    public int ProjetoId { get; set; }

    // Estas são propriedades de navegação.
    // O Entity Framework carrega essas classes automaticamente.
    public virtual Projeto Projeto { get; set; }
    public virtual Atividade Atividade { get; set; }
}

Repare que não preciso usar Fluent API aqui. Posso apenas decorar as propriedades com Attributes e o Entity Framework faz todo o resto sozinho. 
Inclusões e Modificações
Como o mapeamento dos Models está errado, você precisará mapear Codigo nas classes para funcionar:
[Table("tb_atividade")]
public class Atividade : ObjetoPersistente
{
    [Key]
    // Não sei se a coluna no banco chama "codigo", mas suponho que sim.
    [Column("codigo")]
    public int Codigo { get; set; }

    ...
}

[Table("tb_projeto")]
public class Projeto : ObjetoPersistente, IObjetoElementoOption
{       
    ...
    [Key]
    // Não sei se a coluna no banco chama "codigo", mas suponho que sim.
    [Column("codigo")]
    public int Codigo { get; set; }
}

Sobre inclusões, para inserir uma nova associação, faça da seguinte forma:
// Carregue o projeto
var projeto = contexto.Projetos.FirstOrDefault(p => p.Codigo == /* Coloque o valor da chave aqui */);
// Carregue a atividade
var atividade = contexto.Atividades.FirstOrDefault(a => a.Codigo == /* Coloque o valor da chave aqui */);
if (projeto != null && atividade != null)
{
    var projetoAtividade = new ProjetoAtividade 
    {
        // É assim mesmo que usa. __NUNCA__ defina o Id diretamente.
        Atividade = atividade,
        Projeto = projeto
    }

    contexto.ProjetoAtividades.Add(projetoAtividade);
    contexto.SaveChanges();
}

Como a alteração para a entidade associativa não faz sentido, vou ensinar como fazer uma exclusão:
var projetoAtividade = contexto.ProjetoAtividades.FirstOrDefault(/* Coloque aqui a condição para selecionar a associação, aqui usando os Ids */);
if (projetoAtividade != null)
{
    contexto.ProjetoAtividades.Remove(projetoAtividade);
    contexto.SaveChanges();
}

