Melhores práticas para grandes soluções em Visual Studio (2008) [fechado]

87

Temos uma solução com cerca de 100+ projetos, a maioria deles C #. Naturalmente, leva muito tempo para abrir e construir, então estou procurando as melhores práticas para essas feras. Ao longo das linhas de perguntas que espero obter respostas, são:

  • como você lida melhor com referências entre projetos
    • deve "copiar local" estar ativado ou desativado?
  • cada projeto deve ser compilado em sua própria pasta, ou todos devem ser compilados na mesma pasta de saída (todos fazem parte do mesmo aplicativo)

  • As pastas de soluções são uma boa maneira de organizar as coisas?

Eu sei que dividir a solução em várias soluções menores é uma opção, mas isso vem com seu próprio conjunto de refatoração e dores de cabeça de construção, então talvez possamos guardar isso para um thread separado :-)

Eyvind
fonte
2
Posso perguntar por que uma única solução precisa de mais de 100 projetos? Cada um deles está criando sua própria montagem?
Neil N
1
Sim, eles são, e cada um deles representa uma parte da funcionalidade logicamente separada.
Eyvind
2
@Eyvind - Não é para isso que servem as classes e os namespaces? Que benefício as assembléias separadas trazem para você? Posso pensar em alguns, como atualizações potencialmente menores e uso de memória (se alguns não estiverem carregados), mas certamente há um custo em ter mais de 100 conjuntos para compilar e gerenciar.
si618
1
@Marque eu sinto sua dor. Temos até 94 projetos aqui. Não podemos fazer muito a respeito agora, pois teríamos que interromper o desenvolvimento em várias equipes para reestruturar. Temos 3 projetos EXE que fazem referência aos outros 91. Estou testando uma pasta de saída comum, até agora os ganhos são muito impressionantes.
Kevin
2
Cara, mais de 100? Isso não é nada pelo que temos ... chegando a quase 600 aqui agora ...
C Johnson

Respostas:

41

Você pode se interessar por estes dois artigos do MSBuild que escrevi.

MSBuild: práticas recomendadas para criar compilações confiáveis, parte 1

MSBuild: Melhores práticas para criar compilações confiáveis, parte 2

Especificamente na Parte 2, há uma seção Construindo grandes árvores de origem que você pode querer dar uma olhada.

Para responder brevemente às suas perguntas aqui:

  • CopyLocal? Com certeza desligue isso
  • Construir para uma ou mais pastas de saída? Construir em uma pasta de saída
  • Pastas de solução? É uma questão de gosto.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine: Usando MSBuild e Team Foundation Build

Sayed Ibrahim Hashimi
fonte
Obrigado cara, com certeza vou dar uma olhada nisso!
Eyvind
1
Direi que tenho este livro e é incrível. Ele me ajudou a automatizar minhas compilações TFS e as compilações de dev locais de forma muito extensa. Agora estou trabalhando em construções incrementais e o livro aborda o assunto muito profundamente. Vale o preço. Obrigado, Sayed. Continue com o ótimo trabalho.
irperez
Obrigado irperez pelos comentários
Sayed Ibrahim Hashimi
2
-1 para recomendação INCONDICIONAL para desativar CopyLocal. Definir CopyLocal = false pode causar problemas diferentes durante o tempo de implantação. Consulte a postagem do meu blog "NÃO Mude as referências do projeto" Copiar Local "para falsas, a menos que entenda as subsequências." ( Geekswithblogs.net/mnf/archive/2012/12/09/… )
Michael Freidgeim
Pode "construir para uma pasta de saída" causar problemas ao compilar com vários threads?
BrunoLM
9

1 para poupar o uso de pastas de solução para ajudar a organizar as coisas.

+1 para a construção do projeto em sua própria pasta. Inicialmente, tentamos uma pasta de saída comum e isso pode levar a referências sutis e difíceis de encontrar desatualizadas.

FWIW, usamos referências de projeto para soluções e, embora nuget seja provavelmente a melhor escolha hoje em dia, descobrimos que o svn: externals funciona bem para montagens internas de terceiros e (tipo de estrutura). Apenas adquira o hábito de usar um número de revisão específico em vez de HEAD ao fazer referência a svn: externals (culpado conforme cobrado :)

