-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathRuntimeAssemblyLoader.cs
More file actions
135 lines (111 loc) · 4.61 KB
/
Copy pathRuntimeAssemblyLoader.cs
File metadata and controls
135 lines (111 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
using System.IO.Compression;
using System.Reflection;
using System.Runtime.Loader;
using System.Text.Json;
using PixUI;
namespace AppBoxClient;
/// <summary>
/// 客户端运行时加载视图程序集及依赖程序集
/// </summary>
public static class AppAssemblies
{
private static AppAssemblyLoader _loader = new();
private static readonly Dictionary<string, Func<Widget>> ViewCreator = new();
private static readonly Dictionary<string, Type> ViewTypes = new();
/// <summary>
/// 创建视图模型的实例,如果尚未加载程序集则从服务端加载所有依赖的程序集
/// </summary>
/// <param name="viewModelName">eg: sys.HomePage</param>
public static async ValueTask<Widget> MakeViewWidgetAsync(string viewModelName)
{
//TODO: catch exception and return ErrorWidget
if (ViewCreator.TryGetValue(viewModelName, out var exists))
return exists();
var widgetType = await GetViewType(viewModelName);
var creator = () => (Widget)Activator.CreateInstance(widgetType)!;
ViewCreator.Add(viewModelName, creator);
return creator();
}
private static async Task<Type> GetViewType(string viewModelName)
{
if (ViewTypes.TryGetValue(viewModelName, out var exists))
return exists;
//从服务端获取所有依赖的程序集
var asmJson = await Channel.Invoke<byte[]?>("sys.SystemService.GetViewAssemblies", viewModelName);
if (asmJson == null) throw new Exception($"Can't find view: {viewModelName}");
var asmNames = JsonSerializer.Deserialize<string[]>(asmJson);
if (asmNames == null || asmNames.Length == 0)
throw new Exception($"获取视图模型[{viewModelName}]的程序集列表失败");
var needLoads = asmNames.Where(asmName => !_loader.HasLoad(asmName));
foreach (var asmName in needLoads)
{
var data = await Channel.Invoke<byte[]?>("sys.SystemService.LoadAppAssembly", asmName);
if (data == null)
throw new Exception($"Can't load assembly: {asmName}");
using var input = new MemoryStream(data);
using var output = new MemoryStream();
await using var cs = new DeflateStream(input, CompressionMode.Decompress, true);
await cs.CopyToAsync(output);
cs.Flush();
_loader.AddAssemblyData(asmName, output.ToArray());
}
//开始加载程序集
var viewAsm = _loader.GetOrLoadViewAssembly(asmNames[0]);
var names = viewModelName.Split('.');
var viewFullName = $"{names[0]}.Views.{names[1]}";
var widgetType = viewAsm.GetType(viewFullName);
if (widgetType == null)
throw new Exception($"Can't find widget type: {viewFullName}");
ViewTypes.Add(viewModelName, widgetType);
return widgetType;
}
internal static async Task<Type?> TryGetViewType(string viewModelName)
{
try
{
var res = await GetViewType(viewModelName);
return res;
}
catch (Exception ex)
{
Log.Warn(ex.Message);
return null;
}
}
internal static void Reset()
{
var old = Interlocked.Exchange(ref _loader, new AppAssemblyLoader());
ViewTypes.Clear();
ViewCreator.Clear();
old.Unload();
}
}
internal sealed class AppAssemblyLoader : AssemblyLoadContext
{
public AppAssemblyLoader() : base(true) { }
private readonly Dictionary<string, object> _loaded = new();
public bool HasLoad(string assemblyName) => _loaded.ContainsKey(assemblyName);
public void AddAssemblyData(string assemblyName, byte[] data) => _loaded.Add(assemblyName, data);
public Assembly GetOrLoadViewAssembly(string assemblyName)
{
if (!_loaded.TryGetValue(assemblyName, out var data))
throw new Exception("Can't find assembly data");
if (data is Assembly loaded) return loaded;
Log.Debug($"Begin load: {assemblyName}");
var assembly = LoadFromStream(new MemoryStream((byte[])data));
_loaded[assemblyName] = assembly;
return assembly;
}
protected override Assembly? Load(AssemblyName assemblyName)
{
if (assemblyName.Name != null && _loaded.TryGetValue(assemblyName.Name, out var data))
{
Log.Debug($"Begin load: {assemblyName.Name}");
if (data is Assembly assembly) return assembly;
assembly = LoadFromStream(new MemoryStream((byte[])data));
_loaded[assemblyName.Name] = assembly;
return assembly;
}
return null;
}
}