views:

289

answers:

4

I'm hoping that there is just something simple that I am missing here. I am trying to maintain two separate projects

ProjectName.Core &
ProjectName.Infrastructure

This is made in a typical Onion architecture so I can loosely couple my services and gain a greater flexibility. The Infrastructure project references the Core project. Once compiled they generate these DLLs

ProjectName.Core.dll &
ProjectName.Infrastructure.dll

But I would like to have it generate just 1 dll.

ProjectName.Infrastructure.dll (or even ProjectName.dll)

I have tried to use ILMerge to perform this operation but since Infrastructure references Core it throws an exception because it can't find the Core dll. It's obviously not looking within itself.

Now I need to maintain the separate projects because I have some other combinations that reference Core and another project that will be joined together such as

ProjectName.Core &
ProjectName.DataAccess &
ProjectName.Web

EDIT: My current solution calls out to ILMerge using an Nant build script. It merged together successfully. But when I try to use the merged DLL it throws an exception because it can't find the Core library.

  <target name="merge.core">
    <property name="temp.dir" value="${build.dir}\Temp\"/>
    <mkdir dir="${temp.dir}" if="${not directory::exists(temp.dir)}"/>
    <property name="tools.dir" value="&quot;${directory::get-current-directory()}\Tools\&quot;"/>
    <exec program="Tools\ILMerge\ILMerge.exe" workingdir=".">
      <arg value="/t:Library"/>
      <arg value="/ndebug"/>      
      <arg value="/out:&quot;${build.dir}\Temp\ProjectName.Infrastructure.dll&quot;"/>
      <arg value="&quot;${build.dir}ProjectName.Core.dll&quot;"/>
      <arg value="&quot;${build.dir}Xceed.Compression.dll&quot;"/>
      <arg value="&quot;${build.dir}ProjectName.Infrastructure.dll&quot;"/>
      <arg value="&quot;${build.dir}ProjectName.Infrastructure.XmlSerializers.dll&quot;"/>
    </exec>
    <delete file="${build.dir}ProjectName.Core.dll"/>
    <delete file="${build.dir}Xceed.Compression.dll"/>
    <delete file="${build.dir}ProjectName.Infrastructure.dll"/>
    <delete file="${build.dir}ProjectName.Infrastructure.XmlSerializers.dll"/>
    <move file="${build.dir}\Temp\ProjectName.Infrastructure.dll" tofile="${build.dir}ProjectName.Infrastructure.dll"/>
    <delete dir="${temp.dir}" if="${directory::exists(temp.dir)}"/>
  </target>

To be a little more clear. I can use objects out of the Core library but not the Infrastructure library. Because once it tries to instantiate one of those objects it seems .NET attempts to load the dependency but cannot find it.

+1  A: 

As I know that is not possible in Visual Studio, but you can compile every project as a netmodule and then join them together as a DLL. Compile like this:

csc misource1.cs misource2.cs misource3.cs /target:module

and then link together with the al.exe tool, see .netmodule Files as Linker Input and C# Assemblies.

ArsenMkrt
Yes. I can also specify everything as part of my Nant build script. But I want to avoid extra work of maintaining the script and the MSBUILD project files for each project.
Nathan Palmer
+3  A: 

Use ILMerge. It works out of the box to do this.

Preet Sangha
+1  A: 

ILMerge! Almost as good as sliced bread.

http://research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx

A: 

The most recent version of ILMerge has a /closed option which works on the transitive closure of the merged assemblies. It solves this exact problem (see sections 2.6 Closed and 4.1 Input assembly not merged in correctly in the ILMerge.doc user manual).

Note that if your library attempts to dynamically load objects from a named assembly, then it would not work because the merged assembly name is different than the original assembly name. There isn't anything ILMerge can do in this case; you'll have to modify your dependency injection to account for this.

Stephen Cleary