トレースdapper.NETのソースコード- SQL文字列のコントローラーを間違ってスローの効率とメモリリークを引き起こす


GithubのリンクTrace-Dapper.NET-Source-Code

11 . SQL文字列が間違っていると、効率が悪くなりメモリリークが発生します


ここでは、dapperによって使用される重要な概念です.ITSSQL string キャッシュする重要なキー値の一つです.別のSQL文字列が使用されている場合、Dapperはこれに対して新しいダイナミックメソッドとキャッシュを作成しますcan also cause slow query & memory leaks .

なぜ、SQL文字列がキーの1つとして使われているのか、単純にマッピング型のハンドルを使うのではなくorder of query column . 前述のように、dapperは「result convert to code」 動的メソッドを作成する方法fixed , 異なるSQL SELECT列の順序で動的メソッドの同じセットを使用しないでくださいA column value to b column 間違った値の問題.
最も直接的な解決方法は、異なるSQL文字列ごとに異なる動的メソッドを確立し、異なるキャッシュに保存することです.
たとえば、次のコードは単純なクエリ動作ですが、Dapperキャッシュの数は画像表示など999999に達しました
using (var cn = new SqlConnection(@"connectionString"))
{
    for ( int  i  = 0; i  <  999999 ; i ++ )
    {
        var guid = Guid.NewGuid();
        for (int i2 = 0; i2 < 2; i2++)
        {
            var result = cn.Query<User>($"select '{guid}' ").First();
        }  
    }
}

この問題を回避するには、原則を維持する必要がありますReuse SQL string , 最も簡単な方法はparametrization , たとえば、上記のコードを次のコードに変更すると、キャッシュの数が1 , 再利用の目的を達成するために
using (var cn = new SqlConnection(@"connectionString"))
{
    for ( int  i  = 0; i  <  999999 ; i ++ )
    {
        var guid = Guid.NewGuid();
        for (int i2 = 0; i2 < 2; i2++)
        {
            var result = cn.Query<User>($"select @guid ",new { guid}).First();
        }  
    }
}

12 . Dapper SQLの正しい文字列表記方法:文字列置換


例えば、SQL文字列をスプライスする必要がある場合は、文字列を使用するのが効率的ですfixed values .
この時、dapperはLiteral Replacements 関数の使い方{=Attribute_Name} 設定する値文字列を置換し、その値をパラメータに保存します.
void Main()
{
  using (var cn = Connection)
  {
    var result = cn.Query("select N'Wei' Name,26 Age,{=VipLevel} VipLevel", new User{ VipLevel = 1}).First();
  }
}

13 .リテラル置換がキャッシュ問題を回避する理由は?


まず、ソースコードgetcacheinfoの下でGetReitalTokensメソッドをトレースすると、キャッシュDapperがデータを取得する前にSQL string その試合{=Attribute_Name} 役割
private static readonly Regex literalTokens = new Regex(@"(?<![\p{L}\p{N}_])\{=([\p{L}\p{N}_]+)\}", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
internal static IList<LiteralToken> GetLiteralTokens(string sql)
{
  if (string.IsNullOrEmpty(sql)) return LiteralToken.None;
  if (!literalTokens.IsMatch(sql)) return LiteralToken.None;

  var matches = literalTokens.Matches(sql);
  var found = new HashSet<string>(StringComparer.Ordinal);
  List<LiteralToken> list = new List<LiteralToken>(matches.Count);
  foreach (Match match in matches)
  {
    string token = match.Value;
    if (found.Add(match.Value))
    {
      list.Add(new LiteralToken(token, match.Groups[1].Value));
    }
  }
  return list.Count == 0 ? LiteralToken.None : list;
}
次に、CreateParaminfoGeneratorメソッドでパラメーターパラメーター化された動的メソッドを生成します.このセクションのメソッドILは以下の通りです.
IL_0000: ldarg.1    
IL_0001: castclass  <>f__AnonymousType1`1[System.Int32]
IL_0006: stloc.0    
IL_0007: ldarg.0    
IL_0008: callvirt   System.Data.IDataParameterCollection get_Parameters()/System.Data.IDbCommand
IL_000d: pop        
IL_000e: ldarg.0    
IL_000f: ldarg.0    
IL_0010: callvirt   System.String get_CommandText()/System.Data.IDbCommand
IL_0015: ldstr      "{=VipLevel}"
IL_001a: ldloc.0    
IL_001b: callvirt   Int32 get_VipLevel()/<>f__AnonymousType1`1[System.Int32]
IL_0020: stloc.1    
IL_0021: ldloca.s   V_1

IL_0023: call       System.Globalization.CultureInfo get_InvariantCulture()/System.Globalization.CultureInfo
IL_0028: call       System.String ToString(System.IFormatProvider)/System.Int32
IL_002d: callvirt   System.String Replace(System.String, System.String)/System.String
IL_0032: callvirt   Void set_CommandText(System.String)/System.Data.IDbCommand
IL_0037: ret        
次に、マッピングの動的メソッドを生成します.この論理を理解するために、ここでシミュレーションの例を示します.
public  static  class  DbExtension
{
  public static IEnumerable<User> Query(this DbConnection cnn, string sql, User parameter)
  {
    using (var command = cnn.CreateCommand())
    {
      command.CommandText = sql;
      CommandLiteralReplace(command, parameter);
      using (var reader = command.ExecuteReader())
        while (reader.Read())
          yield return Mapping(reader);
    }
  }

  private static void CommandLiteralReplace(IDbCommand cmd, User parameter)
  {
    cmd.CommandText = cmd.CommandText.Replace("{=VipLevel}", parameter.VipLevel.ToString(System.Globalization.CultureInfo.InvariantCulture));
  }

  private  static  User  Mapping ( IDataReader  reader )
  {
    var user = new User();
    var value = default(object);
    value = reader[0];
    if(!(value is System.DBNull))
      user.Name = (string)value;
    value = reader[1];
    if (!(value is System.DBNull))
      user.Age = (int)value;
    value = reader[2];
    if (!(value is System.DBNull))
      user.VipLevel = (int)value;
    return user;
  }
}
上記の例を読んだ後に、dapperリテラル置換の基礎となる原理はstring replace それは文字列の方法にも属します.キャッシュ問題はなぜ回避できるか
これは、置換タイミングがsetParameter Dynamicメソッドであるため、キャッシュSQL Key is unchanged 同じSQL文字列とキャッシュを再利用できます.
また、文字列置換方法でもある.only support basic value type 文字列型を使用する場合、システムはあなたに通知しますThe type String is not supported for SQL literals. SQL注入問題を避けるために.