Implementing Shared Assemblies with Strong Names in .NET

In the .NET framework, an assembly functions as a self-describing deployment unit executed within an application domain (AppDomain). Before an application can execute, the runtime must load its corresponding assemblies into an AppDomain. The distinction between private and shared assemblies arises from how these units are managed and accessed across different application domains.

Private Assemblies

Private assemblies represent the standard deployment model for most .NET applications. When a local project is compiled, it generates a DLL or EXE file acting as a private assembly. If multiple application domains utilize the same private assembly, the runtime creates a separate copy for each domain. This means multiple instances of the same code exist within a single process, leading to higher memory consumption.

Shared Assemblies

Shared assemblies allow multiple application domains to access a single copy of the code, significantly reducing memory overhead. This domain-neutral code sharing is a key advantage in scenarios involving numerous dependent applications. These assemblies are typically stored in the Global Assembly Cache (GAC) rather than an application's local directory. Deployment requires Windows Installer (MSI) rather than simple file copying, as the GAC requires specific registration.

Establishing Unique Identity with Strong Names

Shared assemblies require a globally unique name, known as a strong name. This name prevents naming conflicts and ensures that an assembly cannot be substituted by a malicious version. A strong name consists of the assembly's text name, version number, culture information, and a public/private key pair. The cryptographic key pair guarantees uniqueness and allows the runtime to verify the assembly's origin.

Generating a Key Pair

The Strong Name tool (Sn.exe), included with the .NET SDK, manages keys and signing. To create a new key pair stored in a file, use the following command:

sn -k CompanyKey.snk

This generates a file containing both the public and private keys necessary for signing.

Applying the Strong Name to an Assembly

To sign an assembly, reference the key file within the project's AssemblyInfo file. Visual Studio automatically creates this file for new projects. Apply the AssemblyKeyFileAttribute to link the assembly with the key pair:

[assembly: AssemblyKeyFileAttribute(@"..\Resources\CompanyKey.snk")]

Upon compilation, the assembly is signed with the private key. The public key is embedded in the manifest, allowing consumers to verify the signature.

Installing Assemblies into the Global Assembly Cache

Once signed, the assembly must be registered in the GAC to be shared. The Global Assembly Cache Utility (gacutil.exe) handles this registration. Use the -i flag to install an assembly:

gacutil -i SharedMathLibrary.dll

This command copies the assembly into the system-wide cache, making it available to any application on the machine. Unlike private assemblies, referencing a shared assembly from the GAC does not create a local file copy in the application's directory.

Practical Example: Building a Shared Utility Library

Step 1: Create the Class Library

Start a new Class Library project named SharedMathLibrary. Define a utility class providing a public method:

using System;

namespace SharedMathLibrary
{
    public class CalculatorService
    {
        public string FetchCurrentTimestamp()
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}

Step 2: Sign the Assembly

Open a command prompt and generate a strong name key:

sn -k signingKey.snk

Move the key file to the project directory. Open AssemblyInfo.cs and add the attribute pointing to this key file:

[assembly: AssemblyKeyFile("signingKey.snk")]

Build the project in Release mode to produce the signed DLL.

Step 3: Register in the GAC

Deploy the signed assembly to the GAC:

gacutil -i SharedMathLibrary.dll

Step 4: Consume the Shared Assembly

Create a new Console Application named ClientApp. Add a reference to the shared assembly. Visual Studio will locate it within the GAC. Import the namespace and invoke the method:

using System;
using SharedMathLibrary;

namespace ClientApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var service = new CalculatorService();
            string timestamp = service.FetchCurrentTimestamp();
            Console.WriteLine($"Timestamp from shared library: {timestamp}");
        }
    }
}

Executing the client application demonstrates successful communication with the shared assembly. The library resides in the GAC, and no local copy exists in the client's output directory, illustrating the efficient memory usage characteristic of shared assemblies.

Tags: .NET C# assembly GAC Strong Name

Posted on Mon, 07 Sep 2026 16:48:12 +0000 by gkelley091565