TransformInfo TransformExpression(IBuildContext context, Expression expr, bool enforceServerSide)
        {
            if (_skippedExpressions.Contains(expr))
            {
                return(new TransformInfo(expr, true));
            }

            if (expr.Find(IsNoneSqlMember) != null)
            {
                return(new TransformInfo(expr));
            }

            switch (expr.NodeType)
            {
            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
            {
                if (expr.Type == typeof(object))
                {
                    break;
                }

                var cex = (UnaryExpression)expr;

                _convertedExpressions.Add(cex.Operand, cex);

                var nex = BuildExpression(context, cex.Operand, enforceServerSide);

                if (nex.Type != cex.Type)
                {
                    nex = cex.Update(nex);
                }

                var ret = new TransformInfo(nex, true);

                _convertedExpressions.Remove(cex.Operand);

                return(ret);
            }

            case ExpressionType.MemberAccess:
            {
                if (IsServerSideOnly(expr) || PreferServerSide(expr, enforceServerSide))
                {
                    return(new TransformInfo(BuildSql(context, expr)));
                }

                var ma = (MemberExpression)expr;

                var l = Expressions.ConvertMember(MappingSchema, ma.Expression == null ? null : ma.Expression.Type, ma.Member);
                if (l != null)
                {
                    // In Grouping KeyContext we have to perform calculation on server side
                    if (Contexts.Any(c => c is GroupByBuilder.KeyContext))
                    {
                        return(new TransformInfo(BuildSql(context, expr)));
                    }
                    break;
                }

                if (ma.Member.IsNullableValueMember())
                {
                    break;
                }

                if (ma.Member.IsNullableHasValueMember())
                {
                    Expression e = Expression.NotEqual(
                        ma.Expression, Expression.Constant(null, ma.Expression.Type));

                    return(new TransformInfo(
                               BuildExpression(
                                   context,
                                   ma.Expression.Type.IsPrimitiveEx() ?
                                   Expression.Call(
                                       MemberHelper.MethodOf(() => Sql.AsSql(true)),
                                       e) :
                                   e, enforceServerSide),
                               true));
                }

                var ctx = GetContext(context, ma);

                if (ctx != null)
                {
                    if (ma.Type.IsGenericTypeEx() && typeof(IEnumerable <>).IsSameOrParentOf(ma.Type))
                    {
                        var res = ctx.IsExpression(ma, 0, RequestFor.Association);

                        if (res.Result)
                        {
                            var table = (TableBuilder.AssociatedTableContext)res.Context;
                            if (table.IsList)
                            {
                                var mexpr = GetMultipleQueryExpression(context, MappingSchema, ma, new HashSet <ParameterExpression>());
                                return(new TransformInfo(BuildExpression(context, mexpr, enforceServerSide)));
                            }
                        }
                    }

                    return(new TransformInfo(ctx.BuildExpression(ma, 0, enforceServerSide)));
                }

                var ex = ma.Expression;

                while (ex is MemberExpression)
                {
                    ex = ((MemberExpression)ex).Expression;
                }

                if (ex is MethodCallExpression)
                {
                    var ce = (MethodCallExpression)ex;

                    if (IsSubQuery(context, ce))
                    {
                        if (!IsMultipleQuery(ce))
                        {
                            var info = GetSubQueryContext(context, ce);
                            var par  = Expression.Parameter(ex.Type);
                            var bex  = info.Context.BuildExpression(ma.Transform(e => e == ex ? par : e), 0, enforceServerSide);

                            if (bex != null)
                            {
                                return(new TransformInfo(bex));
                            }
                        }
                    }
                }

                ex = ma.Expression;

                if (ex != null && ex.NodeType == ExpressionType.Constant)
                {
                    // field = localVariable
                    //
                    var c = _expressionAccessors[ex];
                    return(new TransformInfo(Expression.MakeMemberAccess(Expression.Convert(c, ex.Type), ma.Member)));
                }

                break;
            }

            case ExpressionType.Parameter:
            {
                if (expr == ParametersParam)
                {
                    break;
                }

                var ctx = GetContext(context, expr);

                if (ctx != null)
                {
                    return(new TransformInfo(ctx.BuildExpression(expr, 0, enforceServerSide)));
                }

                break;
            }

            case ExpressionType.Constant:
            {
                if (expr.Type.IsConstantable())
                {
                    break;
                }

                if (_expressionAccessors.ContainsKey(expr))
                {
                    return(new TransformInfo(Expression.Convert(_expressionAccessors[expr], expr.Type)));
                }

                break;
            }

            case ExpressionType.Coalesce:

                if (expr.Type == typeof(string) && MappingSchema.GetDefaultValue(typeof(string)) != null)
                {
                    return(new TransformInfo(BuildSql(context, expr)));
                }

                if (CanBeTranslatedToSql(context, ConvertExpression(expr), true))
                {
                    return(new TransformInfo(BuildSql(context, expr)));
                }

                break;

            case ExpressionType.Conditional:

                if (CanBeTranslatedToSql(context, ConvertExpression(expr), true))
                {
                    return(new TransformInfo(BuildSql(context, expr)));
                }
                break;

            case ExpressionType.Call:
            {
                var ce = (MethodCallExpression)expr;

                if (IsGroupJoinSource(context, ce))
                {
                    foreach (var arg in ce.Arguments.Skip(1))
                    {
                        if (!_skippedExpressions.Contains(arg))
                        {
                            _skippedExpressions.Add(arg);
                        }
                    }

                    if (IsSubQuery(context, ce))
                    {
                        if (ce.IsQueryable())
                        //if (!typeof(IEnumerable).IsSameOrParentOf(expr.Type) || expr.Type == typeof(string) || expr.Type.IsArray)
                        {
                            var ctx = GetContext(context, expr);

                            if (ctx != null)
                            {
                                return(new TransformInfo(ctx.BuildExpression(expr, 0, enforceServerSide)));
                            }
                        }
                    }

                    break;
                }

                if (ce.IsAssociation(MappingSchema))
                {
                    var ctx = GetContext(context, ce);
                    if (ctx == null)
                    {
                        throw new InvalidOperationException();
                    }

                    return(new TransformInfo(ctx.BuildExpression(ce, 0, enforceServerSide)));
                }

                if ((_buildMultipleQueryExpressions == null || !_buildMultipleQueryExpressions.Contains(ce)) && IsSubQuery(context, ce))
                {
                    if (IsMultipleQuery(ce))
                    {
                        return(new TransformInfo(BuildMultipleQuery(context, ce, enforceServerSide)));
                    }

                    return(new TransformInfo(GetSubQueryExpression(context, ce, enforceServerSide)));
                }

                if (IsServerSideOnly(expr) || PreferServerSide(expr, enforceServerSide) || ce.Method.IsSqlPropertyMethodEx())
                {
                    return(new TransformInfo(BuildSql(context, expr)));
                }
            }

            break;
            }

            if (EnforceServerSide(context))
            {
                switch (expr.NodeType)
                {
                case ExpressionType.MemberInit:
                case ExpressionType.New:
                case ExpressionType.Convert:
                    break;

                default:
                    if (CanBeCompiled(expr))
                    {
                        break;
                    }
                    return(new TransformInfo(BuildSql(context, expr)));
                }
            }

            return(new TransformInfo(expr));
        }
