我使用IViewLocalizer的localizated resources具有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?
}
我尝试使用this answer中的代码,该代码使用MethodBase.GetMethodBody()。GetILAsByteArray(),但没有用-看起来方法public override Task ExecuteAsync()
很短,所以看起来我的视图在其他地方渲染了。
[此外,我不确定是否应该查找getter的MetadataToken(将IViewLocalizer注入到视图中)还是应该查找实现IViewLocalizer.GetString(string)
和IViewLocalizer.GetString(string, params object[])
的任何具体方法的用法。
PS:我知道,只要注入的本地化程序遵循某种命名标准,就可以在cshtml视图上使用Regex。如果我不知道反射解决方案,那就是计划B。
异步方法(以及迭代器类)是在代表状态机的嵌套帮助器类中生成的。这些帮助程序类可以在主要类型的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
extension,因为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