2009-08-10 17 views
2

Dies ist wahrscheinlich ein Oldie-aber-Goodie. Ich verwende System.Data.Common für eine austauschbare Oracle/SQL Server/SQLite-Datenzugriffsbibliothek. Während des Konstruktors nehme ich den Namen der Verbindungszeichenfolge und verwende diesen, um den zugrunde liegenden Providertyp zu bestimmen. Der Grund dafür besteht darin, die unterschiedlichen IDbParameter-Namenskonventionen für jeden Provider zu behandeln. Zum Beispiel mag Oracle: Parameter, SQL Server und SQLite wie @Parameter. Der Standardwert ist? um Oledb zu decken.Parameter Benennung mit System.Data.Common

Frage: Ist das alles unnötig und gibt es eine einfache Sache, die ich vermisse, sollte sich das einfach kümmern? Wenn meine IDbCommand.CommandText = "ID auswählen, Name von meiner.Tabelle mit ID =: ID" bin ich abgedeckt? Für jetzt nehme ich nur an? als Standard und dann RegEx'ing mein Weg zur richtigen Parameter-Kennung vor dem Ausführen des Befehls.

Danke.

 /// <summary> 
    /// Initializes a new instance of the <see cref="RelationalGateway"/> class. 
    /// </summary> 
    /// <remarks>You must pass in the name of the connection string from the application configuration 
    /// file rather than the connection string itself so that the class can determine 
    /// which data provider to use, e.g., SqlClient vs. OracleClient.</remarks> 
    public RelationalGateway(string connectionStringName) 
    { 
     if (string.IsNullOrEmpty(connectionStringName)) throw new ArgumentNullException("connectionStringName"); 
     if (ConfigurationManager.ConnectionStrings[connectionStringName] == null || 
      ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString.Length == 0 || 
      ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName.Length == 0) 
     { 
      throw new InvalidOperationException(string.Format(
                "The configuration file does not contain the {0} connection ", 
                connectionStringName) + 
               "string configuration section or the section contains empty values. Please ensure the " + 
               "configuration file has the appropriate values and try again."); 
     } 

     _connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString; 
     _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName; 
     _theProvider = DbProviderFactories.GetFactory(_providerName); 
     _adapter = _theProvider.CreateDataAdapter(); 
     //GetConnection(); 
     DetermineProviderSpecificParameters(); 
    } 

Das Bit "DetermineProviderSpecificParameters" kommt im Prinzip auf "?" oder ":" oder "@" oder etwas anderes.

  1. Holen Sie sich den richtigen Parameter string::

    private void DetermineProviderSpecificParameters() { // unterstützten Anbieter prüfen bisher

    UPDATE Hier ist, wie ich die Details bin Handhabung. Dies ist so, dass parametrisierte Abfragen zur Begrenzung // durch räumliche Ausdehnung korrekt erstellt werden. Zeichenfolge shortName = _providerName.Substring (_providerName.LastIndexOf (".") + 1);

    switch (shortName) 
        { 
         case "SqlClient": 
          _param = "@"; 
          _ql = "["; 
          _qr = "]"; 
          break; 
         case "SQLite": 
          _param = "@"; 
          _ql = string.Empty; 
          _qr = string.Empty; 
          break; 
         case "OracleClient": 
          _param = ":"; 
          _ql = string.Empty; 
          _qr = string.Empty; 
          break; 
         default: 
          _param = "?"; 
          _ql = string.Empty; 
          _qr = string.Empty; 
          break; 
        } 
    } 
    
  2. Anruf ein kleiner Helfer, bevor ich jeden Befehl zu „cleanify“ ausführen oder „parameterific“ es oder aber wir diesen halbherzig Hack nennen:

    private void MakeProviderSpecific(IDbCommand command) 
    { 
        foreach (IDataParameter param in command.Parameters) 
        { 
         param.ParameterName = GetProviderSpecificCommandText(param.ParameterName); 
        } 
        command.CommandText = GetProviderSpecificCommandText(command.CommandText); 
    } 
    
  3. Und das nennt ein wenig regex

    public string GetProviderSpecificCommandText(string rawCommandText) 
    { 
        return Regex.Replace(rawCommandText, @"\B\?\w+", new MatchEvaluator(SpecificParam)); 
    } 
    

