Dynamics CRM - query cancellation and receive partial results

108 views Asked by At

I've prepared following code in WPF app. This code simply queries the CRM contact list and places it in the collection, which is then shown in ListBox Control.

Xaml:

<Window x:Class="WPFDynamics365.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WPFDynamics365"
    xmlns:fa="http://schemas.fontawesome.io/icons/"
    mc:Ignorable="d"
    Title="MainWindow" Height="600" Width="800">

<Window.Resources>
    <Storyboard x:Key="WaitStoryboard">
        <DoubleAnimation
    Storyboard.TargetName="Wait"
    Storyboard.TargetProperty="(TextBlock.RenderTransform).(RotateTransform.Angle)"
    From="0"
    To="360"
    Duration="0:0:2"
    RepeatBehavior="Forever" />
    </Storyboard>
</Window.Resources>



<Grid Name="mainGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="2*"></RowDefinition>
        <RowDefinition Height="6*"></RowDefinition>
        <RowDefinition Height="2*"></RowDefinition>
    </Grid.RowDefinitions>

    <!-- fa to jest TextBlock -->
    <fa:FontAwesome
        Panel.ZIndex="999" Icon="Spinner" Name="Wait" 
        Grid.Column="0" Grid.Row="1"
        HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="50" 
        RenderTransformOrigin="0.5, 0.5" Margin="20" Width="100">
        <TextBlock.RenderTransform>
            <RotateTransform Angle="0" />
        </TextBlock.RenderTransform>
    </fa:FontAwesome>

    <Label Margin="0,20,0,0" FontSize="20" HorizontalAlignment="Center" Width="100" Grid.Row="0" Name="count"></Label>

    <ListBox 
        DisplayMemberPath="FullName" SelectedValuePath="Id" Name="contactList" 
        Grid.Column="0" Grid.Row="1" Panel.ZIndex="0"
        Width="300" Height="300" HorizontalAlignment="Center">

    </ListBox>

    <Button Grid.Column="0" Grid.Row="2" 
        Width="200" Height="50" Margin="0,10,0,0" 
        Name="cancel" Content="Stop downoloading Contacts">

    </Button>

</Grid>

Code:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Loaded += async (sender, args) =>
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource();
            cancel.AddHandler(Button.ClickEvent, new RoutedEventHandler((s, a) => 
            {
                tokenSource.Cancel();
            }));
            var context = App.CRM.Context;
            CrmFactor factor = CrmFactor.Create();

            ((Storyboard)FindResource("WaitStoryboard")).Begin();
            EntitiesExplorer exp = new EntitiesExplorer(factor);

            var contacts = await exp.GetContacts(tokenSource.Token);
            count.Content = contacts?.Count.ToString();
            contactList.ItemsSource = contacts;
            ((Storyboard)FindResource("WaitStoryboard")).Stop();
            Wait.Visibility = Visibility.Collapsed;
        };


    }
}

The process of downloading contacts can be interrupted by the user. The whole contact download is done using TPL with cancellation token. Getter:

public async Task<List<Contact>> GetContacts(CancellationToken ct)
    {
        List<Contact> list = new List<Contact>();
        return await System.Threading.Tasks.Task.Run(() =>
        {

            try
            {
                list = _crmFactor.Context.ContactSet.AsParallel().WithCancellation(ct).ToList();
            }
            catch { }

            return list;
        });
    }

After a possible interruption of the operation I receive an empty list. This is OK for now, but I'm just curious if there is a possibility to cancel the operation and obtain a partial list of contacts, not just an empty or complete list.

1

There are 1 answers

1
Alex On BEST ANSWER

You could page the results of the query, with a small page size (1-3 I'd say) you'll be hammering the system but achieve what you want.

What I mean by hammering: if you have 100 records, you can query all of them at once normally (1 query -> 100 results: canceling leaves you empty). With a page size of 2, you'd instead query the CRM 50 times (50x2 = 100 results. Canceling leaves you with the results of however many queries you completed while running).

If you implement this, watch the network, you could end up DoS-ing yourself...