Archive

Posts Tagged ‘DomainDataSource’

WCF RIA Services – “The maximum uri length of 2083 was exceeded.”

November 2, 2011 Leave a comment

I came across this exception the other day and thought it was weird enough to merit a post on it. In the enterprise system that I have designed I have the concept of a quick search and advanced search capabilities. You can see an example of quick search below:

When the user performs a quick search, the results are loaded into the Sidebar as well as into a DataPager for paging the data. Quick search is handled by allowing a user to enter in a ID or a business name. I handle this by creating two FilterDescriptors that operate against the properties that I want as shown below:

SearchFilterDescriptors.Add(new SearchFilterDescriptor("ID", FilterOperator.IsEqualTo, PropertyType.Integer));
SearchFilterDescriptors.Add(new SearchFilterDescriptor("Name", FilterOperator.StartsWith, PropertyType.String));

This works just fine in scenarios when I know exactly what I want to filter against but there are scenarios that I don’t know what the filter definition is going to be ahead of time. This is where the Advanced search screen comes into play:

With this screen, a user can select many properties that make up an “Account” record and then click the search button. A back-end process is created to find the set of records found and then I am dynamically creating FilterDescriptor objects. It was When I used a search criteria that brought back a lot of records that I received the following error:

Doing some research, I found this forum’s post as well as this blog entry discussing what I could do to fix this.

The interesting thing to note is that WCF RIA Services uses GET by default for all Query attributes. This explains why I was getting the exception due to the fact that I was building a query string that was larger than the maximum length.

It turns out that all I really needed to do was make a slight modification to my Query on the server as shown below:

[Query(HasSideEffects=true)]
public IQueryable<Organization> ReturnOrganization()
{
    return this.ObjectContext.Organization
        .OrderBy(x => x.OrganizationName);
}

By adding the HasSideEffects=true parameter WCF RIA Services uses Post instead of the default Get. Now we can have as complex a query as we want. This allowed me to have a dynamic advanced search screen and not worry about how many filters I was creating.

The way I accomplished this dynamically was by creating an instance of a FilterDescriptorCollection

var filter = new FilterDescriptorCollection();
foreach(var item in e.Value)
{
    var dto = item;
    filter.Add(new FilterDescriptor("ID", FilterOperator.IsEqualTo, dto.Value));
}
_eventAggregator.GetEvent<AdvancedSearchEvent>().Publish(filter);

I use Prism’s EventAggregator to send an event so that the other screen can be notified of the Advanced Search Event. On the main screen, I have this code in place to apply the filter:

public void OnAdvancedSearchEvent(FilterDescriptorCollection filter)
{
    if (filter.Count > 0)
    {
        View.DDS.FilterDescriptors.Clear();
        View.DDS.FilterOperator = FilterDescriptorLogicalOperator.Or;
        foreach(var item in filter)
        {
            var fd = item;
            View.DDS.FilterDescriptors.Add(fd);
        }
        View.DDS.Load();
    }
}

Another approach to this could be to handle the filter completely on the server and use a boolean parameter to indicate when to use the filter. Since my filter information was actually coming from the database I could have accomplished the same user experience but I would have never run into the this exception.

Hope this helps….

Filtering a DomainDataSource in Xaml

Another common feature that I use in the DomainDataSource control is the ability to filter data. You can define your filters either in XAML or programmaticallly.

Here are the available properties for a FilterDescriptor:

Name Description
IgnoredValue Gets or sets the value that is not applied to a query operation for filtering.
IsCaseSensitive Gets or sets a value indicating whether the FilterDescriptor is case sensitive for string values.
Operator Gets or sets the filter operator.
PropertyPath Gets or sets the name of the property path used as the data value to assess whether the entity meets the filter check.
Value Gets or sets the value to use for evaluating the filter condition.

The following is an example of filtering based on the value of another control:

<riaCtls:DomainDataSource 
    x:Name="ddsForms" 
    QueryName="ReturnFormsAssocaitedWithAMasterFormOnlyQuery" 
    AutoLoad="True">
    <riaCtls:DomainDataSource.DomainContext>
        <data:MyDomainContext />
    </riaCtls:DomainDataSource.DomainContext>

    <riaCtls:DomainDataSource.FilterDescriptors>
        <riaCtls:FilterDescriptor 
            PropertyPath="ApplicationId" Operator="IsEqualTo" 
            Value="{Binding ElementName=cboApplications, Path=SelectedItem.ApplicationId}" />
    </riaCtls:DomainDataSource.FilterDescriptors>
            
    <riaCtls:DomainDataSource.SortDescriptors>
        <riaCtls:SortDescriptor PropertyPath="Name" Direction="Ascending" />
    </riaCtls:DomainDataSource.SortDescriptors>

