I develop a library with some functional named CompanyName.SDK
which must be integrated in company project CompanyName.SomeSolution
C
You can work at post-compilation assembly level to solve this issue with...
You could try merging the assemblies with ILMerge
ilmerge /target:winexe /out:SelfContainedProgram.exe Program.exe ClassLibrary1.dll ClassLibrary2.dll
The result will be an assembly that is the sum of your project and its required dependencies. This comes with some drawbacks, like sacrificing mono support and losing assembly identities (name, version, culture etc.), so this is best when all the assemblies to merge are built by you.
So here comes...
You can instead embed the dependencies as resources within your projects as described in this article. Here is the relevant part:
At run-time, the CLR won’t be able to find the dependent DLL assemblies, which is a problem. To fix this, when your application initializes, register a callback method with the AppDomain’s ResolveAssembly event. The code should look something like this:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
String resourceName = "AssemblyLoadingAndReflection." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Now, the first time a thread calls a method that references a type in a dependent DLL file, the AssemblyResolve event will be raised and the callback code shown above will find the embedded DLL resource desired and load it by calling an overload of Assembly’s Load method that takes a Byte[] as an argument.
I think this is the option i would use if I were in your shoes, sacrificing some initial startup time.
Have a look here. You could also try using those <probing>
tags in each project's app.config to define a custom sub-folder to look in when the CLR searches for assemblies.
Unity
package isn't a good example because you should use it only in one place called Composition Root. And Composition Root
should be as close as it can be to application entry point. In your example it is CompanyName.SomeSolution.Application
Apart from that, where I work now, exactly the same problem appears. And what I see, the problem is often introduced by cross-cutting concerns like logging. The solution you can apply is to convert your third-party dependencies to first-party dependencies. You can do that by introducing abstractions for that concepts. Actually, doing this have other benefits like:
CompanyName.SDK
really needs the Unity
dependency?)So, let's take for an example imaginary .NET Logging
library:
CompanyName.SDK.dll
depends on .NET Logging 3.0
CompanyName.SomeSolution.Project1
depends on .NET Logging 2.0
CompanyName.SomeSolution.Project2
depends on .NET Logging 1.0
There are breaking changes between versions of .NET Logging
.
You can create your own first-party dependency by introducing ILogger
interface:
public interface ILogger
{
void LogWarning();
void LogError();
void LogInfo();
}
CompanyName.SomeSolution.Project1
and CompanyName.SomeSolution.Project2
should use ILogger
interface. They are dependent on ILogger
interface first-party dependency. Now you keep that .NET Logging
library behind one place and it's easy to perform update because you have to do it in one place. Also breaking changes between versions are no longer a problem, because one version of .NET Logging
library is used.
The actual implementation of ILogger
interface should be in different assembly and it should be only place where you reference .NET Logging
library.
In CompanyName.SomeSolution.Application
in place where you compose your application you should now map ILogger
abstraction to concrete implementation.
We are using that approach and we are also using NuGet
for distribute our abstractions and our implementations. Unfortunately, issues with versions can appear with your own packages. To avoid that issues apply Semantic Versioning in packages you deploy via NuGet
for your company. If something change in in your code base that is distributed via NuGet
you should change in all of the packages that are distributed via NuGet
. For example we have in our local NuGet
server :
DomainModel
Services.Implementation.SomeFancyMessagingLibrary
(that references DomainModel
and SomeFancyMessagingLibrary
)Version between this packages are synchronized, if version is changed in DomainModel
, the same version is in Services.Implementation.SomeFancyMessagingLibrary
. If our applications needs update of our internal packages all dependencies are updated to the same version.