2009-05-06 5 views
5

Aquí está mi diseño propuesto (muy simplificado para ilustrar el espacio problemático) para una aplicación de consola C#. Las conexiones de la base de datos implementan IDisposable, y esta solución no permite using los objetos de conexión de la base de datos. ¿Alguien puede proponer una estructura más correcta para una aplicación de consola? Este es un problema que necesito resolver a menudo.¿Cómo estructurar una aplicación de consola C# para usar de forma eficiente los recursos de base de datos IDisposable?

class Program 
{ 
    SQLiteConnection sourceConnection; 
    SQLiteConnection destinationConnection; 

    static void Main(string[] args) 
    { 
     Program shell = new Program(); 

     // get connection strings from command line arguments 
     string sourceConnectionString = shell.getConnectionString(args); 
     string destinationConnectionString = shell.getConnectionString(args); 

     // call non-static methods that use 
     shell.setUpConnections(sourceConnectionString, destinationConnectionString); 

     shell.doDatabaseWork(); 
    } 

    private void setUpConnections(string sourceConnectionString, string destinationConnectionString) 
    { 
     sourceConnection = new SQLiteConnection(sourceConnectionString); 
     destinationConnection = new SQLiteConnection(destinationConnectionString); 
    } 

    private void doDatabaseWork() 
    { 
     // use the connections here 
    } 
} 

Editar:

Algunas personas no pueden entender por qué me gustaría que ellos como variables miembro. Aquí está mi caso de uso (un poco psuedocoded) de lo que iría en doDatabaseWork:

foreach (Row sourceRow in DBResultSet) 
{ 
    string sourceXml = sourceRow.Columns["MyColumnName"].Value; 
    string destinationXML = transformUsingXSLT(sourceXml); 
    writeToDestination(destinationXml); 
} 

Vea cómo me gustaría mantener estas conexiones abiertas por la vida de este bucle?

+0

RE: Editar. Todavía ... no –

Respuesta

2

Creo que la mejor solución es extraer la lógica principal de la clase de programa. La clase de Programa es una especie de iniciador para el trabajo primario. Y el suministro de envoltorios para SqlConnections no es una buena idea, ya que ya son recursos administrados, su envoltura es redundante. Así que mi solución es el siguiente:

class ProgramCore : IDisposable 
{ 
    internal ProgramCore(string sourceConnectionString, string destinationConnectionString) 
    { 
     setUpConnections(sourceConnectionString, destinationConnectionString); 
    } 

    internal void Execute() 
    { 
     // do whatever you want 
     doDatabaseWork(); 
     // do whatever you want 
    } 

    public void Dispose() 
    { 
     if (_sourceConnection != null) 
      _sourceConnection.Dispose(); 
     if (_destinationConnection != null) 
      _destinationConnection.Dispose(); 
    } 

    private void setUpConnections(string sourceConnectionString, string destinationConnectionString) 
    { 
     _sourceConnection = new SQLiteConnection(sourceConnectionString); 
     _destinationConnection = new SQLiteConnection(destinationConnectionString); 
    } 

    private void doDatabaseWork() 
    { 
     // use the connections here 
    } 

    private SQLiteConnection _sourceConnection; 
    private SQLiteConnection _destinationConnection; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     // get connection strings from command line arguments 
     string sourceConnectionString = GetConnectionString(args); 
     string destinationConnectionString = GetConnectionString(args); 

     using (ProgramCore core = new ProgramCore(sourceConnectionString, destinationConnectionString)) 
     { 
      core.Execute(); 
     } 
    } 

    static string GetConnectionString(string[] args) 
    { 
     // provide parsing here 
    } 
} 
+0

Las conexiones se toman del conjunto de conexiones. Por lo tanto, si no los abre y cierra 1000 veces por segundo, esto no afectará el rendimiento de su programa. Pero si necesita abrir y cerrar conexiones muy a menudo, entonces debe usar el almacenamiento en caché de datos dentro de su programa. Pero esto se refiere a la optimización, y debe perfilar cuidadosamente su programa para optimizarlo. Recuerde lo que dijo Donald Knuth sobre la optimización: "Deberíamos olvidarnos de las pequeñas eficiencias, digamos el 97% del tiempo: la optimización prematura es la raíz de todo mal". –

+0

Lo suficiente, pero mi primera estrategia fue abrir y cerrar las conexiones de DB cuando las necesitaba. Se desaceleró a paso de tortuga durante la vida del guión. No perfilé, pero incluso si solo se agrupan las escrituras en grupos de 1000 por conexión, se puede administrar el rendimiento. – danieltalsky

+0

Honestamente, nadie votó por su estrategia, pero hasta ahora es la más cercana a responder mi pregunta. – danieltalsky

6

¿Qué le parece escribir una clase que implemente IDisposable?

Dentro de su constructor de clase, puede instanciar sus conexiones de base de datos.

Luego, dentro de su IDisposable.Dispose Method, escriba su código de desmontaje para cerrar sus conexiones de base de datos.

Aquí es un ejemplo de código para demostrar lo que quiero decir:

public class DBWrapper : IDisposable 
{ 
    public SqlConnection Connection1 { get; set; } 
    public SqlConnection Connection2 { get; set; } 

