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
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!
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!
5 Answers, 1 is accepted
0
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.
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:
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
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
0
Jill
Top achievements
Rank 1
answered on 04 Jan 2011, 09:23 PM
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;
Any suggestions?
Thanks in advance,
Chris
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
0
Jill
Top achievements
Rank 1
answered on 04 Jan 2011, 09:31 PM
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;
and my distinct class for int looks like this;
Thanks for everything,
Chris
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
0
Mike
Top achievements
Rank 1
answered on 03 Feb 2011, 05:03 PM
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:
The DistinctValue model object definition is simply as follows:
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:
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.
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);
}
}
0
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
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>>