4 votes

Est-ce que Mono traite System.Data.SqlCommands différemment de .NET ?

J'ai quelques problèmes avec une application que nous portons vers Mono.

(Pour référence, le runtime .NET est 4.0, et la version mono est 2.6.7).

EDIT : Ce problème persiste sur Mono 2.10.2

Dans le cadre du démarrage de l'application, celle-ci lit des données en mémoire. Pour cette partie, j'utilise simplement des commandes SQL en ligne, mais pour une raison quelconque, je constate un comportement incohérent sous Linux/Mono (alors que tout fonctionne sous Windows/.NET).

J'ai une requête qui fonctionne bien dans certains scénarios, mais pas dans d'autres.

Cet exemple particulier ne fonctionne pas :

var cmd = new SqlCommand("SELECT ID, Name, VATTerritoryID, NativeCurrencyID FROM PricingZones", conn);
var reader = cmd.ExecuteReader();

var objectToLoad = new SomeObjectType();

while (reader.Read())
{
    objectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("ID"));
    objectToLoad.Property2 = reader.GetString(row.GetOrdinal("Name"));
    objectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("VATTerritoryID"));
    objectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("NativeCurrencyID"));
}

EDIT : A titre de comparaison, en voici un qui fonctionne. :

var cmd = new SqlCommand("SELECT VATTerritoryID, ProductDescriptionID, VATBandID FROM VATTerritoryBandExceptions", conn);
var reader = cmd.ExecuteReader();

var someOtherObjectToLoad = new SomeOtherObjectType();

while (reader.Read())
{
    someOtherObjectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("VATTerritoryID"));
    someOtherObjectToLoad.Property2 = reader.GetString(row.GetOrdinal("ProductDescriptionID"));
    someOtherObjectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("VATBandID"));
}

Je me doutais bien qu'il y avait des différences :

  • La casse (je sais que c'est différent sous Windows/linux), mais mettre tout en minuscules n'a pas résolu le problème.
  • les noms de colonne (peut-être Mono se soucie-t-il davantage des mots réservés ?), mais il semble que le remplacement de Name par [Name] ou 'Name' n'ait pas fait de différence.

L'erreur que j'ai eu dans le premier cas était :

[IndexOutOfRangeException: Array index is out of range.]
at System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i)

Suggérant qu'il n'y a pas de "column1" dans le jeu de résultats retourné.

(EDIT : mise à jour de cette section pour plus de clarté)

Bizarrement, si je fais ça :

var cmd = new SqlCommand("SELECT ID, Name, VATTerritoryID, NativeCurrencyID FROM PricingZones", conn);
var reader = cmd.ExecuteReader();

var objectToLoad = new SomeObjectType();

while (reader.Read())
{
    Console.WriteLine("First row, first column is " + row.GetValue(0));
    Console.WriteLine("First row, second column is " + row.GetValue(1));
    Console.WriteLine("First row, third column is " + row.GetValue(2));
    Console.WriteLine("First row, fourth column is " + row.GetValue(3));
}

La sortie est :

First row, first column is 0
First row, second column is New
Array index is out of range.

Je suppose que quelque chose d'étrange se produit avec le framework Mono dans ce cas, mais je ne trouve pas de rapport de bogue pertinent, et je ne peux pas identifier pourquoi cela ne se produit que dans certains cas et pas dans d'autres ! Quelqu'un d'autre a-t-il eu une expérience similaire ?

EDIT : J'ai modifié certaines des déclarations pour qu'elles correspondent exactement à celles de la requête défaillante. au cas où il y aurait un problème avec les mots réservés ou autres. Notez que la requête que je lance dans le second cas demande réellement quatre colonnes et ne reçoit apparemment que deux colonnes très bizarres (0 | New ).

2voto

Turowicz Points 1120

Ok mon pote, j'ai réussi à reproduire ton problème. Vous avez un problème d'enfilage. C'était ma seule idée de ce qui pourrait être la cause de ce problème et j'ai réussi à le reproduire.

