The Problem
This probably isn't a common situation, but there are arguable
cases where you may want to share files between projects in a
single solution. In my example, I had the following solution
structure:
/Solution
/SharedControls
/Project1
/Project2
(etc)
In the SharedControls project, I had stylesheets, theme content,
scripts, and some common user controls that both of the other
projects were using. It wouldn't be too difficult to manually copy
this content to each project, but it's an extra step to remember
and to track updates to each file.
When I was working on this solution setup locally, I found an
easy way to address this problem, courtesy of Scott Guthrie; start
by following the tutorial here:
http://webproject.scottgu.com/CSharp/UserControls/UserControls.aspx
The summary of the tutorial above is to create a web application
project: SharedControls. Then add SharedControls as a project
reference in each of your other projects. After that, add a copy
command line event to the Pre-Build event in each project to copy
the shared files from the SharedControls project.
This is a great solution for a single developer, but it does
have a few issues:
- The build time is increased, especially if you are copying a
lot of files.
- Each file is copied when you debug, build, or publish a
project/solution. even if it is unchanged.
- You have to make sure any changes in the shared files are made
in the SharedControls project only.
Now, fast forward to my current team's implementation of Visual
Studio Team Foundation Server. A solution like the one above was
added into source control so that multiple developers could begin
working on the projects. The first time someone tried to build a
project, they received a failure message:
The command "copy
"C:\Projects\Articles\Solution\SharedControls\Controls\*.ascx"
"C:\Projects\Articles\Solution\Project2\Controls\"" exited with
code 1.
The build operation was failing because of the pre-build event.
When using TFS, your local workspace files are marked as read-only
unless you have the file checked out, so the copy command is unable
to overwrite the existing files, even with the force flag
specified.
A Solution
I was able to address the build failures and also come up with a
much better way to manage this type of setup in the process.
First, instead of copying the shared files to each project, you
just set up linked files instead:
Add a reference in each project to the SharedControls project
(just like in the original tutorial). Right click on the project
file and select Add Reference:

Select the Projects tab and select the
SharedControls project, and click OK:

In Visual Studio, right-click the Project folder or any folder
in the project and select Add >
Existing Item:

Navigate to the SharedControls project folder and select the
file(s) you would like to link. Click the arrow next to the Add
button and select Add As Link:

Now, any time you change the file from any project, all of the
linked copies stay in sync. The linked file is just a reference to
the copy in the SharedControls folder. If you publish your project
now, you will also see that a physical copy of each linked file
will be added to the target directory, this is exactly what we
wanted!
Now, we're all set. except if you try to debug your project.
more errors?!?
If you have linked control files, aspx pages, scripts, or images
you will notice that all of them are missing when you start the
project in debug mode. This is because the integrated development
server points to the project folder, and the physical versions of
the files no longer exist in your working project directory, even
though they are part of the build.
So you need to make sure that a physical version of the file
exists. Since Team Server no longer manages the project files that
have been linked in each project, the copy operation should no
longer fail.
I mentioned the limitations of Scott Guthrie's solution for
copying files during pre-build. This is the script I used in place
of his original:
xcopy "$(SolutionDir)SharedControls\Controls\*.ascx"
"$(ProjectDir)Controls\" /D /Y
xcopy "$(SolutionDir)SharedControls\App_Themes\*"
"$(ProjectDir)App_Themes\" /D /Y /S
So you still create physical copies in the development folder of
each linked file, but they are no longer part of the project
itself.
The /D flag will only copy the
file from the shared projects folder if it is a more recent version
than the one that already exists in the destination folder.
The /Y option forces an overwrite if the
file already exists. The /S option will
perform a recursive copy operation.
Right-click on the Project and select
Properties. Click on the Build
Events tab on the left and add the xcopy command(s) to the
Pre-build event command line field:

Now if you build your project again, you should be able to debug
without any issues.
One final note: When you drag and drop a linked
control to your page in design mode, it will show up in the source
view like this:
<%@ Register src="../SharedControls/Controls/SampleControl.ascx" tagname="SampleControl"
tagprefix="uc1" %>
The src tag is referencing the path to the physical file; You
just need to update this tag to reflect the control's
"location" in the current project:
<%@ Register src="Controls/SampleControl.ascx" tagname="SampleControl" tagprefix="uc1" %>
There are many different ways to manage shared content between
projects, and this is one of the more basic approaches. If you are
working under limited resources and cannot dedicate a user to
creating control libraries, this is definitely an approach to
consider. You can minimize duplication of work, and make sure that
you are able to manage your projects in a consistent manner.
You can download a sample
solution here to review how this is set up.