Hi, we are using LLBLGen Pro 5.10 and are writing a FreePredicate class with parameters support.
[Serializable]
public class FreePredicate : SD.LLBLGen.Pro.ORMSupportClasses.Predicate
{
private readonly string _filter;
private readonly object[] _args;
public FreePredicate(string filter) => _filter = filter;
public FreePredicate(string filter, params object[] args)
{
_filter = filter;
_args = args;
}
public override string ToQueryText()
{
if (DatabaseSpecificCreator == null)
DatabaseSpecificCreator = new SqlServerSpecificCreator();
Parameters.Clear();
var query = _filter;
var i = 0;
if (_args != null)
{
foreach (var arg in _args)
{
var value = arg.ToString();
// Left %
if (query.Contains("%{" + i.ToString() + "}"))
{
query = query.Replace("%{" + i.ToString() + "}", "{" + i.ToString() + "}");
value = "%" + value;
}
// Right %
if (query.Contains("{" + i.ToString() + "}%"))
{
query = query.Replace("{" + i.ToString() + "}%", "{" + i.ToString() + "}");
value = value + "%";
}
// Remove single quotes decoration
if (query.Contains("'{" + i.ToString() + "}'"))
{
query = query.Replace("'{" + i.ToString() + "}'", "{" + i.ToString() + "}");
}
Parameters.Add(DatabaseSpecificCreator.CreateParameter(System.Data.ParameterDirection.Input, value));
i++;
}
}
return string.Format(query, Parameters.Select(p => p.ParameterName).ToArray());
}
public override string ToQueryText(bool inHavingClause) => ToQueryText();
}
If we lookup the parameter name created by the function "DatabaseSpecificCreator.CreateParameter(System.Data.ParameterDirection.Input, value)", then the names are @p2, @p4, @p6, etc.. instead of just @p1, @p2, @p3, etc.
If I am correct, then this is caused by the following code in DbSpecificCreatorBase.cs:
//
// Summary:
// Creates a name usable for a Parameter, based on "p" and a unique marker, prefixed
// with the parameter prefix for this DQE available in ParameterPrefix.
//
// Returns:
// Usable parameter name.
protected string CreateParameterName()
{
_uniqueMarker++;
return ParameterPrefix + "p" + _uniqueMarker;
}
//
// Summary:
// Creates a new parameter instance. It sets the value but doesn't set the type
// so the parameter type depends on the value.
//
// Parameters:
// valueToSet:
// The value to set.
//
// parameterName:
// Name of the parameter. By default the empty string, which means it will create
// a new name
//
// Returns:
// ready to use parameter object
protected DbParameter CreateParameterInstance(object valueToSet, string parameterName = "")
{
object obj = valueToSet;
if (obj != null && obj.GetType().IsEnum)
{
obj = Convert.ChangeType(obj, Enum.GetUnderlyingType(obj.GetType()));
}
object realValueToUse;
string parameterType = DetermineDbTypeNameForValue(obj, out realValueToUse);
DbParameter dbParameter = CreateParameterInstance(parameterType);
dbParameter.ParameterName = (string.IsNullOrEmpty(parameterName) ? CreateParameterName() : parameterName);
dbParameter.Value = realValueToUse ?? DBNull.Value;
return dbParameter;
}
The line "DbParameter dbParameter = CreateParameterInstance(parameterType);" calls the following method:
//
// Summary:
// Creates a new parameter instance.
//
// Parameters:
// parameterType:
// Type of the parameter.
//
// Returns:
// ready to use parameter object
protected DbParameter CreateParameterInstance(string parameterType)
{
DbParameter dbParameter = FactoryToUse.CreateParameter();
dbParameter.ParameterName = CreateParameterName();
SetParameterType(dbParameter, parameterType);
return dbParameter;
}
Note that the line "dbParameter.ParameterName = (string.IsNullOrEmpty(parameterName) ? CreateParameterName() : parameterName);" calls CreateParameterName() when parameterName is empty (as is in our case). Thus CreateParameterName() is called twice, one time in method "CreateParameterInstance(object valueToSet, string parameterName = "")" and the other in method "protected DbParameter CreateParameterInstance(string parameterType)". The CreateParameterName() method increments the _uniqueMarker each time the method is called.
Is this a bug or a feature ;-) ?