Pour le réparer, vous devez procéder comme suit :

  1. Assurez-vous que chaque lecteur a un appel à reader.Close() après avoir analysé les données. données.

  2. Utilisez le code suivant pour effectuer des appels thread-safe :

    Object executeLock = new Object();
    
    private IDataReader ExecuteThreadSafe(IDbCommand sqlCommand)
    {
        lock (executeLock)
        {
            return sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }

Il semble que mono ait une implémentation des objets SQL différente de celle de .NET. Il est vrai que je n'ai pas pu le reproduire sous Windows !

1voto

Mike Christensen Points 29735

Je commencerais par essayer de déterminer exactement où se situe la différence.

Tout d'abord, vous avez le code suivant :

objectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("column1"));
objectToLoad.Property2 = reader.GetString(row.GetOrdinal("column2"));
objectToLoad.Property3 = reader.GetString(row.GetOrdinal("column ")); //Is the space on the end intended?

Je crois que vous avez dit que les deux premières lignes fonctionnent, et que la troisième explose. On doit d'abord trouver où ça explose. Je vais essayer :

int test = row.GetOrdinal("column ");

Fait test égale un indice de colonne valide ? Si ce n'est pas le cas, c'est votre problème. Assurez-vous qu'il s'agit du nom exact de la colonne, essayez différents casings, etc. Si l'index est valide, essayez :

object foo = reader[test];
Console.WriteLine(foo.GetType().Name);

pour savoir quel est le type de données de cette colonne. Il y a peut-être un problème de casting.

Si cela échoue, j'essaierais de charger le lecteur dans un fichier DataSet à la place, afin que vous puissiez examiner plus attentivement le schéma exact.

Si vous trouvez une différence de comportement entre Mono et le .NET Framework, l'équipe de Mono est généralement très disposée à la corriger. Je vous recommande vivement d'enregistrer ce problème comme un bogue.

J'espère que cela vous aidera !

0voto

Mono n'est pas .NET et il existe de nombreuses différences, notamment avec les versions antérieures. La méthodologie Racine pour se connecter à SQL utilise une implémentation TDS (tabular data stream) en C# qui, pour les versions antérieures de Mono (et TDS par conséquent) peut causer beaucoup de problèmes. Presque toutes les classes essentielles pour SQL et les données ont des différences qui peuvent potentiellement causer des exceptions. Peut-être vaut-il la peine d'essayer avec Mono 2.10+ car l'équipe Mono améliore continuellement l'ensemble du projet.

0voto

12hys Points 366

Essayez d'accéder à votre lecteur de cette manière :

reader["column1isastring"].ToString();
(Int32)reader["column2isInt32"];

Par ailleurs, assurez-vous que vous utilisez la directive "using" pour les objets jetables. Je ne suis pas sûr que Mono implémente cela, mais ça vaut le coup d'essayer. La directive "using" permet de nettoyer les objets jetables dès que vous avez fini de les utiliser. C'est très pratique et cela permet d'avoir un code propre. Un exemple rapide :

using (MySqlCommand command = new MySqlCommand("SELECT column1, column2, column FROM tablename", conn))
{
    try
    {
        conn.Open();
        using (MySqlDataReader reader = command.ExecuteReader())
        {
                reader.Read();
                var someString = reader["column1isastring"].ToString();
                var whatever = (Int32)reader["column2isInt32"];
        } //reader closes and disposes here
    }
    catch (Exception ex)
    {
        //log this exception
    }
    finally
    {
        conn.Close();
    }
} //conn gets closed and disposed of here

De plus, si vous recevez des données d'entrée de l'utilisateur qui vont directement à vos commandes, assurez-vous d'utiliser la classe MySqlParameter pour empêcher les paramètres malveillants de supprimer des tables, par exemple.

0voto

J'utilise Mono 4.0 et j'ai un problème similaire. Mes recherches montrent que la raison la plus probable de ce problème est une implémentation défectueuse du pool de connexion dans Mono. Au moins, si je désactive le pooling, le problème disparaît.

Pour désactiver la mise en commun des connexions, vous devez ajouter la chaîne suivante à votre chaîne de connexion : Pooling=false;

Après cela, la création de l'objet de connexion devrait ressembler à cela :

var conn = new SqlConnection("Server=localhost; Database=somedb; user=user1; password=qwerty; Pooling=false;");

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X