10 Steps to a Successful Versioning and Deployment Strategy for .NET

A well rounded versioning and deployment strategy considers several overlapping and interdependent .NET Framework concepts. The development process must include a strategy around strongly named assemblies. Code access security and runtime security policy must be evaluated. Deployment options may include installation of assemblies to the global assembly cache, and require a predictive approach to assembly versioning and updates. This article will take you through a ten step program that reviews these core concepts, their relationship, and provides guidance for successful application deployments for the .NET Framework. The article is accompanied by a code sample that includes a complete set of scripted instructions to take you through the most of the concepts discussed herein.

NOTE: This discussion does not apply to the .NET Compact Framework which does not adhere to all of the rules in this article.

Step 1: Strongly Name Your Assemblies

Strong names play an important role in every .NET deployment. While this may seem an obvious recommendation to some - the benefits of strong names elude others.

All assemblies comprising a product release should be strongly named.

Strongly named assemblies have a unique identity, and are not identified by filename alone. A strongly named assembly is identified by its filename along with its assembly version, culture, and digital signature. Visual Studio projects usually include an assemblyinfo.cs file indicating these values to the compiler through .NET attributes:

[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyKeyFile("companykey.snk")]

In future versions of .NET, strong names will become a requirement. Get used to it!

Strongly named assemblies are verified for integrity.

When an assembly is signed with a cryptographic key pair (shown above as companykey.snk), a hash of each module (file) included in the assembly is stuffed into the assembly manifest, and the manifest is then hashed and signed with the private key. The .NET Framework runtime environment verifies the integrity of each assembly by recalculating each module hash and comparing it with the embedded manifest's contents - refusing to load assemblies that fail this verification step. Assemblies that have been tampered with will not be loaded.

This is a significant and innovative feature of .NET. Pre-.NET, nothing prevented a component from being replaced by an alternate malicious binary exposing the same COM or DLL interface. <>

Strongly named assemblies enforce strict version binding.

 

The runtime also enforces strict version binding for strongly named assemblies. The assembly manifest includes a list of early-bound dependency references - by their strong name. Unless a version policy is supplied to override this default behavior, the runtime will always load dependencies based on the exact version referenced in the manifest.

Although this is highly desired behavior - protecting clients from invoking malicious components - this also introduces some pain to the process of versioning components. On the bright side, it forces us to adopt better practices during the design phase, since we must think through our deployment and versioning strategies more carefully.

Strongly named assemblies are trusted.

Strongly named assemblies cannot call assemblies that are not strongly named. Any self-respecting vendor will ship products with strong names, and refrain from using friendly-named assemblies. If your product depends on an assembly that is not strongly named, you should question the authenticity of the vendor providing you with that assembly, and look for an alternate provider.

Strongly named assemblies can only be called by assemblies granted FullTrust (by default). This heightens security on users of your assembly, which could be a perceived benefit to users of your product. Only assemblies granted FullTrust - which usually means those copied to the local machine - can invoke your code. That excludes (by default) dynamically downloaded Internet or intranet assemblies, or assemblies that have reduced privileges by security policy.

This can be perceived as a limitation if your assembly should be accessible to clients without FullTrust. So, to work around this requirement you can optionally apply the AllowPartiallyTrustedCallersAttribute to your assembly, shown here as it would appear in assemblyinfo.cs:

[assembly: AllowPartiallyTrustedCallers()]

This attribute should only be placed on assemblies that cannot be exploited by malicious callers. That means your code has gone through a rigorous security-focused code review, and any code that could access protected resources or invoke risky procedures has been protected through other mechanism such as sandboxing (see Step 6).

Strongly named assemblies support side-by-side execution and deployment.

Strongly named assemblies are uniquely identified by four attributes: filename, version, culture, and digital signature. Thus, it is possible to deploy multiple versions of the same assembly (by filename) with a different culture (for localized satellite assemblies) or version (for service packs or version updates). The .NET runtime inherently supports side-by-side execution of multiple versions of the same assembly, even in the same process. Of course if the filename does not change, local deployments of multiple versions requires unique directories to avoid file conflict. However, the GAC directly supports side-by-side deployment of shared assemblies.