si618
fonte
2
+1 Também tenho problemas com a solução de pasta de saída comum. Não entendi o que significa "referências de projetos para soluções", explique um pouco mais ...
Mauricio Scheffer
Como em, usamos VS-> Adicionar Referência-> Projetos, em vez de VS-> Adicionar Referência-> Navegar. Eu sei que é uma questão pequena, mas algumas pessoas fazem isso de maneira diferente (e acho que isso causa mais dores de cabeça). Se você olhar o texto csproj do MSBuild, verá a propriedade ProjectReference em vez de Referência.
si618
Resposta interessante! Acho que ziguezagueamos onde você ziguezagueava. Não temos nenhuma pasta de solução e contamos com a nomenclatura do projeto (os projetos são nomeados da mesma forma que o namespace). Toda a nossa saída vai para uma única pasta, o que torna a configuração do desenvolvedor muito mais próxima da implantação. Se as dependências estiverem definidas corretamente, não temos referências desatualizadas.
Thomas Bratt
sim, o diretório de saída comum causa muitos problemas, especialmente ao usar o resharper. De fato, referências desatualizadas.
Florian Doyon
1
@Kevin, já faz vários anos, mas da memória, um exemplo são dois projetos referenciando o mesmo assembly, digamos uma biblioteca externa que não é referenciada por projeto. Tudo bem quando eles estão na mesma versão, mas então uma nova versão desta biblioteca é lançada, mas apenas um projeto atualiza sua referência, qual versão é usada na pasta de saída comum?
si618
8

Descarregue projetos que você não usa com frequência e compre um SSD. Um SSD não melhora o tempo de compilação, mas o Visual Studio se torna duas vezes mais rápido para abrir / fechar / compilar.

Nicolas Dorier
fonte
3
É importante notar que o descarregamento de um projeto é uma configuração por usuário, de modo que os desenvolvedores de sua equipe podem carregar apenas os projetos que lhes interessam. Isso realmente ajudou nossa equipe.
Página
Você pode usar a extensão visualstudiogallery.msdn.microsoft.com/… do Solution Load Manager para definir o que não carregar por padrão
Michael Freidgeim
6

Temos um problema semelhante, pois temos 109 projetos separados para lidar. Para responder às perguntas originais com base em nossas experiências:

1. Como você lida melhor com referências entre projetos

Usamos a opção de menu de contexto 'adicionar referência'. Se 'projeto' for selecionado, a dependência será adicionada ao nosso único arquivo de solução global por padrão.

2. Deve "copiar local" estar ativado ou desativado?

Fora de nossa experiência. A cópia extra apenas aumenta os tempos de construção.

3. Cada projeto deve ser compilado em sua própria pasta ou devem ser compilados na mesma pasta de saída (todos fazem parte do mesmo aplicativo)

Toda a nossa saída é colocada em uma única pasta chamada 'bin'. A ideia é que esta pasta seja a mesma de quando o software é implantado. Isso ajuda a evitar problemas que ocorrem quando a configuração do desenvolvedor é diferente da configuração de implantação.

4. As pastas de soluções são uma boa maneira de organizar as coisas?

Não em nossa experiência. A estrutura de pastas de uma pessoa é o pesadelo de outra. Pastas profundamente aninhadas apenas aumentam o tempo necessário para encontrar qualquer coisa. Temos uma estrutura completamente plana, mas nomeamos nossos arquivos de projeto, assemblies e namespaces da mesma forma.


Nossa forma de estruturar projetos conta com um único arquivo de solução. Construir isso leva muito tempo, mesmo que os próprios projetos não tenham mudado. Para ajudar com isso, geralmente criamos outro arquivo de solução de 'conjunto de trabalho atual'. Todos os projetos em que estamos trabalhando são adicionados a isso. Os tempos de compilação foram amplamente aprimorados, embora um problema que vimos é que o Intellisense falha para tipos definidos em projetos que não estão no conjunto atual.

Um exemplo parcial de nosso layout de solução:

\bin

OurStuff.SLN

OurStuff.App.Administrator
OurStuff.App.Common
OurStuff.App.Installer.Database
OurStuff.App.MediaPlayer
OurStuff.App.Operator
OurStuff.App.Service.Gateway
OurStuff.App.Service.CollectionStation
OurStuff.App.ServiceLocalLauncher
OurStuff.App.StackTester
OurStuff.Auditing
OurStuff.Data
OurStuff.Database
OurStuff.Database.Constants
OurStuff.Database.ObjectModel
OurStuff.Device
OurStuff.Device.Messaging
OurStuff.Diagnostics
...
[etc]
Thomas Bratt
fonte
Pode "construir para uma pasta de saída" causar problemas ao compilar com vários threads?
BrunoLM
4

