DLL Hell Part II: The monster lives...

  • Thread starter Thread starter Stingray
  • Start date Start date
S

Stingray

Hi all,

I'm looking for some advice regarding assembly versioning. This is a long
one so please bear with me...

I am doing a lot of new development--all .NET (C#) and running on
Windows2000 servers. I am using Visual Studio and have the version
attribute on all assemblies set to automatically change the revision (last
digit) on each build. I have several assemblies that are shared by multiple
applications. Since these use COM+ they are strong named and live in the
GAC. I am using installers to install everything. Since this is a lot of
new development, there are continuous fixes and enhancements. One of our
components is a Queued Component (using MSMQ). This is the main reason we
chose COM+ over simple assemblies and remoting. Because of this queued
component, every assembly it references must also be strong named and GAC'd.

Now, as we all know, it is possible to keep multiple versions of assemblies
in the GAC. The installers know which versions to install (and uninstall)
and everything is happy. Of course the GAC will fill up with multiple
versions, but who cares. After all, it's only disk space. Since the
installers keep track of everything its ok to be messy. The problem is the
QC. In order to instantiate this component I use the code:

string moniker = "queue:/new:MyNamespace.MyObject";
IMyObject obj = (IMyObject) Marshal.BindToMoniker(moniker);

where MyObject is the queued component. This is the only way I've found to
instantiate a QC. If there's a cleaner .NET way, pleas let me know. Since
there is no way to explicitly specify a version it is not possible to have
multiple versions of this component. I've found that it always picks the
oldest version. This component references other assemblies. Also, as I
said earlier, several programs and web sites use it. Because of all this,
any change to this QC or any of its dependencies requires everything from
that changed assembly up the chain to be rebuilt. For example: Prog1.exe
and Prog2.exe use QC1.dll. QC1.dll uses Comp1.dll. Comp1.dll uses
Comp2.dll. If I make a change to Comp2.dll I must also rebuild and redeploy
all of these other assemblies. This is a nighmare (my actual dependency
graph is more complicated than this). If it weren't for the QC I would only
rebuild the assemblies that need to make use of the new features and let the
others continue running with the old version for a while. But as it stands
now I must rebuild everything.

Am I doing something fundamentally wrong? One solution I can think of is to
give fixed version numbers to everything to force all components to think
everything is ok. This would leave me asking questions like: "Is that
Tuesday's 1.0.0.0 or Friday's 1.0.0.0?" A better alternative along those
lines is to change the binding policies on all components so anything goes.
But then I'd be lost not knowing which version really requires what! It
would also require a lot of manual work maintaining the version mappings.

I've been trying to find a book or online reference that addresses issues
like this but can't find anything. Are there any "best practices" regarding
versioning? And how do QCs fit into the .NET Framework (if at all)? Would
upgrading to Windows2003 (COM+ 1.5) help in any way? (That's not an option
at this point but I'd just like to know).

Help!

TIA,
John
 
Stingray said:
Hi all,

I'm looking for some advice regarding assembly versioning. This is a long
one so please bear with me...

I am doing a lot of new development--all .NET (C#) and running on
Windows2000 servers. I am using Visual Studio and have the version
attribute on all assemblies set to automatically change the revision (last
digit) on each build. I have several assemblies that are shared by multiple
applications. Since these use COM+ they are strong named and live in the
GAC. I am using installers to install everything. Since this is a lot of
new development, there are continuous fixes and enhancements. One of our
components is a Queued Component (using MSMQ). This is the main reason we
chose COM+ over simple assemblies and remoting. Because of this queued
component, every assembly it references must also be strong named and GAC'd.

Now, as we all know, it is possible to keep multiple versions of assemblies
in the GAC. The installers know which versions to install (and uninstall)
and everything is happy. Of course the GAC will fill up with multiple
versions, but who cares. After all, it's only disk space. Since the
installers keep track of everything its ok to be messy. The problem is the
QC. In order to instantiate this component I use the code:

string moniker = "queue:/new:MyNamespace.MyObject";
IMyObject obj = (IMyObject) Marshal.BindToMoniker(moniker);

where MyObject is the queued component. This is the only way I've found to
instantiate a QC. If there's a cleaner .NET way, pleas let me know. Since
there is no way to explicitly specify a version it is not possible to have
multiple versions of this component. I've found that it always picks the
oldest version. This component references other assemblies. Also, as I
said earlier, several programs and web sites use it. Because of all this,
any change to this QC or any of its dependencies requires everything from
that changed assembly up the chain to be rebuilt. For example: Prog1.exe
and Prog2.exe use QC1.dll. QC1.dll uses Comp1.dll. Comp1.dll uses
Comp2.dll. If I make a change to Comp2.dll I must also rebuild and redeploy
all of these other assemblies. This is a nighmare (my actual dependency
graph is more complicated than this). If it weren't for the QC I would only
rebuild the assemblies that need to make use of the new features and let the
others continue running with the old version for a while. But as it stands
now I must rebuild everything.

Am I doing something fundamentally wrong?

Yes. But it's not just you.

From "Applied Microsoft .Net Framework Programming" by Jeffrey Richter

"The CSC.exe and AL.exe tools support the ability to automatically increment
the assembly version number with each build. This feature is a bug and
shouldn't be used because changing the assembly version number will break
any assemblies that reference this assembly. The AssemblyInfo.cs file that
Visual Studio .NET automatically create sor you whtn you create a new
project is in error: . . ." p 61

So VS.NET generates a broken AssemblyInfo.xx when you create your project.
The AssemblyVersion attribute needs to be hard-coded, not auto incremented.

The assembly version should not change except when you want to break your
clients and force them to be rebuilt, or allow them to run the old version
side-by-side with the new version. For bug fixes and incremental
development the AssemblyVersion should not change. You can use the
AssemblyFileVersion attribute to keep track of each build and minor
revision: that's what shows up when you right-click on the assembly file and
look at its properties anyway.


David
 
Wow David. My world is shattered! You just blew away everything I knew to
be good! So even though Microsoft recommends and encourages changing
assembly versions by making it the default behavior, this is not a good
practice. I never knew there was an AssemblyFileVersionAttribute that
differed from AssemblyVersionAttribute. The documentation on this is scant
and this attribute isn't even listed with the other "assembly-level
attributes" in the help file. In the properties view I saw the file version
and assembly version but I never even realized they could vary
independently. The product version seems to be linked to the file version.
Is there an attribute for this as well?

Ok, the shock is wearing off and reality is setting in... So where do we go
from here? Let me make sure I'm clear on this... This attribute doesn't
allow any asterisks so I guess we have to remember to manually increment
this each time we change the assembly. I was already conscientious enough
to increment major or minor version numbers whenever there was an interface
change. We should still do this for changes that break an interface. But
we must now also manually increment the file version every time a change is
made. Right? I have no problem with this but it is possible (and probable)
that someone will occasionally forget to change a file version. So is there
any way to automate this? I guess it's not such a big deal but like I said
earlier, I don't want to end up juggling multiple files with the same
version number that vary only by date.

Now I'm curious about how installers will handle this. If I have already
installed MyAssembly.dll version 1.2.3.0, file version 1.2.3.1 into the GAC
and now I try to install a package that contains MyAssembly.dll version
1.2.3.0, file version 1.2.3.2, I assume the installer will see the assembly
versions are the same and not replace the file. Is that correct? Or will
it look at the file versions and make the replacement? Or will it bark at
me and not continue? I can figure this out by experimentation but thought
I'd ask if you just happen to know.

Either way, thank you very much for this interesting and useful information!
My world is coming back together.

John
 
Well,
1,Microsoft does not recommend changing assembly versions every day. On the
contrary, they recommend against that.
See Suzanne's blog
http://blogs.gotdotnet.com/suzcook/commentview.aspx/cc6428d6-69fb-4f05-8f18-65f3eac85274

2. Say you have asm.dll version 1.2.3.0, file version 1.2.3.1 into GAC, and
now you want to install asm.dll version 1.2.3.0, file version 1.2.3.2 to
GAC,the framework will replace it. The rule is simple, if file version is
the same or higher, replace it, otherwise, do nothing.(This is v1.1. v1 does
not know about file version at all, it will just replace it if the assembly
version is the same).
 
BTW, I just tried this and noticed that under .NET v1.0, if asm.dll
v1.2.3.0, file v1.2.3.1 is in the GAC and the installer attempts to install
asm.dll v1.2.3.0, file v1.2.3.2 it will ignore it and leave the previous
version. I can, however, manually drag the new file into the GAC and it
will replace the existing one, not that I'd want to.
John
 
Back
Top