Development and deployment - how to get service assemblies into their proper location? - c#

This has to be a pretty common issue, and while we have answers, I'm trying to determine if there is any better way. We are using IoC / DI with structuremap. This means that our final application (an Asp.Net MVC 2 app) requires the presence of some assemblies which aren't being referenced anywhere else (our implemention of IRepository, etc).
In VS, I've added a Post Build event that simply goes to our lib folder (containing the already build assemblies which are not part of our solution) and copies everything with *Repository*.dll to the $(TargetDir).
On the build server, were we are also building packages for deployment (and hoping to automate deployment to some environments), I've done something similar to get those assemblies.
Is there any better way though? Or do you just accept that this is one of the issues with IoC and have to remember to add a bit of script to include the service assemblies as part of the process?
Thanks
Andy

You should be able to create a project of any type, and then reference everything you need. Then just copy everything, except the dll from that project. At least you don't have to edit a lot of scripts just to add a new project..?

I suppose you could add the assemblies you want into your project just as linked files, not as assembly references, and then mark them as 'Content' as the build type and 'Copy if newer' so they will get copied to your bin folder.
Never tried this, but it ought to work.

Related

Reference all required assemblies for runtime or compile time?

I thought this would have been a question already asked many times, but I can't find it.
We are developing applications and have multiple shared assemblies which are used by multiple targets. When I'm creating a new application I'll probably use such a assembly (eg Framework) and reference it in my project. All fine here. However, when this Framework assembly uses for example the Model assembly, I'm not forced by Visual Studio to reference it. As long as my code doesn't touch any Model types, it will compile fine without a reference.
At runtime, it does require the Model assembly. When releasing this application there is no problem, as I just include all the required references in my installer project.
The problem arrizes when I try to debug the application. The bin folder won't have the Model assembly as it's not referenced.
The question
Is there a best practice to solve this "reference of a reference" situation?
Solutions we've come up with
Add the Model as a reference
This feels wrong, we pollute the project
Include the Model project in the solution and add as project reference
This feels even worse (polluting the solution)
Add post-build step
Could be a solution, but doesn't feel that right either.
The best solution for this is to package your Framework or Library assemblies (or sets) with NuGet. You can then use NuGet in Visual Studio to take care of all these references. This works great, even when using nested dependencies.
NuGet is fully supported and integrated into Visual Studio. It is very easy to host your own package repository (that can be as simple as pointing to a file share with packages).
You can host a private on-site repository for your own (internal) pacakges. That's what most shops do. You can combine that with one or more public NuGet repositories for public things as Log4Net etc..
And while it may seem to take some time to get this up and running (which is relative, try to just use a public package first just to get the hang of using NuGet first), you get a whole lot of benefits as well. For example, you get support for versioning your libraries out of the box.
At my company we've had this problem for years, and we used to build and check-in library assemblies (50+) into source control, and dragging that around across branches. Since we've switched out approach to using NuGet, this problem just gone away for us. Never looking back to that anymore.
A reference to another project does not necessarily need to be a Project reference. In your example, reference Model as a Bin reference within Framework. This way a pre-built Model will be included with the build of Framework.

How to use my DLL when it is not in the GAC

If I have a Project which uses my own made .dll and this .dll is not registered with the GAC but simply in the same folder as my projects App. eg C:\Program Files (x86)\MyApp Folder.
Can I, and more importantly how do I properly reference this .dll if I want to for example build a second project which also uses this .dll. It is possible that I build a few small apps that will use this .dll.
In this case must I have it in the GAC or if it is not there what must I do?
If I have a Project...
You should never put yourself in a situation where you have just a project. You first and foremost have a solution. A collection of projects that, together, build an app. Projects of course have a dependency on each other, you use a project dependency to tell the compiler about. Which automatically takes care of reference assemblies, the output of one project becomes the reference of another. And any changes you make to the source code of such a project automatically propagate to the others.
This is usually as far as teams take it.
This however tends to not work so well on very large solutions with dozens of projects, Visual Studio tends to get sluggish and building can take a long time. An important step to take in such a case is to freeze a root project. A programmer needs to get an explicit permission to make changes to such a core project. Because such a change tends to be very destabilizing, requiring many changes in dependent projects. And effectively destroys many hours of testing and validation time.
You do this by explicitly removing a project from a solution. Which now automatically makes it difficult to make changes to it. The dependent projects need to be updated to use an explicit reference assembly instead of the project dependency. Picking a well-known location for the assembly is important. Either source control or (preferrably) a build server is instrumental to be the source of the assembly. A tool like Nuget can be very useful.

Visual Studio : How to manage code shared between projects

