データベース操作のログ記録と受信Logging and intercepting database operations

注意

EF6 以降のみ - このページで説明する機能、API などは、Entity Framework 6 で導入されました。EF6 Onwards Only - The features, APIs, etc. discussed in this page were introduced in Entity Framework 6. 以前のバージョンを使用している場合、一部またはすべての情報は適用されません。If you are using an earlier version, some or all of the information does not apply.

Entity Framework 6 以降では、Entity Framework は常にコマンドをデータベースに送信します。このコマンドは、アプリケーションコードによって傍受される可能性があります。Starting with Entity Framework 6, anytime Entity Framework sends a command to the database this command can be intercepted by application code. これは、SQL をログ記録するために最も一般的に使用されますが、コマンドを変更または中止するために使用することもできます。This is most commonly used for logging SQL, but can also be used to modify or abort the command.

具体的には、EF には次のものが含まれます。Specifically, EF includes:

  • DataContext と同様のコンテキストのログプロパティ LINQ to SQLA Log property for the context similar to DataContext.Log in LINQ to SQL
  • ログに送信される出力の内容と書式をカスタマイズするためのメカニズムA mechanism to customize the content and formatting of the output sent to the log
  • インターセプトのための低レベルの構成ブロックにより、制御性と柔軟性が向上Low-level building blocks for interception giving greater control/flexibility

コンテキストログのプロパティContext Log property

DbContext. .Log プロパティは、文字列を受け取る任意のメソッドのデリゲートに設定できます。The DbContext.Database.Log property can be set to a delegate for any method that takes a string. 最も一般的に使用されるのは、その TextWriter の "書き込み" メソッドに設定することによって、どの TextWriter でも使用されます。Most commonly it is used with any TextWriter by setting it to the “Write” method of that TextWriter. 現在のコンテキストによって生成されるすべての SQL は、そのライターにログ記録されます。All SQL generated by the current context will be logged to that writer. たとえば、次のコードでは、SQL がコンソールに記録されます。For example, the following code will log SQL to the console:

using (var context = new BlogContext())
{
    context.Database.Log = Console.Write;

    // Your code here...
}

コンテキストに注意してください。データベース .Log は、Console. 書き込みに設定されています。Notice that context.Database.Log is set to Console.Write. これは、SQL をコンソールに記録するために必要なすべてのものです。This is all that is needed to log SQL to the console.

いくつかの出力を確認できるように、単純なクエリ/挿入/更新コードを追加してみましょう。Let’s add some simple query/insert/update code so that we can see some output:

using (var context = new BlogContext())
{
    context.Database.Log = Console.Write;

    var blog = context.Blogs.First(b => b.Title == "One Unicorn");

    blog.Posts.First().Title = "Green Eggs and Ham";

    blog.Posts.Add(new Post { Title = "I do not like them!" });

    context.SaveChangesAsync().Wait();
}

これにより、次の出力が生成されます。This will generate the following output:

SELECT TOP (1)
    [Extent1].[Id] AS [Id],
    [Extent1].[Title] AS [Title]
    FROM [dbo].[Blogs] AS [Extent1]
    WHERE (N'One Unicorn' = [Extent1].[Title]) AND ([Extent1].[Title] IS NOT NULL)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 4 ms with result: SqlDataReader

SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Title] AS [Title],
    [Extent1].[BlogId] AS [BlogId]
    FROM [dbo].[Posts] AS [Extent1]
    WHERE [Extent1].[BlogId] = @EntityKeyValue1
-- EntityKeyValue1: '1' (Type = Int32)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader

UPDATE [dbo].[Posts]
SET [Title] = @0
WHERE ([Id] = @1)
-- @0: 'Green Eggs and Ham' (Type = String, Size = -1)
-- @1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 12 ms with result: 1

INSERT [dbo].[Posts]([Title], [BlogId])
VALUES (@0, @1)
SELECT [Id]
FROM [dbo].[Posts]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'I do not like them!' (Type = String, Size = -1)
-- @1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader

(これは、データベースの初期化が既に行われていることを前提とした出力です。(Note that this is the output assuming any database initialization has already happened. データベースの初期化がまだ行われていない場合は、さらに多くの出力が表示され、新しいデータベースを確認したり、新しいデータベースを作成したりするために、すべてのワーク移行が内部で行われます。If database initialization had not already happened then there would be a lot more output showing all the work Migrations does under the covers to check for or create a new database.)

ログ記録される内容What gets logged?

Log プロパティが設定されると、次のすべてがログに記録されます。When the Log property is set all of the following will be logged:

  • SQL では、さまざまな種類のコマンドを使用できます。SQL for all different kinds of commands. 次に例を示します。For example:
    • クエリ (通常の LINQ クエリ、eSQL クエリ、SqlQuery などのメソッドからの生のクエリを含む)Queries, including normal LINQ queries, eSQL queries, and raw queries from methods such as SqlQuery
    • SaveChanges の一部として生成された挿入、更新、および削除Inserts, updates, and deletes generated as part of SaveChanges
    • 遅延読み込みによって生成されたクエリなどのクエリを読み込むリレーションシップRelationship loading queries such as those generated by lazy loading
  • パラメーターParameters
  • コマンドが非同期的に実行されているかどうかWhether or not the command is being executed asynchronously
  • コマンドの実行開始時刻を示すタイムスタンプA timestamp indicating when the command started executing
  • コマンドが正常に完了したかどうか、例外をスローして失敗したか、または非同期の場合、が取り消されたかどうか。Whether or not the command completed successfully, failed by throwing an exception, or, for async, was canceled
  • 結果の値を示す値Some indication of the result value
  • コマンドの実行に要したおおよその時間です。The approximate amount of time it took to execute the command. これは、コマンドを送信して結果オブジェクトを返すまでの時間です。Note that this is the time from sending the command to getting the result object back. 結果を読み取る時間は含まれません。It does not include time to read the results.

上記の出力例を見ると、次の4つのコマンドがログに記録されます。Looking at the example output above, each of the four commands logged are:

  • コンテキストの呼び出しの結果として得られるクエリ。ブログ。最初The query resulting from the call to context.Blogs.First
    • "First" は ToString が呼び出される IQueryable を提供しないため、SQL を取得する ToString メソッドはこのクエリに対して動作しませんでした。Notice that the ToString method of getting the SQL would not have worked for this query since “First” does not provide an IQueryable on which ToString could be called
  • ブログの遅延読み込みによって生成されるクエリ。等しくThe query resulting from the lazy-loading of blog.Posts
    • 遅延読み込みが発生しているキー値のパラメーターの詳細を確認します。Notice the parameter details for the key value for which lazy loading is happening
    • 既定値以外の値に設定されているパラメーターのプロパティのみがログに記録されます。Only properties of the parameter that are set to non-default values are logged. たとえば、Size プロパティは、0以外の場合にのみ表示されます。For example, the Size property is only shown if it is non-zero.
  • SaveChangesAsync によって生成される2つのコマンド1つは、投稿のタイトルを変更する更新用で、もう1つは、新しい投稿を追加するための挿入用です。Two commands resulting from SaveChangesAsync; one for the update to change a post title, the other for an insert to add a new post
    • FK および Title プロパティのパラメーターの詳細に注意してください。Notice the parameter details for the FK and Title properties
    • これらのコマンドは非同期に実行されていることに注意してください。Notice that these commands are being executed asynchronously

別の場所へのログ記録Logging to different places

前述のように、コンソールへのログ記録はとても簡単です。As shown above logging to the console is super easy. また、異なる種類の TextWriterを使用して、メモリやファイルなどに簡単にログを記録することもできます。It’s also easy to log to memory, file, etc. by using different kinds of TextWriter.

LINQ to SQL を使い慣れている場合は、LINQ to SQL で Log プロパティが実際の TextWriter オブジェクト (たとえば、Console. Out) に設定されていることに気付くかもしれませんが、EF では、Log プロパティは文字列を受け入れるメソッドに設定されています (たとえば、write または Console)。If you are familiar with LINQ to SQL you might notice that in LINQ to SQL the Log property is set to the actual TextWriter object (for example, Console.Out) while in EF the Log property is set to a method that accepts a string (for example, Console.Write or Console.Out.Write). これは、文字列のシンクとして機能するデリゲートを受け入れることによって、TextWriter から EF を分離するためです。The reason for this is to decouple EF from TextWriter by accepting any delegate that can act as a sink for strings. たとえば、既にいくつかのログ記録フレームワークがあり、次のようなログ記録方法が定義されているとします。For example, imagine that you already have some logging framework and it defines a logging method like so:

public class MyLogger
{
    public void Log(string component, string message)
    {
        Console.WriteLine("Component: {0} Message: {1} ", component, message);
    }
}

これは、次のように EF Log プロパティにフックすることができます。This could be hooked up to the EF Log property like this:

var logger = new MyLogger();
context.Database.Log = s => logger.Log("EFApp", s);

結果のログ記録Result logging

既定の logger では、コマンドがデータベースに送信される前に、コマンドテキスト (SQL)、パラメーター、および "実行中" 行がタイムスタンプと共にログに記録されます。The default logger logs command text (SQL), parameters, and the “Executing” line with a timestamp before the command is sent to the database. 経過時間を含む "completed" 行は、コマンドの実行後にログに記録されます。A “completed” line containing elapsed time is logged following execution of the command.

非同期コマンドの場合、非同期タスクが実際に完了、失敗、またはキャンセルされるまで、"completed" 行は記録されません。Note that for async commands the “completed” line is not logged until the async task actually completes, fails, or is canceled.

"Completed" 行には、コマンドの種類と、実行が成功したかどうかによって異なる情報が含まれています。The “completed” line contains different information depending on the type of command and whether or not execution was successful.

成功した実行Successful execution

正常に完了したコマンドの場合、出力は "x ミリ秒で完了しました。結果:" の後に結果を示すいくつかの結果が続きます。For commands that complete successfully the output is “Completed in x ms with result: “ followed by some indication of what the result was. データリーダーを返すコマンドの場合は、返される Dbdatareader の型が結果に示されます。For commands that return a data reader the result indication is the type of DbDataReader returned. 上に示した update コマンドなどの整数値を返すコマンドは、その整数です。For commands that return an integer value such as the update command shown above the result shown is that integer.

失敗した実行Failed execution

例外をスローして失敗するコマンドの場合、出力には例外からのメッセージが含まれます。For commands that fail by throwing an exception, the output contains the message from the exception. たとえば、SqlQuery を使用して、存在するテーブルに対してクエリを実行すると、次のようなログ出力が生成されます。For example, using SqlQuery to query against a table that does exist will result in log output something like this:

SELECT * from ThisTableIsMissing
-- Executing at 5/13/2013 10:19:05 AM
-- Failed in 1 ms with error: Invalid object name 'ThisTableIsMissing'.

実行の取り消しCanceled execution

タスクが取り消された非同期コマンドの場合、結果は例外で失敗する可能性があります。これは、基になる ADO.NET プロバイダーが取り消しを試行したときに頻繁に実行されるためです。For async commands where the task is canceled the result could be failure with an exception, since this is what the underlying ADO.NET provider often does when an attempt is made to cancel. これが行われず、タスクが正常に取り消されると、出力は次のようになります。If this doesn’t happen and the task is canceled cleanly then the output will look something like this:

update Blogs set Title = 'No' where Id = -1
-- Executing asynchronously at 5/13/2013 10:21:10 AM
-- Canceled in 1 ms

ログの内容と書式設定の変更Changing log content and formatting

内部では、データベース .Log プロパティは DatabaseLogFormatter オブジェクトを使用します。Under the covers the Database.Log property makes use of a DatabaseLogFormatter object. このオブジェクトは、IDbCommandInterceptor の実装 (下記参照) を文字列および DbContext を受け入れるデリゲートに効果的にバインドします。This object effectively binds an IDbCommandInterceptor implementation (see below) to a delegate that accepts strings and a DbContext. これは、EF によってコマンドが実行される前と後に、DatabaseLogFormatter のメソッドが呼び出されることを意味します。This means that methods on DatabaseLogFormatter are called before and after the execution of commands by EF. これらの DatabaseLogFormatter メソッドは、ログ出力を収集して書式設定し、デリゲートに送信します。These DatabaseLogFormatter methods gather and format log output and send it to the delegate.

DatabaseLogFormatter のカスタマイズCustomizing DatabaseLogFormatter

ログ記録とその書式設定を変更するには、DatabaseLogFormatter から派生する新しいクラスを作成し、必要に応じてメソッドをオーバーライドします。Changing what is logged and how it is formatted can be achieved by creating a new class that derives from DatabaseLogFormatter and overrides methods as appropriate. オーバーライドする最も一般的な方法は次のとおりです。The most common methods to override are:

  • LogCommand –実行前にコマンドがログに記録される方法を変更するには、これをオーバーライドします。LogCommand – Override this to change how commands are logged before they are executed. 既定では、LogCommand は各パラメーターの Logcommand を呼び出します。オーバーライドで同じ操作を行うことも、パラメーターを別の方法で処理することもできます。By default LogCommand calls LogParameter for each parameter; you may choose to do the same in your override or handle parameters differently instead.
  • LogResult –これをオーバーライドして、コマンドの実行結果がどのようにログに記録されるかを変更します。LogResult – Override this to change how the outcome from executing a command is logged.
  • LogParameter –パラメーターログの書式設定と内容を変更するには、これをオーバーライドします。LogParameter – Override this to change the formatting and content of parameter logging.

たとえば、各コマンドがデータベースに送信される前に1行だけログを記録したいとします。For example, suppose we wanted to log just a single line before each command is sent to the database. これは、次の2つの上書きを使用して行うことができます。This can be done with two overrides:

  • SQL の単一行をフォーマットして書き込むには、LogCommand をオーバーライドします。Override LogCommand to format and write the single line of SQL
  • LogResult をオーバーライドして何も実行しないようにします。Override LogResult to do nothing.

コードは次のようになります。The code would look something like this:

public class OneLineFormatter : DatabaseLogFormatter
{
    public OneLineFormatter(DbContext context, Action<string> writeAction)
        : base(context, writeAction)
    {
    }

    public override void LogCommand<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        Write(string.Format(
            "Context '{0}' is executing command '{1}'{2}",
            Context.GetType().Name,
            command.CommandText.Replace(Environment.NewLine, ""),
            Environment.NewLine));
    }

    public override void LogResult<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
    }
}

