| 

.NET C# Java Javascript Exception

1
Der folgende Post behandelt eigentlich mehrere Themen. Zum einen geht es um das Erstellen eines eigenen ExportProviders, der über die App.config die zu ladenden Parts ausliest. Zum anderen sollen die geladenen Parts in einer eigenen AppDomain ausgeführt werden. Das Managed Extensibility Framework (MEF) nutzt ein attributbasiertes Programmiermodell, um die einzelnen Exports an die Imports zu […]

Der folgende Post behandelt eigentlich mehrere Themen. Zum einen geht es um das Erstellen eines eigenen ExportProviders, der über die App.config die zu ladenden Parts ausliest. Zum anderen sollen die geladenen Parts in einer eigenen AppDomain ausgeführt werden.

Das Managed Extensibility Framework (MEF) nutzt ein attributbasiertes Programmiermodell, um die einzelnen Exports an die Imports zu binden. Manchmal kann es aber von Vorteil sein, wenn über eine Konfigurationsdatei die Auswahl der Parts vorgegeben wird. Hierzu bietet sich die App.config an. In einem älteren Post wurde das Erstellen von benutzerdefinierten Konfigurationsabschnitten in der App.config schon vorgestellt. Der Focus soll mehr auf das Erstellen eines ExportProviders gelegt werden, der die Parts in eine eigene AppDomain lädt und dort mit eingeschränkten Rechten zur Ausführung bringt. Grundlagen zur Erstellung eigener ExportProvider findet ihr hier.

Die zu ladenen Parts sind in der App.config aufgelistet:

<configuration>
 <configSections>
 <section name="AddInSection" type="Sample01.AddInConfigurationSection, CarHost"/>
 </configSections>
 <AddInSection>
 <AddIns>
 <AddIn id="1" contract="CarContract.ICarContract"
 assemblyName="BMW"
 typeName="Sample01.BMW"/>
 <AddIn id="2" contract="CarContract.ICarContract"
 assemblyName="Mercedes"
 typeName="Sample01.Mercedes"/>
 </AddIns>
 </AddInSection>
</configuration>

Beide Parts sind identisch aufgebaut und befinden sich jeweils in separaten Assemblies. Wie auch zu erkennen ist, besitzen die Klassen BMW und Mercedes nicht das Attribut Export. Schließlich soll die App.config vorgeben, welche Assemblies zu laden sind, nicht das attributbasierte Programmiermodell von MEF. Da die Assemblies in einer eigenen AppDomain ausgeführt werden sollen und an die Methode Parameter übergeben werden, müssen die Klassen von MarshallByRefObject abgeleitet werden.

namespace Sample01
{
 public class BMW : MarshalByRefObject, ICarContract
 {
 public void WriteString(string name)
 {
 try
 {
 Console.WriteLine("BMW: WriteString()");
 File.WriteAllText(@"C:\Test.txt", name);
 }
 catch (Exception ex)
 {
 Console.WriteLine(ex.Message);
 }
 }
 }
}

namespace Sample01
{
 public class Mercedes : MarshalByRefObject, ICarContract
 {
 public void WriteString(string name)
 {
 try
 {
 Console.WriteLine("Mercedes: WriteString()");
 File.WriteAllText(@"C:\Test.txt", name);
 }
 catch (Exception ex)
 {
 Console.WriteLine(ex.Message);
 }
 }
 }
}

Die Methode WriteString() soll schreibend auf eine Datei zugreifen. Im Falle einer Exception wird die Fehlermeldung auf die Konsole ausgegeben.

Die Schnittstelle ICarContract liegt ebenfalls in einer separaten Assembly und beinhaltet die gemeinsam genutzte Methode.

namespace CarContract
{
 public interface ICarContract
 {
 void WriteString(string name);
 }
}

Die Hauptanwendung ist eher unspektakulär. Es wird eine Instanz des eigenen ExportProviders angelegt. Dieser wird an den Container übergeben. Die Methode ComposeParts() triggert das Laden und Binden der einzelnen Komponenten an. In der Foreach-Schleife wird zum Schluss von jedem Part die enthaltene Methode WriteString() aufgerufen.

