È importante che la registrazione degli eventi AssemblyResolve si verifichi prima di fare riferimento a qualsiasi tipo dagli assembly che si pianifica per il caricamento tramite tale callback. Anche se non si fa riferimento ai tipi nel costruttore statico, è inoltre necessario assicurarsi di non avere variabili statiche nella classe o le sue classi di base tali tipi di riferimento negli assembly esterni.
Ecco come lo faccio: ho una classe separata per gestire il caricamento di montaggio, giustamente chiamato AssemblyLoader:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
internal static class AssemblyLoader
{
internal static void RegisterAssemblyLoader()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve -= OnResolveAssembly;
currentDomain.AssemblyResolve += OnResolveAssembly;
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
return LoadAssemblyFromManifest(args.Name);
}
private static Assembly LoadAssemblyFromManifest(string targetAssemblyName)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(targetAssemblyName);
string resourceName = DetermineEmbeddedResourceName(assemblyName, executingAssembly);
using (Stream stream = executingAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
return null;
byte[] assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
private static string DetermineEmbeddedResourceName(AssemblyName assemblyName, Assembly executingAssembly)
{
//This assumes you have the assemblies in a folder named "EmbeddedAssemblies"
string resourceName = string.Format("{0}.EmbeddedAssemblies.{1}.dll",
executingAssembly.GetName().Name, assemblyName.Name);
//This logic finds the assembly manifest name even if it's not an case match for the requested assembly
var matchingResource = executingAssembly.GetManifestResourceNames()
.FirstOrDefault(res => res.ToLower() == resourceName.ToLower());
if (matchingResource != null)
{
resourceName = matchingResource;
}
return resourceName;
}
}
Poi, nella mia classe plug-in, io uso un costruttore statico di rimettere in mia AssemblyLoader . Spostando la logica nella classe separata, limito il numero di tipi che sto facendo riferimento al contesto statico della mia classe di plugin, riducendo il rischio che accidentalmente faccio riferimento a qualcosa che si trova negli assembly esterni.
using System;
using Microsoft.Xrm.Sdk;
public class MyPlugin : IPlugin
{
static MyPlugin()
{
AssemblyLoader.RegisterAssemblyLoader();
}
public void Execute(IServiceProvider serviceProvider)
{
//...
}
}
ho anche spostato praticamente tutti gli usi delle assemblee esterne in altre classi in modo che la mia classe plug-in non utilizza il affatto. Per lo più, questo è fuori dall'abbondanza di cautela.
fonte
2013-07-13 01:30:22
ok ... la vostra soluzione non è sostanzialmente diversa da quella che ho postato ... tuttavia, si ottiene il segno di spunta dal momento che mi ha chiesto se il problema sarebbe persistito se la complessità del problema fosse ridotta e aumentata gradualmente. –
La stessa tecnica funziona con il flusso di lavoro personalizzato? –