次のようにIViewLocalizerを使用してローカライズされたリソースを持つRazorビューがあります。
@inject Microsoft.AspNetCore.Mvc.Localization.IViewLocalizer Localizer
...
@Localizer.GetString("Click here to Log in")
(POファイルディクショナリを構築するために)プリコンパイルされたビューからこれらすべての文字列を抽出しようとしているので、ビューに挿入されたIViewLocalizerのすべての参照を見つける必要があると思います。
var findAll = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static;
var asm = Assembly.LoadFile("MySite.Web.Views.dll"));
var view = asm.GetType("AspNetCore.Views_Home_Index");
var props = view.GetProperties(findAll).Where(p=>p.PropertyType.FullName == "Microsoft.AspNetCore.Mvc.Localization.IViewLocalizer").ToList();
// I want to find all references that use this this IViewLocalizer Localizer.get()
var getter = props.First().GetGetMethod().MetadataToken;
// How can I do it?
var allMethods = view.GetMethods(findAll);
foreach(var method in allMethods)
{
// How can I check my methods for usages of that property getter, and in case get the strings?
}
MethodBase.GetMethodBody()。GetILAsByteArray()を使用するこの回答のコードを使用してみましたが、機能しませんでした。メソッドpublic override Task ExecuteAsync()
が非常に短いため、ビューが別の場所にレンダリングされているようです。
そしてさらに、私はゲッターのMetadataTokenを探す必要かどうかわからないんだけど(IViewLocalizerは私の意見に注入)または私はその実装した任意の具体的な方法の使用法を探す必要ならばIViewLocalizer.GetString(string)
とIViewLocalizer.GetString(string, params object[])
。
PS:注入されたローカライザーが何らかの命名基準に従っている限り、cshtmlビューで正規表現を使用できることを認識しています。リフレクションソリューションがわからない場合に備えて、プランBです。
ILSpyの助けを借りて(そして後でこの回答とこれによって確認されました)、非同期メソッド(およびイテレータークラス)がステートマシンを表すネストされたヘルパークラス全体で生成されることを発見しました。これらのヘルパークラスは、メインタイプのNestedTypesプロパティにあります。
内部型のすべてのメソッドを検索に追加すると、探していたメソッド(IViewLocalizerのゲッター)への複数の呼び出しを見つけることができました。これはうまくいきました:
var allMethods = view.GetMethods(findAll).ToList();
allMethods.AddRange( // also search in methods of nested types
view.GetNestedTypes(findAll).SelectMany(x => x.GetMethods(findAll)));
foreach(var method in allMethods)
{
var usages = ((MethodBase)method).GetMethodUsageOffsets(get);
if (usages.Count() > 0)
Debug.WriteLine($"{method.ReflectedType.FullName}.{method.Name}");
}
AspNetCore.Views_Home_Index3.ExecuteAsync()で見つかると期待していたすべての呼び出しは、実際には内部タイプのこれらのメソッドに分散されていました。
AspNetCore.Views_Home_Index3+<ExecuteAsync>d__22.MoveNext
AspNetCore.Views_Home_Index3+<<ExecuteAsync>b__22_0>d.MoveNext
AspNetCore.Views_Home_Index3+<<ExecuteAsync>b__22_1>d.MoveNext
AspNetCore.Views_Home_Index3+<<ExecuteAsync>b__22_2>d.MoveNext
AspNetCore.Views_Home_Index3+<<ExecuteAsync>b__22_3>d.MoveNext
AspNetCore.Views_Home_Index3+<<ExecuteAsync>b__22_4>d.MoveNext
AspNetCore.Views_Home_Index3+<<ExecuteAsync>b__22_5>d.MoveNext
AspNetCore.Views_Home_Index3+<<ExecuteAsync>b__22_6>d.MoveNext
... etc... all states from the state machine
Mono.Cecilを使用すると、このGetMethodUsageOffsets
拡張機能も必要ないため、Cecilはバイトコードを簡単に解釈できるため、さらに簡単になりました。
var module = ModuleDefinition.ReadModule(System.IO.Path.Combine(appFolder, "MyApp.Web.Views.dll"));
var type = module.Types.First(x => x.Name.EndsWith("Index3"));
var allInstructions = typeDefinition.Methods
.Union(typeDefinition.NestedTypes.SelectMany(x => x.Methods))
.Where(m => m.HasBody)
.SelectMany(x => x.Body.Instructions).ToList();
var calls = allInstructions.Where(i => i.ToString().Contains(
"callvirt Microsoft.Extensions.Localization.LocalizedString Microsoft.AspNetCore.Mvc.Localization.IHtmlLocalizer::GetString(System.String)")).ToList();
foreach (var call in calls) // previous is where string parameter is loaded
System.Diagnostics.Debug.WriteLine(i.Previous.ToString());
結果:
IL_020a: ldstr "More Options"
IL_0241: ldstr "Add Text"
IL_15c8: ldstr "Reset"
IL_15ff: ldstr "Save Text Box Settings"
IL_0019: ldstr "None"
...etc
2020-09-23の編集:いくつかのメソッドが複数のレベルに分割できることを発見したので、第1レベルのNestedTypesのみを検索することはできません。私は現在(純粋な反射の代わりに)Cecilを使用しているので、これは私の現在のコードです:
// All assemblies that I want to scan
var modules = new List<ModuleDefinition>();
modules.Add(ModuleDefinition.ReadModule(System.IO.Path.Combine(appFolder, "MyProject.Web.Views.dll")));
modules.Add(ModuleDefinition.ReadModule(System.IO.Path.Combine(appFolder, "MyProject.Web.dll")));
// All types in my Assemblies
var allTypes = modules.SelectMany(m => m.Types).ToList();
// Then for each of those types I get all Nested Types:
// Get all nested types
for(int i = 0; I < allTypes.Count(); i++)
allTypes.AddRange(allTypes[i].NestedTypes);
// Get all instructions using Cecil
var allInstructions = allTypes.SelectMany(x => x.Methods)
.Where(m => m.HasBody)
.SelectMany(x => x.Body.Instructions).ToList();
var i18nInstructions = allInstructions.Where(i =>
i.ToString().Contains("GetString(System.String)")
|| i.ToString().Contains("Microsoft.AspNetCore.Mvc.Localization.HtmlLocalizerExtensions::GetHtml(Microsoft.AspNetCore.Mvc.Localization.IHtmlLocalizer,System.String)")
).ToList();
// This prints all values of "ldstr 'value'"
// which happen before GetString() or GetHtml()
foreach (var i in i18nInstructions)
System.Diagnostics.Debug.WriteLine(i.Previous.Operand.ToString());
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加