namespace Sample01
{ 
 public class Program
 {
 [ImportMany(typeof(ICarContract))]
 private IEnumerable< Lazy< ICarContract > > CarParts { get; set; }
 
 static void Main(string[] args)
 {
 new Program().Run();
 }

 void Run()
 {
 CompositionContainer container = null;

 var provider = new ConfigExportProvider();
 container = new CompositionContainer(provider);
 container.ComposeParts(this);

 foreach (Lazy< ICarContract > carPart in CarParts)
 carPart.Value.WriteString("Test");

 container.Dispose();
 }
 }
}

Spannend ist die Implementierung des ExportProviders. Im Konstruktor wird der Inhalt der App.config durchlaufen und für jeden Eintrag eine Instanz der Klasse Export angelegt.

Wichtig ist die Methode CreatePart() die im Konstruktor übergeben wird (Zeile 23). Diese erzeugt die einzelnen Parts. Ausgeführt wird CreatePart() sobald MEF die einzelnen Parts benötigt. Da die Imports über die Klasse Lazy<T> gespeichert werden, geschieht dieses bei dem ersten Zugriff auf das Objekt, also bei Aufruf der Methode WriteString().

Zuvor ruft MEF noch die Methode GetExportsCore() auf. Dort wird die Liste der Exports zurückgegeben, die zu dem Import passen.

namespace Sample01
{
 public class ConfigExportProvider : ExportProvider
 {
 private List< Export > Exports { get; set; }

 public ConfigExportProvider()
 {
 this.Exports = new List< Export >();
 
 AddInConfigurationSection myConfigurationSection =
 (AddInConfigurationSection)ConfigurationManager.GetSection("AddInSection");
 foreach (AddInSectionValue sectionValue in myConfigurationSection.SectionValues)
 {
 var metadata = new Dictionary< string, object >();
 metadata.Add(
 CompositionConstants.ExportTypeIdentityMetadataName,
 sectionValue.Contract);
 var exportDefinition = new ExportDefinition(sectionValue.Contract, metadata);
 string assemblyName = sectionValue.AssemblyName;
 string typeName = sectionValue.TypeName;
 var export = new Export(exportDefinition,
 () => CreatePart(assemblyName, typeName));
 this.Exports.Add(export);
 } 
 }

 public object CreatePart(string assemblyName, string typeName)
 {
 var info = new AppDomainSetup { 
 ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase };
 var grantSet = new PermissionSet(PermissionState.None);
 grantSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
 // soll der Dateizugriff erlaubt sein, so muss die folgende Zeile eingefügt werden
 // grantSet.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
 var appDomain = AppDomain.CreateDomain("AddIn AppDomain",
 AppDomain.CurrentDomain.Evidence,
 info,
 grantSet);
 var instance = appDomain.CreateInstanceAndUnwrap(assemblyName, typeName); 
 return instance;
 }

 protected override IEnumerable< Export > GetExportsCore(
 ImportDefinition definition,
 AtomicComposition atomicComposition)
 { 
 return this.Exports.Where(x => definition.IsConstraintSatisfiedBy(x.Definition));
 }
 }
}

In der Methode CreatePart() wird eine separate AppDomain mit stark eingeschränkten Rechten angelegt. In dieser wird die Assembly geladen. Die Rechte sind soweit eingeschränkt, dass Dateizugriffe nicht erlaubt sind.

Wird das Programm ausgeführt, so endet der Versuch, in eine Datei zu schreiben, in einer SecurityException.

CommandWindowSample01

Beispiel (Visual Studio 2010)


mef managed-extensibility-framework composablepart exportprovider tags-app.config
Weitere News:
Schreibe einen Kommentar:
Themen:
tags-app.config exportprovider composablepart managed-extensibility-framework mef
Entweder einloggen... ...oder ohne Wartezeit registrieren
Benutzername
Passwort
Passwort wiederholen
E-Mail