0

Web サイトから送信されたジョブを処理するジョブ サーバーがあります。Web サイトのアセンブリはパッケージ化され、ジョブの一部としてジョブ サーバーに送信されます (すべてのサテライト リソース アセンブリを含む)。

これらのアセンブリは、ジョブ処理が開始される前に Assembly.Load( byte[] ) を介してロードされます。

ジョブの処理中に、リソース文字列 (つまり、検証メッセージ) のスナップショットを作成するポイントに到達します。検証ルール/メッセージは時間の経過とともに変化する可能性があり、以前に処理されたジョブ/メッセージを表示するために戻ってきた場合、「最新の」リソース文字列を表示したくないため、単にリソース キーを保存することはできません。表示されるジョブ処理時の値が必要です。また、Web サイトのスレッドが実行されている適切なカルチャでメッセージを表示できるように、サポートされている各カルチャのメッセージのスナップショットが必要です。

メッセージの取得を試行するために、Web サイト アセンブリはサポートされているロケールを指定するため、ジョブ サーバーで次のようにして Web サイト アセンブリの ResourceManager を作成します。

var resourceType = Type.GetType( "BTR.Websites.MadHatter.Clients.ABCCorp.Resources.ClientStrings" );
var rm = resourceType != null ? new ResourceManager( resourceType ) : null;

これは正常に機能し、ResourceManager を取得しますが、XElement validationRule と string[] ロケールが指定されている場合、次のコードは「機能しません」:

validationRule.Add( 
    from l in locales
    let message = rm.GetString( p.Value.ToString(), new CultureInfo( l ) )
    select new XAttribute( string.Format( "Message.{0}", l ), message ) );

基本的に、どの CultureInfo を渡しても、既定の (en-US) カルチャのメッセージが表示されます。

AppDomain.CurrentDomain.GetAssemblies() は、読み込まれた Web サイト アセンブリとサテライト アセンブリの両方を示します。

{ABCCorp.ServiceModel, Version=6.0.4497.18334, Culture=neutral, PublicKeyToken=null} System.Reflection.Assembly {System.Reflection.RuntimeAssembly} {ABCCorp.ServiceModel.resources, Version=6.0.4497.18334, Culture=fr-FR, PublicKeyToken=null} System.Reflection.Assembly {System.Reflection.RuntimeAssembly}

しかし、ウォッチ ウィンドウで次のことを試してみると (上記のコードと同じ)、en-US バージョンが表示されます。

rm.GetString( "ResourceKey", new CultureInfo( "fr-FR" ) )

最後に、おそらく私たちは LINQPad に精通しています :) 次のスクリプトは「動作」します。ABCCorp の ServiceModel.dll またはサテライト dll への参照はありません。

Assembly.LoadFile( @"C:\BTR\Source\ABCCorp.ServiceModel.dll" );
Assembly.LoadFile( @"C:\BTR\Source\fr-FR\ABCCorp.ServiceModel.resources.dll" );
AppDomain.CurrentDomain.GetAssemblies().Dump();
var resourceType = Type.GetType( "BTR.Websites.MadHatter.Clients.ABCCorp.Resources.ClientStrings, ABCCorp.ServiceModel" ).Dump();
var rm = resourceType != null ? new ResourceManager( resourceType ) : null;
rm.GetString( "FlatMemberInformationBasicInformation_DateDeathProof", new CultureInfo( "fr-FR" ) ).Dump();

どんな提案でも大歓迎です。

更新: ジョーからの提案に基づいて、これが最終的なコードです。byte[] コンテンツは、Web サイト アセンブリと、存在する場合はすべてのサテライト リソース アセンブリを含む zip ファイルです。

private Assembly LoadPackagedAssembly( byte[] content )
{
    var loaded = new Dictionary<string, Assembly>();

    AppDomain.CurrentDomain.AssemblyResolve += delegate( object sender, ResolveEventArgs args )
    {
        return loaded.ContainsKey( args.Name )
            ? loaded[ args.Name ]
            : null;
    };

    Assembly mainAssembly = null;

    using ( var stream = new MemoryStream( content ) )
    {
        using ( var zip = new ZipInputStream( stream ) )
        {
            ZipEntry entry;

            while ( ( entry = zip.GetNextEntry() ) != null )
            {
                using ( var buffer = new MemoryStream() )
                {
                    Streams.CopyStream( zip, buffer );

                    var assembly = Assembly.Load( buffer.ToArray() );

                    loaded[ assembly.FullName ] = assembly;

                    if ( mainAssembly == null ) mainAssembly = assembly;
                }
            }
        }
    }

    return mainAssembly;
}
4

1 に答える 1

0

これは、アセンブリ解決の問題のように聞こえます。Load(byte[]) と LoadFile を使用すると、バインディング コンテキストがなくなります。つまり、LoadFrom を使用するか、AppDomain の AssemblyResolve イベントを処理してこれに対処します。

LINQPad は AssemblyResolve イベントを処理して、別の場所で同じ DLL を間接的に参照することによって発生する問題を解決することに注意してください。これの副作用は、そうでなければバインディングが失敗するシナリオでバインディング コンテキストを融合し、他の方法では機能しない可能性があるものを機能させることです。

于 2012-04-25T01:18:23.773 に答える