This has probably been posted before, but I'm not sure what search terms to look for!
Quick explanation.
I have code that is shared between a few projects. This code is still work-in-progress itself. The issue is that whenever I need to update this code for whatever, I don't want to have to do it 3 times, this will become a nightmare.
Is there a way to add it to a project, without copying it into the project folder?
i.e. I want the shared class to be linked into my 3 projects as
C:\code repository\sharedclass.cs NOT \eachproject\bin\sharedclass.cs
Do I have to create it as it's own library project? It would be much better if the compiler could compile it as 'external' code.
Cheers.
As others have said, you can simply right-click on your solution in the solution explorer, select Add > Existing Project, and browse to the common project's .csproj file, and it will be included in the solution from its original location.
There are two problems with this however, which may or may not be an issue, depending on the size of your team:
The common project will be included in each solution with a relative path to the solution file (i.e.: ...\CommonProject\Common.csproj). This means all developers have to have the same working file structure or they will get errors when they try to open the main project.
In the scenario that the common project is referenced by multiple projects (say two - A and B) and a developer working on project A has to make changes to the common project as part of their task, there is no way for that developer to know if the changes they have made will break project B without them actually checking out project B and compiling it. As more and more projects reference the common project, the risk of this happening increases to the point where it becomes unmanageable.
Again, as others have said, there is no 'correct' way to do this. However, the approach I have taken is as follows:
Use continuous integration such as Cruise Control to manage the building of the projects and put the common project as a standalone project on the server.
Create a directory under your source control to house built common DLLs. Have this directory checked out on your build machine and whenever the common project builds, it copies the output DLL into the DLL folder and commits these changes to source control.
Use environment variables on all developers' machines and the build server to control the location of the common DLL folder and reference the DLLs using that variable rather than the hard-coded path. (i.e.: rather than C:\Source\MyCommonProjectDLLS\Common.dll, use $(MyCommonLocation)\Common.dll with the variable 'MyCommonLocation' set to C:\Source\MyCommonProjectDLLS)
For any project which references the common DLL, set up a CI trigger on the build server for that project to watch the common DLL folder. Whenever changes are committed to it, the build server should then build all consuming projects.
This immediately lets you know if you are committing breaking changes for any other project. The only drawback is that, in this model, consuming projects are forced to take updates to the common DLL as soon as they are made. An alternative is to version the Common DLL from the source control revision when it is built, and place each version in its own sub directory under the common DLL folder. So you would end up with:
Common DLLs
-1.0.0.1234
-1.0.0.1235
-1.0.0.1236
And so on. The advantage of this is that each project can then choose when to take updates to the common DLL by simply referencing the new version of the code. However, it cuts both ways as this can mean that some projects are left with older versions of the common code for longer than they should, which can increase the work involved when the time comes to finally bring in those changes.
Yes.
You can add a project from anywhere on your hard drive to a solution. So put the shared code into a class library and add that to your three projects.
Microsoft has been supporting an open source project which comes built into VS now, its called NuGet, you can output your shared project as a nuget file and consume it in your other projects.
It will actually deploy all the files you specify in the package upon build.
This is how .Net supports dependencies now. You will notice that even things like EF come through NuGet packages. You can even host it for free on places like MyGet.org I use this and it works quite well.
http://nuget.org/
I use git submodules to achieve this.
Create a new git repository for each module (project) that you want to share between solutions. I usually also include unit tests for that project in a separate project but in the same git repository.
Add a submodule to the git repository of the solution that will use the shared code. Adding a submodule creates a link to a specific commit of an external repository. When the code in the submodule is updated you will be able to pull the updates to your parent solution, which is essentially the same as updating the reference to the submodule commit. I find that the process is easier to visualise using an app like SourceTree.
Adding the submodule and pulling the latest commit will create a copy of the shared project inside the parent solution folder. Import the project into the parent Visual Studio solution by right-clicking on the solution and selecting "Add existing project".
Add a reference to the shared project in the other projects that will be using it by right-clicking on the project and selecting "Add Reference" and finding the shared project in the "Solution" tab.
Now that the shared project is included in the solution you will be able to push and pull changes to the submodule and these changes will automatically be incorporated into the solution. You will also be able to see the changes in other git repositories that reference the submodule.
Yes, put the code which need to be shared in a separate class library project, build it and reference the DLL created from this build into your other projects.
It is better to extract common part into a separate project library and add reference of this project to all the solutions/dependent projects.
Otherwise you can Add code/file/item as Link.