Yuck: zu tun zu tun. Immer noch auf der Suche nach einer relativ einfachen Lösung, aber die bisherigen Ratschläge werden sicherlich geschätzt.

+0

Nicht sicher, aber eine Möglichkeit zu tun, was Sie gerade tun, ist NHibernate. – Amy

+0

Ich benutze NHibernate für andere Projekte, aber dies ist eine Utility-Bibliothek für meine anderen Entwickler zu verwenden, die ihnen einen schnellen, aber nicht zu dreckigen Db-Zugang Ansatz gibt. Außerdem benutze ich dies als den Kern meines Codes für Kunden, die Anti-Open-Source sind. Ja, ich gebe den Datenzugriffscode öfter ein, als ich denken kann! :-( – Dylan

Antwort

1

Ich habe so etwas für Salamanca getan: siehe ParameterBuilder.cs. Dieser Code verwendet:

Die Sache ist, dass Sie einen gültigen Namen für die Parameter benötigen ("@name" in SQL Server, "name" in Oracle) und einen gültigen Platzhalter in Ihrer SQL-Abfrage ("@name" in Sql Server, ":name" in Oracle).

  1. Mit einer korrekten Verbindung gibt Ihnen GetParameterName einen gültigen Namen für Ihren Parameter.
  2. Erstellen Sie Ihren Platzhalter:

    • Entweder über GetParameterPlaceholder.
    • Oder Abfrage DbMetaDataColumnNames.ParameterMarkerFormat Wert in the schema for your connection enthalten. Sie sollten als Format-String, indem Sie diese Zeichenfolge Ihre Platzhalter erstellen können, die vorherigen Parameternamen als Eingabe nimmt (was bedeutet, dass das Format-String "{0}" für SQL Server und ":{0}" für Oracle):

      // DbConnection connection; 
      // string parameterName 
      DataRow schema=connection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation).Rows[0]; 
      string placeholder=string.Format(
          CultureInfo.InvariantCulture, 
          (string)schema[DbMetaDataColumnNames.ParameterMarkerFormat], 
          name.Substring(0, Math.Min(parameterName.Length, (int)schema[DbMetaDataColumnNames.ParameterNameMaxLength])) 
      ); 
      

Dies wurde mit Sql Server, Access, Sqlite und Oracle getestet (aber beachten Sie, dass, ganz überraschend, this will not work as is with ODP .NET ...).

+0

Danke, Mac. Das scheint meine Frage zu beantworten. Ich werde einen genaueren Blick darauf werfen der Salamanca Code für einige andere Tipps auch. – Dylan

1

Scheint, es gibt keine Konvention oder API dafür. ORMs wie nhibernate implementieren auch ihre eigene Zuordnung des Platzhalterpräfixes für jeden Treiber.

+0

Genau das ist der Punkt, von dem ich meinen aktuellen Implementierungsansatz her habe. Ich poste etwas mehr Code, damit Sie sehen können, was ich gerade mache. – Dylan

0

Sie könnten einen leichten Leistungseinbruch nehmen und die Klassen System.Data.OleDb verwenden. Auf diese Weise können Sie unabhängig von der Datenbank den gleichen Code verwenden. Oder Sie könnten ein Inversion-of-Control-Framework wie Unity verwenden. Sie könnten dann verlangen, dass Ihre Datenzugriffsklasse mit der entsprechenden Fabrik für die Datenbank, die Parameter und die Art, die der Anrufer verwenden möchte, injiziert wird.

+0

Das ist eine Option, aber ich würde ein IoC-Framework lieber vermeiden für etwas, das ich für eine Utility-Klasse halte - einfach zu kompliziert für meine Junior-Entwickler, ganz zu schweigen von meinen Kunden (Beratung beschränkt meine Möglichkeiten ein wenig) – Dylan