Pasar datos a PSCmdlet desde su ArgumentTransformationAttribute
-
27-10-2019 - |
Pregunta
Generalmente, estoy tratando de crear un PSCmdlet
que toma un parámetro de un tipo que implementa IDisposeable
y requiere eliminación para evitar fugas de recursos.También me gustaría aceptar un string
para ese parámetro y creo una instancia de ese tipo, sin embargo, si creo ese objeto yo mismo, entonces necesito desecharlo antes de regresar de ProcessRecord
.
estoy usando un ArgumentTransformationAttribute
con mi parámetro para construir mi IDisposeable
objeto de una cadena, pero no puedo encontrar ninguna manera de pasar datos de esa clase a mi PSCmdlet
sobre si creé el objeto o no.Por ejemplo:
[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());
}
}
Mi mejor suposición aquí es subclasificar mi MyDisposeableClass
solo para etiquetar que necesita eliminación explícita, pero eso parece bastante complicado, y si bien funciona en este caso, obviamente no funcionaría si quisiera tratar con una clase sellada.
¿Hay una mejor manera de hacer esto?
Solución 2
Al final, simplemente uso parámetros que aceptan un tipo que envuelve MyDisposeable
.Mi preocupación inicial al hacer esto fue que el uso de un tipo interno para un parámetro afectaría la accesibilidad de las funciones.(Quizás afectaría negativamente a la documentación, pero en un cmdlet la documentación está totalmente controlada por un archivo XML).
Después de algunas pruebas, no parece haber ningún problema al usar una clase interna como parámetro y simplemente dejar que la transformación acepte tipos públicos.Entonces simplemente creo una clase contenedora:
public class MyDisposeableWrapper
{
public MyDisposeable MyDisposeable
{
get;
set;
}
public bool NeedsDisposed
{
get;
set;
}
public MyDisposeableWrapper(MyDisposeable myDisposeable, bool needsDisposed)
{
MyDisposeable = myDisposeable;
NeedsDisposed = needsDisposed;
}
}
Y haga que el parámetro tome eso en su lugar.En el atributo de transformación, simplemente establezca NeedsDisposed
basado en si el parámetro tomó un MyDisposeable
o construido.p.ej:
if(input is MyDisposeable)
{
return new MyDisposeableWrapper((MyDisposeable) input, false);
}
else
{
/* construct MyDisposeable from the input */
return new MyDisposeableWrapper(myDisposeable, true);
}
Otros consejos
En lugar de crear subclases, ¿puedes agregar una propiedad a tu clase MyDisposable?
public class MyDisposable
{
...
public bool IsAttributeCreated { get; set; }
}
Luego en tu código de atributo
/* We created a MyDisposeable, we *should* dispose it */
return new MyDisposeable(input.ToString()){IsAttributeCreated=true};
Finalmente en tu bloque finalmente
finally
{
/* Should only dispose MyDisposeable if we created it... */
if (MyDisposable.IsAttributeCreated)
MyDisposeable.Dispose();
}