Ticket T203853
Visible to All Users

How to return property name from ICustomFunctionOperatorFormattable.Format method?

created 10 years ago (modified 10 years ago)

I need to return the property name from custom function operator in server mode to allow sorting and filtering:

C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using Creasoft.Core; using DevExpress.Data.Filtering; using DevExpress.ExpressApp; using DevExpress.ExpressApp.DC; using DevExpress.ExpressApp.Model; using DevExpress.Persistent.Base; using DevExpress.Persistent.Validation; namespace Creasoft.DataLocalization { [DomainComponent] [XafDefaultProperty("CalculatedProperty")] public interface IPropertyContainer { [Browsable(false)] string Property1 { get; set; } [Browsable(false)] string Property2 { get; set; } [Browsable(false)] string Property3 { get; set; } [Calculated("GetPropertyName()")]// I can't use either static PersistentAlias or CalculatedPersistentAlias because the property name depends from current logged user settings string CalculatedProperty { get; } } [DomainComponent] [DefaultClassOptions] public interface ITestDC { [Aggregated, ExpandObjectMembers(ExpandObjectMembers.Always)] IPropertyContainer Container1 { get; set; } [Aggregated, ExpandObjectMembers(ExpandObjectMembers.Always)] IPropertyContainer Container2 { get; set; } [Aggregated, ExpandObjectMembers(ExpandObjectMembers.Always)] IPropertyContainer Container3 { get; set; } } [DomainLogic(typeof(ITestDC))] public class TestDCLogic { public void AfterConstruction(ITestDC instance, IObjectSpace objectSpace) { instance.Container1 = objectSpace.CreateObject<IPropertyContainer>(); instance.Container2 = objectSpace.CreateObject<IPropertyContainer>(); instance.Container3 = objectSpace.CreateObject<IPropertyContainer>(); } } public class GetPropertyNameFunctionOperator : ICustomFunctionOperatorFormattable { #region ICustomFunctionOperatorFormattable Members // The function's expression to be evaluated on the server. string ICustomFunctionOperatorFormattable.Format(Type providerType, params string[] operands) { return "Property1"; //this is static result, but in my business scenario the returned value depends from the current logged user settings } #endregion #region ICustomFunctionOperator Members // Evaluates the function on the client. object ICustomFunctionOperator.Evaluate(params object[] operands) { // TODO } string ICustomFunctionOperator.Name { get { return "GetPropertyName"; } } Type ICustomFunctionOperator.ResultType(params Type[] operands) { return typeof(string); } #endregion } }

The problem is that when I trying to sort/filter in the list view - the alias calculates only from client side.

Comments (2)
DevExpress Support Team 10 years ago

    Hello Stanislaw.

    Would you please clarify what you mean by "the alias calculates only from client side"? Please expalin exactly what happens in which scenario. A small sample project demonstrating the issue would be helpful.

    ST ST
    Stanislaw Tristan 10 years ago

      Hello, Michael!
      I've attached a sample project.
      Run it and try to sort by any property - you will see that persistent alias evaluates only from client side, but my business case requires that sorting/filtering by any of properties that have a persistent alias attribute with my custom function is processe on the server-side.

      Answers

      created 10 years ago (modified 4 years ago)

      Thank you for your sample. XPO evaluates custom functions on the client side in some cases for optimization purposes. For example, your function does not have parameters, which means that its values do not depend on the current record and can be calculated once on the client side. To force XPO to evaluate this function on the server side in this case, use the CustomNonDeterministic function:

      C#
      [PersistentAlias("CustomNonDeterministic('GetPersistentProperty')")] public string Value { get { return PersistentProperty1; } set { PersistentProperty1 = value; } }

      However, here comes another issue - since the function is evaluated in the context of the TestObject class, the resulting query selects data from the TestObject table that does not have the PersistentProperty1 column. The data provider will throw an exception notifying you about the missing column. Please clarify what your ultimate goal is; i.e., what is the purpose of the PropertiesContainer class and the GetPersistentProperty function?

      Update
      You can achieve the required result by returning a subquery in the Format method:

      C#
      [PersistentAlias("GetPersistentProperty(Oid)")] public string Value { get { return PersistentProperty1; } set { PersistentProperty1 = value; } } ... public string Format(Type providerType, params string[] operands) { return "(select PersistentProperty1 from PropertiesContainer where Oid = " + operands[0] + ")"; }

      XPO will generate the following query in this case:

      SQL
      select top 201 N0."Oid" from "dbo"."TestObject" N0 where N0."GCRecord" is null order by (select PersistentProperty1 from PropertiesContainer where Oid = N0."PropertiesContainer1") asc,N0."Oid" asc
        Show previous comments (7)
        Anatol (DevExpress) 10 years ago

          I have found that this solution also requires changing the GridColumn.FieldNameSortGroup property, since this property is also defined in our code. Here is a corrected Controller:

          C#
          public class ViewController1 : ObjectViewController<ListView, TestObject> { protected override void OnViewControlsCreated() { base.OnViewControlsCreated(); GridListEditor gridListEditor = View.Editor as GridListEditor; if (gridListEditor != null) { foreach (GridColumn column in gridListEditor.GridView.Columns) { if (column.ColumnType == typeof(PropertiesContainer)) { column.FieldName = column.FieldName.Replace("!", ".PersistentProperty1"); column.FieldNameSortGroup = column.FieldName; column.ColumnEdit = null; } } } } }
          ST ST
          Stanislaw Tristan 10 years ago

            Anatol, the corrected code above works, thank  you!

            Anatol (DevExpress) 10 years ago

              You are welcome!

              Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

              Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.