Řešení zavedení sestavení

.NET poskytuje AppDomain.AssemblyResolve událost pro aplikace, které vyžadují větší kontrolu nad načítáním sestavení. Díky zpracování této události může vaše aplikace načíst sestavení do kontextu zatížení mimo normální cesty sondy, vybrat, která z několika verzí sestavení se má načíst, vygenerovat dynamické sestavení a vrátit ho atd. Toto téma obsahuje pokyny pro zpracování AssemblyResolve události.

Poznámka:

K překladu zatížení sestavení v kontextu pouze reflexe použijte AppDomain.ReflectionOnlyAssemblyResolve místo toho událost.

Jak funguje událost AssemblyResolve

Při registraci obslužné rutiny pro AssemblyResolve událost se obslužná rutina vyvolá pokaždé, když modul runtime selže svázat se sestavením podle názvu. Voláním následujících metod z uživatelského kódu může dojít například k AssemblyResolve vyvolání události:

Co obslužná rutina události dělá

Obslužná rutina události AssemblyResolve obdrží zobrazovaný název sestavení, který se má načíst, ve ResolveEventArgs.Name vlastnosti. Pokud obslužná rutina nerozpozná název sestavení, vrátí null (C#), Nothing (Visual Basic) nebo nullptr (Visual C++).

Pokud obslužná rutina rozpozná název sestavení, může načíst a vrátit sestavení, které splňuje požadavek. Následující seznam popisuje některé ukázkové scénáře.

  • Pokud obslužná rutina zná umístění verze sestavení, může sestavení načíst pomocí Assembly.LoadFrom metody nebo Assembly.LoadFile a může v případě úspěchu vrátit načtené sestavení.

  • Pokud má obslužná rutina přístup k databázi sestavení uložených jako pole bajtů, může načíst pole bajtů pomocí jednoho z Assembly.Load přetížení metody, které přebírají bajtové pole.

  • Obslužná rutina může vygenerovat dynamické sestavení a vrátit ho.

Poznámka:

Obslužná rutina musí načíst sestavení do kontextu načtení z kontextu, do kontextu načtení nebo bez kontextu. Pokud obslužná rutina načte sestavení do kontextu jen reflexe pomocí Assembly.ReflectionOnlyLoad metody nebo Assembly.ReflectionOnlyLoadFrom metody, pokus o načtení, který vyvolal AssemblyResolve událost, selže.

Je zodpovědností obslužné rutiny události vrátit vhodné sestavení. Obslužná rutina může parsovat zobrazovaný název požadovaného sestavení předáním ResolveEventArgs.Name hodnoty vlastnosti konstruktoru AssemblyName(String) . Počínaje rozhraním .NET Framework 4 může obslužná rutina použít ResolveEventArgs.RequestingAssembly vlastnost k určení, zda aktuální požadavek je závislost jiného sestavení. Tyto informace můžou pomoct identifikovat sestavení, které bude vyhovovat závislosti.

Obslužná rutina události může vrátit jinou verzi sestavení, než je požadovaná verze.

Ve většině případů se sestavení vrácené obslužnou rutinou zobrazí v kontextu zatížení bez ohledu na kontext, do kterého obslužná rutina načte. Pokud například obslužná rutina používá metodu Assembly.LoadFrom k načtení sestavení do kontextu načtení z kontextu, sestavení se zobrazí v kontextu načtení, když obslužná rutina vrátí. V následujícím případě se však sestavení zobrazí bez kontextu, když obslužná rutina vrátí:

Informace o kontextech naleznete v Assembly.LoadFrom(String) přetížení metody.

Do stejné domény aplikace lze načíst více verzí stejného sestavení. Tento postup se nedoporučuje, protože může vést k problémům s přiřazením typu. Přečtěte si osvědčené postupy pro načítání sestavení.

Co by obslužná rutina události neměla dělat

Primárním pravidlem pro zpracování AssemblyResolve události je, že byste se neměli pokoušet vrátit sestavení, které nerozpoznáte. Při psaní obslužné rutiny byste měli vědět, která sestavení mohou způsobit vyvolání události. Obslužná rutina by měla vrátit hodnotu null pro jiná sestavení.

Důležité

Počínaje rozhraním .NET Framework 4 AssemblyResolve je událost vyvolána pro satelitní sestavení. Tato změna má vliv na obslužnou rutinu události napsanou pro starší verzi rozhraní .NET Framework, pokud se obslužná rutina pokusí vyřešit všechny požadavky na načtení sestavení. Obslužné rutiny událostí, které ignorují sestavení, která nerozpoznávají, nejsou touto změnou ovlivněny: Vrátí nulla následují normální záložní mechanismy.

Při načítání sestavení obslužná rutina události nesmí používat žádné přetížení AppDomain.Load nebo Assembly.Load metody, které mohou způsobit AssemblyResolve , že událost bude vyvolána rekurzivně, protože to může vést k přetečení zásobníku. (Viz seznam uvedený výše v tomto tématu.) K tomu dochází i v případě, že zadáte zpracování výjimek pro požadavek na načtení, protože není vyvolán žádná výjimka, dokud se nevracejí všechny obslužné rutiny událostí. Následující kód tedy vede k přetečení zásobníku, pokud MyAssembly ho nenajdete:

using System;
using System.Reflection;

class BadExample
{
    static void Main()
    {
        AppDomain ad = AppDomain.CreateDomain("Test");
        ad.AssemblyResolve += MyHandler;

        try
        {
            object obj = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static Assembly MyHandler(object source, ResolveEventArgs e)
    {
        Console.WriteLine("Resolving {0}", e.Name);
        // DO NOT DO THIS: This causes a StackOverflowException
        return Assembly.Load(e.Name);
    }
}

/* This example produces output similar to the following:

Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null

Process is terminated due to StackOverflowException.
 */
Imports System.Reflection

Class BadExample

    Shared Sub Main()

        Dim ad As AppDomain = AppDomain.CreateDomain("Test")
        AddHandler ad.AssemblyResolve, AddressOf MyHandler

        Try
            Dim obj As object = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType")
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End Sub

    Shared Function MyHandler(ByVal source As Object, _
                              ByVal e As ResolveEventArgs) As Assembly
        Console.WriteLine("Resolving {0}", e.Name)
        // DO NOT DO THIS: This causes a StackOverflowException
        Return Assembly.Load(e.Name)
    End Function
End Class

' This example produces output similar to the following:
'
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'...
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'
'Process is terminated due to StackOverflowException.
using namespace System;
using namespace System::Reflection;

ref class Example
{
internal:
    static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
    {
        Console::WriteLine("Resolving {0}", e->Name);
        // DO NOT DO THIS: This causes a StackOverflowException
        return Assembly::Load(e->Name);
    }
};

void main()
{
    AppDomain^ ad = AppDomain::CreateDomain("Test");
    ad->AssemblyResolve += gcnew ResolveEventHandler(&Example::MyHandler);

    try
    {
        Object^ obj = ad->CreateInstanceAndUnwrap(
            "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
            "MyType");
    }
    catch (Exception^ ex)
    {
        Console::WriteLine(ex->Message);
    }
}

/* This example produces output similar to the following:

Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null

Process is terminated due to StackOverflowException.
*/

Správný způsob zpracování AssemblyResolve

Při překladu sestavení z AssemblyResolve obslužné rutiny StackOverflowException události bude nakonec vyvolán, pokud obslužná rutina používá Assembly.Load volání metody.AppDomain.Load Místo toho použijte LoadFile nebo LoadFrom metody, protože nevyvolají AssemblyResolve událost.

Představte si, že MyAssembly.dll se nachází v blízkosti spuštěného sestavení ve známém umístění, lze jej přeložit pomocí Assembly.LoadFile dané cesty k sestavení.

using System;
using System.IO;
using System.Reflection;

class CorrectExample
{
    static void Main()
    {
        AppDomain ad = AppDomain.CreateDomain("Test");
        ad.AssemblyResolve += MyHandler;

        try
        {
            object obj = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static Assembly MyHandler(object source, ResolveEventArgs e)
    {
        Console.WriteLine("Resolving {0}", e.Name);

        var path = Path.GetFullPath("../../MyAssembly.dll");
        return Assembly.LoadFile(path);
     }
}
Imports System.IO
Imports System.Reflection

Class CorrectExample

    Shared Sub Main()

        Dim ad As AppDomain = AppDomain.CreateDomain("Test")
        AddHandler ad.AssemblyResolve, AddressOf MyHandler

        Try
            Dim obj As Object = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType")
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End Sub

    Shared Function MyHandler(ByVal source As Object,
                              ByVal e As ResolveEventArgs) As Assembly
        Console.WriteLine("Resolving {0}", e.Name)

        Dim fullPath = Path.GetFullPath("../../MyAssembly.dll")
        Return Assembly.LoadFile(fullPath)
    End Function
End Class
using namespace System;
using namespace System::IO;
using namespace System::Reflection;

ref class Example
{
internal:
    static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
    {
        Console::WriteLine("Resolving {0}", e->Name);

        String^ fullPath = Path::GetFullPath("../../MyAssembly.dll");
        return Assembly::LoadFile(fullPath);
    }
};

void main()
{
    AppDomain^ ad = AppDomain::CreateDomain("Test");
    ad->AssemblyResolve += gcnew ResolveEventHandler(&Example::MyHandler);

    try
    {
        Object^ obj = ad->CreateInstanceAndUnwrap(
            "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
            "MyType");
    }
    catch (Exception^ ex)
    {
        Console::WriteLine(ex->Message);
    }
}

Viz také