.NET 9.0'da yeni [26]: Dinamik montajların kalıcılığı geri döner

Womanne

Member
Modern .NET'te, infaz sırasında üretilen montaj şimdiye kadar sadece RAM'de tutulmuştur. .NET 9.0 ile, klasik .NET çerçevesinde olduğu gibi, dosya sisteminde Assiemi'nin yürütülmesi sırasında devam etmek artık mümkündür.






Dotnet Doctor-Holger Schwichtenberg




Holger Schwichtenberg, 53 ünlü uzmanla tavsiye ve eğitim yoluyla ve yazılımın geliştirilmesinde çok sayıda orta ve büyük şirketi destekleyen uzmanlar ağının teknik direktörüdür. Çok sayıda ulusal ve uluslararası uzman konferansındaki görünümleri, 90'dan fazla uzman kitabı ve 1.500'den fazla uzman makalesi sayesinde Holger Schwichtenberg, Almanya'daki .NET ve Web teknikleri için en iyi uzmanlardan biridir.







.NET 9.0 Önizleme 1'de Microsoft, dosya sisteminde veya herhangi bir akışta dinamik olarak montajı sürdürmek için klasik .NET Framework tarafından bilinen olasılığı yeniden tanıtmıştı.



Ancak önizleme 3'te Microsoft API'yi tekrar değiştirdi: daha önce kullanılan sınıf yerine AssemblyBuilder

AssemblyBuilder ab = AssemblyBuilder.DefinePersistedAssembly(new AssemblyName("Math"), typeof(object).Assembly);

Şimdi yeni sınıfı kullanın PersistedAssemblyBuilder:

PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("Math"), typeof(object).Assembly);



Aşağıdaki kod yeni sınıfın kullanımını göstermektedir. Giriş noktası gibi meta verilerin adaptasyonuna ilişkin diğer örnekler notlarda mevcuttur.



using System.Reflection;
using System.Reflection.Emit;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using ITVisions;
using Microsoft.NET.HostModel.AppHost;

namespace NET9_Console.FCL90;
internal class FCL9_Reflection
{

/// <summary>
/// Im modernen .NET konnten zur Laufzeit erzeugte Assemblies
/// bisher nur im RAM gehalten werden. Mit .NET 9.0 ist es mit
/// PersistedAssemblyBuilder nun auch wieder möglich, zur Laufzeit
/// erzeugte Assemblies im Dateisystem zu persistieren.
/// siehe auch https://stackoverflow.com/questions/78466316/unhandled-exception-system-io-filenotfoundexception-the-file-or-assembly-syst
/// </summary>
public void CreateAndSaveAssembly()
{
// TODO: Bei RTM-Version anpassen!
var currentVersion = "9.0.0";

CUI.Demo(nameof(CreateAndSaveAssembly));
string AssemblyName = "App42";
string AssemblyNameWithExtension = AssemblyName + ".exe";

CUI.H2($"nErzeuge {AssemblyNameWithExtension}");
string referencePath = @$"C:program FilesdotnetpacksMicrosoft.NETCore.App.Ref{currentVersion}refnet9.0";
PathAssemblyResolver resolver = new(Directory.GetFiles(referencePath, "*.dll"));
using MetadataLoadContext context = new(resolver);
Assembly coreAssembly = context.CoreAssembly!;
Type voidType = coreAssembly.GetType(typeof(void).FullName!)!;
Type objectType = coreAssembly.GetType(typeof(object).FullName!)!;
Type stringType = coreAssembly.GetType(typeof(string).FullName!)!;
Type int32Type = coreAssembly.GetType(typeof(Int32).FullName!)!;
Type stringArrayType = coreAssembly.GetType(typeof(string[]).FullName!)!;
Type consoleType = coreAssembly.GetType(typeof(Console).FullName!)!;

// --> NEU in .NET 9.0: PersistedAssemblyBuilder
// https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.persistedassemblybuilder?view=net-9.0
PersistedAssemblyBuilder assemblyBuilder = new(new AssemblyName(AssemblyName), coreAssembly);

TypeBuilder typeBuilder = assemblyBuilder.DefineDynamicModule(AssemblyName).DefineType(AssemblyName, TypeAttributes.Public | TypeAttributes.Class, objectType);

#region Methode Sum() erzeugen
var mb = typeBuilder.DefineMethod("Sum", MethodAttributes.Public | MethodAttributes.Static,
int32Type, new Type[] { int32Type, int32Type });
var ilSum = mb.GetILGenerator();
ilSum.Emit(OpCodes.Ldarg_0);
ilSum.Emit(OpCodes.Ldarg_1);
ilSum.Emit(OpCodes.Add);
ilSum.Emit(OpCodes.Ret);
CUI.Cyan($"Sum() wurde erzeugt");
#endregion

#region Methode Main() erzeugen
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, voidType, [stringArrayType]);
ILGenerator ilMain = methodBuilder.GetILGenerator();
// Aufruf von Console.WriteLine("Die Antwort auf Ihre Frage ist:")
ilMain.Emit(OpCodes.Ldstr, "Die Antwort auf Ihre Frage ist:");
ilMain.Emit(OpCodes.Call, consoleType.GetMethod("WriteLine", [stringType])!);
// Aufruf von Console.WriteLine(Sum(40,2))
ilMain.Emit(OpCodes.Ldc_I4, 40); // Load the constant 40
ilMain.Emit(OpCodes.Ldc_I4, 2); // Load the constant 2
ilMain.Emit(OpCodes.Call, mb);
var writeLineMethod = consoleType.GetMethod("WriteLine", [int32Type]);
ilMain.Emit(OpCodes.Call, writeLineMethod);
ilMain.Emit(OpCodes.Ret);
CUI.Cyan($"Main() wurde erzeugt");
#endregion

#region Metadaten und PE-Header erzeugen
typeBuilder.CreateType();

MetadataBuilder metadataBuilder = assemblyBuilder.GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData);
PEHeaderBuilder peHeaderBuilder = new(imageCharacteristics: Characteristics.ExecutableImage);

