MSBuild tidak menyalin referensi (file DLL) jika menggunakan dependensi proyek dalam solusi

273

Saya memiliki empat proyek dalam solusi Visual Studio saya (semua orang yang menargetkan .NET 3.5) - untuk masalah saya hanya dua yang penting:

  1. MyBaseProject <- pustaka kelas ini mereferensikan file DLL pihak ketiga (elmah.dll)
  2. MyWebProject1 <- proyek aplikasi web ini memiliki referensi ke MyBaseProject

Saya menambahkan referensi elmah.dll ke MyBaseProject di Visual studio 2008 dengan mengklik "Tambahkan referensi ..." → "Jelajahi" tab → memilih "elmah.dll".

Sifat-sifat Referensi Elmah adalah sebagai berikut:

  • Alias ​​- global
  • Salin lokal - benar
  • Budaya -
  • Deskripsi - Modul dan Penangan Kesalahan Kesalahan (ELMAH) untuk ASP.NET
  • Jenis File - Perakitan
  • Path - D: \ webs \ folder lain \ _myPath \ __ alat \ elmah \ Elmah.dll
  • Terselesaikan - Benar
  • Versi runtime - v2.0.50727
  • Versi yang ditentukan - salah
  • Nama Kuat - salah
  • Versi - 1.0.11211.0

Di MyWebProject1 saya menambahkan referensi ke Project MyBaseProject oleh: "Tambahkan referensi ..." → "Projects" tab → pilih "MyBaseProject". Properti referensi ini sama kecuali anggota berikut:

  • Deskripsi -
  • Path - D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Versi - 1.0.0.0

Jika saya menjalankan build di Visual Studio file elmah.dll disalin ke direktori bin MyWebProject1 saya , bersama dengan MyBaseProject.dll!

Namun jika saya membersihkan dan menjalankan MSBuild untuk solusi (via D: \ webs \ CMS> C: \ WINDOWS \ Microsoft.NET \ Framework \ v3.5 \ MSBuild.exe / t: ReBuild / p: Konfigurasi = Debug MyProject.sln ) elmah.dll hilang dalam direktori bin MyWebProject1 - walaupun build itu sendiri tidak mengandung peringatan atau kesalahan!

Saya sudah memastikan bahwa .csproj dari MyBaseProject berisi elemen pribadi dengan nilai "true" (yang seharusnya menjadi alias untuk " copy local " di Visual Studio):

<Reference Include="Elmah, Version=1.0.11211.0, Culture=neutral, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\mypath\__tools\elmah\Elmah.dll</HintPath>
    **<Private>true</Private>**
</Reference>

(Tag pribadi tidak muncul di xml .csproj secara default, meskipun Visual Studio mengatakan "salin lokal" benar. Saya beralih "salin lokal" ke false - save - dan atur kembali ke true lagi - save!)

Apa yang salah dengan MSBuild? Bagaimana cara mendapatkan referensi (elmah.dll) disalin ke tempat MyWebProject1?

Saya TIDAK ingin menambahkan tindakan copy postbuild ke perintah postbuild setiap proyek! (Bayangkan saya akan memiliki banyak proyek tergantung pada MyBaseProject!)

toebens
sumber
11
Saya ingin mendapatkan jawaban yang lebih jelas mengapa ini terjadi.
David Faivre
1
Lihatlah jawaban yang diberikan di sini
Anuroopa Shenoy
1
ada solusi akhir dengan sampel kode sumber lengkap yang bisa mengatasinya?
Kiquenet
2
Lihat jawaban stackoverflow.com/a/21055664/21579 di bawah ini oleh @deadlydog. Penjelasan yang bagus dan menyelesaikan masalah untuk saya ... jawaban yang paling banyak dipilih di bawah ini tidak tepat untuk VS2012.
Jeff Widmer

Jawaban:

153

Saya tidak yakin mengapa itu berbeda ketika membangun antara Visual Studio dan MsBuild, tapi di sini adalah apa yang saya temukan ketika saya mengalami masalah ini di MsBuild dan Visual Studio.

Penjelasan

Untuk skenario sampel, misalkan kita memiliki proyek X, rakitan A, dan rakitan B. Rakitan A rakitan rakitan B, sehingga proyek X mencakup referensi untuk A dan B. Selain itu, proyek X mencakup kode yang merujuk rakitan A (misalnya A. SomeFunction ()). Sekarang, Anda membuat proyek Y baru yang merujuk proyek X.

