web-dev-qa-db-fra.com

Vous utilisez 2 versions différentes de la même DLL?

On m'a donné 2 dll pré-compilées:

Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL

Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL

Exemple de différences dans les API:

 API

Et j'essaye de charger les deux dans un seul projet pour faire quelque chose comme ceci:

extern alias v10;
extern alias v20;

private static void UpgradeUser()
{
    // Load old user
    var userOld = new v10::Common.Data.UserData();
    userOld.loadData("user.dat");

    // Create new user
    var userNew = new v20::Common.Data.UserData();

    // Copy properties  
    userNew.FirstName = userOld._firstName;
    userNew.LastName = userOld._lastName;
    userNew.Age = userOld._age;

    // Invoke method from v10 and v20 API
    userOld.version();
    userNew.DisplayVersion();

    if (userNew.GetUserInfo() != userOld.getInfo())
    {
        throw new Exception("Discrepencies in upgrade ");
    }

    Console.WriteLine("Upgrade done!");
}

J'ai configuré mes références de projet et app.config à ce qui suit. Je copie également manuellement les dll dans mon dossier de sortie, ce qui correspond aux hrefs appconfig.

<!-- [Edited: see history for previous version] -->
<Reference Include="Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
  <HintPath>libs\Common.Data.1_0_0_0.dll</HintPath>
  <Aliases>v10</Aliases>
</Reference>

<Reference Include="Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
  <HintPath>libs\Common.Data.2_0_0_0.dll</HintPath>
  <Aliases>v20</Aliases>
</Reference>

<runtime>
  <assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Common.Data" publicKeyToken="f3b12eb6de839f43" culture="neutral" />
      <codeBase version="1.0.0.0" href="libs\Common.Data.1_0_0_0.dll" />
      <codeBase version="2.0.0.0" href="libs\Common.Data.2_0_0_0.dll" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

J'ai réussi à construire avec succès.

Mais lorsque j'essaie de l'exécuter, j'obtiens une MissingMethodException pour UserData.loadData.

 Error

J'ai lu plusieurs articles sur stackoverflow, des articles msdn et codeproject, mais je n'arrive pas à le faire fonctionner. 

Lien 1 , Lien 2 , Lien 3 , Lien 4

Pense que je manque une étape importante mais ne peux pas comprendre quoi et pourrait vraiment avoir besoin d’aide.

[Edit1]

J'ai essayé d'utiliser les dll séparément, et elles fonctionnent. (Suppression de l'encombrement. Voir l'historique des versions pour les captures d'écran)

[Edit2]

J'ai essayé la proposition de Mukesh Kumar et modifié mon app.config en:

<runtime>
  <assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Common.Data" 
                        publicKeyToken="f3b12eb6de839f43" 
                        culture="neutral" />
      <bindingRedirect oldVersion="1.0.0.0"
                       newVersion="2.0.0.0"/>
    </dependentAssembly>
  </assemblyBinding>
</runtime>

Mais cela ne fonctionne toujours pas. Je reçois maintenant FileNotFoundException.

[Edit3]

Ok, je suis presque sûr que le <bindingRedirect> n’est pas correct et devrait plutôt être <codeBase>.

J'ai essayé de compiler à partir de la CLI:

csc Program.cs /reference:v10=libs/Common.Data.1_0_0_0.dll /reference:v20=libs/Common.Data.2_0_0_0.dll

Et ça marche bien. J'ai pu utiliser les deux API en même temps:

 enter image description here

Mais lorsque j'essaie de le construire à partir de Visual Studio, il se plaint de/référence, même si j'ai déjà spécifié l'alias de référence:

The extern alias 'v10' was not specified in a /reference option

J'ai essayé de modifier <Reference /> pour inclure/exclure <SpecificVersion>True</SpecificVersion> sans aucun effet.

J'ai trouvé ce post , où la solution consistait à supprimer et à rajouter les chemins. À l'aide de cette solution, Visual Studio fonctionne correctement, mais je suis revenu à System.MissingMethodException.

On se croirait presque l'avoir, mais pas tout à fait. Comment puis-je obtenir Visual Studio pour générer correctement?

[Edit4]

J'ai essayé la méthode de Miguel, mais je ne travaille toujours pas.

Dans cette tentative, j'ai renommé les DLL en leur nom d'origine et je les ai stockées dans des dossiers différents.

 enter image description here

J'ai ensuite mis à jour app.config pour faire un bindingRedirect ainsi que du code base.

En l'exécutant, je reçois MissingMethodException.

Je ne savais pas trop ce que voulait dire 4- Find the and put in False. J'ai donc essayé toutes les combinaisons de <Private>, <SpecificVersion>, ainsi que le paramétrage de <Reference Include> sur FQN, mais aucune de ces combinaisons ne fonctionne.

En regardant ma 3ème édition, j'ai réussi à compiler et à exécuter l'exemple avec succès (avec mes hll renommés dlls + app.config codebase) lorsque vous avez terminé via l'interface de ligne de commande.

Mon problème est maintenant, comment puis-je configurer mon csproj, donc il construit la même chose.

17
jayars

Vous devez utiliser un assembly dépendant avec bindingRedirect, mais vous devez également placer des dll dans un dossier différent ou enregistrer sous un nom différent. Ceci fait, vous devez mettre ce qui suit dans votre app.config:

<assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
  <dependentAssembly>
        <assemblyIdentity name="myAssembly"
                          publicKeyToken="here token dll"
                          culture="neutral" />
       <bindingRedirect oldVersion="0.0.0.0-1.0.0.0" newVersion="1.0.0.0" />
       <bindingRedirect oldVersion="1.0.0.1-2.0.0.0" newVersion="2.0.0.0" />
       <codeBase version="1.0.0.0" href="folder\namedll.dll" />
       <codeBase version="2.0.0.0" href="folder\namedll.dll" />
      </dependentAssembly>
   </assemblyBinding>

Avec ce code devrait compiler et exécuter, mais parfois, VS supprime ou remplace le code dans le fichier app.config lorsqu’il est compilé. Vous devez le vérifier dans le fichier de configuration du dossier de compilation. Si cela réussit, vous pouvez modifier le fichier .csproj. Pour cela vous devez faire:

1- Décharger le projet concerné
2- Faites un clic droit sur le projet
3- Cliquez sur Editer le projet
4- Recherchez la propriété <AutoGenerateBindingRedirects> et définissez-la sur False
5- Enregistrer les modifications et recharger le projet

Cela fonctionne pour moi. Dans mon projet, j'utilise deux versions d'Automapper.

Enfin, une autre solution consiste à utiliser l'événement de construction AppDomain.CurrentDomain.AssemblyResolve et à charger la DLL spécifique.

Pour cela, vous devez assister à l'événement:

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    //debug and check the name
    if (args.Name == "MyDllName")
        return Assembly.LoadFrom("c:\\pathdll\midllv1.dll")
    else if(args.Name ="MyDllName2")
        return Assembly.LoadFrom("c:\\pathdll\midllv2.dll");
    else
        return Assembly.LoadFrom("");
}
7
miguel

