Displaying hierarchical data in the DataGrid


The attached project shows how to display data which is both tabular and hierarchical in nature in the Silverlight (or WPF) DataGrid. Employee list is an example of such data, where each employee is described using the same set of data column but there is also a reporting relationship between employees and their managers (also employees). The implementation uses DataGrid’s functionality to load and display individual rows and columns. It then adds some special UI and functionality to handle the TreeView-like functionality for expanding and collapsing hierarchical relationships between rows. The latter is accomplished through the following:


1.       Data is exposed to the UI through a set of presenters, which describe both the tabular and hierarchical nature of the data model (GridDef, RowDef and ColumnDef classes).


2.       The first column in the DataGrid is defined as DataGridTemplateColumn.


3.       The column template consists of three components, each databound to a property of the row definition (RowDef):


1.       A checkbox. Its IsChecked property is bound to the IsExpanded property of the row definition. Its margin property is bound to the row’s HierarchyLevel property (through a converter which converts sequential level to a Thickness structure).


2.       TextBlock or other element bound to the tabular valu of the first column (e.g. employee name).


4.       The checkbox in the above template is re-templatized so that instead of showing the usual rectangle, swish and other content, it only shows a triangle pointing down or to the side or nothing at all depending on whether the checkbox’s IsChecked property is true, false or null.


5.       The implementation of the IsExpanded property sets or resets row’s IsVisible property (a local property, not a UI property).


6.       The GridDef object (main presenter) has a Display method, which returns an IEnumerable. It iterates over the data and returns (though the yield statement) currently visible rows.


In a future blog I will suggest an implementation to handle sorting functionality.

TreeGridApp.zip

Editable ListBox using template

WPF Templates are useful for defining common themes and styles. They can also be used to extend the functionality of controls. Following is an example of a ListBoxItem template, which makes individual items in a listbox in-place editable (provided the listbox contains strings only). To achieve this effect, the standard ListBoxItem template is modified as follows (note, to achieve similar effect with Win32 ListBox would require significant amount of procedural code):



  • Instead of just having a ContentPresenter (to show the content), the modified template has a ContentPresenter and a TextBox, the latter overlaying the first. Initially the TextBox is in Collapsed state (invisible and not occupying any space).

  • The template includes a property trigger based on IsSelected property. The trigger modifies the setting of the TextBox defined above to make it visible.

  • The trigger also databinds the content of the TextBox to the listbox item’s content using the RelativeSource source reference.

The downside of this approach is that the logical tree now contains an additional control (TextBox) for each item in the listbox item, which if the listbox is bound to a large collection could have performance implications.


<Page
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml
    Title=”Xml Data Binding” FontSize=”20″
    >
  <StackPanel Orientation=”Horizontal”>
    <StackPanel.Resources>
      <Style x:Key=”{x:Type ListBoxItem}” TargetType=”{x:Type ListBoxItem}”>
        <Setter Property=”Template”>
          <Setter.Value>
            <ControlTemplate TargetType=”{x:Type ListBoxItem}”>
              <Grid>
                <ContentPresenter />
                <TextBox Name=”EditableText” Visibility=”Collapsed”/>
              </Grid>
              <ControlTemplate.Triggers>
                <Trigger Property=”IsSelected” Value=”true”>
                  <Setter TargetName=”EditableText” Property=”Visibility” Value=”Visible” />
                  <Setter TargetName=”EditableText” Property=”Text” Value=”{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}”/>
                </Trigger>
              </ControlTemplate.Triggers>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </StackPanel.Resources>
    <ListBox Width=”100″ Height=”155″>
      <ListBoxItem>Item 1</ListBoxItem>
      <ListBoxItem>Item 2</ListBoxItem>
      <ListBoxItem>Item 3</ListBoxItem>
      <ListBoxItem>Item 4</ListBoxItem>
      <ListBoxItem>Item 5</ListBoxItem>
      <ListBoxItem>Item 6</ListBoxItem>
      <ListBoxItem>Item 7</ListBoxItem>
      <ListBoxItem>Item 8</ListBoxItem>
      <ListBoxItem>Item 9</ListBoxItem>
      <ListBoxItem>Item 10</ListBoxItem>
    </ListBox>
    <ListBox Width=”100″ Height=”155″>
      <ListBoxItem>Item 1</ListBoxItem>
      <ListBoxItem>Item 2</ListBoxItem>
      <ListBoxItem>Item 3</ListBoxItem>
      <ListBoxItem>Item 4</ListBoxItem>
      <ListBoxItem>Item 5</ListBoxItem>
      <ListBoxItem>Item 6</ListBoxItem>
      <ListBoxItem>Item 7</ListBoxItem>
      <ListBoxItem>Item 8</ListBoxItem>
      <ListBoxItem>Item 9</ListBoxItem>
      <ListBoxItem>Item 10</ListBoxItem>
    </ListBox>
  </StackPanel>
