private static void TryFinallyLeak()
{
var connection = new SqlConnection("Server=DBSRV;Database=SampleDB");
try
{
connection.Open();
var command = new SqlCommand("select count (*) from Blogs", connection);
command.ExecuteScalar(); // in case of exception here, connection won't be closed
connection.Close();
}
catch(Exception e)
{
// handle exception
}
}
private static void TryFinallyLeak()
{
var connection = new SqlConnection("Server=DBSRV;Database=SampleDB");
try
{
connection.Open();
var command = new SqlCommand("select count (*) from Blogs", connection);
command.ExecuteScalar();
}
catch(Exception e)
{
// handle exception
}
finally
{
connection.Close(); // connection is closed in any case
}
}
private static void ReaderLeak()
{
var reader = GetReader();
while (reader.Read())
;
reader.Close(); // closing the reader doesn't close the connection
}
private static SqlDataReader GetReader()
{
var connection = new SqlConnection("Server=DBSRV;Database=SampleDB");
connection.Open();
var command = new SqlCommand("select * from Blogs", connection);
// the created reader doesn't close the connection as
// it doesn't use CommandBehavior.CloseConnection
return command.ExecuteReader();
}
private static void ReaderLeak()
{
var reader = GetReader();
while (reader.Read())
;
reader.Close();
}
private static SqlDataReader GetReader()
{
var connection = new SqlConnection("Server=DBSRV;Database=SampleDB");
connection.Open();
var command = new SqlCommand("select * from Blogs", connection);
// add behavior that closes connection
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
DB コマンド
コマンド実行回数がしきい値を超えると、DPA は同じコマンドを複数回実行するコードを DB コマンド 問題としてマークします。 デフォルトのしきい値は 50 コマンドです。
このチェックが存在する主な理由は、よく知られている N+1 問題を防ぐためです。 例: ブログのテーブルがあり、各 Blog には多数の投稿があります。 Blog から Post は 1 対多の関係です。 すべてのブログのすべての投稿のリストを取得するとします。 エンティティフレームワークでこれを行う簡単な方法は次のとおりです。
private void nPlus1(int count)
{
using var dbContext = new BlogContext();
var blogs = dbContext.Blogs.ToList(); // get list of blogs (1 query)
// for each blog get all posts (N queries)
foreach (var blog in blogs)
{
Console.WriteLine($"Posts in {blog}:");
foreach (var post in blog.Posts)
Console.WriteLine($"{post}");
}
}
public class BlogContext: DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
// ...
}
private void nPlus1(int count)
{
using var dbContext = new BlogContext();
var blogs = dbContext.Blogs
.Include(b => b.Posts) // get all posts to memory (1 query)
.ToList();
// the code below works locally (0 queries)
foreach (var blog in blogs)
{
Console.WriteLine($"Posts in {blog}:");
foreach (var post in blog.Posts)
Console.WriteLine($"{post}");
}
}
DB レコード
データベースコマンドがしきい値を超えるレコード数を返す場合、DPA はコマンドを実行するコードに DB 接続 の問題としてマークします。 場合によっては、多数のレコードを取得することが設計上暗黙的に行われます。 ただし、最適でないコードパターンが原因で、これが偶然に発生することもあります。