Jadi rantai ketergantungan terlihat seperti ini: Y => X => A => B

Visual Studio / MSBuild mencoba untuk menjadi pintar dan hanya membawa referensi ke proyek Y yang dideteksi oleh proyek X; ia melakukan ini untuk menghindari polusi referensi dalam proyek Y. Masalahnya adalah, karena proyek X tidak benar-benar mengandung kode apa pun yang secara eksplisit menggunakan perakitan B (mis. B.SomeFunction ()), VS / MSBuild tidak mendeteksi bahwa B diperlukan oleh X, dan dengan demikian tidak menyalinnya ke direktori bin proyek Y; hanya menyalin rakitan X dan A.

Larutan

Anda memiliki dua opsi untuk menyelesaikan masalah ini, yang keduanya akan mengakibatkan perakitan B disalin ke direktori bin proyek Y:

  1. Tambahkan referensi ke rakitan B di proyek Y.
  2. Tambahkan kode dummy ke file di proyek X yang menggunakan perakitan B.

Secara pribadi saya lebih suka opsi 2 karena beberapa alasan.

  1. Jika Anda menambahkan proyek lain di masa depan yang merujuk proyek X, Anda tidak perlu ingat untuk juga menyertakan referensi ke rakitan B (seperti yang harus Anda lakukan dengan opsi 1).
  2. Anda dapat memiliki komentar eksplisit yang mengatakan mengapa kode dummy perlu ada di sana dan tidak menghapusnya. Jadi, jika seseorang menghapus kode secara tidak sengaja (misalnya dengan alat refactor yang mencari kode yang tidak digunakan), Anda dapat dengan mudah melihat dari kontrol sumber bahwa kode tersebut diperlukan dan untuk mengembalikannya. Jika Anda menggunakan opsi 1 dan seseorang menggunakan alat refactor untuk membersihkan referensi yang tidak digunakan, Anda tidak memiliki komentar; Anda hanya akan melihat bahwa referensi telah dihapus dari file .csproj.

Berikut adalah contoh "kode boneka" yang biasanya saya tambahkan ketika saya menghadapi situasi ini.

    // DO NOT DELETE THIS CODE UNLESS WE NO LONGER REQUIRE ASSEMBLY A!!!
    private void DummyFunctionToMakeSureReferencesGetCopiedProperly_DO_NOT_DELETE_THIS_CODE()
    {
        // Assembly A is used by this file, and that assembly depends on assembly B,
        // but this project does not have any code that explicitly references assembly B. Therefore, when another project references
        // this project, this project's assembly and the assembly A get copied to the project's bin directory, but not
        // assembly B. So in order to get the required assembly B copied over, we add some dummy code here (that never
        // gets called) that references assembly B; this will flag VS/MSBuild to copy the required assembly B over as well.
        var dummyType = typeof(B.SomeClass);
        Console.WriteLine(dummyType.FullName);
    }
deadlydog
sumber
4
Apa yang tidak dibahas di sini adalah bahwa ada perbedaan antara menyalin produk membangun di / bin proyek web dan menyalin rutin ke direktori output target (misalnya / bin / x86 / Debug). Yang pertama dilakukan oleh proyek yang direferensikan ketika membangun dan yang terakhir dilakukan oleh proyek web dependen. Memeriksa Microsoft.Common.targets membantu memahami hal ini. Salinan ke web / bin sama sekali tidak bergantung pada perilaku penyalinan lokal - salin dampak lokal pada salin ke dir target keluaran yang bukan merupakan bagian dari struktur yang dirujuk oleh Cassini yang berjalan melalui Debug.
user1164178
6
dapatkah Anda menjelaskan mengapa itu bekerja dengan VS TANPA menambahkan "kode dummy" tetapi tidak dengan msbuild?
toebens
3
Bahkan kurang invasif daripada menjalankan fungsi, Anda dapat menetapkan jenis kelas yang terkandung di dalam perakitan ke variabel dummy. Type dummyType = typeof(AssemblyA.AnyClass);
Arithmomaniac
14
Solusi # 2 seperti yang ditunjukkan di atas akan berfungsi kecuali jika Anda memiliki pengaturan 'Kode Optimasi' diperiksa di Visual Studio. Dalam hal ini, masih akan mengecualikan dll. Saya menambahkan satu baris lagi untuk mengganti "optimasinya". Console.WriteLine(dummyType.FullName);
JasonG
3
Solusi 2 tidak bekerja untuk saya di Visual Studio 2017. Saya pertama kali berpikir itu karena, dalam rakitan X saya, saya hanya menggunakan enumdari B dan saya mengasumsikan enum sedang digarisbawahi. Saya menambahkan kode untuk langsung menggunakan tipe dari B dan itu tidak membantu. Itu juga selalu menjadi kasus X saya menggunakan tipe dalam A yang tipe subclass dalam B jadi saya tidak bisa memahami bagaimana kompiler berpikir bahwa B tidak diperlukan oleh X dan dapat diabaikan. Ini gila.
Xharlie
170

