Back to Devexpress

How to: Implement a Custom Criteria Language Function Operator

xpo-5206-examples-how-to-implement-a-custom-criteria-language-function-operator.md

latest9.6 KB
Original Source

How to: Implement a Custom Criteria Language Function Operator

  • Jan 24, 2023
  • 5 minutes to read

While XPO supports a number of built-in criteria language operators, you may need additional function operators either for complex calculations or in cases when you want to exploit certain database-specific functions. This topic demonstrates how to implement the MyGetMonth function (similar to the built-in FunctionOperatorType.GetMonth function) based on the DATEPART function supported by the Microsoft Access database engine. The MyGetMonth function returns the month part (as a number) of a specified date.

First, declare the MyGetMonthFunction class as the implementor of the ICustomFunctionOperatorFormattable interface.

Important

Do not use the function name that starts with Is if this function does not return a Boolean value. It may lead to unexpected results.

csharp
using System;
using DevExpress.Data.Filtering;
using DevExpress.Xpo.DB;

// ...
public class MyGetMonthFunction : ICustomFunctionOperatorFormattable {
    #region ICustomFunctionOperatorFormattable Members
    // The function's expression to be evaluated on the server.
    string ICustomFunctionOperatorFormattable.Format(Type providerType, params string[] operands) {
        // This example implements the function for Microsoft Access databases only.
        if (providerType == typeof(AccessConnectionProvider))
            return string.Format("datepart(\"m\", {0})", operands[0]);
        throw new NotSupportedException(string.Concat(
            "This provider is not supported: ", providerType.Name));
    }

    public static int MyGetMonth(DateTime date) {
        return date.Month;
    }
    #endregion

    #region ICustomFunctionOperator Members
    // Evaluates the function on the client.
    object ICustomFunctionOperator.Evaluate(params object[] operands) {
        return MyGetMonth((DateTime)operands[0]);
    }

    string ICustomFunctionOperator.Name {
        get { return nameof(MyGetMonth); }
    }

    Type ICustomFunctionOperator.ResultType(params Type[] operands) {
        return typeof(int);
    }
    #endregion
}
vb
Imports System
Imports DevExpress.Data.Filtering
Imports DevExpress.Xpo.DB

' ...
Public Class MyGetMonthFunction
    Implements ICustomFunctionOperatorFormattable
    #Region "ICustomFunctionOperatorFormattable Members"
    ' The function's expression to be evaluated on the server.
    Private Function ICustomFunctionOperatorFormattable_Format(ByVal providerType As Type, _
    ParamArray ByVal operands() As String) As String _
    Implements ICustomFunctionOperatorFormattable.Format
        ' This example implements the function for Microsoft Access databases only.
        If providerType Is GetType(AccessConnectionProvider) Then
            Return String.Format("datepart(""m"", {0})", operands(0))
        End If
        Throw New NotSupportedException(String.Concat( _
        "This provider is not supported: ", providerType.Name))
    End Function

    Public Shared Function MyGetMonth(ByVal [date] As DateTime) As Integer
        Return [date].Month
    End Function
    #End Region

    #Region "ICustomFunctionOperator Members"
    ' Evaluates the function on the client.
    Private Function Evaluate(ParamArray ByVal operands() As Object) As Object _
    Implements ICustomFunctionOperator.Evaluate
        Return MyGetMonth(CDate(operands(0)))
    End Function

    Private ReadOnly Property Name() As String Implements ICustomFunctionOperator.Name
        Get
            Return NameOf(MyGetMonth)
        End Get
    End Property

    Private Function ResultType(ParamArray ByVal operands() As Type) As Type _
    Implements ICustomFunctionOperator.ResultType
        Return GetType(Integer)
    End Function
    #End Region
End Class

The next step is to register the custom function. Previously, you had to pass a custom function to a data store provider’s RegisterCustomFunctionOperator method and a dictionary’s XPDictionary.CustomFunctionOperators collection to accomplish this. Starting with v2011 vol 1, you can call the CriteriaOperator.RegisterCustomFunction or CriteriaOperator.RegisterCustomFunctions method instead.

Modify the project’s Main method as shown below. In Visual Basic, add a module to the project, create the Main method and set it as the startup object via the project’s properties.