Figure 1 - The GAC supports side-by-side deployment of assemblies. This makes it possible for systems to run applications for the .NET Framework versions 1.0 and 1.1 in parallel.

Step 2: Think Before You GAC

Strongly named assemblies can be deployed to a shared assembly cache - the global assembly cache, or "the GAC" for short.

gacutil.exe -i ServerLib.dll

Strong name does not imply GAC, this is a myth, but installing assemblies to the GAC does carry some benefits that should be weighed against the simplicity of XCOPY deployment.

Assemblies deployed to the GAC are more secure, sort of...

Assemblies installed to the GAC are verified for integrity during installation. Furthermore, since only administrators have access to the GAC, it is assumed that assemblies installed to the GAC have cannot be removed without authorization. Of course adequate security policy is required to make this so - meaning users should not be logged in to their machines with administrator privileges (see Step 6). Regardless, the same verification process applies if an assembly is removed and/or reinstalled to the GAC.

Assemblies deployed to the GAC load faster.

Because assemblies installed to the GAC are verified on installation and are cannot be tampered with once installed, the runtime need not verify strong name signatures each time the assembly is loaded. This yields subtle performance gains that become particularly important for Web applications and server application deployments. For client applications, deployment simplicity is an easy favorite.

Assemblies deployed to the GAC can be centrally administered.

Another benefit of deploying to the GAC is centralized administration of assemblies. For example, shared component assemblies can be updated in this central location, and all applications using those assemblies can be redirected to use a newer version (see Step 8). This is largely beneficial to server-side deployments, or for component or framework vendors that expect their APIs to be consumed by many applications.

Serviced components should be deployed to the GAC.

Applications that leverage Enterprise Services should deploy their serviced components to the GAC. Server applications require it. Library applications do not require it, but it is highly recommended.

Private assemblies make life easier.

Generally speaking, documentation for .NET applications encourages XCOPY deployment - meaning, private assembly deployment. This removes the need to consider the possibility of multiple unknown applications sharing deployed assemblies. This also removes the need for additional installation scripting, so that applications can literally be downloaded and run on any machine that has the .NET Framework runtime installed.

Step 3: Covet All Private Keys

Assemblies should always have a strong name, however with all the benefits comes the responsibility of protecting those private keys used to digitally sign those assemblies. Imagine if an un-trusted party somehow gained access to the cryptographic key pair used to sign all of Microsoft's assemblies? With that private key a spoofed copy of a framework assembly could be wired with the same metadata, and the same strong name. Assuming the offending party also had access to the target machine's GAC (thus, logged in as Administrator) they could install an imposter assembly. Next thing you know, all applications on the machine invoking ShowMessage() could suddenly be deleting files or worse.

Authenticode signatures are an additional measure beyond strong names that include a certificate that can be verified for trust - and revoked if compromised. This will become commonplace with ClickOnce in .NET 2.0.

The configuration manager (a.k.a. the build guy) is king.

The only participants in the product development lifecycle that should have access to private keys are those in charge of the build process. In fact, once that key is created and installed into a safe certificate store on an isolated machine, this machine should be locked in a dungeon guarded by some sort of fire-breathing dragon, with redundancy in the event of equipment failure. The password for this machine should be minded as you would the production servers for your company's site. When it comes time to generate a build, the private key installed to this machine is required to sign assemblies, which means that the build machine requires access to that certificate store protected by fluffy the fire-breathing dragon.

All environments follow the build process.

In order for development and quality control to accurately test a system deployment as it would be on production, assemblies must be properly signed, even for incremental test builds. The build process should send all assemblies from source control through the build process and spit out a complete system that has assemblies fully signed. This process is preferable to delay signing tactics, outside of local development testing.

Delay signing is ok for development machines.

Developers may need to build their own local assemblies to test against the latest incremental build. Delay signing an assembly allows developers to sign an assembly using a designated public key, so that the runtime can recognize the assembly with its fully qualified strong name - however a placeholder is kept for the actual digital signature. In this way, strong name permission demands would be satisfied against the public key token embedded in the assembly manifest, even in the absence of the private key. Assemblies without a digital signature cannot be verified by the runtime, and will not load unless they are listed in the skip verification list.