Copying indirectly referenced assembly to output directory - assembly is missing [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does Copy-local work?
I have following situation:
there's a project named OLAF.Tools, and that project references Microsoft.Data.SqlXml in C:\Program Files\SQLXML 4.0\bin\Microsoft.Data.SqlXml.dll. Reference Copy Local property is set to True. When I build that project in bin directory I can see both OLAF.Tools.dll and Microsoft.Data.SqlXml.dll
there's a console application named OLAF.Generator, and that application references OLAF.Tools (I've added reference using Project tab). When I build that application in bin directory I can see only OLAF.Generator.exe and OLAF.Tools.dll - there's no Microsoft.Data.SqlXml.dll, what supprises me. Another wierd thing is that even though that dll is missing application is executing properly.
So my questions are:
why Microsoft.Data.SqlXml.dll is not copied to bin folder of OLAF.Generator console application?
how application resolves directory where Microsoft.Data.SqlXml.dll can be found?
Thanks,Pawel
EDIT 1: (after response from Marc Gravell)
#Marc Gravell: Your answer gave me food for thought, as I could swore that I could always see indirectly dependant assemblies in main application's bin directory. And IMHO I don't agree with you - with all due respect :)
Of course, references are not cascaded physically (we're are talking about strong relationship to classes, interfaces etc) - and it's exactly what I wanted to achieve when building OLAF.Tools library. That library provides a level of abstraction, it contains factories, and one factory accepts as a parameter string and returns interface. One particular implementation of that interface uses Microsoft.Data.SqlXml components. As a result,
OLAF.Generator uses interface that is located in OLAF.Tools, but doesn't know about components in Microsoft.Data.SqlXml.
Apart from that (I think we both know what I tried to explain in preceding paragraph), when building application, dependant assemblies should be copied (if Copy Local is set to TRUE). I just wrote sample application, Project B lib has reference to Project A lib, and Project C (console app) has reference to Project B. In Project C's bin directory I can see all: Project A.dll, Project B.dll & Project C.exe. So in discussed scenario, the reason why Microsoft.Data.SqlXml doesn't end up in OLAF.Generator bin folder has something to do with that assembly itself.
Does compiler/visual studio knows that Microsoft.Data.SqlXml is located in directory which is automatically probed (or it's in GAC) and this is the reason why that assembly is not copied?
EDIT 2: I've just checked GAC, and indeed, Microsoft.Data.SqlXml.dll is installed in GAC.
How does Copy-local work? log4net.dll is not being copied to MyProject output directory - this is answer to my question. When library is installed in GAC, it won't be copied even though COPY LOCAL setting is used.
References are not automatically cascaded, so adding a reference to OLAF.Tools does not also add a reference to SQLXML. If you want to deploy SQLXML with your exe, then the most convenient way to do that is to explicitly add a reference to SQLXML from your exe, and set it to copy local. Without this, it is not deployed. Basically, the onus is on the developer to decide which files are actually needed at runtime (which is often a subset of the references used, and depends on a number of deployment decisions which only you can know).
Re how it is resolved at runtime... the probing paths are a bit of a black art, mainly meaning "the app folder", but it depends on the config, and indeed the GAC may be consulted. You also get an opportunity to provide your own resolver, via AppDomain.Current.AssemblyResolve.

ILMerge Best Practices

Do you use ILMerge? Do you use ILMerge to merge multiple assemblies to ease deployment of dll's? Have you found problems with deployment/versioning in production after ILMerging assemblies together?
I'm looking for some advice in regards to using ILMerge to reduce deployment friction, if that is even possible.
I use ILMerge for almost all of my different applications. I have it integrated right into the release build process so what I end up with is one exe per application with no extra dll's.
You can't ILMerge any C++ assemblies that have native code.
You also can't ILMerge any assemblies that contain XAML for WPF (at least I haven't had any success with that). It complains at runtime that the resources cannot be located.
I did write a wrapper executable for ILMerge where I pass in the startup exe name for the project I want to merge, and an output exe name, and then it reflects the dependent assemblies and calls ILMerge with the appropriate command line parameters. It is much easier now when I add new assemblies to the project, I don't have to remember to update the build script.
Introduction
This post shows how to replace all .exe + .dll files with a single combined .exe. It also keeps the debugging .pdb file intact.
For Console Apps
Here is the basic Post Build String for Visual Studio 2010 SP1, using .NET 4.0. I am building a console .exe with all of the sub-.dll files included in it.
"$(SolutionDir)ILMerge\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
Basic hints
The output is a file "AssemblyName.all.exe" which combines all sub-dlls into one .exe.
Notice the ILMerge\ directory. You need to either copy the ILMerge utility into your solution directory (so you can distribute the source without having to worry about documenting the install of ILMerge), or change the this path to point to where ILMerge.exe resides.
Advanced hints
If you have problems with it not working, turn on Output, and select Show output from: Build. Check the exact command that Visual Studio actually generated, and check for errors.
Sample Build Script
This script replaces all .exe + .dll files with a single combined .exe. It also keeps the debugging .pdb file intact.
To use, paste this into your Post Build step, under the Build Events tab in a C# project, and make sure you adjust the path in the first line to point to ILMerge.exe:
rem Create a single .exe that combines the root .exe and all subassemblies.
"$(SolutionDir)ILMerge\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
rem Remove all subassemblies.
del *.dll
rem Remove all .pdb files (except the new, combined pdb we just created).
ren "$(TargetDir)$(TargetName).all.pdb" "$(TargetName).all.pdb.temp"
del *.pdb
ren "$(TargetDir)$(TargetName).all.pdb.temp" "$(TargetName).all.pdb"
rem Delete the original, non-combined .exe.
del "$(TargetDir)$(TargetName).exe"
rem Rename the combined .exe and .pdb to the original project name we started with.
ren "$(TargetDir)$(TargetName).all.pdb" "$(TargetName).pdb"
ren "$(TargetDir)$(TargetName).all.exe" "$(TargetName).exe"
exit 0
We use ILMerge on the Microsoft application blocks - instead of 12 seperate DLL files, we have a single file that we can upload to our client areas, plus the file system structure is alot neater.
After merging the files, I had to edit the visual studio project list, remove the 12 seperate assmeblies and add the single file as a reference, otherwise it would complain that it couldnt find the specific assembly. Im not too sure how this would work on post deployment though, could be worth giving it a try.
I know this is an old question, but we not only use ILMerge to reduce the number of dependencies but also to internalise the "internal" dependencies (eg automapper, restsharp, etc) that are used by the utility. This means they are completely abstracted away, and the project using the merged utility doesn't need to know about them. This again reduces the required references in the project, and allows it to use / update its own version of the same external library if required.
We use ILMerge on quite a few projects. The Web Service Software Factory, for example produces something like 8 assemblies as its output. We merge all of those DLLs into a single DLL so that the service host will only have to reference one DLL.
It makes life somewhat easier, but it's not a big deal either.
We had the same problem with combining WPF dependencies .... ILMerge doesn't appear to deal with these. Costura.Fody worked perfectly for us however and took about 5 minutes to get going... a very good experience.
Just install with Nuget (selecting the correct default project in the Package Manager Console). It introduces itself into the target project and the default settings worked immediately for us.
It merges the all DLLs marked "Copy Local" = true and produces a merged .EXE (alongside the standard output), which is nicely compressed in size (much less than the total output size).
The license is MIT as so you can modify/distribute as required.
https://github.com/Fody/Costura/
Note that for windows GUI programs (eg WinForms) you'll want to use the /target:winexe switch. The /target:exe switch creates a merged console application.
I'm just starting out using ILMerge as part of my CI build to combine a lot of finely grained WCF contracts into a single library. It works very well, however the new merged lib can't easily co-exist with its component libraries, or other libs that depend on those component libraries.
If, in a new project, you reference both your ILMerged lib and also a legacy library that depends on one of the inputs you gave to ILMerge, you'll find that you can't pass any type from the ILMerged lib to any method in the legacy library without doing some sort of type mapping (e.g. automapper or manual mapping). This is because once everything's compiled, the types are effectively qualified with an assembly name.
The names will also collide but you can fix that using extern alias.
My advice would be to avoid including in your merged assembly any publicly available lib that your merged assembly exposes (e.g. via a return type, method/constructor parameter, field, property, generic...) unless you know for sure that the user of your merged assembly does not and will never depend on the free-standing version of the same library.
We ran into problems when merging DLLs that have resources in the same namespace. In the merging process one of the resource namespaces was renamed and thus the resources couldn't be located. Maybe we're just doing something wrong there, still investigating the issue.
We just started using ILMerge in our solutions that are redistributed and used in our other projects and so far so good. Everything seems to work okay. We even obfuscated the packaged assembly directly.
We are considering doing the same with the MS Enterprise Library assemblies.
The only real issue I see with it is versioning of individual assemblies from the package.
I recently had issue where I had ilmerged assembly in the assembly i had some classes these were being called via reflection in Umbraco opensource CMS.
The information to make the call via reflection was taken from db table that had assembly name and namespace of class that implemented and interface. The issue was that the reflection call would fail when dll was il merged however if dll was separate it all worked fine. I think issue may be similar to the one longeasy is having?
It seems to me like the #1 ILMerge Best Practice is Don't Use ILMerge. Instead, use SmartAssembly. One reason for this is that the #2 ILMerge Best Practice is to always run PEVerify after you do an ILMerge, because ILMerge does not guarantee it will correctly merge assemblies into a valid executable.
Other ILMerge disadvantages:
when merging, it strips XML Comments (if I cared about this, I would use an obfuscation tool)
it doesn't correctly handle creating a corresponding .pdb file
Another tool worth paying attention to is Mono.Cecil and the Mono.Linker [2] tool.
[2]: http:// www.mono-project.com/Linker

Categories