csharp
using System;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Xpo.Metadata;
using DevExpress.Data.Filtering;

static class Program {
    static void Main() {
        ConnectionProviderSql provider = 
            (ConnectionProviderSql)XpoDefault.GetConnectionProvider(
            AccessConnectionProvider.GetConnectionString(@"..\..\CustomFunction.mdb"),
            AutoCreateOption.DatabaseAndSchema);
        XPDictionary dict = new ReflectionDictionary();

        // New registration technique.
        CriteriaOperator.RegisterCustomFunction(new MyGetMonthFunction());

        // Outdated registration technique.
        // provider.RegisterCustomFunctionOperator(new MyGetMonthFunction());
        // dict.CustomFunctionOperators.Add(new MyGetMonthFunction());

        // Instantiating a DAL.
        XpoDefault.DataLayer = new SimpleDataLayer(dict, provider);
        XpoDefault.Session = null;
        // ...
    }
}
vb
Imports System
Imports DevExpress.Xpo
Imports DevExpress.Xpo.DB
Imports DevExpress.Xpo.Metadata
Imports DevExpress.Data.Filtering

Friend NotInheritable Class Program
    Private Sub New()
    End Sub
    Shared Sub Main()
        Dim provider As ConnectionProviderSql = CType(XpoDefault.GetConnectionProvider( _
        AccessConnectionProvider.GetConnectionString("..\..\CustomFunction.mdb"), _
        AutoCreateOption.DatabaseAndSchema), ConnectionProviderSql)
        Dim dict As XPDictionary = New ReflectionDictionary()

        ' New registration technique.
        CriteriaOperator.RegisterCustomFunction(New MyGetMonthFunction())

        ' Outdated registration technique.
        ' provider.RegisterCustomFunctionOperator(new MyGetMonthFunction());
        ' dict.CustomFunctionOperators.Add(new MyGetMonthFunction());

        ' Instantiating a DAL.
        XpoDefault.DataLayer = New SimpleDataLayer(dict, provider)
        XpoDefault.Session = Nothing
        ' ...
    End Sub
End Class

After a data access layer has been instantiated, you can use the implemented custom function operator to build filter criteria and expressions.

csharp
static class Program {
    static void Main() {
        // ...
        using (Session session = new Session()) {
            // Using MyGetMonth to build criteria.
            CriteriaOperator criteria = CriteriaOperator.Parse("MyGetMonth(OrderDate) = 5");
            // Applying criteria to filter XPView contents.
            XPView view = new XPView(session, typeof(Order), "OrderDate", criteria);
            // Using MyGetMonth in an XPView column's expression.
            view.AddProperty("Month", "MyGetMonth(OrderDate)");

            foreach (ViewRecord prop in view) {
                Console.WriteLine(prop["OrderDate"]);
                Console.WriteLine("Month: " + prop["Month"]);
            }
        }
        Console.WriteLine("done\npress any key to exit ..");
        Console.ReadKey();
    }
}
vb
Friend NotInheritable Class Program
    Private Sub New()
    End Sub
    Shared Sub Main()
        ' ...
        Using session As New Session()
            ' Using MyGetMonth to build criteria.
            Dim criteria As CriteriaOperator = CriteriaOperator.Parse("MyGetMonth(OrderDate) = 5")
            ' Applying criteria to filter XPView contents.
            Dim view As New XPView(session, GetType(Order), "OrderDate", criteria)
            ' Using MyGetMonth in an XPView column's expression.
            view.AddProperty("Month", "MyGetMonth(OrderDate)")

            For Each prop As ViewRecord In view
                Console.WriteLine(prop("OrderDate"))
                Console.WriteLine("Month: " & prop("Month"))
            Next prop
        End Using
        Console.WriteLine("done" & Constants.vbLf & "press any key to exit ..")
        Console.ReadKey()
    End Sub
End Class

To learn how to implement custom functions and criteria, and use them in LINQ to XPO expressions, see How to: Implement Custom Functions and Criteria in LINQ to XPO.

See Also

Criteria Language Syntax

Build Criteria - Cheat Sheet

How to: Implement a Full-Text Search