Beware skip verification.

In the absence of the digital signature, the .NET runtime cannot verify the assembly which is why developers must turn off assembly verification for that specific assembly. This can be done using the Strong Name Utility (sn.exe) - which adds individual assemblies to the skip verification list in the Windows registry.

sn.exe -Vr ServerLib.dll

This requires administrator privileges or access to the appropriate registry key - but regardless is a potential security hole to be aware of. Be sure and reverse the setting when testing is complete:

sn.exe -Vu ServerLib.dll

Even assemblies that are not delay signed can be installed to this list, therefore run without verification. This is another good reason to covet the administrator login.

Step 4: Get To Know Code Access Security

Code access security is a feature unique to the .NET Framework that many developers are still getting used to. Its power is in the ability to restrict what a component can do, based on its origin or identity. If you are new to code access security, read up on it (see Resources). Assuming you have familiarity I have compiled a list of considerations related to your application deployments and the affect of code access security.

Forget all security policy defaults.

Security policy defaults are installed to each machine with the .NET Framework redistributable. These defaults imply, for example, that assemblies copied to the local machine will have FullTrust, a special permission sometimes required of specific .NET Framework APIs. As administrators come to understand the value of security policy, more and more policy defaults may be altered to protect machines within the enterprise, and this can affect your application deployments. Assume you won't have FullTrust, and assume you have absolutely no idea what security policy will live on the destination machine. Then, figure out what your application really needs to successfully run, so you can ask for those permissions (see Step 5).

Test with custom permission sets and code groups.

Always test your application assemblies on a system that give only those permissions required for the application to run. In this way the test cycle may uncover additional permission requirements, so that you can be sure your documented permission requirements are accurate.

This will be painful, but if you distribute applications to clients that may later impose security policies that cause your application not to run, you will be grateful for having tested on the exact permission set required to run. On the other hand, if your clients will grant FullTrust to your assemblies, based on a digital signature, feel free to ignore this advice.

If you sign your assemblies with the same strong name, a custom code group can be created with the required permission set, be it FullTrust or a custom permission set specifically targeting required permissions.

