適用対象:Azure SQL Database
Azure SQL Managed Instance
Azure Synapse Analytics
Fabric の SQL データベース
この記事では、クライアント アプリケーションが Azure SQL Database、Microsoft Fabric の SQL データベース、Azure SQL Managed Instance、および Azure Synapse Analytics と対話するときに発生する接続エラーと一時的なエラーを防止、トラブルシューティング、診断、軽減する方法について説明します。 再試行ロジックの構成方法、接続文字列の作成方法、およびその他の接続設定の調整方法について説明します。
一時エラー (一過性の障害)
一時エラーは、一過性の障害とも呼ばれ、基になる原因はすぐ自動的に解決されます。 一時エラーを起こす偶発的原因として、Azure システムが、各種ワークロードの負荷分散を行うために行うハードウェア リソースの瞬間的切り替えがあります。 この再構成イベントのほとんどは 60 秒以内に完了します。 この再構成の進行中、SQL Database のデータベースへの接続で問題が発生する場合があります。 データベースに接続するアプリケーションは、これらの一時エラーを想定して構築する必要があります。 これに対処するには、アプリケーション エラーとしてユーザーに表示するのではなく、コードに再試行ロジックを実装します。
クライアント プログラムで ADO.NET を使用している場合、SqlException のスローによって一時的なエラーが通知されます。
接続とコマンド
次の内容に応じて、接続を再試行するか、もう一度確立してください。
- 接続試行中に一時エラーが発生する
数秒間の時間を置いて、接続を再試行します。
- クエリ コマンド中に一時的なエラーが発生する
コマンドをすぐに再試行しないでください。 少し時間を置いて、新たに接続を確立します。 その後、コマンドを再試行します。
一時エラーの再試行ロジック
一時エラーが多少なりとも生じるクライアント プログラムは、再試行ロジックを組み込むことで堅牢性を高めることができます。 ご使用のプログラムがサード パーティのミドルウェアを介して SQL Database のデータベースと通信する場合、一時エラーに対応した再試行ロジックがそのミドルウェアに備わっているかどうかをベンダーに確認してください。
再試行の原則
- 一時的なエラーである場合は、再試行して接続を開いてください。
- 一時的なエラーで失敗した
SELECTステートメントを直接再試行しないでください。 新しい接続を確立してから、SELECTを再試行してください。 -
UPDATEステートメントが一時的なエラーで失敗した場合は、UPDATEを再試行する前に、新しい接続を確立します。 データベース トランザクション全体が完了したのか、トランザクション全体がロールバックされたのかを再試行ロジックで確認する必要があります。
再試行に関するその他の注意点
- 業務終了時刻に自動的に起動され翌朝までに完了するバッチ プログラムは、再試行間隔をある程度長めにしても問題ありません。
- 人間の心理として、待ち時間があまり長いと途中でキャンセルされる可能性があります。ユーザー インターフェイス プログラムはその点を考慮するようにしてください。 数秒おきに再試行するようなやり方は避けてください。そのような方法を用いると、大量の要求でシステムが処理しきれなくなる可能性があります。
再試行の間隔を長くする
最初に再試行する前に、5 秒間待つことをお勧めします。 5 秒未満で再試行すると、クラウド サービスに過度の負荷がかかるおそれがあります。 再試行するたびに、待ち時間を大幅に長くし、最大 60 秒待つ必要があります。
ADO.NET を使用するクライアントのブロック期間については、接続プール (ADO.NET) に関するページを参照してください。
最大再試行回数を設定し、プログラムが自動的に終了するようにすることも考慮してください。
再試行ロジックを含んだコード サンプル
再試行ロジックを含むコード サンプルについては次の記事をご覧ください。
再試行ロジックのテスト
再試行ロジックをテストするには、プログラムの実行中に修復可能なエラーをシミュレートする (人為的に発生させる) 必要があります。
ネットワークから切断することによるテスト
再試行ロジックをテストする手段として、プログラムの実行中にクライアント コンピューターをネットワークから切断する方法が挙げられます。 エラーは次のとおりです。
- SqlException.Number = 11001
- メッセージ:"そのようなホストは不明です"
最初の再試行の一環として、クライアント コンピューターをネットワークに再接続してから接続を試行することができます。
このテストを実践するには、コンピューターをネットワークから切断した後でプログラムを起動します。 プログラムはランタイム パラメーターを認識し、その結果、次の処理を行います。
- エラーのリストに対して一時的に 11001 を追加し、一過性と見なします。
- 通常と同様に初回接続を試みます。
- エラーが捕捉された後、11001 をリストから削除します。
- コンピューターをネットワークに接続するようにユーザーに通知するメッセージが表示されます。
- Console.ReadLine メソッドか、[OK] ボタンを含むダイアログのいずれかを使用して、以降の実行を一時停止します。 コンピューターがネットワークに接続された後に、ユーザーが Enter キーを押します。
- 再度接続を試みます。成功を期待しています。
接続時に間違った綴りのユーザー名を使用することによるテスト
あなたのプログラムは、初回接続を試みる前に意図的にユーザー名を間違えた形で使用することがあります。 エラーは次のとおりです。
- SqlException.Number = 18456
- メッセージ:"ユーザー WRONG_MyUserName はログインできませんでした"
最初の再試行のときに、プログラムでスペルミスを修正してから接続を試みてください。
このテストを実用的にするために、プログラムはランタイムパラメーターを認識して次の処理を行います。
- エラーのリストに対して一時的に 18456 を追加し、一過性と見なします。
- 意図的に 'WRONG_' をユーザー名に追加します。
- エラーが捕捉された後、18456 をリストから削除します。
- ユーザー名から 'WRONG_' を削除します。
- 再度接続を試み、成功を期待しています。
接続再試行用の .NET SqlConnection パラメーター
クライアント プログラムが .NET クラス Microsoft.Data.SqlClient.SqlConnection または System.Data.SqlClient.SqlConnection を使用して Azure SQL Database 内のデータベースに接続する場合は、接続再試行機能を使用できます。
System.Data.SqlClientの場合は、.NET Framework 4.6.1 以降のバージョン (または .NET Core) を使用します。 この機能に関する詳細は、「SqlConnection.ConnectionString Property」を参照してください。
注意
Microsoft.Data.SqlClient は、新しいアプリケーション開発 ADO.NET 推奨されるデータ プロバイダーです。 接続の再試行に加えて、接続とコマンドの再試行の両方に 対して構成可能な再試行ロジック をサポートします。 詳細については、「 Microsoft.Data.SqlClient 名前空間の概要」を参照してください。
接続文字列 を SqlConnection オブジェクト用に作成するときは、次のパラメーター間で値を調整します。
- ConnectRetryCount: 既定値は 1 です。 範囲は 0 から 255 です。
- ConnectRetryInterval: 既定は 10 秒です。 範囲は 1 から 60 です。
- Connection Timeout: 既定値は 15 秒です。 範囲は 0 から 2147483647 です。
接続の回復性には、接続の再試行設定 (ConnectRetryCount と ConnectRetryInterval) が適用されます。 接続の回復性には、次の種類が含まれます。
オープン接続の回復性は、最初の SqlConnection.Open または OpenAsync() メソッドを参照します。 最初の接続試行は、try 0 としてカウントされます。 ConnectRetryCount は、後続の再試行に適用されます。 したがって、接続 0 が失敗した場合 (すぐには発生しない可能性があります)、ConnectRetryInterval が最初に適用され、その後に ConnectRetryCount (および ConnectRetryInterval) が試行されます。 すべての再試行を利用するには、Connection Timeout プロパティですべての試行に時間を指定する必要があります。
アイドル接続の回復性とは、切断された既存のアイドル状態の接続の自動検出と再接続を指します。 切断されたアイドル状態の接続を再接続する最初の試行は、最初の再試行としてカウントされます。 すべての再試行を利用するには、Command Timeout プロパティですべての試行に時間を指定する必要があります。
例: ConnectRetryCount パラメーターと ConnectRetryInterval パラメーターの値を次に示します。
ConnectRetryCount: 3 ConnectRetryInterval: 10 秒
次のシナリオで、これらの値がどのように使用されるかをご覧ください。
シナリオ: 新しい接続
4:10:00 - Connection.Open() - 試行なし
4:10:01 - 接続エラーが検出されました
4:10:11 - 再試行 1 -->ConnectRetryInterval の後に最初の再試行が行われます
4:10:21 - 再試行 2
4:10:31 - 再試行 3
このシナリオでは、選択した値が次の条件を満たす必要があります。
Connection Timeout > = ConnectRetryCount * ConnectionRetryInterval
たとえば、回数が 3、間隔が 10 秒、タイムアウトが 29 秒のみの場合、次のようになるため、システムの 3 回目の最後の接続の試行には十分な時間が与えられません。
29 < 3 × 10
シナリオ: 接続がアイドル状態
ConnectRetryCount: 3 ConnectRetryInterval: 10 秒
4:10:00 - コマンドの実行時に接続の切断が検出されました
4:10:00 - 再試行 1 -->最初の再試行はすぐに行われます
4:10:10 - 再試行 2
4:10:20 - 再試行 3
これは最初の接続ではありません。 そのため、Connection Timeout は適用されません。 ただし、接続の回復はコマンドの実行中に行われるため、ステートメントを実行する SqlCommand の Command Timeout プロパティが適用されます。 Command Timeout: 既定は 30 秒です。 ただし、接続の復旧は一般的な状況では高速ですが、間欠的に停止すると、復旧にコマンドの実行時間がかかることがあります。
このシナリオでは、アイドル状態の接続回復の再試行を最大限に活用する場合は、選択した値が次の条件を満たす必要があります。
Command Timeout > (ConnectRetryCount - 1) * ConnectionRetryInterval
たとえば、カウントが 3 で間隔が 10 秒の場合、20 秒未満のコマンド タイムアウト値では、3 回目と最後の再試行が接続するのに十分な時間が与えられません: (3 - 1) * 10 = 20`
また、接続の復旧後にコマンド自体の実行に時間がかかることを考慮してください。
注意
これらのシナリオで提供される期間の値は、デモンストレーション専用です。 両方のシナリオでの実際の検出時間は、基になるインフラストラクチャによって異なります。
接続とコマンド
ConnectRetryCount パラメーターと ConnectRetryInterval パラメーターを使用すると、プログラムに制御を返すなど、プログラムへの通知や介入なしに、SqlConnection オブジェクトで接続操作を再試行できます。 再試行は次の状況で発生することがあります。
- SqlConnection.Open メソッド呼び出し
- SqlCommand.Execute* メソッド呼び出し
微妙なことがあります。 "クエリ" の実行中に一時エラーが発生した場合、SqlConnection オブジェクトで接続操作が再試行されません。 間違いなくクエリは再試行されません。 ただし、実行するクエリを送信する前に、 SqlConnection ですばやく接続が確認されます。 簡単なチェックで接続の問題が検出された場合、 SqlConnection で接続操作が再試行されます。 再試行に成功すると、実行するクエリが送信されます。
ConnectRetryCount をアプリケーションの再試行ロジックと組み合わせて使用する必要があるかどうか
アプリケーションにカスタムの堅牢な再試行ロジックが組み込まれていると仮定します。 このロジックでは、接続操作が 4 回再試行されます。 接続文字列に ConnectRetryInterval と ConnectRetryCount =3 を追加した場合、再試行回数を 4 * 3 = 12 回に増やします。 このように何回も再試行するのは、適切ではない可能性があります。
SQL Database のデータベースへの接続
接続:接続文字列
データベースに接続するために必要な接続文字列は、SQL Server への接続に使用される文字列とは若干異なります。 データベースの接続文字列は Azure ポータルからコピーすることができます。
Azure ポータルから接続文字列を取得する
Azure Portal を使って、クライアント プログラムが Azure SQL Database と対話するために必要な接続文字列を取得します。
[すべてのサービス]>[SQL データベース] の順にクリックします。
[SQL Database]ウィンドウの左上近辺にあるフィルター テキスト ボックスにデータベースの名前を入力します。
お使いのデータベースの行を選びます。
データベースのウィンドウが表示されたら、画面を見やすくするために[最小化]ボタンを選択し、閲覧とデータベースフィルター処理に使用したウィンドウを折りたたみます。
データベースのウィンドウで、[データベース接続文字列の表示]を選択します。
適切な接続文字列をコピーします。 たとえば、ADO.NET 接続ライブラリを使用する場合は、[ADO.NET] タブから適切な文字列をコピーします。
必要に応じて、接続文字列を編集します。 この例では、接続文字列にパスワードを挿入するか、ユーザー名またはサーバー名が長すぎる場合はユーザー名から
<server-name>を削除します。特定の形式の接続文字列情報を、クライアント プログラム コードに貼り付けます。
詳しくは、「接続文字列と構成ファイル」をご覧ください。
接続:IP アドレス
SQL Database は、クライアント プログラムのホストとなるコンピューターの IP アドレスからの通信を許可するように構成する必要があります。 この構成を設定するには、Azure Portal を通じてファイアウォール設定を編集します。
IP アドレスの構成を怠った場合、必要な IP アドレスを示した役立つエラー メッセージが表示され、プログラムが失敗します。
Azure portal にサインインします。
左側の一覧で、[すべてのサービス] を選びます。
スクロールして、[SQL Server] を選びます。
フィルター テキスト ボックスで、サーバーの名前の入力を開始します。 行が表示されます。
お使いのサーバーの行を選びます。 サーバーのペインが表示されます。
サーバーのウィンドウで、[設定]を選択します。
[ファイアウォール] を選びます。
[クライアント IP の追加] を選びます。 最初のテキスト ボックスに、新しい規則の名前を入力します。
有効にする範囲の下位と上位の IP アドレスの値を入力します。
-
.0で終わる低い値と.255で終わる高い値を持つことは便利です。
-
[保存] を選択します。
詳細については、Azure SQL Database と Azure Synapse の IP ファイアウォール規則に関するページを参照してください。
接続:ポート
通常、クライアント プログラムをホストするコンピューターでは、ポート 1433 を外向き通信に対して開放する必要があります。
たとえば、クライアント プログラムが Windows コンピューターでホストされている場合、そのホストの Windows ファイアウォールを使用してポート 1433 を開放することができます。
- [コントロール パネル] を開きます。
- [すべてのコントロール パネル項目]>[Windows ファイアウォール]>[詳細設定]>[送信の規則]>[アクション]>[新しい規則] の順に選択します。
クライアント プログラムが Azure 仮想マシン (VM) でホストされている場合は、 ADO.NET 4.5 の 1433 を超えるポートを読み取ります。
データベース内のポートと IP アドレスの構成の背景情報については、 Azure SQL Database と Azure Synapse の IP ファイアウォール規則に関するページを参照してください。
接続:ADO.NET 4.6.2 以降
プログラムで System.Data.SqlClient.SqlConnection などの ADO.NET クラスを使用して SQL Database に接続する場合は、.NET Framework バージョン 4.6.2 以降を使用することをお勧めします。 新しいアプリケーションの場合は、 Microsoft.Data.SqlClientを使用することを検討してください。これは、同じ接続性と強化された機能を提供します。
ADO.NET 4.6.2 から始める
- Azure SQL に対して接続を開く試みが直ちに再試行されます。そのため、クラウド対応アプリのパフォーマンスが向上します。
ADO.NET 4.6.1 から始める
- SQL Database では、SqlConnection.Open メソッドを使用して接続を開くと信頼性が向上します。 Open メソッドには、接続タイムアウト期間内の特定のエラーを対象に、一過性の障害に対応するベスト エフォート再試行メカニズムが組み込まれました。
- 接続プールがサポートされているため、プログラムに割り当てた接続オブジェクトが正常に動作しているかどうかを効率的に検証することが可能です。
接続プールから取得した接続オブジェクトを使用するとき、すぐに使用しないのであれば、プログラムで一時的に接続を閉じることをお勧めします。 接続を再度開くコストはかからず、新しい接続を作成することです。
ADO.NET 4.0 以前のバージョンを使用する場合、最新の ADO.NET. にアップグレードすることをお勧めします。 2018 年 8 月の時点で、ADO.NET 4.6.2 のダウンロードが可能になりました。
診断
診断:ユーティリティから接続できるかどうかをテストする
プログラムから SQL Database のデータベースに接続できないときの診断方法として 1 つ考えられるのは、ユーティリティ プログラムを使用して接続する方法です。 プログラムで使用しているのと同じライブラリを使用して接続するユーティリティがあれば理想的です。
任意の Windows コンピューターで、次のユーティリティを試すことができます。
- SQL Server Management Studio (ssms.exe)。ADO.NET を使用して接続します。
-
sqlcmd.exe。ODBC を使用して接続します。
プログラムが接続された後に、短い SQL SELECT クエリが正しく動作するかどうかをテストしてください。
診断:開放ポートを確認する
ポートの問題が原因で接続に失敗している可能性がある場合は、ポートの構成に関するレポート作成に対応したユーティリティをご使用のコンピューターで実行してください。
Linux では、次のユーティリティが役に立つ場合があります。
netstat -nap-
nmap -sS -O 127.0.0.1:例の値を実際の IP アドレスに変更してください。
Windows では PortQry.exe ユーティリティが利用できます。 以下は、SQL Database のデータベースのポートの状況の照会をノート PC 上で実行する例を示しています。
[C:\Users\johndoe\]
>> portqry.exe -n johndoesvr9.database.windows.net -p tcp -e 1433
Querying target system called: johndoesvr9.database.windows.net
Attempting to resolve name to IP address...
Name resolved to 23.100.117.95
querying...
TCP port 1433 (ms-sql-s service): LISTENING
[C:\Users\johndoe\]
>>
診断:エラーのログを記録する
断続的な問題は、過去数日から数週間にわたる一般的なパターンを検出することによって診断できる場合が多々あります。
診断のためには、クライアントが遭遇した全てのエラーをログに記録することが役立ちます。 SQL Database が内部的に記録するエラー データとそれらのログ エントリを相互に関連付けることも可能です。
Enterprise Library 6 (EntLib60) には、ログ記録をサポートする .NET マネージド クラスがあります。 詳細については、「5 - As easy as falling off a log: Use the Logging Application Block (5 - きわめて簡単: Logging アプリケーション ブロックの使用)」を参照してください。
診断:エラーの発生をシステム ログで調べる
以下に示したのは、エラー ログや各種情報を照会する Transact-SQL SELECT ステートメントの例です。
| ログのクエリ | 説明 |
|---|---|
SELECT e.*FROM sys.event_log AS eWHERE e.database_name = 'myDbName'AND e.event_category = 'connectivity'AND 2 >= DateDiff(hour, e.end_time, GetUtcDate())ORDER BY e.event_category,e.event_type, e.end_time; |
sys.event_log ビューには、一時エラーや接続障害を引き起こす可能性のあるものを含む、個々のイベントに関する情報が表示されます。 理想的には、start_time や end_time の値を、クライアント プログラムに問題が発生した時間の情報に関連付けます。 このクエリを実行するには、"マスター" データベースに接続する必要があります。 |
SELECT c.*FROM sys.database_connection_stats AS cWHERE c.database_name = 'myDbName'AND 24 >= DateDiff(hour, c.end_time, GetUtcDate())ORDER BY c.end_time; |
sys.database_connection_stats ビューには、イベントの種類ごとに集計されたカウントが表示され、詳しい診断を行うことができます。 このクエリを実行するには、"マスター" データベースに接続する必要があります。 |
診断:SQL Database のログから問題のイベントを検索する
SQL Database のログで問題のイベントに関するエントリを検索することができます。 master データベースで次の Transact-SQL SELECT ステートメントを試してみてください。
SELECT
object_name
,CAST(f.event_data as XML).value
('(/event/@timestamp)[1]', 'datetime2') AS [timestamp]
,CAST(f.event_data as XML).value
('(/event/data[@name="error"]/value)[1]', 'int') AS [error]
,CAST(f.event_data as XML).value
('(/event/data[@name="state"]/value)[1]', 'int') AS [state]
,CAST(f.event_data as XML).value
('(/event/data[@name="is_success"]/value)[1]', 'bit') AS [is_success]
,CAST(f.event_data as XML).value
('(/event/data[@name="database_name"]/value)[1]', 'sysname') AS [database_name]
FROM
sys.fn_xe_telemetry_blob_target_read_file('el', null, null, null) AS f
WHERE
object_name != 'login_event' -- Login events are numerous.
and
'2015-06-21' < CAST(f.event_data as XML).value
('(/event/@timestamp)[1]', 'datetime2')
ORDER BY
[timestamp] DESC
;
sys.fn_xe_telemetry_blob_target_read_file から返された数行
次の例は、どのような行が返されるかを示しています。 他の行では null でない値が多いため、ここに示した行で表示されている null 値は必ずしも null とは限りません。
object_name timestamp error state is_success database_name
database_xml_deadlock_report 2015-10-16 20:28:01.0090000 NULL NULL NULL AdventureWorks
エンタープライズ ライブラリ 6
Enterprise Library 6 (EntLib60) は、.NET クラスのフレームワークです。クラウド サービス (SQL Database もその 1 つ) に対する堅牢なクライアントをこのフレームワークを使って実装することができます。 EntLib60 の利便性が発揮される個々の領域の説明については、「Enterprise Library 6」を参照してください。
一時エラーを処理するための再試行ロジックは、EntLib60 を利用できる 1 つの領域です。 詳細については、「4 - 忍耐、すべての勝利の秘訣:瞬時障害対応アプリケーションブロックを使用する方法」をご覧ください。
注意
EntLib60 のソース コードは、ダウンロード センターから入手できます。 EntLib に対して機能の更新や保守目的での更新を行う予定はありません。
一時エラーと再試行に関連した EntLib60 のクラス
再試行ロジックで特に利用する機会の多い EntLib60 のクラスは次のとおりです。 これらのクラスはすべて、Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling 名前空間に属しています。
名前空間 Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling:
-
RetryPolicy クラス
- ExecuteAction メソッド
- ExponentialBackoff クラス
- SqlDatabaseTransientErrorDetectionStrategy クラス
-
ReliableSqlConnection クラス
- ExecuteCommand メソッド
Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.TestSupport 名前空間の:
- AlwaysTransientErrorDetectionStrategy クラス
- NeverTransientErrorDetectionStrategy クラス
EntLib60 に関する情報は以下のリンクから入手できます。
- 無料の電子ブック ダウンロード:Microsoft Enterprise Library 開発者ガイド、第 2 版。
- ベスト プラクティス:再試行全般のガイダンス には、再試行のロジックが詳しく解説されていてお勧めです。
- NuGet ダウンロード:Enterprise Library - Transient Fault Handling Application Block 6.0
EntLib60: ロギングブロック
- ログブロックは、非常柔軟で構成可能なソリューションであり、次の用途で使用できます。
- ログ メッセージをさまざまな場所に作成して保存する。
- メッセージを分類したりフィルタリングしたりする。
- デバッグやトレース、監査要件、全般的なログ要件に利用できるコンテキスト情報を収集する。
- Logging ブロックは、ログ出力先が備えるログ機能を抽象化したものです。対象となるログ ストアの場所や種類に関係なく、アプリケーション コードの一貫性を確保することができます。
詳細については、「5 - As easy as falling off a log: Use the Logging Application Block (5 - きわめて簡単: Logging アプリケーション ブロックの使用)」を参照してください。
EntLib60 IsTransient メソッドのソース コード
以下に示したのは、SqlDatabaseTransientErrorDetectionStrategy クラスの IsTransient メソッドの C# ソース コードです。 どのようなエラーが一過性で、再試行する価値があるかは、このソース コードを見るとはっきりわかります。
public bool IsTransient(Exception ex)
{
if (ex != null)
{
SqlException sqlException;
if ((sqlException = ex as SqlException) != null)
{
// Enumerate through all errors found in the exception.
foreach (SqlError err in sqlException.Errors)
{
switch (err.Number)
{
// SQL Error Code: 40501
// The service is currently busy. Retry the request after 10 seconds.
// Code: (reason code to be decoded).
case ThrottlingCondition.ThrottlingErrorNumber:
// Decode the reason code from the error message to
// determine the grounds for throttling.
var condition = ThrottlingCondition.FromError(err);
// Attach the decoded values as additional attributes to
// the original SQL exception.
sqlException.Data[condition.ThrottlingMode.GetType().Name] =
condition.ThrottlingMode.ToString();
sqlException.Data[condition.GetType().Name] = condition;
return true;
case 10928:
case 10929:
case 10053:
case 10054:
case 10060:
case 40197:
case 40540:
case 40613:
case 40143:
case 233:
case 64:
// DBNETLIB Error Code: 20
// The instance of SQL Server you attempted to connect to
// does not support encryption.
case (int)ProcessNetLibErrorCode.EncryptionNotSupported:
return true;
}
}
}
else if (ex is TimeoutException)
{
return true;
}
else
{
EntityException entityException;
if ((entityException = ex as EntityException) != null)
{
return this.IsTransient(entityException.InnerException);
}
}
}
return false;
}