Saya hanya menghadapinya seperti ini. Buka properti referensi Anda dan lakukan ini:

Set "Copy local = false"
Save
Set "Copy local = true"
Save

dan hanya itu.

Visual Studio 2010 awalnya tidak meletakkan: <private>True</private> di tag referensi dan pengaturan "salin lokal" ke false menyebabkannya membuat tag. Setelah itu akan mengaturnya menjadi benar dan salah.

andrew
sumber
10
Ini adalah anugerah. Terima kasih untuk ini!
Rebecca
22
Tidak bekerja untuk saya dengan MSBuild 4 / VS2012. Artinya, saya bisa memperbarui referensi untuk mengatakan <Private>true</Private>tetapi tampaknya tidak berpengaruh pada MSBuild. Pada akhirnya, saya baru saja menambahkan referensi NuGet ke proyek downlevel.
Michael Teper
2
Tidak bekerja untuk saya. Itu masih tidak menyalin System.Net.Http.Formatting ke folder bin.
Akira Yamamoto
4
Ini setara dengan Have you tried turning it off and on again?, dan itu berhasil!
guanome
6
Sepertinya VS2015 masih berperilaku sama: pengaturan 'Salin Lokal' ke 'Salah' lalu kembali ke 'Benar' pada referensi .dll's, berfungsi.
balik
38

Jika Anda tidak menggunakan perakitan langsung dalam kode maka Visual Studio sambil berusaha membantu mendeteksi bahwa itu tidak digunakan dan tidak termasuk dalam output. Saya tidak yakin mengapa Anda melihat perilaku yang berbeda antara Visual Studio dan MSBuild. Anda bisa mencoba mengatur output build ke diagnostik untuk keduanya dan membandingkan hasilnya lihat di mana itu menyimpang.

Adapun referensi elmah.dll Anda jika Anda tidak mereferensikannya secara langsung dalam kode Anda bisa menambahkannya sebagai item ke proyek Anda dan mengatur Build Action ke Contentdan Salin ke Direktori Output untuk Always.

John Hunter
sumber
3
+1 untuk salinan Anda ke komentar Direktori Keluaran, jika Elmah tidak digunakan dalam kode, masuk akal untuk menyalin sebagai konten.
Kit Roed
4
Memang itu mengabaikan majelis yang tidak digunakan, TETAPI satu hal utama yang perlu diperhatikan, pada VS 2010 menggunakan perakitan dalam kamus sumber daya XAML tidak dianggap sebagai menggunakan assmebly oleh VS, sehingga tidak akan menyalinnya.
Alex Burtsev
Ini lebih baik untuk proyek Unit Test di mana saya butuh dll. Saya tidak ingin menambahkan dll bahwa proyek utama tidak perlu hanya untuk menjalankan tes!
Lukos
14

Melihat:

Utas forum MSBuild ini saya mulai

Anda akan menemukan solusi / solusi sementara saya di sana!

(MyBaseProject membutuhkan beberapa kode yang mereferensikan beberapa kelas (apa pun) dari elmah.dll untuk elmah.dll disalin ke tempat MyWebProject1!)

toebens
sumber
1
Sial - ini adalah satu-satunya solusi yang saya datangi juga - berharap mungkin ada cara yang lebih baik untuk melakukannya!
nickspoon
2
Lihat tanggapan andrew di bawah ini untuk solusi yang kurang simpel (jangan tersinggung!)
MPritchard
1
Bagi mereka yang melihat ini sekarang, jawaban toebens di MSDN pada dasarnya sama dengan jawaban deadlydog , diberikan beberapa tahun kemudian.
jpaugh
8