出力をログに記録するには、書き込みメソッドを呼び出して、構成された書き込みデリゲートに出力を送信します。To log output simply call the Write method which will send output to the configured write delegate.

(このコードでは、例と同じように改行を単純に削除します。(Note that this code does simplistic removal of line breaks just as an example. 多くの場合、複雑な SQL の表示には適していません)。It will likely not work well for viewing complex SQL.)

DatabaseLogFormatter の設定Setting the DatabaseLogFormatter

新しい DatabaseLogFormatter クラスを作成したら、EF に登録する必要があります。Once a new DatabaseLogFormatter class has been created it needs to be registered with EF. これは、コードベースの構成を使用して行います。This is done using code-based configuration. 簡単に言うと、これは、Dbconfiguration クラスと同じアセンブリ内の DbConfiguration から派生した新しいクラスを作成し、この新しいクラスのコンストラクターで SetDatabaseLogFormatter を呼び出すことを意味します。In a nutshell this means creating a new class that derives from DbConfiguration in the same assembly as your DbContext class and then calling SetDatabaseLogFormatter in the constructor of this new class. 次に例を示します。For example:

public class MyDbConfiguration : DbConfiguration
{
    public MyDbConfiguration()
    {
        SetDatabaseLogFormatter(
            (context, writeAction) => new OneLineFormatter(context, writeAction));
    }
}

