Asp.net Webサイトをイントラネットで数週間ライブで実行しています。未処理の例外を含むapplication_error電子メールメソッドから電子メールを受信しました。
ここにあります(より見やすくするためにいくつかのパスをクリーンアップしました)
例外:オブジェクト参照がオブジェクトのインスタンスに設定されていません。スタックトレース:System.Collections.Generic.Dictionary`2.Insert(TKey key、TValue value、Boolean add)at System.Collections.Generic.Dictionary`2.Add(TKey key、TValue value)at TimesheetDomain.DataMappers.StaffMemberData TimesheetDomain\DataMappers\StaffMemberData.cs:line 362にある.ReadStaff(SqlDataReader reader)
timesheetDomain\DataMappers\StaffMemberData.cs:line 401のTimesheetDomain.DataMappers.StaffMemberData.GetStaffMember(String name)で
timesheetDomain\ServiceLayer\TimesheetManager.cs:line 199のTimesheetDomain.ServiceLayer.TimesheetManager.GetUserFromName(String name)で
\ App_Code\UserVerification.cs:line 29のUserVerification.GetCurrentUser()で\ WebTimesheets\WebTimesheets.master.cs:line 159のWebTimesheets.OnInit(EventArgs e)で
system.Web.UI.Control.InitRecursive(Control namingContainer)でSystem.Web.UI.Control.InitRecursive(Control namingContainer)でSystem.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint、Boolean includeStagesAfterAsyncPoint)
基本的に、データリーダーを読み取ってスタッフメンバーオブジェクトを構築するReadStaffメソッドでエラーが発生しているように見えます。ここにコードのビットがあります:
while (reader != null && reader.Read())
{
StaffMember newMember = null;
string firstName = reader["FirstName"].ToString();
string lastName = reader["LastName"].ToString();
int staffID = (int)reader["StaffID"];
int employSection = (int)reader["EmploySection"];
StaffType employType = (StaffType)employSection;
string emailAddress = reader["EmailInt"].ToString();
int employCode = (int)reader["ibbwid"];
//check if they are an admin staff member
if (IsAdminStaff(employType))
{
newMember = new AdminOfficer(firstName, lastName, employType, staffID, emailAddress, employCode);
}
else
{
//check if they are a supervisor
if (IsASupervisor(staffID))
newMember = new Supervisor(firstName, lastName, employType, staffID, emailAddress, employCode);
else
newMember = new StaffMember(firstName, lastName, employType, staffID, emailAddress, employCode);
}
//add to identity map
if (!_staffMembers.ContainsKey(staffID))
_staffMembers.Add(staffID, newMember); //****THIS IS LINE 362*****
else
_staffMembers[staffID] = newMember;
}
(行362は最後の3行目です)アイデンティティマップを使用しています(パターンについての野鳥集を読んで、それが良いアイデアだと思った-間違っていたかもしれない、コメントに満足しているかもしれません)が、後で使用するので、あまり関係ありません別の場所にあるnewMember
オブジェクトなので、そのブロックを削除するとNullReferenceException
が発生します。
最後の3行目(エラーが発生した行)でnewMember
がどのようにnullになるかを確認するのに苦労しています。
Resharper/VSは、それがnull
である可能性があるという警告を表示しません-私が選択する3つのコンストラクターがあるためです。
誰かが私がこのエラーを試し、修正するためにどこを見ればよいかを提案できますか?それは一度だけ起こり、その方法はサイトが稼働して以来何千回も呼び出されました。
ありがとう
[編集]リクエストに応じて、こちらがスタッフメンバーのIComparerです
/// <summary>
/// Comparer for staff members - compares on name
/// </summary>
public class StaffMemberComparer : IComparer
{
public int Compare(object x, object y)
{
//check they are staff members
if (x is StaffMember && y is StaffMember)
{
//do a simple string comparison on names
StaffMember staffX = x as StaffMember;
StaffMember staffY = y as StaffMember;
return String.Compare(staffX.FirstName, staffY.FirstName);
}
throw new Exception("This is for comparing Staff Members");
}
}
iComparable実装で使用されている
/// <summary>
/// IComparable implementaiton
/// </summary>
/// <param name="obj">object to compare to</param>
/// <returns></returns>
public int CompareTo(object obj)
{
StaffMemberComparer comparer = new StaffMemberComparer();
return comparer.Compare(this, obj);
}
これはほぼ間違いなくスレッドの問題です (この質問とその回答 を参照してください。
挿入操作中に辞書インスタンスが別のスレッドから変更された場合、Dictionary<>.Insert()
は内部でNullReferenceException
をスローします。
.NET 4.0以降では、ConcurrentDictionaryを使用して、複数のスレッドから同じ辞書を同時に操作することに関連するスレッドの問題を回避できます。
はっきりとは見えません。 SQLを実行して、データベースに不良データがないかチェックします。問題は、関連する入力フォームの異常なバグである可能性があります。これまで何の問題もなくコードが何千回も実行された場合は、問題のコードブロックの周りに追加の例外処理/レポートをラップして、少なくとも次に発生した場合に、staffIdを取得できるようにします。
あなたはこのようなものに多くの時間をかけることができます。最も好都合なアプローチは、上記の/制御された条件下で再び失敗させることです...それによって引き起こされる混乱のレベルが許容可能/管理可能/マイナーであると仮定します。
私はそれを知る必要性をすぐには満たすことができないことを感謝しますが、それは特にそのような低い故障率で問題を管理する最良の方法かもしれません。
それは一度だけ起こり、その方法はサイトが稼働して以来何千回も呼び出されました。
これを読んだ後、.NETがメモリを使い果たして、それ以上のディクショナリキーを作成できなかった可能性があり、実際にはどこにも障害がない可能性があります。しかし、はい、セッション/アプリケーション変数に多くの情報を保存しようとしたときに、この種のエラーが発生したため、Webアプリケーションのメモリフットプリントが増加しました。しかし、辞書やリストなどに10,000アイテムを格納するなど、数値が非常に高くなると、このようなエラーが発生しました。
パターンは良好ですが、データベースを使用して情報をリレーショナル形式で格納していることも理解しておく必要があります。メモリを使用して同様のものを格納し始めた場合、強力なデータベースは無視されます。データベースは値をキャッシュすることもできます。
ばかげているように聞こえるかもしれませんが、トラフィックのない真夜中に、24時間ごとにWindowsサーバーを再起動しています。これは、そのようなエラーを取り除くのに役立ちました。すべてのキャッシュ/ログをクリアするために、定期的にサーバーを修正スケジュールで再起動します。
スレッド化とは関係なく、この例外が発生した別の方法は、辞書をシリアル化する場合です。この場合は空でしたが、逆シリアル化されたインスタンスのInsert()メソッドでNullReferenceExceptionが発生しました。
私の場合の単純な変更は、逆シリアル化の後に新しいインスタンスを作成することだけでした。シリアル化がディクショナリを壊すだけなのか、それともディクショナリが定義されている型なのかはわかりません。
私の場合、型はシリアル化サロゲートなしではシリアル化できませんでした(そして私はこれらを提供しました)が、おそらく辞書の何かがここで問題を抱えていました。繰り返しますが、辞書は空であり、これはまだ起こりました。
他の人が言ったように、比較が問題を引き起こしている可能性があります。
比較の基準のいずれかにnull値が含まれていますか?具体的には、文字列プロパティ?
つまり、firstname、lastname、またはemailIdがnullであり、それが比較で使用されている場合、ディクショナリ内で比較に使用すると、失敗する可能性があります。
編集:SupervisorとStaffMemberとAdminStaffクラスはどのように関連していますか?
コードでは、両方のインスタンスをStaffMemberにキャストしています。私の推測では、SupervisorクラスとStaffMemberクラスがnotに関連している場合は問題になる可能性があります。
EDIT2:辞書インスタンスの頭字語は何ですか?アプリケーションレベル/セッションレベルで共有されますか?複数のスレッドがそこから読み取り/書き込みを試みる可能性はありますか?