如何卸载 .NET Core 中的程序集/使其可收集?
How to unload an assembly in .NET Core / make it collectible?
如何在 .NET Core 中卸载 assemlby?
注:
.NET Core 不支持 AppDomain。
背景:
我必须动态计算 user-generated VisualBasic 表达式。
为此,我使用 Roslyn 动态编译表达式。
我从 Roslyn 编译器生成的字节数组中加载生成的程序集。
然后我创建一个实现抽象 class 的实例(因此我不必使用反射)。
然后我调用抽象class的方法EvaluateExpression。
做完这些之后,我要卸载加载的程序集(否则,我将有内存泄漏的快感)。
所以我在评估表达式后立即卸载程序集:
Parameters.AbstractEvaluator x = RoslynExpressionEvaluator.CreateEvaluator(expression, report.Code);
object value = x.EvaluateExpression();
x.LoadContext.Unload();
(loadContext在生成时保存在摘要class中)
到目前为止一切正常,但在 x.LoadContext.Unload();
,我得到
System.InvalidOperationException: "Cannot unload non-collectible
AssemblyLoadContext."
有可能解决这个问题吗?
我怎样才能使装配成为收藏品?
另外,我注意到我可以加载具有相同 class 名称的程序集(如您所见,代码中没有命名空间)
这在 multi-threading 环境(又名网络)中表现如何?
我可以无限地加载和加载动态生成的 class 的不同版本,直到机器用完 RAM 而没有出现故障吗?
或者为什么在两次加载相同的 class 时这完全有效?
using Microsoft.CodeAnalysis.Operations;
namespace ReportTester
{
public static class RoslynExpressionEvaluator
{
// a utility method that creates Roslyn compilation
// for the passed code.
// The compilation references the collection of
// passed "references" arguments plus
// the mscore library (which is required for the basic
// functionality).
private static Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation
CreateCompilationWithMscorlib
(
string assemblyOrModuleName,
string code,
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions compilerOptions = null,
System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.MetadataReference> references = null)
{
// create the syntax tree
Microsoft.CodeAnalysis.SyntaxTree syntaxTree =
Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(code, null, "");
// get the reference to mscore library
Microsoft.CodeAnalysis.MetadataReference mscoreLibReference =
Microsoft.CodeAnalysis.AssemblyMetadata
.CreateFromFile(typeof(string).Assembly.Location)
.GetReference();
// create the allReferences collection consisting of
// mscore reference and all the references passed to the method
System.Collections.Generic.List<Microsoft.CodeAnalysis.MetadataReference> allReferences =
new System.Collections.Generic.List<Microsoft.CodeAnalysis.MetadataReference>() { mscoreLibReference };
if (references != null)
{
allReferences.AddRange(references);
} // End if (references != null)
// create and return the compilation
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation compilation =
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation.Create
(
assemblyOrModuleName,
new[] {syntaxTree},
options: compilerOptions,
references: allReferences
);
return compilation;
} // End Function CreateCompilationWithMscorlib
// emit the compilation result into a byte array.
// throw an exception with corresponding message
// if there are errors
private static byte[] EmitToArray( this Microsoft.CodeAnalysis.Compilation compilation )
{
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{
// emit result into a stream
Microsoft.CodeAnalysis.Emit.EmitResult emitResult = compilation.Emit(stream);
if (!emitResult.Success)
{
// if not successful, throw an exception
foreach (Microsoft.CodeAnalysis.Diagnostic thisError in emitResult.Diagnostics)
{
if(thisError.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error)
throw new System.Exception(thisError.GetMessage());
} // Next thisError
} // End if (!emitResult.Success)
// get the byte array from a stream
return stream.ToArray();
} // End Using stream
} // End Function EmitToArray
public static Parameters.AbstractEvaluator CreateEvaluator(string expression, string code)
{
try
{
// the main class Program contain static void Main()
// that calls A.Print() and B.Print() methods
string mainProgramString = @"
Option Strict Off
Option Explicit Off
Option Infer On
Imports ReportTester.Parameters
imports System
Imports System.Collections.Generic
Imports Microsoft.VisualBasic.Strings
Imports Microsoft.VisualBasic.Interaction
Imports Microsoft.VisualBasic.Information
Public Class CodeImplementation
" + code + @"
End Class ' CodeImplementation
Public Class RsEval
Inherits AbstractEvaluator
Public Code As CodeImplementation
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
MyBase.New(allParameters)
'code
End Sub
Public Overrides Function EvaluateExpression() As Object
Return " + expression + @"
End Function
End Class ' RsEval
";
Microsoft.CodeAnalysis.MetadataReference sysRuntime =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(
typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location);
Microsoft.CodeAnalysis.MetadataReference vbRuntime =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(Microsoft.VisualBasic.Constants)
.Assembly
.Location);
Microsoft.CodeAnalysis.MetadataReference sysCorlib =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
Microsoft.CodeAnalysis.MetadataReference sysConsole =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(System.Console).Assembly.Location);
Microsoft.CodeAnalysis.MetadataReference reportParameters =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(ReportTester.Parameters.ParameterValue).Assembly.Location);
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions co =
new Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions
(
Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary
);
co.WithOptionStrict(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.Off);
co.WithOptionExplicit(false);
co.WithOptionInfer(true);
// create the Roslyn compilation for the main program with
// ConsoleApplication compilation options
// adding references to A.netmodule and B.netmodule
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation mainCompilation =
CreateCompilationWithMscorlib
(
"program",
mainProgramString,
// note that here we pass the OutputKind set to ConsoleApplication
compilerOptions: co,
references: new[] {sysRuntime, vbRuntime, sysCorlib, sysConsole, reportParameters }
);
// Emit the byte result of the compilation
byte[] result = mainCompilation.EmitToArray();
// System.AppDomain temporaryAppDomain = System.AppDomain.CreateDomain("TemporaryAppDomain");
// System.Reflection.Assembly assembly = temporaryAppDomain.Load(result);
// not supported ...
// Load the resulting assembly into the domain.
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(result);
// here we get the Program type and
// call its static method Main()
// to test the program.
// get the type Program from the assembly
System.Type programType = assembly.GetType("RsEval");
Parameters.AbstractEvaluator x = (Parameters.AbstractEvaluator)System.Activator.CreateInstance(programType);
x.LoadContext = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(assembly);
// Get the static Main() method info from the type
// System.Reflection.MethodInfo method = programType.GetMethod("EvaluateExpression");
// invoke Program.Main() static method
// object retValue = method.Invoke(null, null);
// System.AppDomain.Unload(temporaryAppDomain);
return x;
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.ToString());
throw;
}
return null;
} // End Sub Test
} // End Class RoslynExpressionEvaluator
} // End Namespace ReportTester
摘要class:
Public MustInherit Class AbstractEvaluator
Public Parameters As ParameterCollection
Public LoadContext As System.Runtime.Loader.AssemblyLoadContext
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
Me.Parameters = allParameters
End Sub
Public Overridable Sub SetValue(ByVal parameterName As String, parameter As ReportParameter)
Me.Parameters.Parameters(parameterName) = parameter
End Sub
Public Overridable Function GetValue(ByVal parameterName As String) As ReportParameter
Return Me.Parameters.Parameters(parameterName)
End Function
Public Overridable Sub SetParameters(ByVal allParameters As ParameterCollection)
Me.Parameters = Nothing
Me.Parameters = allParameters
End Sub
Public MustOverride Function EvaluateExpression() As Object
' Public Parameters As ParameterCollection
' Public MustOverride Sub SetCompulsoryParameter()
End Class ' AbstractEvaluator
' example
Public Class ConcreteEvaluator
Inherits AbstractEvaluator
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
MyBase.New(allParameters)
'code
End Sub
'Public Overrides Sub SetCompulsoryParameter()
'End Sub
Public Overrides Function EvaluateExpression() As Object
Dim expression As String = "System.DateTime.Now.AddDays(1+2+3).ToString(""dd.MM.yyyy"")" ' string expression would come from report, compiled with roslyn
Return " + expression + @"
End Function
End Class
嗯,问题是它卸载了整个上下文。
这意味着,如果我将程序集加载到默认上下文中,又名
System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
;
然后,在卸载时,它将卸载整个默认上下文。
绝对不好... ==>
System.InvalidOperationException: "Cannot unload non-collectible
AssemblyLoadContext."
所以你必须在不同的上下文中加载程序集(类似于 AppDomain)
public class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
public CollectibleAssemblyLoadContext() : base(isCollectible: true)
{ }
protected override Assembly Load(AssemblyName assemblyName)
{
return null;
}
}
byte[] result = null; // Assembly Emit-result from roslyn
System.Runtime.Loader.AssemblyLoadContext context = new CollectibleAssemblyLoadContext();
System.IO.Stream ms = new System.IO.MemoryStream(result);
System.Reflection.Assembly assembly = context.LoadFromStream(ms);
System.Type programType = assembly.GetType("RsEval");
MyAbstractClass eval = (MyAbstractClass) System.Activator.CreateInstance(programType);
eval.LoadContext = context;
eval.Stream = ms;
// do something here with the dynamically created class "eval"
然后你可以说
eval.LoadContext.Unload();
eval.Stream.Dispose();
如果你把它放到抽象的 IDisposable 接口中,那么奖励 class,然后你可以只使用 using,如果你想的话。
using (Parameters.AbstractEvaluator x = RoslynExpressionEvaluator.CreateEvaluator(expression, report.Code))
{
object value = x.EvaluateExpression();
System.Console.WriteLine(value);
}
所以摘要 class 看起来像这样:
Public MustInherit Class AbstractEvaluator
Implements IDisposable
Public Parameters As ParameterCollection
Public LoadContext As System.Runtime.Loader.AssemblyLoadContext
Public Stream As System.IO.Stream
Private disposedValue As Boolean ' Dient zur Erkennung redundanter Aufrufe.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
' TODO: verwalteten Zustand (verwaltete Objekte) entsorgen.
If Me.LoadContext IsNot Nothing Then
Me.LoadContext.Unload()
Me.LoadContext = Nothing
End If
If Me.Stream IsNot Nothing Then
Me.Stream.Dispose()
Me.Stream = Nothing
End If
End If
' TODO: nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() weiter unten überschreiben.
' TODO: große Felder auf Null setzen.
End If
disposedValue = True
End Sub
' TODO: Finalize() nur überschreiben, wenn Dispose(disposing As Boolean) weiter oben Code zur Bereinigung nicht verwalteter Ressourcen enthält.
'Protected Overrides Sub Finalize()
' ' Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in Dispose(disposing As Boolean) weiter oben ein.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' Dieser Code wird von Visual Basic hinzugefügt, um das Dispose-Muster richtig zu implementieren.
Public Sub Dispose() Implements IDisposable.Dispose
' Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in Dispose(disposing As Boolean) weiter oben ein.
Dispose(True)
' TODO: Auskommentierung der folgenden Zeile aufheben, wenn Finalize() oben überschrieben wird.
' GC.SuppressFinalize(Me)
End Sub
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
Me.Parameters = allParameters
End Sub
Public Overridable Sub SetValue(ByVal parameterName As String, parameter As ReportParameter)
Me.Parameters.Parameters(parameterName) = parameter
End Sub
Public Overridable Function GetValue(ByVal parameterName As String) As ReportParameter
Return Me.Parameters.Parameters(parameterName)
End Function
Public Overridable Sub SetParameters(ByVal allParameters As ParameterCollection)
Me.Parameters = Nothing
Me.Parameters = allParameters
End Sub
Public MustOverride Function EvaluateExpression() As Object
' Public Parameters As ParameterCollection
' Public MustOverride Sub SetCompulsoryParameter()
End Class ' AbstractEvaluator
' example
Public Class ConcreteEvaluator
Inherits AbstractEvaluator
Class SimplisticExampleCode
Public Function Tomorrow() As System.DateTime
Return System.DateTime.Now.AddDays(1)
End Function
End Class
Friend Code As SimplisticExampleCode
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
MyBase.New(allParameters)
'code
Me.Code = New SimplisticExampleCode
End Sub
'Public Overrides Sub SetCompulsoryParameter()
'End Sub
Public Overrides Function EvaluateExpression() As Object
'Dim expression As String = "System.DateTime.Now.AddDays(1+2+3).ToString(""dd.MM.yyyy"")" ' string expression would come from report, compiled with roslyn
'Return " + expression + @"
Return Code.Tomorrow().ToString("dd.MM.yyyy")
End Function
End Class
我在我的一个项目中使用了以下方法来获取所有实现了 IEntity
接口的类型:
IEnumerable<Type> types;
var context = new AssemblyLoadContext(null, true);
using (var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
{
var assembly = context.LoadFromStream(fs);
types = assembly.GetTypes().Where(t => t.IsClass && t.GetInterfaces().Any(i => i.FullName == typeof(IEntity).FullName));
}
context.Unload();
使用AssemblyLoadContext
好像就是你要找的
如何在 .NET Core 中卸载 assemlby?
注:
.NET Core 不支持 AppDomain。
背景:
我必须动态计算 user-generated VisualBasic 表达式。
为此,我使用 Roslyn 动态编译表达式。
我从 Roslyn 编译器生成的字节数组中加载生成的程序集。
然后我创建一个实现抽象 class 的实例(因此我不必使用反射)。
然后我调用抽象class的方法EvaluateExpression。
做完这些之后,我要卸载加载的程序集(否则,我将有内存泄漏的快感)。
所以我在评估表达式后立即卸载程序集:
Parameters.AbstractEvaluator x = RoslynExpressionEvaluator.CreateEvaluator(expression, report.Code);
object value = x.EvaluateExpression();
x.LoadContext.Unload();
(loadContext在生成时保存在摘要class中)
到目前为止一切正常,但在 x.LoadContext.Unload();
,我得到
System.InvalidOperationException: "Cannot unload non-collectible AssemblyLoadContext."
有可能解决这个问题吗?
我怎样才能使装配成为收藏品?
另外,我注意到我可以加载具有相同 class 名称的程序集(如您所见,代码中没有命名空间)
这在 multi-threading 环境(又名网络)中表现如何?
我可以无限地加载和加载动态生成的 class 的不同版本,直到机器用完 RAM 而没有出现故障吗?
或者为什么在两次加载相同的 class 时这完全有效?
using Microsoft.CodeAnalysis.Operations;
namespace ReportTester
{
public static class RoslynExpressionEvaluator
{
// a utility method that creates Roslyn compilation
// for the passed code.
// The compilation references the collection of
// passed "references" arguments plus
// the mscore library (which is required for the basic
// functionality).
private static Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation
CreateCompilationWithMscorlib
(
string assemblyOrModuleName,
string code,
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions compilerOptions = null,
System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.MetadataReference> references = null)
{
// create the syntax tree
Microsoft.CodeAnalysis.SyntaxTree syntaxTree =
Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseSyntaxTree(code, null, "");
// get the reference to mscore library
Microsoft.CodeAnalysis.MetadataReference mscoreLibReference =
Microsoft.CodeAnalysis.AssemblyMetadata
.CreateFromFile(typeof(string).Assembly.Location)
.GetReference();
// create the allReferences collection consisting of
// mscore reference and all the references passed to the method
System.Collections.Generic.List<Microsoft.CodeAnalysis.MetadataReference> allReferences =
new System.Collections.Generic.List<Microsoft.CodeAnalysis.MetadataReference>() { mscoreLibReference };
if (references != null)
{
allReferences.AddRange(references);
} // End if (references != null)
// create and return the compilation
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation compilation =
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation.Create
(
assemblyOrModuleName,
new[] {syntaxTree},
options: compilerOptions,
references: allReferences
);
return compilation;
} // End Function CreateCompilationWithMscorlib
// emit the compilation result into a byte array.
// throw an exception with corresponding message
// if there are errors
private static byte[] EmitToArray( this Microsoft.CodeAnalysis.Compilation compilation )
{
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{
// emit result into a stream
Microsoft.CodeAnalysis.Emit.EmitResult emitResult = compilation.Emit(stream);
if (!emitResult.Success)
{
// if not successful, throw an exception
foreach (Microsoft.CodeAnalysis.Diagnostic thisError in emitResult.Diagnostics)
{
if(thisError.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error)
throw new System.Exception(thisError.GetMessage());
} // Next thisError
} // End if (!emitResult.Success)
// get the byte array from a stream
return stream.ToArray();
} // End Using stream
} // End Function EmitToArray
public static Parameters.AbstractEvaluator CreateEvaluator(string expression, string code)
{
try
{
// the main class Program contain static void Main()
// that calls A.Print() and B.Print() methods
string mainProgramString = @"
Option Strict Off
Option Explicit Off
Option Infer On
Imports ReportTester.Parameters
imports System
Imports System.Collections.Generic
Imports Microsoft.VisualBasic.Strings
Imports Microsoft.VisualBasic.Interaction
Imports Microsoft.VisualBasic.Information
Public Class CodeImplementation
" + code + @"
End Class ' CodeImplementation
Public Class RsEval
Inherits AbstractEvaluator
Public Code As CodeImplementation
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
MyBase.New(allParameters)
'code
End Sub
Public Overrides Function EvaluateExpression() As Object
Return " + expression + @"
End Function
End Class ' RsEval
";
Microsoft.CodeAnalysis.MetadataReference sysRuntime =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(
typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location);
Microsoft.CodeAnalysis.MetadataReference vbRuntime =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(Microsoft.VisualBasic.Constants)
.Assembly
.Location);
Microsoft.CodeAnalysis.MetadataReference sysCorlib =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
Microsoft.CodeAnalysis.MetadataReference sysConsole =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(System.Console).Assembly.Location);
Microsoft.CodeAnalysis.MetadataReference reportParameters =
Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(ReportTester.Parameters.ParameterValue).Assembly.Location);
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions co =
new Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions
(
Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary
);
co.WithOptionStrict(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.Off);
co.WithOptionExplicit(false);
co.WithOptionInfer(true);
// create the Roslyn compilation for the main program with
// ConsoleApplication compilation options
// adding references to A.netmodule and B.netmodule
Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation mainCompilation =
CreateCompilationWithMscorlib
(
"program",
mainProgramString,
// note that here we pass the OutputKind set to ConsoleApplication
compilerOptions: co,
references: new[] {sysRuntime, vbRuntime, sysCorlib, sysConsole, reportParameters }
);
// Emit the byte result of the compilation
byte[] result = mainCompilation.EmitToArray();
// System.AppDomain temporaryAppDomain = System.AppDomain.CreateDomain("TemporaryAppDomain");
// System.Reflection.Assembly assembly = temporaryAppDomain.Load(result);
// not supported ...
// Load the resulting assembly into the domain.
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(result);
// here we get the Program type and
// call its static method Main()
// to test the program.
// get the type Program from the assembly
System.Type programType = assembly.GetType("RsEval");
Parameters.AbstractEvaluator x = (Parameters.AbstractEvaluator)System.Activator.CreateInstance(programType);
x.LoadContext = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(assembly);
// Get the static Main() method info from the type
// System.Reflection.MethodInfo method = programType.GetMethod("EvaluateExpression");
// invoke Program.Main() static method
// object retValue = method.Invoke(null, null);
// System.AppDomain.Unload(temporaryAppDomain);
return x;
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.ToString());
throw;
}
return null;
} // End Sub Test
} // End Class RoslynExpressionEvaluator
} // End Namespace ReportTester
摘要class:
Public MustInherit Class AbstractEvaluator
Public Parameters As ParameterCollection
Public LoadContext As System.Runtime.Loader.AssemblyLoadContext
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
Me.Parameters = allParameters
End Sub
Public Overridable Sub SetValue(ByVal parameterName As String, parameter As ReportParameter)
Me.Parameters.Parameters(parameterName) = parameter
End Sub
Public Overridable Function GetValue(ByVal parameterName As String) As ReportParameter
Return Me.Parameters.Parameters(parameterName)
End Function
Public Overridable Sub SetParameters(ByVal allParameters As ParameterCollection)
Me.Parameters = Nothing
Me.Parameters = allParameters
End Sub
Public MustOverride Function EvaluateExpression() As Object
' Public Parameters As ParameterCollection
' Public MustOverride Sub SetCompulsoryParameter()
End Class ' AbstractEvaluator
' example
Public Class ConcreteEvaluator
Inherits AbstractEvaluator
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
MyBase.New(allParameters)
'code
End Sub
'Public Overrides Sub SetCompulsoryParameter()
'End Sub
Public Overrides Function EvaluateExpression() As Object
Dim expression As String = "System.DateTime.Now.AddDays(1+2+3).ToString(""dd.MM.yyyy"")" ' string expression would come from report, compiled with roslyn
Return " + expression + @"
End Function
End Class
嗯,问题是它卸载了整个上下文。
这意味着,如果我将程序集加载到默认上下文中,又名
System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
;
然后,在卸载时,它将卸载整个默认上下文。
绝对不好... ==>
System.InvalidOperationException: "Cannot unload non-collectible AssemblyLoadContext."
所以你必须在不同的上下文中加载程序集(类似于 AppDomain)
public class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
public CollectibleAssemblyLoadContext() : base(isCollectible: true)
{ }
protected override Assembly Load(AssemblyName assemblyName)
{
return null;
}
}
byte[] result = null; // Assembly Emit-result from roslyn
System.Runtime.Loader.AssemblyLoadContext context = new CollectibleAssemblyLoadContext();
System.IO.Stream ms = new System.IO.MemoryStream(result);
System.Reflection.Assembly assembly = context.LoadFromStream(ms);
System.Type programType = assembly.GetType("RsEval");
MyAbstractClass eval = (MyAbstractClass) System.Activator.CreateInstance(programType);
eval.LoadContext = context;
eval.Stream = ms;
// do something here with the dynamically created class "eval"
然后你可以说
eval.LoadContext.Unload();
eval.Stream.Dispose();
如果你把它放到抽象的 IDisposable 接口中,那么奖励 class,然后你可以只使用 using,如果你想的话。
using (Parameters.AbstractEvaluator x = RoslynExpressionEvaluator.CreateEvaluator(expression, report.Code))
{
object value = x.EvaluateExpression();
System.Console.WriteLine(value);
}
所以摘要 class 看起来像这样:
Public MustInherit Class AbstractEvaluator
Implements IDisposable
Public Parameters As ParameterCollection
Public LoadContext As System.Runtime.Loader.AssemblyLoadContext
Public Stream As System.IO.Stream
Private disposedValue As Boolean ' Dient zur Erkennung redundanter Aufrufe.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
' TODO: verwalteten Zustand (verwaltete Objekte) entsorgen.
If Me.LoadContext IsNot Nothing Then
Me.LoadContext.Unload()
Me.LoadContext = Nothing
End If
If Me.Stream IsNot Nothing Then
Me.Stream.Dispose()
Me.Stream = Nothing
End If
End If
' TODO: nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() weiter unten überschreiben.
' TODO: große Felder auf Null setzen.
End If
disposedValue = True
End Sub
' TODO: Finalize() nur überschreiben, wenn Dispose(disposing As Boolean) weiter oben Code zur Bereinigung nicht verwalteter Ressourcen enthält.
'Protected Overrides Sub Finalize()
' ' Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in Dispose(disposing As Boolean) weiter oben ein.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' Dieser Code wird von Visual Basic hinzugefügt, um das Dispose-Muster richtig zu implementieren.
Public Sub Dispose() Implements IDisposable.Dispose
' Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in Dispose(disposing As Boolean) weiter oben ein.
Dispose(True)
' TODO: Auskommentierung der folgenden Zeile aufheben, wenn Finalize() oben überschrieben wird.
' GC.SuppressFinalize(Me)
End Sub
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
Me.Parameters = allParameters
End Sub
Public Overridable Sub SetValue(ByVal parameterName As String, parameter As ReportParameter)
Me.Parameters.Parameters(parameterName) = parameter
End Sub
Public Overridable Function GetValue(ByVal parameterName As String) As ReportParameter
Return Me.Parameters.Parameters(parameterName)
End Function
Public Overridable Sub SetParameters(ByVal allParameters As ParameterCollection)
Me.Parameters = Nothing
Me.Parameters = allParameters
End Sub
Public MustOverride Function EvaluateExpression() As Object
' Public Parameters As ParameterCollection
' Public MustOverride Sub SetCompulsoryParameter()
End Class ' AbstractEvaluator
' example
Public Class ConcreteEvaluator
Inherits AbstractEvaluator
Class SimplisticExampleCode
Public Function Tomorrow() As System.DateTime
Return System.DateTime.Now.AddDays(1)
End Function
End Class
Friend Code As SimplisticExampleCode
Public Sub New()
Me.New(New ParameterCollection)
End Sub
Public Sub New(ByVal allParameters As ParameterCollection)
MyBase.New(allParameters)
'code
Me.Code = New SimplisticExampleCode
End Sub
'Public Overrides Sub SetCompulsoryParameter()
'End Sub
Public Overrides Function EvaluateExpression() As Object
'Dim expression As String = "System.DateTime.Now.AddDays(1+2+3).ToString(""dd.MM.yyyy"")" ' string expression would come from report, compiled with roslyn
'Return " + expression + @"
Return Code.Tomorrow().ToString("dd.MM.yyyy")
End Function
End Class
我在我的一个项目中使用了以下方法来获取所有实现了 IEntity
接口的类型:
IEnumerable<Type> types;
var context = new AssemblyLoadContext(null, true);
using (var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
{
var assembly = context.LoadFromStream(fs);
types = assembly.GetTypes().Where(t => t.IsClass && t.GetInterfaces().Any(i => i.FullName == typeof(IEntity).FullName));
}
context.Unload();
使用AssemblyLoadContext
好像就是你要找的