新しい DatabaseLogFormatter の使用Using the new DatabaseLogFormatter

この新しい DatabaseLogFormatter は、いつでもデータベース .Log が設定されるようになりました。This new DatabaseLogFormatter will now be used anytime Database.Log is set. そのため、パート1のコードを実行すると、次の出力が生成されるようになります。So, running the code from part 1 will now result in the following output:

Context 'BlogContext' is executing command 'SELECT TOP (1) [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title]FROM [dbo].[Blogs] AS [Extent1]WHERE (N'One Unicorn' = [Extent1].[Title]) AND ([Extent1].[Title] IS NOT NULL)'
Context 'BlogContext' is executing command 'SELECT [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], [Extent1].[BlogId] AS [BlogId]FROM [dbo].[Posts] AS [Extent1]WHERE [Extent1].[BlogId] = @EntityKeyValue1'
Context 'BlogContext' is executing command 'update [dbo].[Posts]set [Title] = @0where ([Id] = @1)'
Context 'BlogContext' is executing command 'insert [dbo].[Posts]([Title], [BlogId])values (@0, @1)select [Id]from [dbo].[Posts]where @@rowcount > 0 and [Id] = scope_identity()'

インターセプトの構成要素Interception building blocks

ここまでは、EF によって生成された SQL をログに記録するために、DbContext を使用する方法を見てきました。So far we have looked at how to use DbContext.Database.Log to log the SQL generated by EF. しかし、このコードは実際には、より一般的なインターセプトを行うために、一部の低レベルの構成要素に対して比較的シンファサードです。But this code is actually a relatively thin facade over some low-level building blocks for more general interception.

