Le “BindingRedirect” permet d’indiquer comment charger des assemblies à la compilation d’un exécutable. L’intérêt de cette technique est:
- de pouvoir choisir une version différente de celle utilisée à la compilation,
- de faire ces indications sans avoir à recompiler l’application.
Chargement des assemblies
Par défaut, les dépendances sont chargées par Fusion exécuté par le compilateur JIT au moment où une méthode nécessite ce chargement. Ce chargement se fait en cherchant les assemblies à différents endroits dans un ordre déterminé et suivant certaines conditions. Les assemblies sont chargées en fonction de leur identité “AssemblyIdentify”.
L’identité “AssemblyIdentify” d’une assembly se définit par:
- Le nom de l’assembly
- Sa clé publique: la PublicKeyToken est présente si l’assembly est signée c’est-à-dire qu’elle possède un nom fort. La PublicKeyToken n’est pas indispensable si l’application n’est pas elle-même signée.
- Sa version: la version recherchée par défaut est la version définie à la compilation.
- Sa culture
- Les indications d’architecture cible: l’architecture doit être compatible avec l’architecture de l’application (cf. Plateforme cible en .NET en 5 min).
Le chargement de l’assembly se fait dans un ordre déterminé en cherchant le fichier qui satisfait tous les critères qui se trouvent dans “l’AssemblyIdentity”:
- Répertoire de l’application: la recherche se fait d’abord dans le répertoire de l’application, y compris les répertoires enfants de ce répertoire. Si l’application n’est pas signée, l’assembly qui se trouve dans le même répertoire ne doit pas forcément être signée.
- GAC: l’assembly est recherché dans le Global Assembly Cache. Si elle se trouve dans le GAC, elle est forcément signée.
Si l’assembly n’a pas été trouvée, une exception de type FileNotFoundException est lancée. Pour éviter cette erreur il est possible d’indiquer une version différente de celle recherchée ou en indiquant un répertoire différent du répertoire de l’application. Le mécanisme de BindingRedirect permet de donner des informations à fusion pour qu’il puisse charger une dépendance différemment qu’avec la méthode par défaut.
Redirection par fichier de configuration
Redirection de la version de l’assembly
Le “BindingRedirect” peut se faire à des niveaux différents et les paramètres d’un niveau peuvent être surchargé par ceux d’un autre niveau.
Les niveaux de paramétrage de la redirection de la version d’une assembly sont:
- Par la politique de l’éditeur: ce niveau permet aux éditeurs d’effectuer une redirection d’une assembly vers une version supérieure de façon à ce qu’elle s’applique dès qu’une application consomme l’assembly lorsqu’elle se trouve dans le GAC. Les paramêtres de redirection se trouvent aussi dans le GAC. Plus de détails sur la redirection par la politique de l’éditeur sur MSDN.
- Par l’intermédiaire du fichier de configuration de l’application: ce niveau peut surcharger la redirection définie au niveau de la politique de l’éditeur.
- Au niveau de la machine: ce niveau peut surcharger les redirections définies dans les 2 niveaux précédents. Les paramètres de redirection à ce niveau peuvent être définies localement sur la machine dans un fichier machine.config, user.config ou system.config de façon à modifier le comportement de Fusion respectivement pour la machine, l’utilisateur ou le système.
Redirection de la version d’une assembly par fichier de configuration
On peut indiquer une version supérieure ou inférieure à la version définie à la compilation. Toutefois il faut avoir en tête que ce sont juste des indications à Fusion pour qu’il modifie son comportement lors du chargement d’une dépendance. Il faut que l’assembly vers laquelle on effectue la redirection soit compatible avec celle définie à la compilation.
Cette redirection se fait en complêtant le fichier de configuration de l’application en ajoutant le noeud assemblyBinding
:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity
name="AssemblyToRedirect"
publicKeyToken="bcabfaff346163aa"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0"
newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Cette configuration permet de rediriger la version de l’assembly “AssemblyToRedirect” avec la clé publique “bcabfaff346163aa” et dont la version est entre 1.0.0.0 et 2.0.0.0 vers la version 3.0.0.0.
Pour que la redirection fonctionne il faut absolument renseigner l’attribut xmlns="urn:schemas-microsoft-com:asm.v1"
dans le noeud assemblyBinding
.
Surcharger la redirection définie par la politique de l’éditeur
Pour une assembly donnée, si on veut appliquer une redirection différente de celle définie par la politique de l’éditeur, il faut ajouter le noeud publisherPolicy
dans le fichier de configuration de l’application de la façon suivante:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity
name="AssemblyToRedirect"
publicKeyToken="bcabfaff346163aa"
culture="neutral" />
<publisherPolicy apply="no" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Dans ce cas, il n’y aura pas de redirection appliquée et le chargement de l’assembly se fera suivant le comportement par défaut.
Limiter la redirection à une version spécifique du framework .NET
Dans le cas où on redéfinit la redirection d’assembly du framework .NET, il est possible de préciser une condition d’application pour qu’elle s’applique à une version spécifique du framework.
Pour indiquer cette condition d’application, il faut ajouter l’attribut “appliesTo” dans le noeud “assemblyBinding” en précisant la version du framework à laquelle s’applique la redirection:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"
appliesTo="v3.5">
<dependentAssembly>
<assemblyIdentity name="AssemblyToRedirect"
publicKeyToken="b03f5f7f11d50a3a" culture=""/>
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="3.5.1.17000"/>
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"
appliesTo="v4.0.30319">
<dependentAssembly>
<assemblyIdentity name="AssemblyToRedirect"
publicKeyToken="b03f5f7f11d50a3a" culture=""/>
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="4.5.30319.17000"/>
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="AssemblyToRedirect"
publicKeyToken="b03f5f7f11d50a3a" culture=""/>
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="4.6.30319.17000"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Indiquer des chemins de recherche des assemblies
Indiquer des sous-répertoires de recherche des assemblies
Il est possible de préciser des chemins de répertoires enfant du répertoire de l’application que Fusion devra parcourir pour chercher une dépendance.
Ces chemins peuvent être préciser en utilisant l’attribut “privatePath” du noeud “probing”:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="subFolder1;subFolder2\subSubFolder" />
</assemblyBinding>
</runtime>
</configuration>
Indiquer le chemin d’une assembly
On peut préciser à Fusion directement le chemin d’une assembly. Toutefois ce paramètre s’applique suivant certaines conditions:
- Si l’assembly est signée: le chemin peut rediriger vers une adresse sur un intranet local, internet ou sur la machine.
- Si l’assembly n’est pas signée: le chemin doit être un sous-répertoire du répertoire de l’application. Le chemin indiqué doit être relatif à ce répertoire.
Ce paramètre peut être définit en utilisant le noeud codeBase
:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="AssemblyToRedirect"
publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<codeBase version="2.0.0.0"
href="http://assemblies.com/AssemblyToRedirect.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Le chemin sur la machine peut être préciser de la façon suivante:
<codeBase version="2.0.0.0"
href="file:///C:\folder\AssemblyToRedirect.dll"/>
Redirection par programmation
Il est possible d’indiquer des informations de redirection à Fusion en utilisant l’évènement AppDomain.AssemblyEvent
.
Cet évènement ne se déclenche que si Fusion n’a pas réussi à trouver l’assembly suivant le comportement par défaut (c’est-à-dire en cherchant dans le répertoire de l’application puis dans le GAC).
L’évènement AppDomain.AssemblyEvent
peut s’utiliser de la façon suivante:
using System;
using System.Reflection;
//...
AppDomain.CurrentDomain.AssemblyResolve +=
new ResolveEventHandler(ResolveEventHandler);
Avec:
private static Assembly ResolveEventHandler(object sender, ResolveEventArgs args)
{
string longName = "CustomAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
return Assembly.Load(longName);
}
Pour utiliser AppDomain.AssemblyEvent
, il faut prendre certaines précautions:
- S’abonner à l’évènement avant l’exécution du compilateur JIT: l’abonnement à l’évènement doit se faire avant l’exécution de Fusion et donc avant toute méthode dont la compilation par la compilateur JIT exécutera le chargement d’une dépendance.
- Assemblies chargées dans l’ordre des dépendances: si une assembly dépend d’une assembly tierce, l’assembly tierce doit être chargée avant.
StackoverflowException
etAssembly.Load()
: si le chargement de l’assembly avec Assembly.Load() échoue, l’évènement sera redéclenché, ce qui peut mener à une boucle infinie puis une exception de typeStackOverflowException
.
Pour définir les paramètres de chargement de l’assembly
On peut utiliser l’objet AssemblyName
:
private static Assembly ResolveEventHandler(object sender, ResolveEventArgs args)
{
AssemblyName requestedAssembly = new AssemblyName(args.Name);
if (requestedAssembly.Name != shortName)
return null;
requestedAssembly.Version = new Version(4, 0, 0, 0);
requestedAssembly.SetPublicKeyToken(
new AssemblyName("CustomAssembly, PublicKeyToken="
+ publicKeyToken).GetPublicKeyToken());
requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;
return Assembly.Load(requestedAssembly);
}
Pour éviter la boucle infinie
Si le chargement échoue et pour éviter une StackOverflowException, on peut se désabonner de l’évènement dans la callback de l’évènement:
public static void AddResolveEventHandler(string longName)
{
ResolveEventHandler handler = (sender, args) => {
AppDomain.CurrentDomain.AssemblyResolve -= handler;
return Assembly.Load(longName);
};
AppDomain.CurrentDomain.AssemblyResolve += handler;
}
Plus d’information sur l’évènement AppDomain.AssemblyResolve sur MSDN.
Redirection automatique avec Visual Studio
A partir de Visual Studio 2013, si on utilise des assemblies de version différente dans une même solution, on aura une erreur du type:
Found conflicts between different versions of the same dependent assembly.
Please set the "AutoGenerateBindingRedirects" property to true in the project file.
For more information, see http://go.microsoft.com/fwlink/?LinkId=294190.
La fonction “AutoGenerateBindingRedirects” permet d’ajouter dans le fichier de configuration de l’application des indications de redirection “BindingRedirect”. Ces indications permettront à l’exécutable d’utiliser une seule version de l’assembly dupliquée dans les projets de la solution. Le fichier de configuration source n’est pas modifié, seul le fichier de configuration résultat (i.e. dont le nom est [nom de l’assembly].exe.config) dans le répertoire de l’exécutable est modifié.
Activer la redirection automatique
Il faut ajouter le noeud AutoGenerateBindingRedirects
directement en éditant le fichier .csproj:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
...
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
...
</Project>
Désactiver la redirection automatique
La redirection automatique du fichier de configuration peut mener à des conflits si on souhaite indiquer manuellement les indications de redirection “BindingRedirect”.
Pour désactiver la fonctionnalité, il faut paramétrer AutoGenerateBindingRedirects
à “false” en éditant le fichier .csproj:
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
Plus de détails sur la redirection automatique sur MSDN.
- .NET Binding Redirects – Updating Referenced Assemblies Without Recompiling Code: http://geekswithblogs.net/robz/archive/2009/07/19/net-binding-redirects-ndash-updating-referenced-assemblies-without-recompiling-code.aspx
- Redirecting assembly versions: https://msdn.microsoft.com/fr-fr/library/7wd6ex19%28v=vs.110%29.aspx
- Redirecting Assembly Loads at Runtime: http://blog.slaks.net/2013-12-25/redirecting-assembly-loads-at-runtime/
- AppDomain.AssemblyResolve Event: https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve
- How is an assembly resolved in .NET?: http://stackoverflow.com/questions/1235253/how-is-an-assembly-resolved-in-net
- How to: Enable and Disable Automatic Binding Redirection: https://msdn.microsoft.com/en-us/library/2fc472t2%28v=vs.110%29.aspx