2016-04-29 108 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)); 
    } 
}