Trabalhamos em um grande projeto semelhante aqui. As pastas de solução provaram ser uma boa maneira de organizar as coisas, e tendemos a apenas deixar o conjunto local de cópia como verdadeiro. Cada projeto é compilado em sua própria pasta e, então, sabemos que, para cada projeto implantável, temos o subconjunto correto dos binários no local.

Quanto à abertura e construção do tempo, isso será difícil de consertar sem quebrar em soluções menores. Você pode investigar a paralelização da compilação (google "Parallel MS Build" para obter uma maneira de fazer isso e integrar à IU) para melhorar a velocidade aqui. Além disso, observe o design e veja se refatorar alguns de seus projetos para resultar em menos no geral pode ajudar.

David M
fonte
3

Em termos de aliviar a dor de construção, você pode usar a opção "Configuration Manager ..." para construções para habilitar ou desabilitar a construção de projetos específicos. Você pode ter um "Projeto [n] Build" que pode excluir determinados projetos e usá-lo quando estiver almejando projetos específicos.

No que diz respeito aos mais de 100 projetos, sei que você não quer se preocupar com os benefícios de reduzir o tamanho da solução, mas acho que você não tem outra opção quando se trata de acelerar o tempo de carregamento (e uso de memória) de devenv.

Michael Meadows
fonte
Hm, isso foi rebaixado por estar errado ou apenas por discordar? Posso viver com o primeiro se você puder me dizer por quê.
Michael Meadows
Concordo, não gosto de downvoting sem comentários, então Michael, você recebe meu +1 para equilibrar a equação ;-)
si618
2
Aliás, eu pessoalmente não gosto de mexer com o gerenciamento de configuração para esse tipo de coisa. Por quê? porque se você acidentalmente confirmar sua configuração modificada, seu servidor de compilação ou outros desenvolvedores serão afetados.
si618
Acordado. Na verdade, o mesmo problema se aplica a soluções com muitos projetos. :)
Michael Meadows
3

O que normalmente faço com isso depende um pouco de como o processo de "depuração" realmente acontece. Normalmente, embora eu NÃO defina a cópia local como verdadeira. Eu configurei o diretório de compilação para cada projeto para produzir tudo no ponto final desejado.

Portanto, após cada compilação, tenho uma pasta preenchida com todas as dll e qualquer aplicativo do Windows / Web e todos os itens estão no local adequado. Não foi necessário copiar localmente, pois a dll acabou no lugar certo.

Nota

O que está acima funciona para minhas soluções, que normalmente são aplicativos da web e não tive problemas com referências, mas pode ser possível!

Vendedores Mitchel
fonte
3

Temos um problema semelhante. Nós resolvemos isso usando soluções menores. Temos uma solução master que abre tudo. Mas perf. sobre isso é ruim. Portanto, segmentamos soluções menores por tipo de desenvolvedor. Portanto, os desenvolvedores de banco de dados têm uma solução que carrega os projetos com os quais eles se importam, desenvolvedores de serviços e desenvolvedores de IU a mesma coisa. É raro quando alguém precisa abrir toda a solução para fazer o que precisa no dia a dia. Não é uma panacéia - tem suas vantagens e desvantagens. Consulte "modelo de várias soluções" neste artigo (ignore a parte sobre o uso de VSS :)

JP Alioto
fonte
2

Acho que com soluções tão grandes, a melhor prática deve ser separá-las. Você pode pensar na "solução" como um lugar para reunir os projetos necessários e talvez outras peças para trabalhar na solução de um problema. Ao dividir os mais de 100 projetos em várias soluções especializadas para desenvolver soluções para apenas uma parte do problema geral, você pode lidar com menos em um determinado momento, agilizando suas interações com os projetos necessários e simplificando o domínio do problema.

Cada solução produziria a saída pela qual é responsável. Essa saída deve ter informações de versão que podem ser definidas em um processo automatizado. Quando a saída é estável, você pode atualizar as referências em projetos e soluções dependentes com a distribuição interna mais recente. Se você ainda deseja entrar no código e acessar a fonte, pode realmente fazer isso com o servidor de símbolos da Microsoft, que o Visual Studio pode usar para permitir que você entre em assemblies referenciados e até mesmo busque o código-fonte.

