2011-12-14 13 views
5

Supongamos que deseo heredar de System.Data.SqlClient.SqlTransaction que es sellado. Supongamos que quiero simplemente poner un contenedor alrededor de SqlTransaction y siempre usar MyTransaction en lugar de SqlTransaction. ¿Existe alguna forma de que pueda emitir MyTransaction a SqlTransaction utilizando un operador de ampliación/implícito?¿Hay alguna forma de FALSIFICAR herencia en C#/VB.NET?

+5

'Supongamos que quiero heredar de System.Data.SqlClient.SqlTransaction' => ¿por qué querrías hacer algo como esto? –

+4

Darin tiene razón; estás pidiendo un taladro en lugar de decirnos por qué necesitas un agujero en esa viga de acero en primer lugar. El hecho de que el tipo esté sellado es una gran bandera roja que dice NO QUIERO QUE SE HEREDA DE ESTO. ¿Por qué intentas trabajar en contra de los deseos del diseñador de la clase? Ellos tienen tus mejores intereses en el corazón. Explique lo que realmente quiere lograr, porque heredar de una clase sellada no va a suceder. –

+1

¿No es posible desbloquear una clase usando reflexión? –

Respuesta

3

Si realmente desea la conversión implícita (aunque yo no lo recomiendo, ya que es una idea horrible y un diseño horrible, OMI), se puede hacer algo como esto:

class MyTransaction 
    { 
     private readonly SqlTransaction _transaction; 

     public MyTransaction(SqlConnection conn) 
     { 
      _transaction = conn.BeginTransaction(); 
     } 

     public SqlTransaction Transaction 
     { 
      get 
      { 
       return _transaction; 
      } 
     } 

     public static implicit operator SqlTransaction(MyTransaction t) 
     { 
      return t.Transaction; 
     } 
    } 
+0

Lamentablemente esto está en VB.NET y su operador de ensanchamiento no parece funcionar ya que C# es "implícito" lo que acabo de descubrir, pero también estaba pensando en esta ruta ... Aunque como dijiste, tampoco me gusta ... – Denis

+0

@Denis podría intentar codificar la clase en C# y luego usar la DLL en su aplicación VB –

+1

@Denis Code en C#, usar Refelctor para desmontar en VB y verá la sintaxis. – TomTom

6

Puede crear una clase que tenga una variable de transacción interna y luego exponer los métodos y las propiedades. Algo así como esto:

public class MyTransaction 
{ 
    System.Data.SqlTransaction myTx = someConnection.CreateTransaction(); 

    public void CommitTransaction() : { 
     myTx.CommitTransaction() 
    } 
} 

También puede hacer que herede de DbTransaction y vuelva a grabar los procedimientos abstractos y virtuales para utilizar la variable myTx interior, pero empieza a ser un poco compleja sin razón aparente ...

+1

¡Absolutamente! : -Þ Pero si el hombre quiere envolver una transacción, ¡puede haber una razón! –

+0

Solo porque quiera hacerlo, y aunque sea posible, no significa que deba hacerlo. –

+0

@Ramhound Estoy de acuerdo, pero igual es una buena forma de aprender para otros casos más adecuados donde una solución como esta es adecuada –

3

Si solo está interesado en agregar métodos adicionales a una clase, puede usar métodos de extensión. Eso no le dará acceso a ningún estado interno ni le permitirá anular comportamientos, pero le permitirá agregar funcionalidades limitadas. No estoy al tanto de ninguna forma de heredar de una clase sellada.

Puede crear un verdadero objeto envoltorio como otros lo han mencionado, pero no podrá usarlo polimórficamente en lugar del objeto original.

+0

Esto está en .NET 2.0 entonces no hay métodos de extensión ... Pero también necesito hacer un seguimiento de cosas desde la creación, por lo que los métodos de extensión no funcionarán de todos modos. – Denis

+0

No es que esto importe debido a .net 2, pero los métodos de extensión pueden rastrear datos de objetos independientes utilizando un diccionario descodificado del primer parámetro del método de extensión (el objeto manipulado). Esto puede ofrecer un conjunto de datos "particionados" que se asemeja al estado interno pero que se almacena de forma externa al objeto. Se puede acceder a los miembros internos a través de la reflexión, por lo que puede hacer algunas cosas interesantes dependiendo de qué tan "feo" esté dispuesto a permitir que se obtenga el código. –

+0

Espero que no te importe, pero agregué una etiqueta .net2.0 a tu pregunta para que otros sepan que la versión del marco es limitada. –