インターセプトインターフェイスInterception interfaces

インターセプトコードは、インターセプトインターフェイスの概念を中心に構築されています。The interception code is built around the concept of interception interfaces. これらのインターフェイスは、IDbInterceptor から継承し、EF が何らかのアクションを実行するときに呼び出されるメソッドを定義します。These interfaces inherit from IDbInterceptor and define methods that are called when EF performs some action. 目的は、オブジェクトの型ごとに1つのインターフェイスを受け取ることです。The intent is to have one interface per type of object being intercepted. たとえば、IDbCommandInterceptor インターフェイスは、EF が ExecuteNonQuery、ExecuteScalar、ExecuteReader、および関連メソッドの呼び出しを行う前に呼び出されるメソッドを定義します。For example, the IDbCommandInterceptor interface defines methods that are called before EF makes a call to ExecuteNonQuery, ExecuteScalar, ExecuteReader, and related methods. 同様に、インターフェイスは、これらの各操作が完了したときに呼び出されるメソッドを定義します。Likewise, the interface defines methods that are called when each of these operations completes. 上で見た DatabaseLogFormatter クラスは、このインターフェイスを実装してコマンドを記録します。The DatabaseLogFormatter class that we looked at above implements this interface to log commands.

インターセプトコンテキストThe interception context

任意のインターセプターインターフェイスで定義されているメソッドを見ると、すべての呼び出しには DbInterceptionContext 型のオブジェクト、または DbCommandInterceptionContext などの派生型のオブジェクトが指定されていることがわかり <> ます。Looking at the methods defined on any of the interceptor interfaces it is apparent that every call is given an object of type DbInterceptionContext or some type derived from this such as DbCommandInterceptionContext<>. このオブジェクトには、EF が行っているアクションに関するコンテキスト情報が含まれています。This object contains contextual information about the action that EF is taking. たとえば、アクションが DbContext の代わりに実行されている場合、DbContext は DbInterceptionContext に含まれます。For example, if the action is being taken on behalf of a DbContext, then the DbContext is included in the DbInterceptionContext. 同様に、非同期に実行されるコマンドの場合、IsAsync フラグは DbCommandInterceptionContext に設定されます。Similarly, for commands that are being executed asynchronously, the IsAsync flag is set on DbCommandInterceptionContext.

