發現問題#
近日在製作 Avalonia 程序過程中,發現調試時很正常,發佈後卻無法運行,感覺很奇怪,我的發佈配置有以下兩條:
<PublishTrimmed>True</PublishTrimmed>
<PublishAot Condition="'$(Configuration)' != 'Debug'">true</PublishAot>
第一條啟用了裁剪,第二條啟用了 AOT 編譯(但是在開發時沒開啟,因為預覽窗口不支持 AOT)
經過一番排查,發現這兩個都有問題:
- 反射對 AOT 不友好,我的
ViewLocator.cs
使用了反射來創建對象 - 有的庫對剪裁支持不好,因此要對其關閉剪裁
解決方案#
問題 1: 消除反射#
原代碼使用反射創建對象:
public Control? Build(object? param)
{
if (param != null && param is ViewModelBase)
{
var viewModelType = param.GetType();
if (_viewModelViewMappings.TryGetValue(viewModelType, out var viewType))
{
return (Control)Activator.CreateInstance(viewType)!; // 這裡使用了反射
}
return new TextBlock { Text = "Not Found: " + viewModelType.FullName };
}
return null;
}
修改後使用工廠方法:
public Control? Build(object? param)
{
if (param != null && param is ViewModelBase)
{
var viewModelType = param.GetType();
if (_viewModelViewMappings.TryGetValue(viewModelType, out var viewFactory))
{
return viewFactory(); // 使用了工廠方法
}
return new TextBlock { Text = "Not Found: " + viewModelType.FullName };
}
return null;
}
這樣就可以消除反射了
問題 2: 關閉特定庫的剪裁#
發佈時我注意到很多這樣的剪裁警告
2>Assembly 'Serilog' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries
2>Assembly 'ReactiveUI' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries
2>Assembly 'SukiUI' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries
2>Assembly 'Avalonia.Controls.DataGrid' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries
2>Assembly 'Avalonia.Controls.DataGrid' produced AOT analysis warnings.
因此我修改項目的 sln 文件,增加以下片段,通過外部的 xml 文件來限制剪裁
<ItemGroup>
<TrimmerRootDescriptor Include="TrimmerRoots.xml" />
</ItemGroup>
下面是TrimmerRoots.xml
的內容
<?xml version="1.0" encoding="utf-8" ?>
<linker>
<assembly fullname="Serilog" />
<assembly fullname="Serilog.Sinks.Console" />
<assembly fullname="Serilog.Sinks.File" />
<assembly fullname="ReactiveUI" />
<assembly fullname="SukiUI" />
<assembly fullname="Serilog.Sinks.File" />
</linker>
我將所有報警告的項目都加入了進去,雖然後面的編譯依舊有新的警告,但是程序可以正常運行了。至此,所有問題解決。