Übergeben von Daten aus dem ArgumentTransformationAttribute an PSCmdlet
-
27-10-2019 - |
Frage
Im Allgemeinen versuche ich, einen PSCmdlet
zu erstellen, der einen Parameter eines Typs verwendet, der IDisposeable
implementiert und entsorgt werden muss, um ein Auslaufen von Ressourcen zu vermeiden.Ich möchte auch einen string
für diesen Parameter akzeptieren und eine Instanz dieses Typs erstellen. Wenn ich dieses Objekt jedoch selbst erstelle, muss ich es entsorgen, bevor ich vom ProcessRecord
zurückkehre.
Ich verwende einen ArgumentTransformationAttribute
mit meinem Parameter, um mein IDisposeable
-Objekt aus einer Zeichenfolge zu erstellen, aber ich kann keine Möglichkeit finden, Daten aus dieser Klasse an meinen PSCmdlet
zu übergeben, ob ich das Objekt erstellt habe oder nicht.Zum Beispiel:
[Cmdlet("Get", "MyDisposeableName")]
public class GetMyDisposeableNameCommand : PSCmdlet
{
[Parameter(Mandatory = true, Position = 0), MyDisposeableTransformation]
public MyDisposeable MyDisposeable
{
get;
set;
}
protected override void ProcessRecord()
{
try
{
WriteObject(MyDisposeable.Name);
}
finally
{
/* Should only dispose MyDisposeable if we created it... */
MyDisposeable.Dispose();
}
}
}
class MyDisposeableTransformationAttribute : ArgumentTransformationAttribute
{
public override Object Transform(EngineIntrinsics engineIntrinsics, Object input)
{
if (input is PSObject && ((PSObject)input).BaseObject is MyDisposeable)
{
/* We were passed a MyDisposeable, we should not dispose it */
return ((PSObject)input).BaseObject;
}
/* We created a MyDisposeable, we *should* dispose it */
return new MyDisposeable(input.ToString());
}
}
Meine beste Vermutung hier ist, meinen MyDisposeableClass
zu unterordnen, nur um zu kennzeichnen, dass er explizit entsorgt werden muss, aber das scheint ziemlich hackig zu sein, und obwohl es in diesem Fall funktioniert, würde es offensichtlich nicht funktionieren, wenn ich mich mit einer versiegelten Klasse befassen möchte.
Gibt es einen besseren Weg, dies zu tun?
Lösung 2
Am Ende verwende ich einfach Parameter, die einen Typ akzeptieren, der MyDisposeable
umschließt.Meine anfängliche Sorge dabei war, dass die Verwendung eines internen Typs für einen Parameter die Zugänglichkeit der Funktionen beeinträchtigen würde.(Möglicherweise würde sich dies negativ auf die Dokumentation auswirken, aber in einem Cmdlet wird die Dokumentation vollständig von einer XML-Datei gesteuert.)
Nach einigen Tests scheint es keine Probleme zu geben, eine interne Klasse für einen Parameter zu verwenden und die Transformation nur öffentliche Typen akzeptieren zu lassen.Also erstelle ich einfach eine Wrapper-Klasse:
public class MyDisposeableWrapper
{
public MyDisposeable MyDisposeable
{
get;
set;
}
public bool NeedsDisposed
{
get;
set;
}
public MyDisposeableWrapper(MyDisposeable myDisposeable, bool needsDisposed)
{
MyDisposeable = myDisposeable;
NeedsDisposed = needsDisposed;
}
}
Und lassen Sie den Parameter das stattdessen nehmen.Legen Sie im Transformationsattribut einfach den NeedsDisposed
fest, basierend darauf, ob der Parameter einen MyDisposeable
verwendet oder einen erstellt hat.zB:
if(input is MyDisposeable)
{
return new MyDisposeableWrapper((MyDisposeable) input, false);
}
else
{
/* construct MyDisposeable from the input */
return new MyDisposeableWrapper(myDisposeable, true);
}
Andere Tipps
Können Sie Ihrer MyDisposable-Klasse anstelle von Unterklassen eine Eigenschaft hinzufügen?
public class MyDisposable
{
...
public bool IsAttributeCreated { get; set; }
}
Dann in Ihrem Attributcode
/* We created a MyDisposeable, we *should* dispose it */
return new MyDisposeable(input.ToString()){IsAttributeCreated=true};
Endlich in Ihrem finally-Block
finally
{
/* Should only dispose MyDisposeable if we created it... */
if (MyDisposable.IsAttributeCreated)
MyDisposeable.Dispose();
}