Пример #2
0
        public void RefreshContextsFromCache()
        {
            // Authentication factory is already registered in `OnImport()`
            AzureSession.Instance.TryGetComponent(
                PowerShellTokenCacheProvider.PowerShellTokenCacheProviderKey,
                out PowerShellTokenCacheProvider tokenCacheProvider);

            string authority = null;

            if (TryGetEnvironment(AzureSession.Instance.GetProperty(AzureSession.Property.Environment), out IAzureEnvironment sessionEnvironment))
            {
                authority = $"{sessionEnvironment.ActiveDirectoryAuthority}organizations";
            }
            var accounts = tokenCacheProvider.ListAccounts(authority);

            if (!accounts.Any())
            {
                if (!Contexts.Any(c => c.Key != "Default" && c.Value.Account.Type == AzureAccount.AccountType.User))
                {
                    // If there are no accounts in the cache, but we never had any existing contexts, return
                    return;
                }

                WriteWarningMessage($"No accounts found in the shared token cache; removing all user contexts.");
                var removedContext = false;
                foreach (var contextName in Contexts.Keys)
                {
                    var context = Contexts[contextName];
                    if (context.Account.Type != AzureAccount.AccountType.User)
                    {
                        continue;
                    }

                    removedContext |= TryCacheRemoveContext(contextName);
                }

                // If no contexts were removed, return now to avoid writing to file later
                if (!removedContext)
                {
                    return;
                }
            }
            else
            {
                var removedUsers   = new HashSet <string>();
                var updatedContext = false;
                foreach (var contextName in Contexts.Keys)
                {
                    var context = Contexts[contextName];
                    if ((string.Equals(contextName, "Default") && context.Account == null) || context.Account.Type != AzureAccount.AccountType.User)
                    {
                        continue;
                    }

                    if (accounts.Any(a => string.Equals(a.Username, context.Account.Id, StringComparison.OrdinalIgnoreCase)))
                    {
                        continue;
                    }

                    if (!removedUsers.Contains(context.Account.Id))
                    {
                        removedUsers.Add(context.Account.Id);
                        WriteWarningMessage(string.Format(Resources.UserMissingFromSharedTokenCache, context.Account.Id));
                    }

                    updatedContext |= TryCacheRemoveContext(contextName);
                }

                // Check to see if each account has at least one context
                foreach (var account in accounts)
                {
                    if (Contexts.Values.Where(v => v.Account != null && v.Account.Type == AzureAccount.AccountType.User)
                        .Any(v => string.Equals(v.Account.Id, account.Username, StringComparison.OrdinalIgnoreCase)))
                    {
                        continue;
                    }

                    WriteWarningMessage(string.Format(Resources.CreatingContextsWarning, account.Username));
                    var environment = sessionEnvironment ?? AzureEnvironment.PublicEnvironments
                                      .Where(env => env.Value.ActiveDirectoryAuthority.Contains(account.Environment))
                                      .Select(env => env.Value)
                                      .FirstOrDefault();
                    var azureAccount = new AzureAccount()
                    {
                        Id   = account.Username,
                        Type = AzureAccount.AccountType.User
                    };

                    List <IAccessToken> tokens = null;
                    try
                    {
                        tokens = tokenCacheProvider.GetTenantTokensForAccount(account, environment, WriteWarningMessage);
                    }
                    catch (Exception e)
                    {
                        //In SSO scenario, if the account from token cache has multiple tenants, e.g. MSA account, MSAL randomly picks up
                        //one tenant to ask for token, MSAL will throw exception if MSA home tenant is chosen. The exception is swallowed here as short term fix.
                        WriteWarningMessage(string.Format(Resources.NoTokenFoundWarning, account.Username));
                        EnqueueDebugMessage(e.ToString());
                        continue;
                    }

                    foreach (var token in tokens)
                    {
                        var azureTenant = new AzureTenant()
                        {
                            Id = token.TenantId
                        };
                        azureAccount.SetOrAppendProperty(AzureAccount.Property.Tenants, token.TenantId);
                        var subscriptions = tokenCacheProvider.GetSubscriptionsFromTenantToken(account, environment, token, WriteWarningMessage);
                        if (!subscriptions.Any())
                        {
                            subscriptions.Add(null);
                        }

                        foreach (var subscription in subscriptions)
                        {
                            var context = new AzureContext(subscription, azureAccount, environment, azureTenant);
                            if (!TryGetContextName(context, out string name))
                            {
                                WriteWarningMessage(string.Format(Resources.NoContextNameForSubscription, subscription.Id));
                                continue;
                            }

                            if (!TrySetContext(name, context))
                            {
                                WriteWarningMessage(string.Format(Resources.UnableToCreateContextForSubscription, subscription.Id));
                            }
                            else
                            {
                                updatedContext = true;
                            }
                        }
                    }
                }

                // If the context list was not updated, return now to avoid writing to file later
                if (!updatedContext)
                {
                    return;
                }
            }

            Save(ProfilePath, false);
        }