    public DBWrapper() 
    { 
     Connection1 = new SqlConnection(); 
     Connection1.Open(); 
     Connection2 = new SqlConnection(); 
     Connection2.Open(); 
    } 
    public void DoWork() 
    { 
     // Make your DB Calls here 
    } 

    public void Dispose() 
    { 
     if (Connection1 != null) 
     { 
      Connection1.Dispose(); 
     } 
     if (Connection2 != null) 
     { 
      Connection2.Dispose(); 
     } 
    } 
} 

Y luego, desde dentro de su método principal de su clase del Programa:

class Program 
{ 
    static void Main(string[] args) 
    { 
     using (DBWrapper wrapper = new DBWrapper()) 
     { 
      wrapper.DoWork(); 
     } 
    } 
} 
+0

Correcto, pero tengo dos conexiones de bases de datos y necesitan poder tomar recursos de uno, transformarlos y escribir en el otro. No quiero tener que volver a conectarme para cada lectura y escritura. Solo quiero tener un control para ambos abrir, y luego ser capaz de realizar operaciones en cada uno cuando lo necesite. ¿Hago sourceWrapper.DoWork (destinationWrapper)? – danieltalsky

+0

Intente abrir 2 o más lectores en ese objeto de conexión y vea por qué esto no funcionaría tan bien –

+0

Acabo de cambiar mi muestra de código para ser más explícito con 2 objetos de conexión Dentro del método DoWork, tiene acceso a ambos SqlConnections, puede hacer lo que necesite, sin tener que volver a conectarse para cada lectura y escritura. Básicamente, está abstrayendo la lógica DB en el método DBWrapper.DoWork(), y lejos de su método Program.Main. –

2

respuesta de Scott es una manera de hacerlo . ¿También podría considerar usar try {} finalmente?

static void Main(string[] args) 
{ 
    Program shell = new Program(); 

    // get connection strings from command line arguments 
    string sourceConnectionString = shell.getConnectionString(args); 
    string destinationConnectionString = shell.getConnectionString(args); 

    // call non-static methods that use 
    shell.setUpConnections(sourceConnectionString, destinationConnectionString); 
    try 
    { 
     shell.doDatabaseWork(); 
    } 
    finally 
    { 
     if(sourceConnection != null) 
     sourceConnection.Dispose(); 
     if(destinationConnection != null) 
     destinationConnection.Dispose(); 
    } 
} 
+2

¿Por qué no simplemente usa la palabra clave using para sourceConnection y destinationConnection? –

+0

¿Funcionará eso? Responda y muestre una muestra de código válida, Brian? – danieltalsky

2

personalmente, creo que usted tiene más de pensar en esto y los ejemplos de código en este tema son demasiado complejos en mi humilde opinión. No tengo idea de por qué las personas están implementando IDisposable en su clase de Programa, ya que está dispuesto cuando se cierra.

No puedo pensar en una sola razón para no usar o por qué no puedes usar la instrucción using() {}.

¿Desea abrir una conexión y mantenerla? ¿Por qué? Todas las conexiones reales están detrás de escena en la agrupación de conexiones de .net, por lo que los nuevos objetos de conexión no son un gran problema. Simplemente abre y cierra cuando los necesites y la agrupación de conexiones maneja todo eso detrás de las escenas.

He editado mi ejemplo para envolverlo en una clase para que pueda tener su encapsulamiento también.

class Program 
{ 
    static void Main(string[] args) 
    { 
     DBWorker worker = new DBWorker(); 
     worker.DoDatabaseWork(); 
    } 
} 

public class DBWorker 
{ 

    private void DoDatabaseWork() 
    { 
     using (SQLiteConnection sourceDB = new SQLiteConnection(GetConnectionString())) 
     { 
      sourceDB.Open(); 
      using (SQLiteConnection destDB = new SQLiteConnection(GetConnectionString())) 
      { 
       destDB.Open(); 
      } 
     } 
    } 

} 
+0

Quiero ser capaz de tener métodos de ayuda por separado que realmente hacen las lecturas y escrituras para que pueda tener la lógica que traduce los datos de uno a otro en su propio método. Si no almaceno los datos como miembro, entonces no tengo acceso a las conexiones cuando los necesito. – danieltalsky

+0

Agregué una edición para mostrar por qué tendría problemas con ese código. – danieltalsky

0

Hmm, veo que nadie ha mencionado hacerlo de esta manera. No tiene que tener las variables que se usan en el using declaradas localmente.


class Program 
{ 
    SQLiteConnection sourceConnection; 
    SQLiteConnection destinationConnection; 

    static void Main(string[] args) 
    { 
     Program shell = new Program(); 

     // get connection strings from command line arguments 
     string sourceConnectionString = shell.getConnectionString(args); 
     string destinationConnectionString = shell.getConnectionString(args); 

     using (sourceConnection = new SQLiteConnection(sourceConnectionString)) 
     using (destinationConnection = new SQLiteConnection(destinationConnectionString)) 
     { 
      shell.doDatabaseWork(); 
     } 
    } 

    private void doDatabaseWork() 
    { 
     // use the connections here 
    } 
} 
Cuestiones relacionadas