/// <summary>Checks parameter type and determines if parameter may be used as DB command parameter. /// Sets parameter value SqlUse property to DbParameter or Literal. </summary> /// <param name="parameter">Input parameter expression to check.</param> public virtual void CheckQueryParameter(ExternalValueExpression parameter) { var type = parameter.Type; if (type.IsNullableValueType()) type = type.GetUnderlyingType(); // this includes Guid and enum if (type.IsDbPrimitive() || type == typeof(Binary) || type == typeof(TimeSpan)) { parameter.SqlUse = ExternalValueSqlUse.Parameter; return; } // if it is a list of primitive types, check if provider support list parameters if (type.IsListOfDbPrimitive()) { var canUseArrayParam = Driver.Supports(DbFeatures.ArrayParameters) && !DbModel.Config.Options.IsSet(DbOptions.ForceArraysAsLiterals); if (canUseArrayParam) parameter.SqlUse = ExternalValueSqlUse.Parameter; else parameter.SqlUse = ExternalValueSqlUse.Literal; return; } // throw error var msg = "Sql provider does not support parameter/value type: {0}."; if (typeof(System.Collections.IList).IsAssignableFrom(parameter.Type)) msg += " List/array values are supported only for primitive types."; Util.Throw(msg, parameter.Type); }
public virtual string GetParameter(ExternalValueExpression parameter) { return "{" + (parameter.LinqParameter.Index + 2) + "}"; }
public override string GetParameter(ExternalValueExpression parameter) { var baseValue = base.GetParameter(parameter); //Handling list-type parameters if(!parameter.IsList) return baseValue; // We use Sql_variant column in table UDT that holds list. It was found that it causes index scan instead of seek // So we add CAST here var elType = parameter.ListElementType; var template = @"(SELECT ""Value"" FROM {0})"; if (elType == typeof(string)) template = @"(SELECT CAST(""Value"" AS NVarchar) FROM {0})"; else if (elType == typeof(Guid)) template = @"(SELECT CAST(""Value"" AS uniqueidentifier) FROM {0})"; else if (elType == typeof(long) || elType == typeof(ulong)) template = @"(SELECT CAST(""Value"" AS bigint) FROM {0})"; else if (elType.IsInt() || elType.IsEnum) template = @"(SELECT CAST(""Value"" AS int) FROM {0})"; return string.Format(template, baseValue); }
private TranslatedLinqCommand TranslateNonQuery(LinqCommand command) { LinqCommandPreprocessor.PreprocessCommand(_dbModel.EntityApp.Model, command); var rewriterContext = new TranslationContext(_dbModel, command); var cmdInfo = command.Info; // convert lambda params into an initial set of ExternalValueExpression objects; foreach(var prm in cmdInfo.Lambda.Parameters) { var inpParam = new ExternalValueExpression(prm); rewriterContext.ExternalValues.Add(inpParam); } //Analyze/transform base select query var exprChain = ExpressionChain.Build(cmdInfo.Lambda.Body); var selectExpr = BuildSelectExpression(exprChain, rewriterContext); // Analyze external values (parameters?), create DbParameters var cmdParams = BuildParameters(command, rewriterContext); var flags = command.Info.Flags; // If there's at least one parameter that must be converted to literal (ex: value list), we cannot cache the query bool canCache = !rewriterContext.ExternalValues.Any(v => v.SqlUse == ExternalValueSqlUse.Literal); if (!canCache) flags |= LinqCommandFlags.NoQueryCache; // !!! Before that, everyting is the same as in TranslateSelect var targetEnt = command.TargetEntity; var targetTableInfo = _dbModel.GetTable(targetEnt.EntityType); TableExpression targetTable; bool isSingleTable = selectExpr.Tables.Count == 1 && selectExpr.Tables[0].TableInfo == targetTableInfo; if(isSingleTable) { targetTable = selectExpr.Tables[0]; } else targetTable = _translator.CreateTable(targetEnt.EntityType, rewriterContext); var commandData = new NonQueryLinqCommandData(command, selectExpr, targetTable, isSingleTable); // Analyze base query output expression var readerBody = selectExpr.Reader.Body; switch(command.CommandType) { case LinqCommandType.Update: case LinqCommandType.Insert: Util.Check(readerBody.NodeType == ExpressionType.New, "Query for LINQ {0} command must return New object", commandData.CommandType); var newExpr = readerBody as NewExpression; var outValues = selectExpr.Operands.ToList(); for(int i = 0; i < newExpr.Members.Count; i++) { var memberName = newExpr.Members[i].Name; var memberInfo = targetEnt.GetMember(memberName); Util.Check(memberInfo != null, "Member {0} not found in entity {1}.", memberName, targetEnt, targetEnt.EntityType); switch(memberInfo.Kind) { case MemberKind.Column: var col = _translator.CreateColumn(targetTable, memberName, rewriterContext); commandData.TargetColumns.Add(col); commandData.SelectOutputValues.Add(outValues[i]); break; case MemberKind.EntityRef: var fromKey = memberInfo.ReferenceInfo.FromKey; Util.Check(fromKey.ExpandedKeyMembers.Count == 1, "References with composite keys are not supported in LINQ non-query operations. Reference: ", memberName); var pkMember = fromKey.ExpandedKeyMembers[0].Member; var col2 = _translator.CreateColumn(targetTable, pkMember.MemberName, rewriterContext); commandData.TargetColumns.Add(col2); commandData.SelectOutputValues.Add(outValues[i]); break; default: Util.Throw("Property cannot be used in the context: {0}.", memberName); break; } } break; case LinqCommandType.Delete: commandData.SelectOutputValues.Add(readerBody); //should return single value - primary key break; } // Build SQL var sqlBuilder = new SqlBuilder(_dbModel); var sqlStmt = sqlBuilder.BuildNonQuery(commandData); var sqlTemplate = sqlStmt.ToString(); var defaultSql = FormatSql(sqlTemplate, cmdParams); return new TranslatedLinqCommand(sqlTemplate, defaultSql, cmdParams, flags); }
private ExternalValueExpression DeriveMemberAccessParameter(ExternalValueExpression oldPrm, MemberInfo memberInfo, TranslationContext context) { MemberExpression newSource = Expression.MakeMemberAccess(oldPrm.SourceExpression, memberInfo); Expression safeNewSource = newSource; if(!memberInfo.IsStaticMember() && oldPrm.Type.IsInterface) safeNewSource = MakeSafeEntityParameterMemberAccess(newSource); return DeriveInputParameter(oldPrm, safeNewSource, context); }
private ExternalValueExpression DeriveInputParameter(ExternalValueExpression oldPrm, Expression newSource, TranslationContext context) { oldPrm.SqlUseCount--; var newPrm = new ExternalValueExpression(newSource); newPrm.SqlUseCount++; context.ExternalValues.Add(newPrm); return newPrm; }
private Expression ConvertArrayToConstant(ExternalValueExpression parameter, TranslationContext context) { object value = null; try { //value = parameter.GetValue(null); } catch (Exception ex) { var msg = "Failed to translate 'Contains' call: supported only for arrays or lists that can be converted to constant list. Inner error: " + ex.Message; throw new Exception(msg, ex); } // do not unregister - it will break parameter sequence //UnregisterParameter(parameter, context); return Expression.Constant(value); }
private TranslatedLinqCommand TranslateSelect(LinqCommand command) { LinqCommandPreprocessor.PreprocessCommand(_dbModel.EntityApp.Model, command); var context = new TranslationContext(_dbModel, command); var cmdInfo = command.Info; // convert lambda params into an initial set of ExternalValueExpression objects; foreach (var prm in cmdInfo.Lambda.Parameters) { var inpParam = new ExternalValueExpression(prm); context.ExternalValues.Add(inpParam); } //Analyze/transform query expression var exprChain = ExpressionChain.Build(cmdInfo.Lambda.Body); var selectExpr = BuildSelectExpression(exprChain, context); // Analyze external values (parameters?), create DbParameters var commandParams = BuildParameters(command, context); // If there's at least one parameter that must be converted to literal (ex: value list), we cannot cache the query bool canCache = !context.ExternalValues.Any(v => v.SqlUse == ExternalValueSqlUse.Literal); if (!canCache) command.Info.Flags |= LinqCommandFlags.NoQueryCache; //Build SQL, compile object materializer var sqlBuilder = new SqlBuilder(_dbModel); var sqlStatement = sqlBuilder.BuildSelect(selectExpr); // Parameters are represented as {2}, {3}, etc. // Braces in string literals are escaped and are represented as '{0}' and '{1}' var sqlTemplate = sqlStatement.ToString(); var sql = FormatSql(sqlTemplate, commandParams); var objMaterializer = CompileObjectMaterializer(context); var outType = context.CurrentSelect.Reader.Body.Type; var resultListCreator = ReflectionHelper.GetCompiledGenericListCreator(outType); //check if we need to create implicit result set processor if (selectExpr.ResultsProcessor == null) { var returnsResultSet = typeof(IQueryable).IsAssignableFrom(cmdInfo.Lambda.Body.Type); if (!returnsResultSet) selectExpr.ResultsProcessor = QueryResultsProcessor.CreateFirstSingleLast("First", outType); } var sqlQuery = new TranslatedLinqCommand(sqlTemplate, sql, commandParams, command.Info.Flags, objMaterializer, selectExpr.ResultsProcessor, resultListCreator); return sqlQuery; }
private void CheckExternalValue(ExternalValueExpression extValue) { }