</Page>

Developing an Azure AD B2C multi-tenant application

The ‘regular’ Azure AD has build-in support for multi-tenant applications. In that case, a user from any Azure AD tenant can sign in to an application registered in another tenant. The application can then use the user’s security context to give the user a view of data that is specific to that tenant.

The goal of this article is to explore providing similar support using Azure AD B2C with one major difference: instead of using multiple Azure AD tenants, we will use a single B2C tenant and allow all registered users (using social ids or local user ids) to access the application with a ‘tenant’ context of their choice. For example, consider a SaaS application for small doctor clinics. The doctors are not employed by any single clinic. They use their own social ids (live, gmail, etc.) to authenticate and may in fact work for several clinics at different times. In this case, each clinic is a tenant. In B2C terms, where an individual has a single social or local id, we will need to allow each doctor to use the application within the context of a single clinic at any given time but allow them to change that context over time, for example to record their work against different clinics at different times.

To support this, AAD B2C needs to support three user scenarios:

  1. A B2C user wants to create/establish a new tenant (clinic) in the application.
  2. A B2C user  wants to become a registered user of a tenant (doctor joins a clinic).
  3. A B2C user already registered as per (2) above, wants to use the application within the context of a particular tenant (clinic).

This demo application illustrates how the above can be implemented. The application exposes two separate sign-in controls (hyperlinks): one for users who want to create a new tenant (1 above) and another for those who want to use an existing tenant (2 and 3). These could also be exposed as two separate applications: one for taking on new tenants and another for regular use of existing tenants. If a user signs-in using the first control, the application will take the user though logic needed to create a new tenant. At the end of the process, the user will be registered as an administrator of that tenant. Once a tenant is created, the administrator can create redemption codes (secrets), which can then be distributed to potential other users to enable them to sign-up into this tenant. Once a user has sign-up for a tenant, subsequent sign-ins to the system will either allow the user to access that tenant or give the user a list of tenants they can operate within (if they signed up for more than one). From then onwards, a user operates within the application as if it was dedicated to the selected tenant.

Currently, all this logic is implemented as part of the application and kept in a local SQL database. The next step is to move as much of it as possible into the B2C custom policies.

 

The source code for this project is now on https://github.com/mrochon/b2cmultitenantapp

Using OAuth2 with SOAP

I started at Microsoft when SOAP was all the rage, before there was such a thing as WCF. So it is with some nostalgia that I tried to combine one of latest technologies: Universal App Platform (UAP) with SOAP using OAuth2 protocol for authentication. One possible application of this approach would be for folks who are slowly migrating from WCF to REST and/or needing to mix REST and SOAP services in their current applications. Although the sample uses a UWP client, same code would apply to other .NET clients, e.g. WPF needing to use OAuth2 for authenticating to a SOAP service.