Пример #3
0
        TransformInfo TransformExpression(IBuildContext context, Expression expr, bool enforceServerSide, string?alias)
        {
            if (_skippedExpressions.Contains(expr))
            {
                return(new TransformInfo(expr, true));
            }

            alias ??= _optimizationContext.GetExpressionAlias(expr);

            switch (expr.NodeType)
            {
            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
            {
                if (expr.Type == typeof(object))
                {
                    break;
                }

                var cex = (UnaryExpression)expr;

                _convertedExpressions.Add(cex.Operand, cex);

                var newOperand = BuildExpression(context, cex.Operand, enforceServerSide);

                if (newOperand.Type != cex.Type)
                {
                    if (cex.Type.IsNullable() && newOperand is ConvertFromDataReaderExpression readerExpression)
                    {
                        newOperand = readerExpression.MakeNullable();
                    }

                    newOperand = cex.Update(newOperand);
                }
                var ret = new TransformInfo(newOperand, true);

                RemoveConvertedExpression(cex.Operand);

                return(ret);
            }

            case ExpressionType.MemberAccess:
            {
                var ma = (MemberExpression)expr;

                if (IsServerSideOnly(ma) || PreferServerSide(ma, enforceServerSide) && !HasNoneSqlMember(ma))
                {
                    return(new TransformInfo(BuildSql(context, expr, alias)));
                }

                var l = Expressions.ConvertMember(MappingSchema, ma.Expression?.Type, ma.Member);
                if (l != null)
                {
                    // In Grouping KeyContext we have to perform calculation on server side
                    if (Contexts.Any(c => c is GroupByBuilder.KeyContext))
                    {
                        return(new TransformInfo(BuildSql(context, expr, alias)));
                    }
                    break;
                }

                if (ma.Member.IsNullableValueMember())
                {
                    break;
                }

                var ctx = GetContext(context, ma);

                if (ctx != null)
                {
                    var prevCount  = ctx.SelectQuery.Select.Columns.Count;
                    var expression = ctx.BuildExpression(ma, 0, enforceServerSide);

                    if (expression.NodeType == ExpressionType.Extension && expression is DefaultValueExpression &&
                        ma.Expression?.NodeType == ExpressionType.Parameter)
                    {
                        var objExpression = BuildExpression(ctx, ma.Expression, enforceServerSide, alias);
                        var varTempVar    = objExpression.NodeType == ExpressionType.Parameter
                                                                        ? objExpression
                                                                        : BuildVariable(objExpression, ((ParameterExpression)ma.Expression).Name);

                        var condition = Expression.Condition(
                            Expression.Equal(varTempVar,
                                             new DefaultValueExpression(MappingSchema, ma.Expression.Type)), expression,
                            Expression.MakeMemberAccess(varTempVar, ma.Member));
                        expression = condition;
                    }
                    else if (!alias.IsNullOrEmpty() && (ctx.SelectQuery.Select.Columns.Count - prevCount) == 1)
                    {
                        ctx.SelectQuery.Select.Columns[ctx.SelectQuery.Select.Columns.Count - 1].Alias = alias;
                    }
                    return(new TransformInfo(expression));
                }

                var ex = ma.Expression;

                while (ex is MemberExpression memberExpression)
                {
                    ex = memberExpression.Expression;
                }

                if (ex is MethodCallExpression ce)
                {
                    if (IsSubQuery(context, ce))
                    {
                        if (!IsMultipleQuery(ce, context.Builder.MappingSchema))
                        {
                            var info = GetSubQueryContext(context, ce);
                            if (alias != null)
                            {
                                info.Context.SetAlias(alias);
                            }
                            var par = Expression.Parameter(ex.Type);
                            var bex = info.Context.BuildExpression(ma.Transform(e => e == ex ? par : e), 0, enforceServerSide);

                            if (bex != null)
                            {
                                return(new TransformInfo(bex));
                            }
                        }
                    }
                }

                ex = ma.Expression;

                if (ex != null && ex.NodeType == ExpressionType.Constant)
                {
                    // field = localVariable
                    //
                    if (!_expressionAccessors.TryGetValue(ex, out var c))
                    {
                        return(new TransformInfo(ma));
                    }
                    return(new TransformInfo(Expression.MakeMemberAccess(Expression.Convert(c, ex.Type), ma.Member)));
                }

                break;
            }

            case ExpressionType.Parameter:
            {
                if (expr == ParametersParam || expr == PreambleParam)
                {
                    break;
                }

                var ctx = GetContext(context, expr);

                if (ctx != null)
                {
                    var buildExpr = ctx.BuildExpression(expr, 0, enforceServerSide);
                    if (buildExpr.Type != expr.Type)
                    {
                        buildExpr = Expression.Convert(buildExpr, expr.Type);
                    }
                    return(new TransformInfo(buildExpr));
                }

                break;
            }

            case ExpressionType.Constant:
            {
                if (expr.Type.IsConstantable(true))
                {
                    break;
                }

                if ((_buildMultipleQueryExpressions == null || !_buildMultipleQueryExpressions.Contains(expr)) && IsSequence(new BuildInfo(context, expr, new SelectQuery())))
                {
                    return(new TransformInfo(BuildMultipleQuery(context, expr, enforceServerSide)));
                }

                if (_expressionAccessors.TryGetValue(expr, out var accessor))
                {
                    return(new TransformInfo(Expression.Convert(accessor, expr.Type)));
                }

                break;
            }

            case ExpressionType.Coalesce:

                if (expr.Type == typeof(string) && MappingSchema.GetDefaultValue(typeof(string)) != null)
                {
                    return(new TransformInfo(BuildSql(context, expr, alias)));
                }

                if (CanBeTranslatedToSql(context, ConvertExpression(expr), true))
                {
                    return(new TransformInfo(BuildSql(context, expr, alias)));
                }

                break;

            case ExpressionType.Call:
            {
                var ce = (MethodCallExpression)expr;

                if (IsGroupJoinSource(context, ce))
                {
                    foreach (var arg in ce.Arguments.Skip(1))
                    {
                        if (!_skippedExpressions.Contains(arg))
                        {
                            _skippedExpressions.Add(arg);
                        }
                    }

                    if (IsSubQuery(context, ce))
                    {
                        if (ce.IsQueryable())
                        //if (!typeof(IEnumerable).IsSameOrParentOf(expr.Type) || expr.Type == typeof(string) || expr.Type.IsArray)
                        {
                            var ctx = GetContext(context, expr);

                            if (ctx != null)
                            {
                                return(new TransformInfo(ctx.BuildExpression(expr, 0, enforceServerSide)));
                            }
                        }
                    }

                    break;
                }

                if (ce.IsAssociation(MappingSchema))
                {
                    var ctx = GetContext(context, ce);
                    if (ctx == null)
                    {
                        throw new InvalidOperationException();
                    }

                    return(new TransformInfo(ctx.BuildExpression(ce, 0, enforceServerSide)));
                }

                if ((_buildMultipleQueryExpressions == null || !_buildMultipleQueryExpressions.Contains(ce)) && IsSubQuery(context, ce))
                {
                    if (IsMultipleQuery(ce, MappingSchema))
                    {
                        return(new TransformInfo(BuildMultipleQuery(context, ce, enforceServerSide)));
                    }

                    return(new TransformInfo(GetSubQueryExpression(context, ce, enforceServerSide, alias)));
                }

                if (IsServerSideOnly(expr) || PreferServerSide(expr, enforceServerSide) || ce.Method.IsSqlPropertyMethodEx())
                {
                    return(new TransformInfo(BuildSql(context, expr, alias)));
                }
            }

            break;

            case ExpressionType.New:
            {
                var ne = (NewExpression)expr;

                List <Expression>?arguments = null;
                for (var i = 0; i < ne.Arguments.Count; i++)
                {
                    var argument    = ne.Arguments[i];
                    var memberAlias = ne.Members?[i].Name;

                    var newArgument = ConvertAssignmentArgument(context, argument, ne.Members?[i], enforceServerSide, memberAlias);
                    if (newArgument != argument)
                    {
                        if (arguments == null)
                        {
                            arguments = ne.Arguments.Take(i).ToList();
                        }
                    }
                    arguments?.Add(newArgument);
                }

                if (arguments != null)
                {
                    ne = ne.Update(arguments);
                }

                return(new TransformInfo(ne, true));
            }

            case ExpressionType.MemberInit:
            {
                var mi      = (MemberInitExpression)expr;
                var newPart = (NewExpression)BuildExpression(context, mi.NewExpression, enforceServerSide);
                List <MemberBinding>?bindings = null;
                for (var i = 0; i < mi.Bindings.Count; i++)
                {
                    var binding    = mi.Bindings[i];
                    var newBinding = binding;
                    if (binding is MemberAssignment assignment)
                    {
                        var argument = ConvertAssignmentArgument(context, assignment.Expression,
                                                                 assignment.Member, enforceServerSide, assignment.Member.Name);
                        if (argument != assignment.Expression)
                        {
                            newBinding = Expression.Bind(assignment.Member, argument);
                        }
                    }

                    if (newBinding != binding)
                    {
                        if (bindings == null)
                        {
                            bindings = mi.Bindings.Take(i).ToList();
                        }
                    }

                    bindings?.Add(newBinding);
                }

                if (mi.NewExpression != newPart || bindings != null)
                {
                    mi = mi.Update(newPart, bindings ?? mi.Bindings.AsEnumerable());
                }

                return(new TransformInfo(mi, true));
            }
            }

            if (enforceServerSide || EnforceServerSide(context))
            {
                switch (expr.NodeType)
                {
                case ExpressionType.MemberInit:
                case ExpressionType.Convert:
                    break;

                default:
                    if (!enforceServerSide && CanBeCompiled(expr))
                    {
                        break;
                    }
                    return(new TransformInfo(BuildSql(context, expr, alias)));
                }
            }

            return(new TransformInfo(expr));
        }