We have a particular project in our solution at work, serving as a data access layer. It contains classes with two purposes:
- A few generic classes that can accept a few interfaces and actually handle retrieving any requested data
- Many other classes implementing those few interfaces, which request specific data (employees, customers, etc)
Then someone on the team created separate projects with new classes to request data. They needed access to the more general classes in the first project, but simply referencing that project would pull in all kinds of references to other projects along with it… references the new projects didn’t technically need.
At a cross-roads, one of a few things could have been done:
- Abandon separate projects and keep everything in the original project (gets worse as the code-base grows)
- Copy the core files into each new project (copy/paste anti-pattern)
- Do some refactoring, and split the more general data-access classes into a separate “core” project, which these other projects could all reference
There’s another option – one I didn’t even know about until someone pulled it out of his trick bag.
Here’s a small solution, a console app with a class library project (
ClassOne… yeah, stupid name, should’ve been ProjectOne but I’m too lazy to re-screenshot). The static main method is referencing a method in the class library. Standard fare so far.
Now let’s suspend reality and create yet another project (
ClassTwo). Instead of referencing the first library class and pulling in unwanted dependencies, or copying the file and duplicating code, we’ll just create a link to the first file.
gimmick feature that allows this is “Add as Link”. You choose to add an existing file, find it on disk, and then click the drop-down next to “Add”. Now it’s technically in one place on disk, but two places in the solution. Changes made in one project are automatically reflected in the other project, because hey, it’s just one file.
You can tell when a file is linked in your project by a small blue arrow in a square.
The project file marks a linked file differently.
A normal class file, located within the project folder:
A class file “added as link” to the project:
The file is marked as a link, and actually references the relative location on disk of the other file. So now we have something worse than a few unwanted references in our new project. Our new project is actually tied to a location on disk. Any time ANY other solution tries to reference the
ClassTwo project, it will fail to compile until
ClassTwo resides on disk (not necessarily in the solution itself) in the same exact relative location as the current solution.
The other fun this creates, and why I started looking more into how this works, is it messes up intellisense and class referencing. Normally, even if a class name is the same, the namespace will differ in some way. You may have to go all the way to the top, but you cannot have two classes with the same name in the same namespace. Until now.
If, from the console app in this example, we reference both projects and then try to use a method in the shared class file, VS cannot resolve the correct method to call. Two identical methods, with identical namespaces. No
using statement can fix it. No fully-qualified namespace before the method call. It’s borked.
The way to fix it is to migrate the class in question to a new project that all can share. Which eliminates the need for “Add as Link” in the first place. Ugh.
Off to refactor…
(Update: Apparently there’s some use-case for “add as link” in the world of windows phone and portable class libraries, but even after reading Microsoft’s explanation I’m still not buying it.)