You could do with a new predicate but as an expression already is able to refer to 2 fields, why not use that instead?
So as you requested some help, here we go.
Let's call this expression the ConcatenateFieldName expression. I'll re-use the Expression.cs sourcecode, and put it in a new class. You can then place this class in your own project.
[Serializable]
public class ConcatenateFieldNameExpression: IExpression, ISerializable
{
#region Class Member Declarations
private IExpressionElement _leftOperand, _rightOperand;
private ExOp _operator;
[NonSerialized]
private List<IDataParameter> _parameters;
[NonSerialized]
private IDbSpecificCreator _creator;
#endregion
#region Constructors
/// <summary>
/// CTor
/// </summary>
/// <remarks>Empty constructor, do not use, use one of the constructor overloads to create an expression instance.</remarks>
public ConcatenateFieldNameExpression()
{
InitClass();
_leftOperand = null;
_rightOperand = null;
_operator = ExOp.None;
}
/// <summary>
/// CTor for (expression) operator (expression) expressions.
/// </summary>
/// <param name="leftOperand">The first field to concatenate</param>
/// <param name="operatorToUse">operator to use in this expression. E.g. use for SqlServer ExOp.Add and for oracle ExOp.Or </param>
/// <param name="rightOperand">The second field to concatenate.</param>
public ConcatenateFieldNameExpression(IEntityFieldCore leftOperand, ExOp operatorToUse, IEntityFieldCore rightOperand)
{
InitClass();
_leftOperand = new ExpressionFieldElement( ExpressionElementType.Field, leftOperand, leftOperand as IFieldPersistenceInfo );
_rightOperand = new ExpressionFieldElement( ExpressionElementType.Field, rightOperand, rightOperand as IFieldPersistenceInfo );
_operator = operatorToUse;
}
/// <summary>
/// Initializes a new instance of the <see cref="Expression"/> class.
/// </summary>
/// <param name="info">Info.</param>
/// <param name="context">Context.</param>
protected ConcatenateFieldNameExpression(SerializationInfo info, StreamingContext context)
{
InitClass();
_leftOperand = (IExpressionElement)info.GetValue("_leftOperand", typeof(IExpressionElement));
_rightOperand = (IExpressionElement)info.GetValue("_rightOperand", typeof(IExpressionElement));
_operator = (ExOp)info.GetValue("_operator", typeof(ExOp));
}
#endregion
/// <summary>
/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> with the data needed to serialize the target object.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> to populate with data.</param>
/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"></see>) for this serialization.</param>
/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("_leftOperand", _leftOperand, typeof(IExpressionElement));
info.AddValue("_rightOperand", _rightOperand, typeof(IExpressionElement));
info.AddValue("_operator", _operator, typeof(ExOp));
}
/// <summary>
/// Retrieves a ready to use text representation of the contained expression.
/// </summary>
/// <param name="uniqueMarker">int counter which is appended to every parameter. The refcounter is increased by every parameter creation,
/// making sure the parameter is unique in the expression and also in the expression(s) containing the expression.</param>
/// <returns>The contained expression in textual format.</returns>
/// <exception cref="System.ApplicationException">When IExpression.DatabaseSpecificCreator is not set to a valid value.</exception>
public string ToQueryText(ref int uniqueMarker)
{
return ToQueryText(ref uniqueMarker, false);
}
/// <summary>
/// Retrieves a ready to use text representation of the contained expression.
/// </summary>
/// <param name="uniqueMarker">int counter which is appended to every parameter. The refcounter is increased by every parameter creation,
/// making sure the parameter is unique in the expression and also in the expression(s) containing the expression.</param>
/// <param name="inHavingClause">if set to true, it will allow aggregate functions to be applied to fields.</param>
/// <returns>The contained expression in textual format.</returns>
/// <exception cref="System.ApplicationException">When IExpression.DatabaseSpecificCreator is not set to a valid value.</exception>
public virtual string ToQueryText(ref int uniqueMarker, bool inHavingClause)
{
if(_creator==null)
{
throw new System.ApplicationException("DatabaseSpecificCreator object not set. Cannot create query part.");
}
StringBuilder queryText = new StringBuilder(128);
_parameters.Clear();
// we always have 2 fields and a string operator.
OperandToText(ref queryText, _leftOperand, ref uniqueMarker, true, inHavingClause);
switch(_operator)
{
case ExOp.Add:
queryText.Append(" + ");
break;
case ExOp.Or:
queryText.Append(" || ");
break;
}
OperandToText(ref queryText, _rightOperand, ref uniqueMarker, false, inHavingClause);
return queryText.ToString();
}
/// <summary>
/// Converts the passed in operand to text, appended to queryText. parameters created are added to _selectParameters.
/// </summary>
/// <param name="queryText"></param>
/// <param name="operand"></param>
/// <param name="uniqueMarker"></param>
/// <param name="isLeftOperand"></param>
/// <param name="inHavingClause"></param>
private void OperandToText(ref StringBuilder queryText, IExpressionElement operand, ref int uniqueMarker, bool isLeftOperand, bool inHavingClause)
{
// always a field.
IExpressionFieldElement fieldElement = (IExpressionFieldElement)operand;
IEntityFieldCore fieldCore = (IEntityFieldCore)fieldElement.Contents;
queryText.AppendFormat(null, "{0}", _creator.CreateFieldName(fieldCore, fieldElement.PersistenceInfo, fieldCore.Name, fieldCore.ObjectAlias,
ref uniqueMarker, inHavingClause));
if(fieldCore.ExpressionToApply!=null)
{
// add parameters to this expression's collection.
_parameters.AddRange(fieldCore.ExpressionToApply.Parameters);
}
}
/// <summary>
/// inits members
/// </summary>
private void InitClass()
{
_parameters = new List<IDataParameter>();
_creator = null;
}
#region Class Property Declarations
/// <summary>
/// The list of parameters created when the Expression was translated to text usable in a query. Only valid after a succesful call to ToQueryText
/// </summary>
public List<IDataParameter> Parameters
{
get { return _parameters;}
}
/// <summary>
/// Object which will be used to create valid parameter objects, field names, including prefix/postfix characters,
/// and conversion routines, and field names, including prefix/postfix characters.
/// Uses the strategy pattern so the generic code can work with more than one target database.
/// </summary>
public IDbSpecificCreator DatabaseSpecificCreator
{
get { return _creator;}
set {_creator = value;}
}
/// <summary>
/// Gets the left expression operand. Set by the constructor used.
/// </summary>
public IExpressionElement LeftOperand
{
get { return _leftOperand;}
}
/// <summary>
/// Gets the right expression operand. Set by the constructor used.
/// Can be null
/// </summary>
public IExpressionElement RightOperand
{
get { return _rightOperand;}
}
/// <summary>
/// Gets the operator of the expression.
/// </summary>
public ExOp Operator
{
get { return _operator;}
}
#endregion
}
Most code is comments, so the code is pretty small and straight forward.
You then use it like:
IEntityField contatenatedFields = CustomersFields.CustomerId.SetExpression(new ConcatenateFieldNameExpression(CustomersFields.CompanyName, ExOp.Or, CustomersFields.ContactName));
Which will result in a field || field result.
If you're on sqlserver, use ExOp.Add.