Figure 2 - A security policy can be configured so that a particular code group, when assigned to an assembly, should be the only code group considered (thus, removing all other permissions assigned that aren't listed in the code group).

Use metadata to publish permission requirements.

Requesting a minimum set of permissions that your assembly requires to run, can be useful during the development cycle, for publishing requirements as you go.

[assembly:ReflectionPermission(SecurityAction.RequestMinimum, Flags=ReflectionPermissionFlag.Members)]

Beware of RequestRefuse and RequestOptional.

If your application assemblies do require FullTrust to run, refusing or requesting optional permission will cause FullTrust (if granted) to be revoked. So, for example, by informing administrators through metadata that your assembly can be trusted not to call unmanaged code, by definition your assembly must be able to run without FullTrust.

[assembly: SecurityPermission(SecurityAction.RequestRefuse, Flags=SecurityPermissionFlag.UnmanagedCode)]

[assembly: SecurityPermission(SecurityAction.RequestOptional, Flags=SecurityPermissionFlag.UnmanagedCode)]

Demand required security permissions.

Many .NET Framework APIs invoke permission demands on callers, to prevent luring attacks where un-trusted callers invoke highly trusted code. So long as your code has been granted that permission, it will satisfy the stack walk. If a malicious caller invokes your assembly, the stack walk fails. For highly privileged code, you should also invoke security demands to prevent the same type of luring attack.

[SecurityPermission(SecurityAction.Demand, SecurityPermissionFlag.UnmanagedCode)]
public void MethodThatCallsUnmanagedComponents
{
   ...
}

In the event a stack walk is too expensive, a link demand can also be used to verify the immediate caller has the appropriate permissions to invoke specific components or methods. This does not prevent a luring attack, but combined with other security measures may be a better option, particularly for performance reasons.

[SecurityPermission(SecurityAction.LinkDemand, SecurityPermissionFlag.UnmanagedCode)]
public class ObjectThatCallsUnmanagedComponents
{
   ...
}

Apply security assertions with caution.

Permission demands are very effective, but can be costly for performance, or limiting to callers that do not have the demanded permission. There are times when your code may need to assert a permission, to circumvent the stack walk of a permission demand. For example, in an ASP.NET deployment that reduces code access security for the application assembly, assemblies invoked during the round-trip will not satisfy a stack walk to access certain resources such as the Windows registry, network directories, or unmanaged code. Assemblies granted permission to assert, can circumvent the stack walk in this way:

[SecurityPermission(SecurityAction.Assert, SecurityPermissionFlag.UnmanagedCode)]
public class ObjectThatCallsUnmanagedComponents
{
   ...
}

Each time an assertion is made, it is important to demand an alternate permission to reduce the likelihood of a luring attack.

Step 5: Evaluate Runtime Security Requirements

Now that we've established that security policy is out of your control, you should become very familiar with the permission sets required of your application assemblies.

Learn permission demands of your dependencies.

To evaluate your assembly's code access security requirements is not easy. A metadata inspection tool can determine what declarative permission demands and assertions exist in your assemblies and their dependencies; a code inspection tool can take this a step further by evaluating calls made to access the registry, file system, to unmanaged code, and more; but at the end of the day, nothing replaces intelligent foresight on the part of developers, who knows what their code requires: such as access to unmanaged components or reflection capabilities. Gathering this information while code is written is more effective. Help descriptions for .NET Framework APIs (usually) clearly indicate any security permissions required to invoke them. Further quality control testing is necessary to uncover things that slip through the crack on this front.

Distribute security policy for your applications.

The required code groups and permission sets for your application assemblies can be distributed as an MSI installation for administrators to review. Realistically, this policy cannot be installed to machines running your application, because it does not do a merge or append to the security policy when installed. It completely replaces it. So, this is useful only for administrators to learn the required permission set.

A more reliable way to distribute the required policy during installation is to write code to manually create permission sets and code groups. Then wrap this code into an installer class that inherits System.Configuration.Install.Installer and applies the RunInstallerAttribute (see Resources).

Step 6: Run With Least Privilege

Running with least privilege refers to code access security as well as runtime user security permissions. In concert they can prevent hackers from attacking the system through your application.

Client applications should run with user accounts.

Applications should be developed and tested under user accounts. Clients should be advised to disallow login as administrator, so that applications are not run with this elevated permission. This prevents potentially malicious code from having access to registry settings, local machine security policy and more.

Reduce ASP.NET application privileges.

ASP.NET applications are run with a lower privilege account, however the code access security settings default to FullTrust if the security policy for the deployment machine is not altered. To further prevent hackers from accessing sensitive resources or performing restricted actions from within the application thread, code access security can be reduced by a Web.config setting:

<trust level="Medium" originUrl="" />

This may be painful at first, but it enforces a number of best practices for ASP.NET development, namely: separate business logic from the code-behind; and use sandboxing techniques to elevate privileges as needed by business components.

Sandbox components that require additional privileges.

Components that require access to restricted resources, or perform special actions may need additional code access or runtime principal permissions. The easy way out is to grant the entire application the required permissions, and allow administrators to run the application. But this is unacceptable.

Components that require elevated user credentials to run can impersonate those credentials at runtime, or be launched in another process (such as a serviced component) that runs under the specified account. In this way, the initiating process need not be launched using the elevated user account.

For example, a database user account may be impersonated for the duration of the method call that accesses the database:

public void SomeMethodThatCallsDatabase()
{
   IntPtr dbUserToken;

   // dbUserToken is retrieved from the LogonUser() API method

   WindowsImpersonationContext saveContext = WindowsIdentity.Impersonate(dbUserToken)

   // access the database here

   saveContext.Undo();
}

This means safely configuring the account to impersonate, using encryption to protect the account information, and storing the information in a location that cannot be accessed by regular users (possibly the registry). Alternatively, administrators that configure the application may also be configuring serviced components that run with the specified account.

Figure 3 - Reducing runtime code access privileges for ASP.NET applications adds layer of protection from potential hackers.

Of course, there is much more to discuss with this topic, but the point is to run code requiring higher privilege in a separate process that has been safely granted those privileges, or programmatically grant higher privileges using impersonation and code access privilege elevation. This introduces another layer between your privileged code and potential hackers.

Step 7: Protect Your Intellectual Property

Your applications need protection. Intermediate Language (IL) can be disassembled. Obfuscated IL can be de-obfuscated (not easy, but possible). If your assemblies perform highly restricted actions, encapsulate highly proprietary functionality, or internalize private information, it is particularly important to know what you can do to prevent unauthorized use or access to information.

Encrypt sensitive configuration data.

Have you ever hard-coded a connection string, license key or password in an assembly? Is it stored in a private member variable? Well, regardless, this information should be encrypted inside the assembly, or configured externally, and encrypted. Even private data members can be viewed through disassembly or by using reflection to retrieve/modify the member.

Use hash algorithms for one-way data.

Passwords, social security numbers, and other private user information are sometimes collected to identify and validate users, but can (and should) be stored with one-way hashing algorithms to prevent database administrators and other employees from viewing the data. This security measure can elevate your clients' trust in your application. This data can then be verified by applying the same hashing algorithm against data input by the user.

Prevent misuse of component libraries.

Reflection can be used to dynamically create types, gather type information, and invoke members on any assembly. Assemblies with ReflectionPermission can freely invoke even protected and private members of another assembly, unless the assembly exerts demands not satisfied by the calling assembly.

System.Reflection.Assembly assembly = Assembly.LoadWithPartialName("SomeAssembly");

Type type = assembly.GetType("SomeAssembly.SomeObject");
object instance = Activator.CreateInstance(type);

type.InvokeMember("SomePrivateMethod",
   BindingFlags.InvokeMethod|BindingFlags.NonPublic|
   BindingFlags.Instance|BindingFlags.Public, null, instance, null);

For example, you can apply a StrongNamePermission demand to types and methods that perform actions only authorized for your application assemblies. The following declarative statement demands that the immediate caller have a particular strong name signature:

[StrongNameIdentityPermission(SecurityAction.LinkDemand,
PublicKey="002400000480000094000000060200000024000052534131000400000100010
01DB065C32D743D3CF3CC55C8C94C16E061FB05EB8F50C9B52354EE4412C03CAD744F7E4CD
CA1CE60B72A23DDCB52618050F5E7F72366A0E24973BCC20E76A57D68784D0EDAF29F293CE
1EF2159AF2CD9C11FB7753B9BD705A3444B3A4CCA0995B2575981ED51835DA7E0E8878842B
0409974E8FF880010B7D1787EF77A7D5BC7")]
public class MyObject
{
   ...
}

This can be applied at the method level as well. To demand all callers, use a demand instead of a link demand, but beware that the call stack may begin with a .NET Framework callback (like a Windows form button click event) and therefore an assertion may be necessary to allow the demand to succeed.

Step 8: Master Assembly Versioning

It is true that DLL hell was put to rest with the .NET Framework's new deployment and versioning model. Developers disliked the fact that a component could be overwritten and replace its predecessor, without the client's awareness. Many an application was broken when new ActiveX controls were deployed without a proper approach to versioning. So, with .NET we have strict version binding between assemblies and their dependencies. A simple overwrite won't do.

Now we have new issues. How do we deploy updates to dependencies, without recompiling their consuming clients? How do we allow clients to have control over this process, if they aren't interested in the update? Who has the final say in this matter - vendor or client?

Automate versioning with build scripts.

Let's start with versioning in general. All assemblies in a particular deployment should carry the same version number. Assembly version is usually propagated to file version as well, but the former is what the runtime uses to evaluate assembly bindings. This assembly version should be explicitly applied in the version attributes at compile time, which can be done using the AssemblyVersion attribute, stuffed by the build script for all assemblies:

[assembly: AssemblyVersion("1.0.1234.5678")]

Version number should be incremented explicitly by the build process, and applied to all assemblies in the release. For service packs and other updates, all changed files should carry the same, new version.

Override local bindings with application configuration.

Before .NET the only choices we had for versioning components was to overwrite them, or (if COM) version them in the registry and install updates to a new directory. The latter of these choices was too difficult for most vendors to follow therefore it was rarely effectively executed if even attempted.

With .NET have options. For example we can update assemblies over the Web by specifying a codeBase:

<codeBase version="1.0.0.0" href="http://localhost/ServerLib.dll"/>

or redirect to new assembly version with a bindingRedirect the application configuration file (web.config for ASP.NET applications):

<bindingRedirect oldVersion="1.0.0.0" newVersion="1.1.0.0"/>

In theory this looks good, until you start talking about how to deploy changes to the application configuration file. What happens if the client has modified it? How do you merge your changes with the file? These are questions only answered by supplying custom code to manage this process, and slinging it together with an installation program for the application update. Of course, you could always walk your client through the change over the phone if you prefer that route.

All joking aside, this means you have to consider what happens if you update any of the files in the application, so that you can figure out the potential requirements of that update installation.

Use publisher policies for GAC assemblies.

This is a tough recommendation to make, after the recent release of the .NET Framework 1.1 Service Pack 1 did not follow this remedy (see my recent blog post on this topic - http://www.dasblonde.net/PermaLink.aspx?guid=5064d95e-5b3c-4f9c-8f22-d6eb9f04f67b). Sometimes a judgment call has to be made, but if you deploy assemblies to the GAC, and you ship an update, you should apply a new version to the updated files and distribute a publisher policy for each assembly in the update if you want to force applications to use the new version.

For example, the following XML configuration file includes a policy to redirect 1.x versions of ServerLib to version 2.1.0.0:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <dependentAssembly>
            <assemblyIdentity name="ServerLib" publicKeyToken="0622eb5aa947655d" />
            <bindingRedirect oldVersion="0.0.0.0-1.65535.65535.65535" newVersion="2.1.0.0" />
         </dependentAssembly>
      </assemblyBinding>
   </runtime>
</configuration>

To build the publisher policy assembly, use the assembly linker (al.exe) to build an assembly from the configuration file, and spit out a file that follows the naming convention policy.majorversion.minorversion.assemblyname.dll. The policy assembly must be signed with the same private key as used for the related assembly, and must be installed into the GAC:

al.exe /link:ServerLib.dll.config /out:policy.1.0.ServerLib.dll /keyfile:mlbkey.snk
gacutil.exe -i Policy.1.0.ServerLib.dll

Consumers still have the option to opt-out with a publisher policy override in their application configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <publisherPolicy apply="no"/>
      </assemblyBinding>
   </runtime>
</configuration>

Consider the impact of .NET Framework runtime versions.

The executable process determines the runtime version of the .NET Framework that will host your assemblies. Of course, side-by-side deployment is a beautiful thing - however it does introduce the possibility of conflict when your shared assembly is invoked by a client executable invoking a different version of the runtime. In the event your components are not distributed in an environment where you have control over the available runtime versions, or the clients that invoke your assemblies, you can distribute a policy with your application configuration.

The following example makes it possible to run .NET 1.0 applications with the runtime for 1.1 even if both are installed:

<configuration>
   <startup>
      <supportedRuntime version="v1.1.4322"/>
   </startup>
</configuration>

Step 9: Design a Deployment and Upgrade Strategy

This step requires more than just a checklist. Taking the information passed on in steps 1 through 8, you must evaluate your application's requirements for deployment and subsequent upgrade, considering business goals, client limitations and demands, and available technologies. There are several options to choose from for installation and upgrade. Installation programs are still required for complex applications, but several choices exist today for deploying and updating applications over the Web.

Your deployment strategy should minimize the deployment effort for clients, and save your company money by automating version updates.

Evaluate the need for a full-touch installation.

Server-side and distributed applications traditionally require an installation program that can perform actions such as deploying security policy, configuring serviced components, applying registry settings, installing assemblies to the GAC, and more. Deploying this type of application for the first time requires a footprint installation to the local machine, but this doesn't preclude the application from automating updates over the Web, in the spirit of Windows update.

The current version of the .NET Framework also supports no-touch deployment. By hosting an application and its dependencies on an accessible Web server, in theory you can navigate to the main application URL from your Web browser to run it. The main assembly and its dependencies are downloaded to the assembly download cache, and subsequently run from that location. Of course this has code access security implications because the origin of the application may not be trusted. Thus, this application may require initial installation with a custom security policy and/or local footprint to do anything useful.

The bottom line, until we have ClickOnce, a rich application needs some an initial footprint on the machine to be trusted. Oh, and don't forget to bootstrap the .NET Framework during installation.

Don't over-engineer with custom Web updates and stub applications.

There are several solutions to choose from for automating updates over the Web. They all require that the first installation makes a local footprint on the machine, and from there check for updates over the Web based on some configuration. You can also roll your own version of this through with application configuration settings - specifically, using the codeBase option to download assemblies remotely. But, that implies you will write your own code to handle a process that can be integrated with application updater tools with much less pain and suffering. Just don't do it.

Do leverage application updater tools for Web updates.

The .NET Application Updater Component provides functionality for applications to automatically update themselves. The Updater Application Block for .NET is a ligher weight, yet capable, adaptation with the same purpose. These are useful tools that have minimal impact on your application code in order to manage updates without sending media to clients. This can save your company lots of cash, so it is well worth the effort to implement up front.

Step 10: Prepare for the Future of .NET

While considering your strategies for deployment and versioning for applications today, it is always valuable to evaluate the direction of the platform. This can help you position your choices to better migrate to future releases of .NET.

Get ready for ClickOnce

ClickOnce is a feature of the .NET Framework 2.0 that will simplify application deployment and update methodologies, specifically for those harder-to-deploy smart client applications. Consistent with the theme of no-touch deployment, applications may be downloaded from the Web, dynamically deployed and executed. The difference with ClickOnce is in the ability to negotiate elevated privileges for your application, and bootstrap the .NET Framework as part of this process. Furthermore, Visual Studio 2005 will include an integrated code access security evaluation that can help you determine the privileges required to run your application in a secure sandbox on the client machine.

Figure 4 - The Security configuration tab in Visual Studio 2005 will support evaluation of required permissions, and customization of this as part of the ClickOnce deployment.

Watch Longhorn futures.

Although this may not help us with .NET Framework 1.1 Service Pack 1, you should be interested to know that .NET versioning is heading in a direction that will support distinguishing platform assemblies from library assemblies. The way the runtime will treat this assemblies will vary: platform assemblies (like the .NET Framework core) will be treated such that version updates are automatically picked up; library assemblies require strict version controls. This makes it possible for platforms to be updated with security fixes, for example - the assumption being that those assemblies maintain backward compatibility as needed. More commentary on this subject can be found here: http://www.dasblonde.net/PermaLink.aspx?guid=5064d95e-5b3c-4f9c-8f22-d6eb9f04f67b

Resources

Michele's Versioning and Deployment Resource Site
http://www.dotnetdashboard.net/sessions/versiondeploy.aspx

Michele' Blog
http://www.dasblonde.net/

IDesign Downloads
http://www.idesign.net/

Security in .NET: Enforce Code Access Rights with the Common Language Runtime
http://msdn.microsoft.com/msdnmag/issues/01/02/CAS/default.aspx

Using Code Access Security with ASP.NET
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmod/html/secmod82.asp

Increasing Permissions for Web-Deployed Windows Forms Applications
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms11122002.asp

No-Touch Deployment in the .NET Framework
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vbtchNo-TouchDeploymentInNETFramework.asp

.NET Application Updater Component
http://www.windowsforms.net/articles/appupdater.aspx

Application Updater Block for .NET
http://msdn.microsoft.com/library/en-us/dnbda/html/updater.asp

Deploy and Update Your Smart Client Projects Using a Central Server
http://msdn.microsoft.com/msdnmag/issues/04/05/ClickOnce/default.aspx

Redistributing the .NET Framework 1.1
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetdep/html/redistdeploy1_1.asp

Desaware CAS/Tester
http://www.desaware.com/products/castester/index.aspx

posted on 2008-04-23 13:43  TeeBye  阅读(333)  评论(0编辑  收藏  举报

导航