The basics of the attached samples are as follows:

  1. It is using Azure AD to provide the authentication service and therefore an OAuth2 access token to a UAP client.
  2. The client is using the ADAL library to acquire the token (see https://github.com/Azure-Samples/active-directory-dotnet-windows-store for more details).
  3. The SOAP service is a web hosted WCF service.
There are two main choices for passing authentication data to a SOAP service: via a custom SOAP header or, if the service is using http (as in my case) via an http header, e.g. the Authorization header. The first approach is more generic in that it could support non-http WCF services. The 2nd is likely faster (no xml parsing). Since UWP only supports basicHttpBinding, unless the service also supports other, non UWP clients, using non-http bindings, the http header approach is just as good. The attached sample uses the custom header approach but includes commented code for the http-header approach as well.
To call the SOAP service, after regular service proxy was added to the UWP client and an OAuth2 token was obtained, the client does the following to create and include a custom SOAP header with the OAuth2 access token:
 var svc = new ServiceReference1.Service1Client();
using (var scope = new OperationContextScope(svc.InnerChannel))
{
var authHeader = MessageHeader.CreateHeader("Token", "http://tempuri.org", result.AccessToken);
OperationContext.Current.OutgoingMessageHeaders.Add(authHeader);
// Do this if you want to use http header instead
//var httpRequestProperty = new HttpRequestMessageProperty();
//httpRequestProperty.Headers[System.Net.HttpRequestHeader.Authorization] = result.AccessToken;
//OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
var res = await svc.GetDataAsync(inp);
_result.Text = res.ToString();
}
The service, which unfortunately cannot use the OWIN stack to validate the JWT token, implements its own ServiceAuthorizationManager-derived class and a JWT-validator. The validator (called AADJWTValidator in the sample) relies on the Azure AD federation metadata endpoint to retrieve the signing key.
That simple!

SOAPWithUWP.zip

Custom data and dynamic templates in WPF

This blog describes how TemplateSelector in WPF can be used to dynamically modify contents of the UI. This is particularly useful in a common scenario many ISV’s need to support: ability of 3rd parties and users to extend the ISV’s data model with their own data columns.


For example, the original application may include a Product table with some columns (e.g. Id, Name, etc.). However, application’s users may want to extend that definition to include additional columns. One of the issues that presents is using the data in UI: the UI provided by the ISV obviously has no notion of the new data columns. WPF, through template selector functionality allows the ISV to include these items in their UI.


One of the options on data binding is the specification of a template selector. Template selector is a custom class derived from DataTemplateSelector, which implements a method returning a DataTemplate given reference to the data that needs to be presented. The method can return an existing data template but it can also construct a new data template dynamically and return it. It is the last option that is particularly useful in the ISV scenario. The custom selector can dynamically discover the new columns added by the user, possibly collect additional meta-data about them (from SQL or some other proprietary mechanism used by the ISV) and return a data template, which provides a mapping of the data columns to WPF UI elements.


The attached sample shows how this can be done. For simplicity’s sake the sample generates all its source data. Note that the recommended way of creating the new data template is by constructing xaml and using XamlReader. (The alternative, using the DataTemplate class, populating it with data, etc. is not recommended).


 

DynamicDataTemplate.zip

Using Azure AD to authenticate public clients to SQL Azure

Azure AD enables access authorization to SQL Azure as an alternative to providing username/password information in the connection string: https://azure.microsoft.com/en-us/documentation/articles/sql-database-aad-authentication/. It is somewhat analogous to using Windows Authentication when both the client and the database are on a Windows domain network. It is particularly useful on public (non-confidential) clients where storing secrets is inappropriate and the only alternative would be to have the user use special SQL-only credentials. Azure AD authentication allows the user to access SQL by using their work or school credentials and supports single signon.

Sample code showing use of this approach in a WinForms/C# project is available here: https://github.com/mrochon/AADPublicClient2SQLAzure/. It shows three options for using AAD to connect to SQL Azure:

  1.  Using current Windows identity (assuming user is on-domain and Azure AD is federated with on-premises AD)
  2.  Using Azure AD challenge mechanism (including MFA) to authenticate the user
  3.  Using username/password entered directly into the client’s UI

It also shows the use of these authentication methods with both ADO.NET and Entity Framework 6 (Code First). The SQL Azure and Azure AD setup follows instructions in: https://azure.microsoft.com/en-us/documentation/articles/sql-database-aad-authentication/. In my implementation I have defined a security group (‘sql users’ in my case) and added it as the SQL user login in  SQL Azure, as per above instructions. That way, to allow a new user to access the database all I need to do is to add this user to the group).

(There seems to be a missing setup step in the setup instructions provided in https://azure.microsoft.com/en-us/documentation/articles/sql-database-aad-authentication/. In addition to what it describes, you also need to allow your client application to request OAuth2 access tokens intended for SQL Azure as resource. To do that, use the Azure AD application manifest download/upload functionality to modify the manifest by including the following in the requiredResourceAccess section – see source code for example manifest).

{
     “resourceAppId”: “022907d3-0f1b-48f7-badc-1ba6abab6d66”
}

Oct 7th, 2016: to use non-public, confidential clients to login to SQL, you can add the client app’s display name (in Azure AD) as user to SQL and assign appropriate roles. You can then use OAuth2 client credential flow (with symmetric key or certificate) to obtain an access token.

 

Using Azure AD to enable partner access to SharePoint 201x

Introduction

The following summarizes my experience with setting Azure AD as authentication provider for Sharepoint 2013 or 2016. This setup enables access to SharePoint for external users (business partners, customers). While there are other approaches that could be used for this purpose, e.g. an on-premises AD with ADFS, using Azure AD has a number of advantages:

  1. No need to create new identities for these users, which implies costs and risks of having own processes to manage user life-cycle in the directory (e.g. handling lost/forgotten passwords).
  2. No need to create and maintain hardware and network infrastructure to maintain the directory.
  3. External users can use their own corporate or social credentials or manage their own passwords.

Lastly, when the on-premises SharePoint is moved to O365  SharePoint Online, all that is needed is a simple checkbox selection to maintain the external user access to the new environment: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-b2b-o365-external-user.

Architecture

The basic architecture of this approach is shown below.

network-components

The Root Azure AD tenant serves as the primary token supplier to SharePoint. However, using the Azure Ad B2B features, it contains links to other users invited to be guests in that tenant: https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2b-collaboration-overview/. These user’s credentials are maintained in their own host tenants. Users whose organizations already have an Azure AD tenant (e.g. because they are using O365) will use their regular corporate credentials to authenticate. For others, Azure AD will set up a viral, in-managed tenant and ask these users to create a new password. Users with Microsoft Accounts will be able to these to login as well.

ADFS in this scenario serves to convert tokens issued by Azure AD from the SAML2.0 to SAML1.1 format needed by SP. It also normalizes the UPN claim handled differently for work and social accounts in Azure AD.

Setup steps

The sequence of steps needed to set this environment up is described below. It assumes that both an Azure AD tenant (root tenant) and SharePoint installation with AD, ADFS and WAP have been completed. In addition to that, the following set up will be needed:

  1. Configure Azure AD to service token requests from ADFS
  2. Configure ADFS to use Azure AD root tenant to a Claims Provider
  3. Configure SharePoint as Relying Party in ADFS
  4. Configure SharePoint to use Azure AD to provide People Picker data (optional)
  5. Invite external users using the B2B process

Optionally, the user experience can be enhanced by ensuring that on-premises users always use AD rather than being presented with a choice of using AD or Azure AD, and by enabling a People Picker in SharePoint which uses Azure AD as source of user information.

Configure Azure AD to service token requests from ADFS

Use Azure portal (https://portal.azure.com) or PowerShell to add your ADFS as an application. You will need to configure the following parameters:

Application id must be the same as the ADFS’ Federation Service Identifier (see below).

adfs-id

Reply url must be the public address of the ADFS server with adfs/ls suffix, e.g. https://myadfs.contoso.com/adfs/ls

Signin url should be the public address for the SharePoint site. It will be used in the user’s myapps.microsoft.com portal as entry point to SharePoint. This approach works only if there is only one site all users will be accessing. If there are multiple sites, the myapps portal cannot be used.

Configure ADFS to use Azure AD

Use ADFS management console or PowerShell to add Azure AD as a Claims Provider. All that is needed is to provide Claims Provider functionality with the Azure AD Federation Metadata address, e.g. https://login.windowsazure.com/contoso.onmicrosoft.com/federationmetadata/2007-06/federationmetadata.xml.

Configure ADFS to accept all claims from Azure AD

Use the following claim rule to pass all incoming claims from Azure AD to the issuance pipeline:

c:[]  => issue(claim = c);

Configure SharePoint as relying party in ADFS

Use ADFS management console or PowerShell to SharePoint as a Relying Party. The following values will need to be used in this operation (replace contoso with your organization name)

Relying party identifier: urn:sharepoint:contoso

Enable WS-Federation and provide the following passive reply url: https://<address of the SP site>/_trust/

Configure Claim Issuance rules for SharePoint

SharePoint will use UPN as user identifier. Azure AD uses different claim types for users who logged in with work or social addresses. Therefore the following claim issuance rules will be needed:

Convert an incoming name claim (work address user) to UPN:

c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name”]
 => issue(Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

Convert an incoming email claim (social user) to UPN:

c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress”]
 => issue(Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

Configure ADFS to use AD only for intranet users (optional)

If the ADFS is also used as federation provider to Azure AD (for O365 users for example) configure the following setting using PowerShell:

Set-AdfsRelyingPartyTrust -TargetName <name of Azure AD as RP in ADFS, e.g. O365 Directory> -ClaimsProviderName @(“Active Directory”)

To automatically redirect all users redirected to ADFS by SharePoint, use the following PowerShell command:

Set-AdfsRelyingPartyTrust -TargetName <name of SP as Relying Party in ADFS>  -ClaimsProviderName @(“Contoso AAD”)

Finally, to have all intranet users use AD and not be redirected to Azure AD execute the following command:

Set-AdfsProperties -IntranetUseLocalClaimsProvider $true

Where Contoso AAD is the name of the Azure AD tenant as Claims Provider in ADFS (see earlier step)

Configure SharePoint to use ADFS as federation provider

To configure SharePoint to use ADFS as token provider obtain the public key of the ADFS signing certificate. It can be obtained either directly or by extracting it from the federation metadata document of the ADFS. That file (.cer) is then used in the following PowerShell script:

Add-PSSnapIn Microsoft.SharePoint.PowerShell
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(“c:\adfs cert file.cer”)
New-SPTrustedRootAuthority -Name “ADFS” -Certificate $cert
$map = New-SPClaimTypeMapping -IncomingClaimType “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn” -IncomingClaimTypeDisplayName “UPN” -SameAsIncoming
$realm = “urn:sharepoint:contoso”
$ap = New-SPTrustedIdentityTokenIssuer -Name “ADFS” -Description “SharePoint secured by SAML from AAD via ADFS” -realm $realm -ImportTrustCertificate $cert -ClaimsMappings $map -SignInUrl “https://adfs.contoso.local/adfs/ls/” -ProviderSignoutUrl “https://adfs.contoso.local/adfs/ls/” -IdentifierClaim “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”

Note that realm name must be exactly as entered earlier in ADFS and the provider sign/in/out url must correspond to the ADFS address. (SP 2013 update 2 needs to be installed before the sign out option shows up; in SP 2016 these options do not have the word ‘Provider’ in their name).

Finally enable use of ADFS in SharePoint through the Central Administration -> Security -> Specify authentication providers and selecting the appropriate zone. You should see the following ui:

sp

Install AzureCP to enable People Picker (optional)

Without any other setup, external users can be given access to individual SP sites by adding their upn to the allowed users lists. To enable a People Picker which allows using the Azure AD tenant to list or search for users, use a custom picker. There is an excellent one available here: https://github.com/Yvand/AzureCP

Invite external users

Use the Azure portal (https://portal.azure.com) to invite individual users from the outside of your organization. You need to have ‘User inviter’ privileges to access this functionality. The following shows the UI of Add user panle, after entering an email address with domain other that the current Azure AD tenant:

newuser

The Azure classic portal (https://manage.windowsazure.com) allows bulk upload of external user email addresses via the Add User functionality. Lastly, there is the B2B invitation API that can be used in an application to have full control over how external users are invited to the root Azure AD tenant.

 

 

 

 

 

Changing user password in Azure AD using GraphAPI

The following pertains to a very specific scenario:

  1. You use Azure AD for some applications (e.g. Office365), but…
  2. …one of your applications does NOT use Azure AD (yet). It has its own authentication store and method (e.g. forms authn).
  3. However, you want to keep the application’s credentials in sync with AAD. Basically, allow same signon (not single signon) to both your application and AAD managed applications. You want to keep username/pwds in-sync between your application and AAD. You want to ensure that whenever the user changes his/her password in the custom store, it is also changed in Azure AD.

To implement this, I have registered the application in Azure AD as a Native Application (not a Web App). This allows me to use the OAuth2 Resource Owner flow to programmatically (no UI) sign to AAD as the user. I gave the application one permission in addition to the standard Sign in and read user profile: Access the directory as the signed-in user. The application uses a GraphAPI 1.6 version API changePassword.

Here is the code which updates Azure AD with the new password, using the user’s privilege, and, if it was successful in my local identity store (an xml file – this is obviously for demo purposes only). The code assumes that the user has already signed in and that we know his/her UPN -in this case the UPN is retrieved from the ClaimsPrincipal established earlier on. The old/new passwords are retrieved from TextBox UI elements. NativeClientId is the key of the client id obtained from AAD for this application registered as a native application. By using the user’s identity to update the passport I avoid having to give my application a global or password admin privilege, usually considered too high for a web application.

Update code (from an ASP.NET project) 
            var upn = ClaimsPrincipal.Current.Claims.First(c => c.Type == UPN).Value;
            var tenantId = System.Configuration.ConfigurationManager.AppSettings[“ida:TenantId”];
            var authority = String.Format(CultureInfo.InvariantCulture, “https://login.microsoftonline.com/{0}”, tenantId);
            var ctx = new AuthenticationContext(authority, false);
            var clientId = System.Configuration.ConfigurationManager.AppSettings[“ida:NativeClientId”];
            var result = ctx.AcquireToken(“https://graph.windows.net”, clientId, new UserCredential(upn, _currPwd.Text));
            var client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(“Bearer”, result.AccessToken);
            ctx.TokenCache.Clear();
            var requestUri = “https://graph.windows.net/me/changePassword?api-version=1.6”;
            var newPwd = new { currentPassword = _currPwd.Text, newPassword = _newPwd.Text };
            var body = JsonConvert.SerializeObject(newPwd);
            var response = client.PostAsync(new Uri(requestUri), new StringContent(body, System.Text.Encoding.UTF8, “application/json”)).Result;
            if (response.IsSuccessStatusCode)
            {
                //
                // Update external file
                //
                var path = Server.MapPath(“/App_Data/Users.xml”);
                var doc = XDocument.Load(path);
                var userId = ClaimsPrincipal.Current.Claims.First(c => c.Type == ClaimsIdentity.DefaultNameClaimType).Value;
                var userXml = doc.Root.Elements(“user”).FirstOrDefault(el => el.Attribute(“userId”).Value == userId);
                userXml.Attribute(“password”).Value = _newPwd.Text;
                doc.Save(path);
                _status.Text = “Password changed”;
            }
            else
                _status.Text = “Change failed. Try a more complex password.”;

Using Azure Queues to schedule work items

Attached sample shows how to use Azure Storage Queues to schedule processing of an item for a later point in time. It solves a common problem of scaling out a process based on iterating over a collection and processing some items based on time specific properties. Such an iteration, while easy enough to implement in a single process becomes difficult when implemented in multiple parallel processes. Using Azure Storage Queues (Azure Service Bus Queues have similar facilities) one can create messages which can only be accessed at a specific future point in time (within 7 days for Azure Queues). This way multiple processes reading the queue can execute in parallel in a time-based sequence.

The sample consists of two console projects: a sender and a receiver. The sender creates 10 messages scheduled for processing with up to 10 minutes delay, randomly distributed among the messages. After the messages are sent, they are listed in the sequence they should be received. The second projects runs an endless loop waiting for messages and displays them in the sequence they arrive. The two displays should match (mostly: Azure Storage Queue, unlike ServiceBus queues do not guarantee sequence of delivery).

QueueMsgScheduling.zip