RadDataDomainSource Syntax (Rossen Blog Sample)

6 posts, 0 answers
  1. Jill
    Jill avatar
    20 posts
    Member since:
    Nov 2010

    Posted 03 Jan 2011 Link to this post

    Hi,

    Not sure where to put this question so I thought this would be the best place seeing how I'm working with an internal build and RadDataDomainSource. In Rossen Hristov blog he posted a sample that uses the RDDS. Got everything to work but one thing. The code below 
    private static Expression<Func<Customer, string>> CreateSelectorExpression(string propertyName)
    {
        var paramterExpression = Expression.Parameter(typeof(Customer));
        return (Expression<Func<Customer, string>>)Expression.Lambda(Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression);
    }

    works great if the field (propertyName) is based on a column that is a varchar or nvarchar (translates to a string in c#), but throws an error when your field (propertyName) in your table is a Int value. Anyone at support guys/gals or Rossen know the syntax for allowing this code to work with any type of table field??

    The error is;
    Unable to cast object of type 'System.Linq.Expressions.Expression`1[System.Func`2[CIMM.Web.GetProductsDashboard_Result,System.Nullable`1[System.Int32]]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[CIMM.Web.GetProductsDashboard_Result,System.String]]'.

    Thanks in advance!
  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 04 Jan 2011 Link to this post

    Hi Chris,

    That is because I have tailored the sample to use string columns only. For every additional type that you want to return from the server you will have to make an additional selector method, i.e.

    private static Expression<Func<Customer, int>>...

    Also, if you take a look at the DistinctValue objects returned from the server you will notice that their value is of type string. If you want to return other kinds of distinct values you will have to create similar class, i.e.e IntDistinctValue which will have its value typed as an int.

    In other words, if you look at all the following code:

        public List<DistinctValue> GetDistinctValues(string propertyName)
        {
            var list = new List<DistinctValue>();
            var values = this.ObjectContext.Customers.Select(CreateSelectorExpression(propertyName)).Distinct();
            int i = 0;
            foreach (var value in values)
            {
                list.Add(new DistinctValue() { ID = i, Value = value });
                i++;
            }
     
            return list;
        }
     
        private static Expression<Func<Customer, string>> CreateSelectorExpression(string propertyName)
        {
            var paramterExpression = Expression.Parameter(typeof(Customer));
            return (Expression<Func<Customer, string>>)Expression.Lambda(Expression.PropertyOrField(paramterExpression, propertyName), paramterExpression);
        }
    }
     
    public class DistinctValue
    {
        [Key]
        public int ID { get; set; }
        public string Value { get; set; }
    }

    You basically need to clone this whole code, but this time for Int32, or DateTime, etc. This is due to the fact that you can't return List<object> from the server. It has to be something that has a Key and that is why we are wrapping the actual distinct values in such dummy "mule" classes that are simply used for piggy-backing.

    In fact you may be able to make the two methods more generic, so you don't have to copy-paste code for every possible columns type. I hope that this makes some sense.

    I am currently working on a blog post that will explain how to get distinct values from the server and hopefully it will make things clearer.

    I really wish you could simply get distinct values from WCF RIA out-of-the-box, but currently you need all of these tricks to transport them to the client.

    Greetings,
    Ross
    the Telerik team
    Browse the videos here>> to help you get started with RadControls for Silverlight
  3. Jill
    Jill avatar
    20 posts
    Member since:
    Nov 2010

    Posted 04 Jan 2011 Link to this post

    Hi Rossen,

    Thanks for all your help. I did as you suggested and split things out into two different procedures. At this time for this grid I only have int & string type fields. Your suggestion still didn't work. I keep getting the following error;

    "Unable to cast object of type 'System.Linq.Expressions.Expression`1[System.Func`2[CIMM.Web.GetProductsDashboard_Result,System.Nullable`1[System.Int32]]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[CIMM.Web.GetProductsDashboard_Result,System.Int32]]'."

    Here is what my code looks like;
    private static Expression<Func<GetProductsDashboard_Result, int>> ProductsDashboardCreateSelectorExpressionInt32( string propertyName )
    {
    var paramterExpression = Expression.Parameter( typeof( GetProductsDashboard_Result ) );
    return ( Expression<Func<GetProductsDashboard_Result, int>> )Expression.Lambda( Expression.PropertyOrField( paramterExpression, propertyName ), paramterExpression );
    }

    Any suggestions?

    Thanks in advance,
    Chris
  4. Jill
    Jill avatar
    20 posts
    Member since:
    Nov 2010

    Posted 04 Jan 2011 Link to this post

    Hi Rossen,

    Nevermind I got it. I needed to add System.Nullable<int> to make it equal what Linq wanted. The working code looks like this;

    private static Expression<Func<GetProductsDashboard_Result, System.Nullable<int>>> ProductsDashboardCreateSelectorExpressionInt32( string propertyName )
    {
                var paramterExpression = Expression.Parameter( typeof( GetProductsDashboard_Result ) );
                return ( Expression<Func<GetProductsDashboard_Result, System.Nullable<int>>> )Expression.Lambda( Expression.PropertyOrField( paramterExpression, propertyName ), paramterExpression );
    }

    and my distinct class for int looks like this;
    public class DistinctValueInt
    {
           [Key]
           public int ID { get; set; }
           public System.Nullable<int> Value { get; set; }
    }

    Thanks for everything,
    Chris
  5. Mike
    Mike avatar
    6 posts
    Member since:
    Jun 2010

    Posted 03 Feb 2011 Link to this post

    You don't have to create separate methods and classes for each value type. While unfortunately you can't marshall base object types or use generics with RIA services, you can use a generic object type for the RadObservableCollection on the client side, and achieve a similar effect using an object with individual properties for each data type you use and the null coalescing operator to select the value in use on the client side.

    For example.

    Code behind on the client side. In this case my user control is called PersonList:

    public partial class PersonList : Page
        {
            RadObservableCollection<object> distinctValues = new RadObservableCollection<object>();
     
          private void OnRadGridViewDistinctValuesLoading(object sender, GridViewDistinctValuesLoadingEventArgs e)
            {
                this.distinctValues.Clear();
                e.ItemsSource = this.distinctValues;
     
                this.distinctValuesDataSource.QueryParameters.Last().Value = e.Column.GetDataMemberName();
                this.distinctValuesDataSource.Load();
            }
     
            private void OnDistinctValuesDataSourceLoadingData(object sender, System.Windows.Controls.LoadingDataEventArgs e)
            {
                e.LoadBehavior = LoadBehavior.RefreshCurrent;
            }
     
            private void OnDistinctValuesDataSourceLoadedData(object sender, System.Windows.Controls.LoadedDataEventArgs e)
            {
                this.distinctValues.AddRange(e.Entities.Cast<DistinctValue>().Select(dv => (object)dv.Value ?? (object)dv.IntValue ?? (object)dv.FloatValue ?? (object)dv.DateValue));
            }


    The DistinctValue model object definition is simply as follows:

    public class DistinctValue
    {
        [Key]
        public int ID { get; set; }
        public string Value { get; set; }
        public DateTime? DateValue { get; set; }
        public long? IntValue { get; set; }
        public double? FloatValue { get; set; }
    }

    Note that I have continued to call the string Value property just "Value". This maintains compatibility with Rosens original solution and allows this modification to drop in seemlessly to existing solutions. String filters will continue to :just work"

    As helper function on my Domain Service I have the following:

    public partial class CostService
     {
     
         static Expression<Func<T, string>> CreateSelectorExpression<T>(string propertyName, ParameterExpression parameterExpression=null,  MemberExpression parentPropertyExpression=null)
         {
             if (parameterExpression==null)
                 parameterExpression = Expression.Parameter(typeof(T));
     
             if (propertyName.Contains("."))
             {
                 var properties = propertyName.Split(new char[] {'.'}, 2);
     
                 MemberExpression propExpression =
                     Expression.Property(parentPropertyExpression as Expression ?? parameterExpression as Expression, properties[0]);
     
                 return CreateSelectorExpression<T>(properties[1], parameterExpression, propExpression);
             }
             else
             {
                 return (Expression<Func<T, string>>)Expression.Lambda(Expression.PropertyOrField(parentPropertyExpression as Expression ?? parameterExpression as Expression, propertyName), parameterExpression);
             }
         }
     
     
         static List<DistinctValue> _distinctValueList(IQueryable<string> values)
         {
             return _distinctValueList<string>(values);
         }
     
         static List<DistinctValue> _distinctValueList<T>(IQueryable<T> values)
         {
             var list = new List<DistinctValue>();
     
             int i = 0;
             if (values != null)
             {
                 foreach (var value in values)
                 {
                     if (value is DateTime)
                     {
                         list.Add(new DistinctValue() { ID = i, DateValue = value as DateTime? });
                     }
                     else
                     {
                         list.Add(new DistinctValue() { ID = i, Value = value as String });
                     }
                     i++;
                 }
             }
             return list;
         }
     }

    Firstly there is a slight modification of Rosens CreateSelectorExpression function that supports creating expressions for Navigation properties.

    Next there is a helper function that returns a list of distinct values given a list of strings. This is just a stub that calls the generic helper function that creates a list of distinct values for any type (to maintain compatibility with my previous string only code). Turns out that numeric types work just fine when converted to string (so you probably don't really need the IntValue and probably FloatValue properties, but there may be some corner cases so I left them in). You definitely need DateTime properties to filter on date types though.

    Finally, here is an example of a GetDistinctValues function that uses the above code. Note that I specifically trap my non-string properties in a case statement and manually create expressions rather than use CreateSelectorExpression. It would probably not be alot of trouble to modify CreateSelectorExpression to handle non string types, however as I only have a couple of instances where this is required (and I am stupidly busy) I have not undertaken the exercise.

    public List<DistinctValue> GetDistinctPeopleValues(string company, string propertyName)
    {
     
     
        switch (propertyName)
        {
     
            case "st_id":
                return _distinctValueList<long?>(
                        this.GetPeopleByCompany(company).Select(p => (long?)p.st_id).Distinct().OrderBy(s => s)
                    );
            default:
                IQueryable<string> values = this.ObjectContext.People
                    .Where(p => p.company_code == company)
                    .Select(CreateSelectorExpression<Person>(propertyName))
                    .Distinct()
                    .OrderBy(s => s);
                return _distinctValueList(values);
        }
    }






  6. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 07 Feb 2011 Link to this post

    Hello Graeme,

    Your solution is much more elegant and generic and I am sure that many members of our community will benefit from it.

    Thank you for your contribution to our community forums. I have updated your Telerik points.

    Best wishes,
    Ross
    the Telerik team
    Let us know about your Windows Phone 7 application built with RadControls and we will help you promote it. Learn more>>
Back to Top