O desenvolvimento simultâneo pode ser feito especificando interfaces antecipadamente e simulando os assemblies em desenvolvimento enquanto você espera por dependências que não estão completas, mas você deseja desenvolver contra elas.

Acho que esta é a melhor prática porque não há limite para o quão complexo o esforço geral pode se tornar quando você o quebra fisicamente dessa maneira. Colocar todos os projetos em uma única solução acabará atingindo um limite superior.

Espero que esta informação ajude.

Blake Taylor
fonte
2

Temos cerca de 60+ projetos e não usamos arquivos de solução. Temos uma mistura de projetos C # e VB.Net. O desempenho sempre foi um problema. Não trabalhamos em todos os projetos ao mesmo tempo. Cada desenvolvedor cria seus próprios arquivos de solução com base nos projetos em que está trabalhando. Os arquivos de solução não são verificados em nosso controle de origem.

Todos os projetos de biblioteca de classes seriam construídos em uma pasta CommonBin na raiz do diretório de origem. Projetos Executáveis ​​/ da Web são compilados em suas pastas individuais.

Não usamos referências de projeto, em vez disso, referência baseada em arquivo da pasta CommonBin. Eu escrevi uma tarefa MSBuild personalizada que inspecionaria os projetos e determinaria a ordem de construção.

Temos usado isso há alguns anos e não temos queixas.

Vasu Balakrishnan
fonte
3
Você se importaria de compartilhar sua tarefa personalizada do msbuild?
Andreapier
0

Tudo tem a ver com sua definição e visão do que são uma solução e um projeto. Na minha opinião, uma solução é apenas isso, um agrupamento lógico de projetos que resolvem um requisito muito específico. Desenvolvemos um grande aplicativo de intranet. Cada aplicação dentro daquela Intranet possui sua própria solução, que também pode conter projetos para exes ou serviços windows. E então temos uma estrutura centralizada com coisas como classes básicas e auxiliares e módulos httphandlers / http. A estrutura básica é bastante grande e é usada por todos os aplicativos. Ao dividir as várias soluções dessa forma, você reduz a quantidade de projetos exigidos por uma solução, já que a maioria deles não tem nada a ver um com o outro.

Ter tantos projetos em uma solução é simplesmente um design ruim. Não deveria haver razão para ter tantos projetos sob uma solução. O outro problema que vejo é com as referências de projeto, elas podem realmente bagunçar você eventualmente, especialmente se você quiser dividir sua solução em outras menores.

Meu conselho é fazer isso e desenvolver uma estrutura centralizada (sua própria implementação da Biblioteca Corporativa, se preferir). Você pode usar o GAC para compartilhar ou fazer referência direta ao local do arquivo para ter um armazenamento central. Você também pode usar a mesma tática para objetos de negócios centralizados.

Se você quiser fazer referência direta à DLL, deverá fazer referência a ela em seu projeto com cópia local false (em algum lugar como c: \ minhaempresa \ bin \ minhaempresa.dll). Em tempo de execução, você precisará adicionar algumas configurações ao app.config ou web.config para fazer referência a um arquivo que não está no GAC ou no bin de tempo de execução. Na verdade, não importa se é uma cópia local ou não, ou se a dll vai parar no bin ou até mesmo no GAC, porque a configuração irá sobrescrever ambos. Eu acho que é uma má prática copiar localmente e ter um sistema bagunçado. Você provavelmente terá que copiar localmente temporariamente se precisar depurar em um desses assemblies.

Você pode ler meu artigo sobre como usar uma DLL globalmente sem o GAC. Eu realmente não gosto do GAC principalmente porque ele impede a implantação de xcopy e não dispara uma reinicialização automática nos aplicativos.

http://nbaked.wordpress.com/2010/03/28/gac-alternative/

user306031
fonte
0

Definir CopyLocal = false reduzirá o tempo de construção, mas pode causar diversos problemas durante o tempo de implantação.

Existem muitos cenários, quando você precisa deixar Copy Local 'para True, por exemplo, projetos de nível superior, dependências de segundo nível, DLLs chamados por reflexão.

Minha experiência com a configuração de CopyLocal = false não foi bem-sucedida. Veja o resumo dos prós e contras em minha postagem do blog "NÃO Mude as referências de projeto" Copiar Local "para falsas, a menos que entenda as subsequências."

Michael Freidgeim
fonte