結果の処理Result handling

DbCommandInterceptionContext クラスには、 <> Result、OriginalResult、Exception、および Originalresult というプロパティが含まれています。The DbCommandInterceptionContext<> class contains a properties called Result, OriginalResult, Exception, and OriginalException. これらのプロパティは、操作が実行される前に呼び出されるインターセプトメソッド (つまり...) の呼び出しに対して null または0に設定されます。メソッドを実行しています。These properties are set to null/zero for calls to the interception methods that are called before the operation is executed — that is, for the …Executing methods. 操作が実行され、成功した場合、Result と OriginalResult は操作の結果に設定されます。If the operation is executed and succeeds, then Result and OriginalResult are set to the result of the operation. これらの値は、操作が実行された後に呼び出されるインターセプトメソッド (つまり...実行されたメソッド。These values can then be observed in the interception methods that are called after the operation has executed — that is, on the …Executed methods. 同様に、操作がをスローした場合、Exception プロパティと OriginalException プロパティが設定されます。Likewise, if the operation throws, then the Exception and OriginalException properties will be set.

実行の抑制Suppressing execution

コマンドが実行される前にインターセプターが Result プロパティを設定する場合 (メソッドを実行すると、EF は実際にはコマンドを実行しませんが、代わりに結果セットを使用します。If an interceptor sets the Result property before the command has executed (in one of the …Executing methods) then EF will not attempt to actually execute the command, but will instead just use the result set. つまり、インターセプターはコマンドの実行を抑制できますが、コマンドが実行されたかのように EF を続行します。In other words, the interceptor can suppress execution of the command but have EF continue as if the command had been executed.

これがどのように使用されるかの例として、通常、ラッピングプロバイダーで実行されたコマンドバッチ処理があります。An example of how this might be used is the command batching that has traditionally been done with a wrapping provider. インターセプターは、後で実行するためにコマンドをバッチとして格納しますが、コマンドが通常どおり実行されたことを EF に "示す" とします。The interceptor would store the command for later execution as a batch but would “pretend” to EF that the command had executed as normal. バッチ処理を実装するには、これを超えるものが必要であることに注意してください。ただし、これはインターセプトの結果を変更する方法の例です。Note that it requires more than this to implement batching, but this is an example of how changing the interception result might be used.

[例外] プロパティを設定することにより、実行を抑制することもできます。メソッドを実行しています。Execution can also be suppressed by setting the Exception property in one of the …Executing methods. これにより、指定された例外をスローすることで、操作の実行が失敗した場合と同様に EF が続行します。This causes EF to continue as if execution of the operation had failed by throwing the given exception. もちろん、これによってアプリケーションがクラッシュすることがありますが、一時的な例外や EF によって処理される他の例外である場合もあります。This may, of course, cause the application to crash, but it may also be a transient exception or some other exception that is handled by EF. たとえば、これをテスト環境で使用して、コマンドの実行が失敗したときにアプリケーションの動作をテストすることができます。For example, this could be used in test environments to test the behavior of an application when command execution fails.

実行後の結果の変更Changing the result after execution

コマンドが実行された後にインターセプターが Result プロパティを設定する場合 (実行されたメソッド)、EF は、実際に操作から返された結果ではなく、変更された結果を使用します。If an interceptor sets the Result property after the command has executed (in one of the …Executed methods) then EF will use the changed result instead of the result that was actually returned from the operation. 同様に、コマンドの実行後にインターセプターが Exception プロパティを設定した場合、EF は、操作によって例外がスローされたかのように set 例外をスローします。Similarly, if an interceptor sets the Exception property after the command has executed, then EF will throw the set exception as if the operation had thrown the exception.

インターセプターでは、例外をスローする必要がないことを示すために、Exception プロパティを null に設定することもできます。An interceptor can also set the Exception property to null to indicate that no exception should be thrown. これは、操作の実行に失敗しても、操作が成功したかのように、インターセプターが EF を使用する場合に便利です。This can be useful if execution of the operation failed but the interceptor wishes EF to continue as if the operation had succeeded. これには、通常、EF が処理を続行するための結果値があるように結果を設定する必要もあります。This usually also involves setting the Result so that EF has some result value to work with as it continues.

OriginalResult と OriginalresultOriginalResult and OriginalException

EF によって操作が実行されると、実行が失敗した場合は Result プロパティと OriginalResult プロパティが設定され、例外が発生して実行が失敗した場合は例外および Originalresult プロパティが設定されます。After EF has executed an operation it will set either the Result and OriginalResult properties if execution did not fail or the Exception and OriginalException properties if execution failed with an exception.

OriginalResult プロパティと Originalresult プロパティは読み取り専用であり、実際に操作を実行した後に EF によってのみ設定されます。The OriginalResult and OriginalException properties are read-only and are only set by EF after actually executing an operation. これらのプロパティは、インターセプターでは設定できません。These properties cannot be set by interceptors. つまり、どのインターセプターも、他のインターセプターによって設定された例外と結果を、実際の例外や、操作の実行時に発生した結果と区別することはできません。This means that any interceptor can distinguish between an exception or result that has been set by some other interceptor as opposed to the real exception or result that occurred when the operation was executed.

インターセプターの登録Registering interceptors

1つ以上のインターセプトインターフェイスを実装するクラスが作成されたら、DbInterception クラスを使用して EF に登録できます。Once a class that implements one or more of the interception interfaces has been created it can be registered with EF using the DbInterception class. 次に例を示します。For example:

DbInterception.Add(new NLogCommandInterceptor());

インターセプターは、DbConfiguration コードベースの構成メカニズムを使用して、アプリドメインレベルで登録することもできます。Interceptors can also be registered at the app-domain level using the DbConfiguration code-based configuration mechanism.

例: NLog へのログ記録Example: Logging to NLog

ここでは、IDbCommandInterceptor と Nlog を使用して、次のことを行う例について説明します。Let’s put all this together into an example that using IDbCommandInterceptor and NLog to:

  • 非同期的に実行されていないコマンドに対して警告をログに記録します。Log a warning for any command that is executed non-asynchronously
  • 実行時にスローされるすべてのコマンドに対してエラーをログに記録します。Log an error for any command that throws when executed

ログ記録を実行するクラスを次に示します。これは、上記のように登録する必要があります。Here’s the class that does the logging, which should be registered as shown above:

public class NLogCommandInterceptor : IDbCommandInterceptor
{
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    public void NonQueryExecuting(
        DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }

    public void NonQueryExecuted(
        DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }

    public void ReaderExecuting(
        DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }

    public void ReaderExecuted(
        DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }

    public void ScalarExecuting(
        DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }

    public void ScalarExecuted(
        DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }

    private void LogIfNonAsync<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        if (!interceptionContext.IsAsync)
        {
            Logger.Warn("Non-async command used: {0}", command.CommandText);
        }
    }

    private void LogIfError<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        if (interceptionContext.Exception != null)
        {
            Logger.Error("Command {0} failed with exception {1}",
                command.CommandText, interceptionContext.Exception);
        }
    }
}

このコードでは、コマンドが非同期に実行されていないことを検出し、コマンドの実行中にエラーが発生したことを検出するために、インターセプトコンテキストを使用する方法に注意してください。Notice how this code uses the interception context to discover when a command is being executed non-asynchronously and to discover when there was an error executing a command.