En l'absence d'une réponse complète qui répond à mes besoins, j'ai décidé de faire don de ma solution avec la méthode AssemblyResolve pour utiliser deux dll et charger le fichier correspondant à partir du fichier, en utilisant la réflexion pour résoudre le type. Pour cette démonstration, j'ai créé deux DLL appelées MathFuncs. Si vous essayez d'appeler leur fonction Add.add, le problème sera résolu en deux implémentations différentes. Pour voir les différents résultats, basculez l'entier de la version entre les valeurs 1 et 2:

public static int version = 1;
    public static void Main(string[] args)
    {
        AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
        version = 1;
        Type a = Type.GetType("MathFuncs.Add, MathFuncs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        MethodInfo methodInfo = a?.GetMethod("add");
        object result = null;
        if (methodInfo != null)
        {
            object[] parametersArray = new object[] {1, 2};
            result = methodInfo.Invoke(Activator.CreateInstance(a, null), parametersArray);
        }

        if (result != null)
        {
            Console.WriteLine((int)result);
        }
        else
        {
            Console.WriteLine("failed");
        }

        Console.Read();
    }


    public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        FileInfo fi = null;
        if (version == 1)
        {
            fi = new FileInfo("C:\\Users\\ohbitton\\Desktop\\MathFuncs\\MathFuncs.dll");
        }
        else
        {
            fi = new FileInfo("C:\\Users\\ohbitton\\Desktop\\MathFuncs2\\MathFuncs.dll");
        }
        return Assembly.LoadFrom(fi.FullName);

    }

Pour obtenir le nom complet de l'espace de noms pour le Type.GetType, vous pouvez utiliser Powershell: 

([system.reflection.Assembly]::loadfile("C:\Users\ohbitton\Desktop\MathFuncs\MathFuncs.dll")).FullName
1
Ohad Bitton

Essayez de mettre vos détails de configuration comme suit.

 <dependentAssembly>
            <assemblyIdentity name="myAssembly"
                              publicKeyToken="32ab4ba45e0a69a1"
                              culture="neutral" />
            <bindingRedirect oldVersion="1.0.0.0"
                             newVersion="2.0.0.0"/>
  </dependentAssembly>

ici, vous pouvez vous fournir l'ancienne version de la DLL et la nouvelle version. Vous pouvez simplement suivre cet article article et aussi ceci .

J'espère que ceci vous aidera.

1
Mukesh Kumar