2008-12-15 9 views
2

J'ai trouvé cela dans un journal des erreurs et j'essaie de voir comment c'est possible. Ce n'est pas tous les jours qu'une NullReferenceException apparaît au plus profond des classes de base .net!SqlDataReader lève NullReferenceException! qu'est-ce qui pourrait causer cela et comment puis-je déboguer?

1) Exception Information 
********************************************* 
Exception Type: System.NullReferenceException 
Message: Object reference not set to an instance of an object. 
Data: System.Collections.ListDictionaryInternal 
TargetSite: Void Bind(System.Data.SqlClient.TdsParserStateObject) 
HelpLink: NULL 
Source: System.Data 

StackTrace Information 
********************************************* 
    at System.Data.SqlClient.SqlDataReader.Bind(TdsParserStateObject stateObj) 
    at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) 
    at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) 
    at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult esult) 
    at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) 
    at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) 
    at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) 
    at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior) 
    at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior) 
    at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior) 
    at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet) 
    at MyCode.Shared.Data.DataSocket.GetTable(String SPString) 
    at <rest of stack trace> 

La seule chose que je pouvais venir avec est, il y a une chance (mince), il y avait deux fils d'exécution de la même méthode en même temps, et un effacé ou modifié le DataSet qui est passé à remplir() . Donc, ma question est vraiment:

  • comment pourrait cette exception se fait jeter
  • pourrait une cause de scénario multi-thread cette exception
  • Comment puis-je être sûr, par exemple, est-il un moyen que je peux parcourir le système Méthodes .Data pour répliquer le problème?

Soit dit en passant, j'ai trouvé deux autres cas de ce problème, l'un en fil this et un autre dans this cache de Google de la page de quelqu'un. Cependant, aucun d'entre eux ne semble être utile.

Procédé GetTable() de la mine qui est exécutée ressemble à ceci:

public DataTable GetTable(string SPString) 
{ 
    //Setup the data objects by calling helper 
    prepareDataAdaptor(SPString,CommandType.Text); 

    dataAdaptor.Fill(dataSet); 
    dataAdaptor.SelectCommand.Connection.Close(); 
    DataTable dt; 
    //ensure we dispose okay 
    using(dataSet) 
    { 

     if(dataSet.Tables.Count==0) 
     { 
      dataSet.Tables.Add(new DataTable("EmptyTable")); 
     } 

     dt=dataSet.Tables[0]; 
     //because we are disposing we need to remove the table from the dataset 
     dataSet.Tables.Clear(); 

    } 
    return dt; 
} 

private void prepareDataAdaptor(string SPString,CommandType Type) 
{ 
    checkForConnection(); 
    dataSet=new DataSet(); 
    dbCommand.CommandText=SPString; 
    dbCommand.CommandTimeout = MySettings.CommandTimeout; 
    dataAdaptor.SelectCommand=dbCommand; 
    dataAdaptor.SelectCommand.CommandType=Type; 
    dataAdaptor.SelectCommand.Connection=dbConnection; 
    dataAdaptor.SelectCommand.Parameters.Clear(); 
} 

dataAdaptor (sic) est une variable d'instance déclarée comme IDbDataAdapter peuplée avec un SqlDataAdapter. dataSet est une variable d'instance de type DataSet. Ma théorie part du principe que le thread A parcourt la méthode SqlDataAdapter.Fill() et se place partiellement dans la méthode SqlDataAdapter.Fill(). Pendant ce temps, exécute également le thread B et fait quelque chose qui salit fil A, comme cette ligne:

dataAdaptor.SelectCommand.Connection.Close(); 

Je peux voir que ce code est à moi est thread-safe, mais comment puis-je être sûr que c'est le problème qui a causé l'exception ci-dessus?

merci beaucoup pour vos suggestions!

Rory

EDIT: cas de l'orthographe fixe moche. Je ne l'ai pas mis à jour dans le code car c'est comme ça. MISE À JOUR: Je suis d'accord pour dire qu'il y a plusieurs problèmes avec ce code qui devrait être corrigé, mais mon principal intérêt est de savoir s'il existe un moyen de vérifier que c'est ce problème de thread qui aurait causé cette erreur. Compte tenu de mon application, c'est un peu tiré par les cheveux mais la seule chose à laquelle je puisse penser. Avant de modifier le code pour le rendre globalement meilleur, je voudrais être sûr d'avoir trouvé la cause de l'exception afin que je puisse être sûr de l'avoir corrigé.

Y a-t-il un moyen d'entrer dans le code .net par exemple? J'utilise VS 2005/.net 2.0 mais je pense que dans VS 2008 vous pouvez voir la source du framework .NET? Si c'est le cas, puis-je créer un scénario à deux fils et passer à travers pour recréer ce problème? Ou y a-t-il un moyen qui ne nécessite pas l'installation de VS 2008?

+0

"légèrement tiré par les cheveux" est ce code multi-thread? Définissez la variable locale de l'ensemble de données et vérifiez si cela se produit à nouveau. – dotjoe

Répondre

1

pourrait un scénario multi-thread provoquent cette exception

méthodes d'instance des classes d'ADO.NET (par exemple SqlCommand) ne sont généralement pas thread-safe. Ainsi, si vous utilisez de telles instances à partir de plusieurs threads, vous pouvez vous attendre à des problèmes tels que celui que vous décrivez.

0

Je suppose que la suppression de l'ensemble de données invalide la table qu'il contient!

BTW vous n'avez pas besoin de l'ensemble de données, vous pouvez remplir un datatable directement - ou appelez l'autre fonction d'adaptateur de données qui crée un datatable pour vous (Get, je pense)

et il est orthographié pas « adaptateur » "adaptateur" ;-)

+0

Damnés codeurs hérités qui ne pouvaient pas épeler! m'a tous confus ... Donc, des idées sur la façon d'être sûr que cet accès multi-thread et ensuite disposer est réellement ce qui s'est passé, par exemple pour reproduire le scénario? – Rory

1

Je voudrais ajouter du code de journalisation juste autour de dataAdaptor.Fill (dataSet) qui affiche l'information ThreadID et d'autres informations. Vous pouvez utiliser Console.Writeline, mais je recommande fortement log4net. En outre, faites votre code thread-safe. Chaque thread doit avoir son propre DataSet et DataAdapter, ou utiliser mutex.

2

Votre jeu de données ne doit pas être une variable de niveau classe. Il est probablement disposé ou accédé par l'autre thread entre ces deux appels ou pendant l'opération de remplissage.

prepareDataAdaptor (SPString, CommandType.Text);

dataAdaptor.Fill (ensemble de données);

Questions connexes