2016-04-29 42 views
3

如果我有一個Dapper.NET查詢,像這樣:的Visual Studio編譯時SQL驗證

conn.Execute("insert into My_Table values ('blah', 'blah, 'blah', 'blah')"); 

我怎麼能強迫視覺工作室做編譯時該查詢驗證針對某一數據庫模式?我知道有庫可以做查詢驗證(提供了一個字符串和連接),但是這裏的工作是什麼正確的工具?

擴展Roslyn檢查字符串我標記爲查詢字符串(語法類似於熟悉的@「非轉義字符串」)?自定義預處理?

或者我問錯了問題?將數據庫項目中的存儲過程中的所有查詢邏輯封裝起來會更安全(這使我能夠驗證查詢本身)?現在我寫下來,我想我會用這個解決方案,但我仍然對上述問題感到好奇。我希望能夠寫出:

conn.Execute(#"insert into My_Table values ('blah', 
'blah, 'blah', 'blah')"); //Hashtag to mark as query 

並讓編譯器根據給定的數據庫模式驗證字符串。

+1

爲什麼不使用單元測試庫創建一些庫的測試?這將允許您傳遞受控數據並測試結果。 –

+0

我同意單元測試會讓我的問題沒有意義,但我仍然認爲從編譯時檢查中可以得到一些東西(尤其是因爲它會是完全自動的,並且永遠不會從我的代碼中移除一個主要的錯誤來源,參與)。不幸的是,我是個獨立開發者。 – user3527893

+0

最好的方法是避免首先將臨時查詢發送到服務器。在SQL存儲過程中執行所有操作(正如您在最後所建議的那樣)是處理此問題的最佳方法。您不僅可以驗證您的SQL,還可以更好地保護您的SQL服務器免受攻擊。 – DunningKrugerEffect

回答

0

一個選擇是編寫一個Roslyn分析器。分析器會做的是找到所有對函數的調用,如Execute(),如果它們的參數是一個常量字符串,則使用您提到的庫進行驗證。

實施分析器時,您遇到的一個問題是如何指定要驗證的數據庫模式。除非你想以某種方式將它硬編碼到你的分析儀中,否則似乎要這樣做的方法是使用"additional files"(其中currently requires hand-editing the csproj of any project where you want to use the analyzer)。

分析儀看起來是這樣的(請注意,你可能會需要修改代碼,使其更加堅固):

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics 
    => ImmutableArray.Create(BadSqlRule, MissingOptionsFileRule); 

public override void Initialize(AnalysisContext context) 
{ 
    context.RegisterCompilationStartAction(AnalyzeCompilationStart); 
    context.RegisterCompilationAction(AnalyzeCompilation); 
    context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); 
} 

private Config config = null; 

private void AnalyzeCompilationStart(CompilationStartAnalysisContext context) 
{ 
    var configFile = context.Options.AdditionalFiles 
     .SingleOrDefault(f => Path.GetFileName(f.Path) == "myconfig.json"); 

    config = configFile == null 
     ? null 
     : new Config(configFile.GetText(context.CancellationToken).ToString()); 
} 

private void AnalyzeCompilation(CompilationAnalysisContext context) 
{ 
    if (config == null) 
     context.ReportDiagnostic(Diagnostic.Create(MissingOptionsFileRule, Location.None)); 
} 

private void AnalyzeNode(SyntaxNodeAnalysisContext context) 
{ 
    if (config == null) 
     return; 

    var node = (InvocationExpressionSyntax) context.Node; 

    var symbol = (IMethodSymbol) context.SemanticModel.GetSymbolInfo(
     node, context.CancellationToken).Symbol; 

    // TODO: properly check it's one of the methods we analyze 
    if (symbol.ToDisplayString().Contains(".Execute(string")) 
    { 
     var arguments = node.ArgumentList.Arguments; 
     if (arguments.Count == 0) 
      return; 

     var firstArgument = arguments.First().Expression; 

     if (!firstArgument.IsKind(SyntaxKind.StringLiteralExpression)) 
      return; 

     var sqlString = (string)((LiteralExpressionSyntax) firstArgument).Token.Value; 

     if (Verify(config, sqlString)) 
      return; 

     context.ReportDiagnostic(
      Diagnostic.Create(BadSqlRule, firstArgument.GetLocation(), sqlString)); 
    } 
}