0

Puede definir su propia clase MyTransaction como un contenedor alrededor de SqlTransaction. Mantenga una instancia de SqlTransaction en un campo privado dentro de MyTransaction. Su contenedor no será compatible con SqlTransaction, pero si implementa las mismas interfaces que SqlTransaction implementa, puede acercarse mucho.

+0

Pensé que esto funcionaría también pero luego tienes algo como SqlClient.SQLCommand.Transaction que quiere SqlTransaction, no IDbTransaction que realmente es sux, así que necesito alguna forma de falsear esto que inició esta búsqueda ... – Denis

1

No, no puede hacer que su clase personalizada sea heredada de SqlTransaction o que la falsifiquen.

Sin embargo, si el contexto de lo que está haciendo le permite utilizar una DbTransaction, podría heredar su clase de transacción personalizada de DbTransaction, terminando una SqlTransaction dentro con cualquier otra funcionalidad que requiera.

+0

Pensé que esto funcionaría también, pero luego tienes algo como SqlClient.SQLCommand.Transaction que quiere SqlTransaction – Denis

+0

Sí , te metes en un montón de objetos envueltos ... puedes usar todo el envoltorio personalizado del espacio de nombres System.Data que envuelve las versiones SQL ... todo depende de cuáles sean tus requisitos y por qué estás jugando con estos objetos en el primer lugar. Es probable que haya otras (mejores) formas de lograr el objetivo. –

1

Haces tiene otra opción, puede usar Reflection.Emit() para agregar una interfaz de su elección a SqlTransaction, y luego usar esa misma interfaz, en su nueva clase MyTransaction y luego puede hacer llamadas a la interfaz, en lugar de la clase.

Tenga cuidado de que esto solo funcione en las bibliotecas que cree, o modifique específicamente los tipos cargados usando Reflection.

+1

Además, esta es una solución particularmente sucia ... –

+0

Nunca he intentado Reflection.Emit(). Voy a mirar alrededor, pero ¿tiene algún buen ejemplo/tutorial sobre cómo funcionaría esto? – Denis

1

Puede crear métodos de extensión.

public static class SqlTransactionExtensions 
{ 
    public static void DoSomething(this SqlTransaction transaction, int myParameter) 
    { 
     // do something here 
    } 
} 

La clase debe ser estática. Coloque la palabra mágica this delante del primer parámetro que debe ser del tipo de la clase que está extendiendo. También puedes extender interfaces. Si desea utilizar este método de extensión, debe tener un using namspace con el espacio de nombre de esta clase de extensión, si no está definido en el mismo espacio de nombres en el que está trabajando.

A continuación, puede llamar al método de extensión como si fuera un método regular de SqlTransaction:

SqlTransaction t = new SqlTransaction(); 
t.DoSomething(5); 
+0

Esta es una buena idea que alguien propuso aquí también, pero hay 2 problemas con esto: (1) estoy usando .NET 2.0 y (2) si no estuviera usando .NET 2.0, ¿cómo usaría los métodos de extensión para crear objetos en creación de SqlTransaction? (Digamos que quiero rastrear quién creó este sqlTransaction o la hora en que se inició) – Denis

+0

Crearía una clase de ayuda estática para las transacciones. Muy parecido a la clase de extensiones, pero sin la palabra clave 'this'. En lugar de llamar a los métodos con 't.DoSomething (5);', tendrá que llamarlos con 'TransactionsHelper.DoSomething (t, 5);'. Los métodos de extensión son en realidad solo azúcar sintáctico y no pueden hacer nada que no se pueda hacer con los métodos normales. –

2

OK, así descartar la herencia y se centra en la tarea que realmente quiere resolver (Basado en el comentario trapos).

He tenido éxito en el pasado al ejecutar todas las llamadas a través de una biblioteca auxiliar e implementar la lógica allí. En el pasado, he usado SqlHelper, que se publica en Microsoft Data Application Block. Este es un módulo fuente, que puede adaptar a sus necesidades. Puede agregar cualquier log u otra lógica que necesite.

También hace que el código sea muy legible. Puede hacer cosas como:

SqlHelper.ExecuteDataset() de consultas que devuelven conjuntos de datos,

SqlHelper.ExecuteScalar() para las consultas de la devolución de valores individuales,

SqlHelper.ExecuteNonQuery() para los comandos que no tienen retornos (como INSERT 's).

etc.

Cuestiones relacionadas