工作中,需要进行任务调度,并且把技术栈切换到.NET Core 2.2上,然后,AppDomain 只支持 .net framwork ,所以,没有办法。.net core不支持 AppDomain,经过查找发现,只有.net Core 3.0 才支持。并且是 AssemblyLoadContext 实现.
代码如下
class Program
{
static void Main(string[] args)
{
List<LoadDll> list = new List<LoadDll>();
Console.WriteLine("开始加载DLL");
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskOne\bin\Debug\netstandard2.0\Demo.TaskOne.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskOne\bin\Debug\netstandard2.0\Demo.TaskOne.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskOne\bin\Debug\netstandard2.0\Demo.TaskOne.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskOne\bin\Debug\netstandard2.0\Demo.TaskOne.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskOne\bin\Debug\netstandard2.0\Demo.TaskOne.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskTwo\bin\Debug\netstandard2.0\Demo.TaskTwo.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskTwo\bin\Debug\netstandard2.0\Demo.TaskTwo.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskTwo\bin\Debug\netstandard2.0\Demo.TaskTwo.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskTwo\bin\Debug\netstandard2.0\Demo.TaskTwo.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskTwo\bin\Debug\netstandard2.0\Demo.TaskTwo.dll"));
list.Add(Load(@"E:\项目文件\AssemblyLoadContextDemo\Demo.TaskTwo\bin\Debug\netstandard2.0\Demo.TaskTwo.dll"));
foreach (var item in list)
{
item.StartTask();
}
Console.WriteLine("开启了任务!");
SpinWait.SpinUntil(() => false, 10 * 1000);
foreach (var item in list)
{
var s = item.UnLoad();
SpinWait.SpinUntil(() => false, 2 * 1000);
Console.WriteLine($"任务卸载:{s}");
}
Console.WriteLine("任务测试完毕");
Console.ReadLine();
}
public static LoadDll Load(string filePath)
{
var load = new LoadDll();
load.LoadFile(filePath);
return load;
}
}
具体的获取DLL的方法 第一版
/// <summary>
/// dll文件的加载
/// </summary>
public class LoadDll
{
/// <summary>
/ 任务实体
/// </summary>
public ITask _task;
public Thread _thread;
/// <summary>
/// 核心程序集加载
/// </summary>
public AssemblyLoadContext _AssemblyLoadContext { get; set; }
/// <summary>
/// 获取程序集
/// </summary>
public Assembly _Assembly { get; set; }
/// <summary>
/// 载入load
/// </summary>
/// <param name="filepath"></param>
public bool LoadFile(string filepath)
{
try
{
_AssemblyLoadContext = new AssemblyLoadContext("", true);
using (var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read))
{
var _Assembly = _AssemblyLoadContext.LoadFromStream(fs);
foreach (var item in _Assembly.GetTypes())
{
if (item.GetInterface("ITask") != null)
{
_task = (ITask)Activator.CreateInstance(item);
break;
}
}
return true;
}
}
catch (Exception)
{
}
return false;
}
public bool StartTask()
{
bool RunState = false;
try
{
if (_task != null)
{
_thread = new Thread(new ThreadStart(_Run));
_thread.IsBackground = true;
_thread.Start();
RunState = true;
}
}
catch (Exception) { };
return RunState;
}
private void _Run()
{
try
{
_task.Run();
}
catch (Exception)
{
}
}
public bool UnLoad()
{
try
{
if (_thread != null)
{
try
{
_thread.Interrupt();
}
catch (Exception)
{
}
_thread = null;
}
if (_task != null)
{
_task = null;
}
if (_AssemblyLoadContext != null)
{
try
{
_AssemblyLoadContext.Unload();
}
catch (Exception)
{ }
}
return true;
}
catch (Exception)
{ }
return false;
}
}
具体的获取DLL的方法 第二版(解决加载别的依赖的问题)
/// <summary>
/// dll文件的加载
/// </summary>
public class LoadDll
{
/// <summary>
/ 任务实体
/// </summary>
public ITask _task;
public Thread _thread;
/// <summary>
/// 核心程序集加载
/// </summary>
public AssemblyLoadContext _AssemblyLoadContext { get; set; }
/// <summary>
/// 获取程序集
/// </summary>
public Assembly _Assembly { get; set; }
/// <summary>
/// 文件地址
/// </summary>
public string filepath = string.Empty;
/// <summary>
/// 指定位置的插件库集合
/// </summary>
AssemblyDependencyResolver resolver { get; set; }
public bool LoadFile(string filepath)
{
this.filepath = filepath;
try
{
resolver = new AssemblyDependencyResolver(filepath);
_AssemblyLoadContext = new AssemblyLoadContext(Guid.NewGuid().ToString("N"), true);
_AssemblyLoadContext.Resolving += _AssemblyLoadContext_Resolving;
using (var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read))
{
var _Assembly = _AssemblyLoadContext.LoadFromStream(fs);
var Modules = _Assembly.Modules;
foreach (var item in _Assembly.GetTypes())
{
if (item.GetInterface("ITask") != null)
{
_task = (ITask)Activator.CreateInstance(item);
break;
}
}
return true;
}
}
catch (Exception ex) { Console.WriteLine(ex.Message); };
return false;
}
private Assembly _AssemblyLoadContext_Resolving(AssemblyLoadContext arg1, AssemblyName arg2)
{
Console.WriteLine($"加载{arg2.Name}");
var path = resolver.ResolveAssemblyToPath(arg2);
if (!string.IsNullOrEmpty(path))
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return _AssemblyLoadContext.LoadFromStream(fs);
}
}
return null;
}
public bool StartTask()
{
bool RunState = false;
try
{
if (_task != null)
{
_thread = new Thread(new ThreadStart(_Run));
_thread.IsBackground = true;
_thread.Start();
RunState = true;
}
}
catch (Exception ex) { Console.WriteLine(ex.Message); };
return RunState;
}
private void _Run()
{
try
{
_task.Run();
}
catch (Exception ex) { Console.WriteLine(ex.Message); };
}
public bool UnLoad()
{
try
{
_thread?.Interrupt();
}
catch (Exception)
{
}
finally
{
_thread = null;
}
_task = null;
try
{
_AssemblyLoadContext?.Unload();
}
catch (Exception)
{ }
finally
{
_AssemblyLoadContext = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
return true;
}
}
以及 定义的接口
/// <summary>
/// 任务接口
/// </summary>
public interface ITask
{
/// <summary>
/// 任务的运行方法
/// </summary>
/// <returns></returns>
void Run();
}
两个插件化的实例代码
public class PrintStr : ITask
{
public void Run()
{
int a = 0;
while (true)
{
Console.WriteLine($"PrintStr:{ a}");
a++;
Thread.Sleep(1 * 1000);
}
}
}
public class PrintDate : ITask
{
public void Run()
{
while (true)
{
Console.WriteLine($"PrintDate:{ DateTime.Now.ToString()}");
Thread.Sleep(1 * 1000);
}
}
}
以及运行的结果如下:
测试成功,并且,不会引起 DLL 删除。可以热加载。