static bool GetRootEntityTypeFromLinqQuery(ExpressionSyntax expr, SyntaxNodeAnalysisContext context, EFUsageContext efContext, out EFCodeFirstClassInfo clsInfo) { clsInfo = null; // from $var in ??? var memberExpr = expr as MemberAccessExpressionSyntax; var ident = expr as IdentifierNameSyntax; if (memberExpr != null) //??? = $ident.$prop { return(LinqExpressionValidator.MemberAccessIsAccessingDbContext(memberExpr, context, efContext, out clsInfo)); } else if (ident != null) { var si = context.SemanticModel.GetSymbolInfo(ident); var local = si.Symbol as ILocalSymbol; if (local != null) { var nts = local.Type as INamedTypeSymbol; if (nts != null && nts.TypeArguments.Length == 1) { var typeArg = nts.TypeArguments[0]; clsInfo = efContext.GetClassInfo(typeArg); if (clsInfo != null) { return(LinqExpressionValidator.SymbolCanBeTracedBackToDbContext(local, context, efContext, clsInfo)); } } } } return(false); }
public static Dictionary <string, ContextualLinqParameter> BuildContext(QueryExpressionSyntax query, SyntaxNodeAnalysisContext context, EFUsageContext efContext) { var cparams = new Dictionary <string, ContextualLinqParameter>(); //From x var fromExpr = query.FromClause.Identifier; //in <expr> var inExpr = query.FromClause.Expression; string name = fromExpr.ValueText; var memberExpr = inExpr as MemberAccessExpressionSyntax; if (memberExpr != null) { EFCodeFirstClassInfo cls; if (LinqExpressionValidator.MemberAccessIsAccessingDbContext(memberExpr, context, efContext, out cls)) { cparams[name] = new ContextualLinqParameter(name, cls); } } //Still not set, just set as a contextual parameter with no known type if (!cparams.ContainsKey(name)) { cparams[name] = new ContextualLinqParameter(name); } return(cparams); }
private static void AnalyzeQueryExpression(SyntaxNodeAnalysisContext context, EFUsageContext efContext, QueryExpressionSyntax query) { //I can't believe how much easier this is compared to Extension Method syntax! Then again //query syntax does mean its own dedicated set of C# keywords, which would means its own //dedicated set of syntax node types //First item on checklist, find out our root queryable EFCodeFirstClassInfo cls; bool isConnectedToDbContext = GetRootEntityTypeFromLinqQuery(query.FromClause.Expression, context, efContext, out cls); if (cls != null) { bool treatAsWarning = !isConnectedToDbContext; var paramNodes = ContextualLinqParameter.BuildContext(query, context, efContext); var descendants = query.Body.DescendantNodes(); LinqExpressionValidator.ValidateLinqToEntitiesUsageInSyntaxNodes(descendants, cls, context, efContext, paramNodes, treatAsWarning); } }
private static void AnalyzeLambdaInLinqExpression(SyntaxNodeAnalysisContext context, EFUsageContext efContext, LambdaExpressionSyntax lambda) { var lambdaAssign = lambda.Parent as EqualsValueClauseSyntax; var arg = lambda.Parent as ArgumentSyntax; if (arg != null) //The lambda in question is being passed as an argument { var parent = arg.Parent; while (parent != null && !(parent is ArgumentListSyntax)) { parent = parent.Parent; } if (parent != null) //Which should be part of an ArgumentList { var argList = parent; var invoc = argList?.Parent as InvocationExpressionSyntax; if (invoc != null) //Which should be part of an invocation { var memberExpr = invoc.Expression as MemberAccessExpressionSyntax; if (memberExpr != null) { if (CanonicalMethodNames.IsLinqOperator(memberExpr?.Name?.Identifier.ValueText)) { var si = context.SemanticModel.GetSymbolInfo(memberExpr.Expression); var lts = si.Symbol as ILocalSymbol; var pts = si.Symbol as IPropertySymbol; //Is this method called on a property? if (pts != null) { var nts = pts.Type as INamedTypeSymbol; if (nts != null) { //Like a DbSet<T>? if (nts.IsDbSet()) { //That is part of a class derived from DbContext? if (pts?.ContainingType?.BaseType?.Name == EFSpecialIdentifiers.DbContext) { var typeArg = nts.TypeArguments[0]; //Let's give our method some assistance, by checking what T actually is var clsInfo = efContext.GetClassInfo(typeArg); if (clsInfo != null) { //Okay now let's see if this lambda is valid in the EF context LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext); } } } } } else if (lts != null) //The linq method was called on a local variable { var nts = lts.Type as INamedTypeSymbol; if (nts != null && nts.TypeArguments.Length == 1) { //This is some generic type with one type argument var typeArg = nts.TypeArguments[0]; var clsInfo = efContext.GetClassInfo(typeArg); if (clsInfo != null) { if (nts.IsDbSet()) { //TODO: Should still actually check that it is ultimately assigned //from a DbSet<T> property of a DbContext derived class LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext); } else if (nts.IsQueryable()) { bool treatAsWarning = !LinqExpressionValidator.SymbolCanBeTracedBackToDbContext(lts, context, efContext, clsInfo); LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext, treatAsWarning); } } } } } } } } } else if (lambdaAssign != null) //The lambda in question is being assigned { var localLambdaDecl = lambdaAssign?.Parent?.Parent?.Parent as LocalDeclarationStatementSyntax; if (localLambdaDecl != null) { var declType = localLambdaDecl?.Declaration?.Type as GenericNameSyntax; if (declType != null) { //Is Expression<T> if (declType.Identifier.ValueText == EFSpecialIdentifiers.Expression && declType.TypeArgumentList.Arguments.Count == 1) { //The T is Func<TInput, TOutput> var exprTypeArg = declType.TypeArgumentList.Arguments[0] as GenericNameSyntax; if (exprTypeArg != null && exprTypeArg.Identifier.ValueText == EFSpecialIdentifiers.Func && exprTypeArg.TypeArgumentList.Arguments.Count == 2) { var inputType = exprTypeArg.TypeArgumentList.Arguments[0] as IdentifierNameSyntax; var outputType = exprTypeArg.TypeArgumentList.Arguments[1] as PredefinedTypeSyntax; //The TOutput in Func<TInput, TOutput> is bool if (inputType != null && outputType != null && outputType.Keyword.ValueText == EFSpecialIdentifiers.BooleanShort) { var si = context.SemanticModel.GetSymbolInfo(inputType); var ts = efContext.EntityTypes.FirstOrDefault(t => t == si.Symbol); if (ts != null) { var clsInfo = efContext.GetClassInfo(ts); if (clsInfo != null) { LinqExpressionValidator.ValidateLinqToEntitiesExpression(lambda, clsInfo, context, efContext); } } } } } } } } }