</riaCtls:DomainDataSource>

Most of the properties are self explanator but I wanted to go over the three main attributes on the FilterDescriptor object. You tell the filter what property you want it to operate on by passing in a value to the PropertyPath attribute. You tell it how to operate using the Operator attribute by choosing one of the following values:

Member name Description
IsLessThan The data value must be smaller than the filter value.
IsLessThanOrEqualTo The data value must be smaller than or equal to the filter value.
IsEqualTo The data value must be equal to the filter value.
IsNotEqualTo The data value must be different from the filter value.
IsGreaterThanOrEqualTo The data value must be larger than or equal to the filter value.
IsGreaterThan The data value must be larger than the filter value.
StartsWith The data value must start with the filter value.
EndsWith The data value must end with the filter value.
Contains The data value must contain the filter value.
IsContainedIn The data value must be contained in the filter value.

Finally, the Value attribute is what value you are going to pass to the filter.

If you want to provide a more complex criteria, you can simple do the following:

<riaCtls:DomainDataSource 
    x:Name="ddsForms" 
    QueryName="ReturnFormsAssocaitedWithAMasterFormOnlyQuery" 
    FilterOperator="And"
    AutoLoad="True">
    <riaCtls:DomainDataSource.DomainContext>
        <data:MyDomainContext />
    </riaCtls:DomainDataSource.DomainContext>

    <riaCtls:DomainDataSource.FilterDescriptors>
        <riaCtls:FilterDescriptor 
            PropertyPath="ApplicationId" Operator="IsEqualTo" 
            Value="{Binding ElementName=cboApplications, Path=SelectedItem.ApplicationId}" />
        <riaCtls:FilterDescriptor 
            PropertyPath="TagId" Operator="IsEqualTo" 
            Value="{Binding ElementName=cboTags, Path=SelectedItem.TagId}" />
    </riaCtls:DomainDataSource.FilterDescriptors>
            
    <riaCtls:DomainDataSource.SortDescriptors>
        <riaCtls:SortDescriptor PropertyPath="Name" Direction="Ascending" />
    </riaCtls:DomainDataSource.SortDescriptors>

</riaCtls:DomainDataSource>

The thing to notice is the new attribute FilterOperator. You can either do logical AND or logical OR operations. This gives you quite abit of flexibility when trying to provide your user with the flexibility of sifting through data.

I try to keep all of my behavior interactions bound to properties on my corresponding ViewModel . By exposing properties, I can use these values in other parts of my ViewModel without needing to expose the individual controls on my View. I sometimes also run into problems when I create my views dynamically and I can’t always rely on other controls being loaded or working when I want them to. Thus binding to properties seem to be the natural place for me when I want to filter my DomainDataSource.

The following is an example of binding to a ViewModel:

<riaCtls:DomainDataSource 
    x:Name="ddsForms" 
    QueryName="ReturnFormsAssocaitedWithAMasterFormOnlyQuery" 
    AutoLoad="True">
    <riaCtls:DomainDataSource.DomainContext>
        <data:MyDomainContext />
    </riaCtls:DomainDataSource.DomainContext>

    <riaCtls:DomainDataSource.FilterDescriptors>
        <riaCtls:FilterDescriptor 
            PropertyPath="ApplicationId" Operator="IsEqualTo" 
            Value="{Binding CurrentApplicationId}" />
    </riaCtls:DomainDataSource.FilterDescriptors>
            
    <riaCtls:DomainDataSource.SortDescriptors>
        <riaCtls:SortDescriptor PropertyPath="Name" Direction="Ascending" />
    </riaCtls:DomainDataSource.SortDescriptors>

</riaCtls:DomainDataSource>

Hope this helps…

Sorting a DomainDataSource in Xaml

June 2, 2010 2 comments

Sometimes when using the DomainDataSource control, it just doesn’t makes sense to write a custom query method only to provide sorting capabilities. Fortunately this is provided out of the box for you. You can do this either in XAML or you and programmatically code this up yourself.

The following is an example of doing a simple sort operation in Xaml:

