3 votes

Comment vérifier si IDataReader est fermé en utilisant l'API du compilateur .net ?

J'essaie d'écrire un analyseur de code qui vérifiera s'il y a des IDataReaders qui ne sont pas fermés.

Je suis passé par cette question mais il n'explique pas comment faire. J'ai également essayé de lire la documentation dans la base de données de l'UE. github lien La langue anglaise utilisée ici est trop compliquée et je n'ai pas compris comment je vais pouvoir trouver toutes les instances du type IDataReader et vérifier que la méthode close() est appelée sur elle avant qu'une variable dudit type ne sorte de la portée.

J'ai essayé de créer un projet de type Analyzer avec code fix dans visual studio, j'ai essayé d'enregistrer le contexte d'opération dans la méthode Initialize de ma classe (qui est étendue du type DiagnosticAnalyzer comme suit :

 [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class DataReaderAnalyzerAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "DataReaderAnalyzer";

    private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources));
    private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
    private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources));
    private const string Category = "DBConnectionCheck";

    private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

    public override void Initialize(AnalysisContext context)
    {

        context.RegisterOperationAction((operationContext) => 
        {
            ((Microsoft.CodeAnalysis.CSharp.Syntax.AssignmentExpressionSyntax)((Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax)operationContext.Operation.Syntax).Expression).Left
        }
           , OperationKind.ExpressionStatement);
    }
}

Je veux trouver toutes les références de l'occurrence de la variable qui contient le type IDataReader, m'assurer que la méthode close est appelée dans cette variable avant qu'elle ne soit perdue hors de la portée.

Voici un exemple de mon code que j'aimerais analyser.

 class Program
{
    static void Main(string[] args)
    {
        IDataReader reader = null;
        try
        {

            Database db = DatabaseFactory.CreateDatabase("ApplicationConnection");

            reader = GetDataReader(db);
            while (reader.Read())
            {
                   //Do somethig with the data here
            }
            reader.Close();
        }
        catch (Exception)
        {

            throw;
        }
        finally
        {
            if (reader != null && !reader.IsClosed)
            {
                reader.Close();
            }
        }
    }
public static IDataReader GetDataReader(Database db)
    {
        DbCommand dbcmd = db.GetSqlStringCommand("some select statement to get data from oracle data base");
        var reader = db.ExecuteReader(dbcmd);
        return reader;
    }
}

4voto

Marc Gravell Points 482669

En fin de compte, le code présenté n'est pas génial et, selon moi, ce serait une mauvaise solution d'écrire un analyseur pour le faire respecter.

Il existe un moyen très simple de réaliser ce type d'opération, qui consiste principalement à oublier les éléments suivants Close et en utilisant le fait que c'est IDisposable - qui est l'API prévue pour ce type de scénario. Ensuite, cela devient beaucoup, beaucoup plus simple - tellement plus simple que a : vous n'avez pas besoin d'un analyseur spécial pour cela, et b : les analyseurs existants qui travaillent contre les IDisposable fera probablement le travail pour vous.

using var reader = GetDataReader(db);
while (reader.Read())
{
    //Do somethig with the data here
}

avec pas de try / catch / finally etc ; le compilateur ajoutera tout ce dont vous avez besoin pour que cela fonctionne correctement, simplement via l'option using . Notez qu'avec les anciens compilateurs, cela doit être :

using (var reader = GetDataReader(db))
{
    while (reader.Read())
    {
        //Do somethig with the data here
    }
}

En passant, je suggère fortement pas lutter contre l'API ADO.NET - ce n'est pas une façon utile de passer son temps ; des outils comme Dapper le font déjà. les choses les plus courantes pour vous Vous n'avez donc pas besoin d'écrire ce code, et il connaît tous les cas particuliers à éviter.

Une utilisation typique de Dapper pourrait être :

string region = ...
var users = connection.Query<User>(
    "some * from Users where Region = @region",
    new { region } // parameters
).AsList();

la bibliothèque s'occupant de tous les détails de ADO.NET en interne.

0voto

JohanP Points 2218

Le code ci-dessous est une approche non militarisée que vous pouvez suivre.

analysisContext.RegisterCompilationStartAction(compilationContext =>
{
    var variables = new HashSet<string>();
    var tree = compilationContext.Compilation.SyntaxTrees.First();

    //iterate over all childnodes starting from root
    foreach (var node in tree.GetRoot().ChildNodes())
    {
        var flat = Flatten(node).ToList();
        //find all variable declarations
        var varDecls = flat.OfType<VariableDeclarationSyntax>();
        foreach (var decl in varDecls)
        {
            if (!(decl.Type is IdentifierNameSyntax id)) continue;
            if (!id.Identifier.Text.Equals("IDataReader")) continue;
            //if you are declaring an IDataReader, go store the var name in set
            foreach (var reader in decl.Variables)
            {
                variables.Add(reader.Identifier.Text);
            }
        }
        //find all method calls i.e. reader.Read() etc
        var invokes = flat.OfType<InvocationExpressionSyntax>();
        foreach (var invoke in invokes)
        {
            var memberAccess = invoke.Expression as MemberAccessExpressionSyntax;
            var ident = memberAccess.Expression as IdentifierNameSyntax;
            if(!variables.Contains(ident.Identifier.Text)) continue;
            var name = memberAccess.Name as IdentifierNameSyntax;
            //if we find any Close() method on reader, remove from var set
            if (name.Identifier.Text.Equals("Close"))
            {
                variables.Remove(ident.Identifier.Text);
            }
        }
    }
    // if we have any variables left in set it means Close() was never called
    if (variables.Count != 0)
    {
        //this is where you can report
        //var diagnostic = Diagnostic.Create(Rule, location, value);

        //context.ReportDiagnostic(diagnostic);
    }

});

public static IEnumerable<SyntaxNode> Flatten(SyntaxNode node)
{
    yield return node;
    var childNodes = node.ChildNodes();
    foreach (var child in childNodes)
        foreach (var descendant in Flatten(child))
            yield return descendant;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X