In early pre-release versions of RIA Services, the DomainDataSource control did not support filter descriptors for Enum properties.  We were able to get this fixed for our V1.0 RC release though, and I mentioned that back in March.  I said in that post that I’d follow up with details about our Enum support, but I never did (until now).

Someone actually called me out on this by commenting on the post.  However, I thought the details of the comment were a bit humorous.  Here’s what I received in my inbox:

You mentioned enum filters are now supported.  Can you point me to any documentation for enum filters?

So, you’ve asked me to point you to documentation, but you said your name is “Enum Filters,” you left no email address, and no Url.  Oh well, my blog is the right place to answer anyway, so here ‘goes.

As an aside: when commenting on my blog, the email address you leave will be used for two things and two things only:

  1. As the from address in the email I receive, so that I can reply to you (I do my best to reply to people); and
  2. Your Gravatar image.

I’ve defined the following model that I want to expose through RIA Services: Automobile.cs within the Web project

using System.ComponentModel.DataAnnotations;

namespace EnumFilters.Web
{
public class Automobile
{
[Key]
public int Id { get; set; }

public int Year { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string TrimLevel { get; set; }

public AutoType Type { get; set; }
}

public enum AutoType
{
Coupe,
Sedan,
Wagon,
Pickup,
SUV,
Van
}
}

 

NOTE: You CANNOT nest the Enum type within an entity type.  RIA Services DOES NOT SUPPORT nested types.

I then created the following DomainService to expose some automobiles: AutomobileService.cs within the Web project

namespace EnumFilters.Web
{
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;

[EnableClientAccess()]
public class AutomobileService : DomainService
{
private static readonly Automobile[] automobileList = new[]
{
new Automobile
{
Id = 1, Year = 1983,
Make = "Volkswagon", Model = "Jetta",
Type = AutoType.Sedan
},
new Automobile
{
Id = 2, Year = 1992,
Make = "Plymouth", Model = "Sundance",
TrimLevel = "America", Type = AutoType.Coupe
},
new Automobile
{
Id = 3, Year = 2000,
Make = "Subaru", Model = "Impreza",
TrimLevel = "2.5RS", Type = AutoType.Coupe
}
};

public IQueryable<Automobile> GetAutomobiles()
{
return AutomobileService.automobileList.AsQueryable();
}
}
}

Now, in the Home.xaml file within my Views folder in the Silverlight project, I can define the following:

<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:Automobile, CreateList=true}"
QueryName="GetAutomobilesQuery" Width="0">
<riaControls:DomainDataSource.DomainContext>
<my:AutomobileContext />
</riaControls:DomainDataSource.DomainContext>
<riaControls:DomainDataSource.FilterDescriptors>
<riaControls:FilterDescriptor PropertyPath="Type"
Operator="IsEqualTo"
Value="{Binding ElementName=AutoType, Path=SelectedItem}"
IgnoredValue="{x:Null}" />
</riaControls:DomainDataSource.FilterDescriptors>
</riaControls:DomainDataSource>
<ComboBox x:Name="AutoType" Width="150" HorizontalAlignment="Center" />

The key is that I have defined a FilterDescriptor whose value is pulled from the SelectedItem property of a ComboBox.  Using the IgnoredValue property with its value set to {x:Null} allows the filter to be ignored if the ComboBox has no selection.  If you don’t specify that IgnoredValue and your ComboBox has no selection, then you’ll get an error when the DomainDataSource attempts to load.

We still need to put items into the ComboBox though.  There are various ways to do this, but I decided to grab all of the members of the Enum type and put them in as string values.  Here’s the code I have in Home.xaml.cs within the OnNavigatedTo event handler:

this.AutoType.ItemsSource = from member in typeof(AutoType)
.GetMembers(BindingFlags.Public | BindingFlags.Static)
select member.Name;

The DomainDataSource can take the string Enum value name and convert it appropriately.  The numeric values are also supported as well as actual Enum values.  In fact, here is some code that would result in a successful load of 1 entity from the list of automobiles I defined:

var filters = this.automobilesDataSource.FilterDescriptors;

// We can filter against actual enum values
filters.Add(new FilterDescriptor("Type", FilterOperator.IsEqualTo, AutoType.Sedan));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThan, AutoType.Coupe));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThanOrEqualTo, AutoType.Coupe));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThan, AutoType.Van));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThanOrEqualTo, AutoType.Van));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsNotEqualTo, AutoType.Van));

// Or we can filter against string representations of the enum values
filters.Add(new FilterDescriptor("Type", FilterOperator.IsEqualTo, "Sedan"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThan, "Coupe"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThanOrEqualTo, "Coupe"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThan, "Van"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThanOrEqualTo, "Van"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsNotEqualTo, "Van"));

// Or we can filter against numeric values
filters.Add(new FilterDescriptor("Type", FilterOperator.IsEqualTo, 1));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThan, 0));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThanOrEqualTo, 0));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThan, 5));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThanOrEqualTo, 5));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsNotEqualTo, 5));

// Or we can filter against numeric values represented as strings
filters.Add(new FilterDescriptor("Type", FilterOperator.IsEqualTo, "1"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThan, "0"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsGreaterThanOrEqualTo, "0"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThan, "5"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsLessThanOrEqualTo, "5"));
filters.Add(new FilterDescriptor("Type", FilterOperator.IsNotEqualTo, "5"));

// But if we try to provide an enum type from the wrong enum, the load will FAIL
// filters.Add(new FilterDescriptor("Type", FilterOperator.IsEqualTo, SomeOtherEnum.Van));

With the final line that is commented out, if we tried to execute a load with that line in place, we’d get a load error with the following message:

 

The FilterDescriptor with its PropertyPath equal to ‘Type’ cannot be evaluated.

 

You’ll notice from the above code that the following value types are supported:

  1. Enum values of the same Enum type
  2. Strings representing the Enum value names
  3. Integer values representing the Enum values
  4. Strings representing the Enum integer values

Against any of these, we support any operator that works against numeric values:

  1. IsEqualTo
  2. IsGreaterThan
  3. IsGreaterThanOrEqualTo
  4. IsLessThan
  5. IsLessThanOrEqualTo
  6. IsNotEqualTo

Additionally, we support Nullable Enums.  With Nullable Enums, we only support IsEqualTo and IsNotEqualTo if you specify a Null value.  If you attempt to use IsGreaterThan null, it won’t cause a load error, but it would never evaluate to true, so no entities would ever come back.

I hope this helps, and I hope that the person who asked comes back to see the answer to the question.

Technorati Tags: ,