Saya memiliki masalah yang sama.

Periksa apakah versi kerangka kerja proyek Anda sama dengan versi kerangka kerja dll yang Anda cantumkan.

Dalam kasus saya, klien saya dikompilasi menggunakan "Framework 4 Client" dan DLL berada di "Framework 4".

Andre Avelar
sumber
6

Masalah yang saya hadapi adalah saya memiliki proyek yang tergantung pada proyek perpustakaan. Untuk membangun saya mengikuti langkah-langkah ini:

msbuild.exe myproject.vbproj /T:Rebuild
msbuild.exe myproject.vbproj /T:Package

Itu tentu saja berarti saya kehilangan file dll perpustakaan saya di bin dan yang paling penting dalam paket file zip. Saya menemukan ini bekerja dengan sempurna:

msbuild.exe myproject.vbproj /T:Rebuild;Package

Saya tidak tahu mengapa ini bekerja atau mengapa tidak di tempat pertama. Tetapi harapan itu membantu.

vangorra
sumber
Saya memiliki masalah yang sama untuk membangun seluruh solusi menggunakan / t: Build from TeamCity dalam satu langkah kemudian pada / t berikutnya: Paket pada proyek WebAPI. Dll yang dirujuk oleh referensi proyek apa pun tidak dimasukkan. Ini diperbaiki dengan menggunakan paket di atas - / T: Rebuild; di WebAPI kemudian memasukkan dll.
Andy Hoyle
5

Saya hanya punya masalah yang sama persis dan ternyata disebabkan oleh kenyataan bahwa 2 proyek dalam solusi yang sama mereferensikan versi yang berbeda dari perpustakaan pihak ke-3.

Setelah saya koreksi semua referensi semuanya bekerja dengan sempurna.

Steven Engels
sumber
4

Seperti yang dikatakan Alex Burtsev dalam komentar apa pun yang hanya digunakan dalam kamus sumber daya XAML, atau dalam kasus saya, apa pun yang hanya digunakan dalam XAML dan bukan dalam kode di belakang, tidak dianggap 'digunakan' oleh MSBuild.

Jadi hanya dengan membuat referensi tiruan ke kelas / komponen dalam perakitan dalam beberapa kode di belakang sudah cukup meyakinkan MSBuild bahwa perakitan itu sebenarnya sedang digunakan.

Scott
sumber
Ini persis masalah saya dan solusi yang berhasil. Menghabiskan terlalu lama untuk mencari tahu ini. Terima kasih Scott & @Alex Burstev
karol
Astaga, ini membuatku gila. Ya , saya menggunakan FontAwesome.WPF, dan hanya dari dalam XAML (untuk alasan yang jelas). Menambahkan metode dummy membantu. Terima kasih! Dan ya, VS 2017 15.6 masih terpengaruh, jadi saya mengajukan bug: github.com/dotnet/roslyn/issues/25349
Sören Kuklau
3

Mengubah kerangka kerja target dari .NET Framework 4 Profil Klien ke .NET Framework 4 memperbaiki masalah ini untuk saya.

Jadi dalam contoh Anda: atur kerangka target pada MyWebProject1 ke .NET Framework 4

Kesemek Fuyu
sumber
3

Menggunakan skema deadlydog,

Y => X => A => B ,

masalah saya adalah ketika saya membangun Y, majelis (A dan B, semuanya 15) dari X tidak muncul di folder bin Y.

Saya mendapatkannya diselesaikan dengan menghapus referensi X dari Y, menyimpan, membangun, kemudian menambahkan kembali referensi X (referensi proyek), dan menyimpan, membangun, dan A dan B mulai muncul di folder bin Y.

JZ
sumber
Setelah berjam-jam mencari dan mencoba banyak solusi lain, yang ini berhasil untuk saya.
Suncat2000
2

Saya memiliki masalah yang sama dan dll adalah referensi yang dimuat secara dinamis. Untuk mengatasi masalah saya telah menambahkan "menggunakan" dengan namespace dll. Sekarang dll disalin dalam folder output.

Spam
sumber
2

Ini membutuhkan penambahan .targetsfile ke proyek Anda dan pengaturannya untuk dimasukkan dalam bagian termasuk proyek.

Lihat jawaban saya di sini untuk prosedurnya.

Alex Essilfie
sumber
2