ManagedPEBuilder peBuilder = new(
header: peHeaderBuilder,
metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
ilStream: ilStream,
mappedFieldData: fieldData,
entryPoint: MetadataTokens.MethodDefinitionHandle(methodBuilder.MetadataToken));
CUI.Cyan($"Metadaten und PE-Header wurden erzeugt");
#endregion

#region Speichern der DLL
BlobBuilder peBlob = new();
peBuilder.Serialize(peBlob);
using (FileStream fileStream = new($"{AssemblyName}.dll", FileMode.Create, FileAccess.Write))
{
peBlob.WriteContentTo(fileStream);
}
CUI.Cyan($"{AssemblyName}.dll wurde gespeichert");
#endregion

#region AppHost und runtimeconfig erzeugen
HostWriter.CreateAppHost(
@$"C:program FilesdotnetpacksMicrosoft.NETCore.App.Host.win-x64{currentVersion}runtimeswin-x64nativeapphost.exe",
$"{AssemblyName}.exe",
$"{AssemblyName}.dll");

CUI.Cyan($"{AssemblyName}.exe wurde gespeichert");

File.WriteAllText($"{AssemblyName}.runtimeconfig.json",
$$"""
{"runtimeOptions": {
"tfm": "net9.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "{{currentVersion}}"
}
}
}
""");

CUI.Cyan($"{AssemblyName}.runtimeconfig.json wurde gespeichert");
#endregion

CUI.Success("OK");

#region Testen
CUI.H2($"nStarte {AssemblyName}.exe");
System.Diagnostics.Process.Start($"{AssemblyName}.exe");
#endregion
}
}





Örnek kodun sürümünün ekran görüntüsü



Kod, üretilirken bireysel pasajları yayar ve montajda devam eder.


(Resim: Ekran görüntüsü (Holger Schwichtenberg))




18 Kasım 2025'te BetterCode Online Konferansı () 10.0



(Resim: Coffeemill/123rf.com)


Sonraki LTS çıktısı devam ediyor: BetterCode Online Konferansı () .NET 10.0'da IX ve DPunkt.verlag tarafından düzenlenen 18 Kasım 2025'te IT-visions.de-Sesler Bu makalenin yazarı Dr. Holger Schwichtenberg ve diğer uzmanlar en önemli yenilikler. Bunlar.

Çevrimiçi mağazada indirimli kör kuş biletleri zaten mevcut olana kadar program henüz yayınlanmadı. Bir önceki yılın programı arşivde görüntülenebilir.








(RME)




Ne yazık ki, bu bağlantı artık geçerli değil.

Boşa harcanan eşyalara olan bağlantılar, 7 günlük daha büyükse veya çok sık çağrılmışsa gerçekleşmez.


Bu makaleyi okumak için bir Haberler+ paketine ihtiyacınız var. Şimdi yükümlülük olmadan bir hafta deneyin – yükümlülük olmadan!
 
Üst