<riaCtls:DomainDataSource
    x:Name="ddsApplications" QueryName="GetCg_xa_ApplicationQuery"
    DomainContext="{Binding DomainContext, Mode=TwoWay}">
    <riaCtls:DomainDataSource.SortDescriptors>
        <riaCtls:SortDescriptor PropertyPath="Name" Direction="Ascending" />
    </riaCtls:DomainDataSource.SortDescriptors>
</riaCtls:DomainDataSource>

The underlying grid is just setting its ItemsSource property to the Data property of the DomainDataSource. Here is the output of the sorted data in a grid:

Sorting Sample

A little more complex example is if you want to have a multi-column sort:

<riaCtls:DomainDataSource
    x:Name="ddsMasterForms" QueryName="ReturnMasterFormsIncludingForeignKeysQuery"
    DomainContext="{Binding DomainContext, Mode=TwoWay}">
    <riaCtls:DomainDataSource.SortDescriptors>
        <riaCtls:SortDescriptor PropertyPath="Tag.Name" Direction="Ascending" />
        <riaCtls:SortDescriptor PropertyPath="Name" Direction="Ascending" />
    </riaCtls:DomainDataSource.SortDescriptors>
</riaCtls:DomainDataSource>

As you can see, I wrote a custom query that will also bring back all of the foreign keys associated with my main object. I then insert the corresponding SortDescriptor objects in the order that I wish to perform the sorting operation. This is nice since I can go across objects or properties at the same level. The underlying grid is just setting its ItemsSource property to the Data property of the DomainDataSource.

Here is a screen shot of the complex sort:

Complex Sorting Sample

Finally, you can now using DataBinding if you wanted to have a more dynamic sorting.

<riaCtls:DomainDataSource
    x:Name="ddsApplications" QueryName="GetCg_xa_ApplicationQuery"
    DomainContext="{Binding DomainContext, Mode=TwoWay}">
    <riaCtls:DomainDataSource.SortDescriptors>
        <riaCtls:SortDescriptor
            PropertyPath="{Binding Path=SelectedItem.Content, ElementName=cboSort}"
            Direction="Ascending" />
    </riaCtls:DomainDataSource.SortDescriptors>
</riaCtls:DomainDataSource>

The above example assumes you have a ComboBox named, “cboSort”. If you want to bind to your ViewModel in MVVM:

<riaCtls:DomainDataSource
    x:Name="ddsApplications" QueryName="GetCg_xa_ApplicationQuery"
    DomainContext="{Binding DomainContext, Mode=TwoWay}">
    <riaCtls:DomainDataSource.SortDescriptors>
        <riaCtls:SortDescriptor
            PropertyPath="{Binding CurrentSortText}"
            Direction="Ascending" />
    </riaCtls:DomainDataSource.SortDescriptors>
</riaCtls:DomainDataSource>

The above example assumes you have a property called, “CurrentSortText” on your ViewModel. This gives you some flexibility of how you want to interact with the DomainDataSource.

NOTE: If you leave off the Direction property on the SortDescriptor, the default direction will be ascending.

Hope this helps…

The type DomainContext does not expose a method called ‘GetCustomer2Query’

May 26, 2010 1 comment

This error message was a common one that I was receiving when I switched from Linq2Sql to Linq2Entities. The reason behind this was that Linq2Sql pluralized my data model and so I was trying to look for the same corresponding query for Linq2Entities exposed by WCF RIA Services.

Here is the Xaml that causes the problem. It is valid Xaml but you will get a runtime error:

<UserControl
    x:Class="SampleView"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:riaCtls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
    >
    <Grid >
        <riaCtls:DomainDataSource x:Name="_dds"
            QueryName="GetCustomer2Query"
            AutoLoad="False" PageSize="25" LoadSize="100"
            DomainContext="{Binding DomainContext, Mode=TwoWay}" />

        ...

    </Grid>
</UserControl>

At runtime, you will get the following error:

The type DomainContext does not expose a method called ‘GetCustomer2Query’

This message is simple to fix, all I did was correct the query as you can see below:

<UserControl
    x:Class="SampleView"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:riaCtls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
    >
    <Grid >
        <riaCtls:DomainDataSource x:Name="_dds"
            QueryName="GetCustomerQuery"
            AutoLoad="False" PageSize="25" LoadSize="100"
            DomainContext="{Binding DomainContext, Mode=TwoWay}" />

        ...

    </Grid>
</UserControl>

The subtle difference is in the actual name of the query. This could be a blatant error or something as subtle as pluralization.

As a side note, the word “Query” can be ommitted. WCF RIA Services will honor the query name regardless.

Hope this helps…