Majelis rujukan yang tidak digunakan selama pembangunan bukanlah praktik yang benar. Anda harus menambah file build Anda sehingga itu akan menyalin file tambahan. Baik dengan menggunakan acara pembuatan pos atau dengan memperbarui grup properti.

Beberapa contoh dapat ditemukan di pos lain

verbedr
sumber
2

Skenario lain di mana ini muncul adalah jika Anda menggunakan jenis proyek "Situs Web" yang lebih lama di Visual Studio. Untuk jenis proyek itu, tidak dapat referensi .dlls yang berada di luar struktur direktori itu sendiri (folder saat ini dan ke bawah). Jadi dalam jawaban di atas, katakanlah struktur direktori Anda terlihat seperti ini:

masukkan deskripsi gambar di sini

Di mana ProjectX dan ProjectY adalah direktori induk / anak, dan referensi ProjectX A.dll yang pada gilirannya merujuk B.dll, dan B.dll berada di luar struktur direktori, seperti dalam paket Nuget pada root (Paket), lalu A. dll akan dimasukkan, tetapi B.dll tidak akan.

Focker
sumber
0

Saya memiliki masalah yang sama hari ini, dan ini tentunya bukan jawaban untuk pertanyaan Anda. Tapi saya ingin memberi tahu semua orang, dan mungkin memberikan percikan wawasan.

Saya memiliki aplikasi ASP.NET. Proses membangun diatur untuk membersihkan dan kemudian membangun.

Saya memiliki dua skrip Jenkins CI . Satu untuk produksi dan satu untuk pementasan. Saya menyebarkan aplikasi saya ke panggung dan semuanya bekerja dengan baik. Digunakan untuk produksi dan tidak ada file DLL yang direferensikan. File DLL ini hanya di root proyek. Tidak dalam repositori NuGet. DLL diatur ke do not copy.

Script CI dan aplikasi itu sama antara dua penyebaran. Masih setelah bersih dan menggunakan di lingkungan pementasan, file DLL diganti di lokasi penggunaan aplikasi ASP.NET ( bin/). Ini tidak terjadi pada lingkungan produksi.

Ternyata di cabang pengujian saya telah menambahkan langkah ke proses membangun untuk menyalin file DLL ini ke bin direktori. Sekarang bagian yang butuh sedikit waktu untuk mencari tahu. Proses CI tidak membersihkan dirinya sendiri. DLL ditinggalkan di direktori kerja dan sedang tidak sengaja dikemas dengan file .zip ASP.NET. Cabang produksi tidak pernah memiliki file DLL disalin dengan cara yang sama dan tidak pernah secara tidak sengaja menyebarkan ini.

TLDR; Periksa dan pastikan Anda tahu apa yang dilakukan server build Anda.

Jason Portnoy
sumber
0

Pastikan bahwa kedua proyek dalam versi .net yang sama juga memeriksa salinan properti lokal tetapi ini harus truesebagai default

john.kernel
sumber
0

Menggunakan Visual Studio 2015 menambahkan parameter tambahan

/ deployonbuild = false

ke baris perintah msbuild memperbaiki masalah.

Giles Roberts
sumber
-1

Saya baru saja mengalami masalah yang sangat mirip. Saat kompilasi menggunakan Visual Studio 2010, file DLL dimasukkan dalam binfolder. Tetapi ketika mengkompilasi menggunakan MSBuild, file DLL pihak ketiga tidak disertakan.

Sangat membuat frustrasi. Cara saya menyelesaikannya adalah dengan memasukkan referensi NuGet ke paket di proyek web saya walaupun saya tidak menggunakannya langsung di sana.

Gary Brunton
sumber
Ini adalah duplikat dari jawaban teratas.
Giles Roberts
-3

Menyertakan semua file DLL yang dirujuk dari referensi proyek Anda di proyek Situs web tidak selalu merupakan ide yang baik, terutama ketika Anda menggunakan injeksi dependensi : proyek web Anda hanya ingin menambahkan referensi ke antarmuka file / proyek DLL, bukan DLL implementasi konkret mengajukan.

Karena jika Anda menambahkan referensi langsung ke file / proyek implementasi DLL, Anda tidak dapat mencegah pengembang Anda memanggil "baru" pada kelas konkret dari file / proyek implementasi DLL alih-alih melalui antarmuka. Anda juga telah menyatakan "hardcode" di situs web Anda untuk menggunakan implementasinya.

Hung Nguyen
sumber