Let’s hookup with ASP.NET Webhooks Preview

Webhook – A simple HTTP POST based pub/sub mechanism between web applications or services. This is a very effective that most of the modern web applications use this in order to handle event based pub/sub.

ASP.NET WebHooks is a framework which is in the preview release; this eases the task of incorporating webhooks in ASP.NET applications. It provides some predefined clients to subscribe events from, like Instagram, GitHub and others. It also provides a way to setup our custom ASP.NET web applications to send webhook notifications to the subscribed clients and prepare the clients to receive the webhook notifications. See the below URL for the more information.

https://github.com/aspnet/WebHooks

How WebHooks work and the structure of the ASP.NET WebHook Framework

Webhooks are simple HTTP POST based pub/sub.

Webhooks has the following structure.

  • The web application/service which publishes the events should provide an interface for the subscribers to register for the webhooks.
  • Subscribers select the events they want to subscribe to, submit the callback URL to be notified along with other optional parameters. Security keys are most common among the other optional parameters.
  • The publisher will persist the subscriber details.
  • When the event occurs publisher notifies all the eligible subscribers by triggering POST request to the callback back along with the event data.

The above 4 steps are the most vital steps of a working webhook. Let’s see how ASP.NET Webhooks implement this.

As of this writing ASP.NET Webhooks are in preview and nugget packages also in the preview release.

The support for sending WebHooks is provided by the following Nuget packages:

  • Microsoft.AspNet.WebHooks.Custom: This package provides the core functionality for adding WebHook support to your ASP.NET project. The functionality enables users to register WebHooks using a simple pub/sub model and for your code to send WebHooks to receivers with matching WebHook registrations.
  • Microsoft.AspNet.WebHooks.Custom.AzureStorage This package provides optional support for persisting WebHook registrations in Microsoft Azure Table Storage.
  • Microsoft.AspNet.WebHooks.Custom.Mvc This package exposes optional helpers for accessing WebHooks functionality from within ASP.NET MVC Controllers. The helpers assist in providing WebHook registration through MVC controllers as well as creating event notification to be sent to WebHook registrants.
  • Microsoft.AspNet.WebHooks.Custom.Api This package contains an  optional set of ASP.NET Web API Controllers for managing filters and registrations through a REST-style interface.

ASP.NET Webhooks works well in Web API and it has the Azure Table Storage provider for persisting the publisher metadata and event data.

Please go through this article for the detailed information

What is missing

According to the article, webhooks are delivered to the users based on authorization. So ASP.NET webhooks store the information of the subscriber along with the user information. This helps the ASP.NET webhooks to publish the event to the right subscriber.

For example, 2 users have subscribed to the same event.

User Id Event Callback URL
Thuru PhotoAdded https://thuru.net/api/webhooks/in/content
Bob PhotoAdded http://bob.net/api/hooks/incoming/photo

ASP.NET Webhooks requires the subscribers to login to the application or the REST service in order to subscribe the events.

So when the event is triggered based on the logged in user the POST request is made to the right client. ASP.NET webhooks use user based notifications. Is there any limitation in this?

Yes, consider a scenario – where you have an application with multiple customers. Each customer has many users. And the admin of one customer wants subscribe to the PhotoAdded event as above. Her intention is to be notified whenever any of her users add a photo. So if she registers for a webhook by logging in using her credentials, she will get the notifications only when she adds a photo, because ASP.NET webhooks by default provide user based notifications. Also we can’t register this event in the global level with no authentication, the she will be notified when users of the other customers add a photo.

I hope ASP.NET webhook will provide a way to customize the notification. As of now NotifyAsync is a static extension method to which the overriding is not possible.

Advertisement

Facebook like link sharing

When you paste a URL in the FB share, FB pulls out some information about the URL along with any available images.

I created a similar feature in ASP.NET few years ago and I got the project file while clearing out  one of my disk partitions and thought of sharing it.

This project uses HTMLAgilitPack (http://htmlagilitypack.codeplex.com/) and XPATH for processing the HTML.

A working preview.

image

 

 

 

 

 

 

You can download the code here (http://sdrv.ms/MM5zA1) from SkyDrive.

CRUD operations in Infragistics WebDataGrid

DataGrids are vital controls in many user interfaces. Like it or not still 2D tables are one of the most comfortable user interface elements in not only viewing data but also in performing rapid inserts and updates.

CRUD operations of DataGrid is not a big innovation, but there are plenty of ways to do that. This post explains how to perform CRUD operations using Infragistics WebDataGrid control in ASP.NET.

This code is written using Infragistics version 12.2

Create a simple WebDataGrid.

   1: <ig:WebDataGrid ID="WebGridCarton" runat="server" Height="350px" Width="100%" AutoGenerateColumns="False" 

   2:    DataKeyFields="CartonViewModelGuid" EnableDataViewState="True" 

   3:    OnRowAdded="WebGridCarton_RowAdded" OnRowAdding="WebGridCarton_RowAdding" 

   4:    OnRowDeleted="WebGridCarton_RowDeleted" OnRowsDeleting="WebGridCarton_RowsDeleting" 

   5:    OnRowUpdated="WebGridCarton_RowUpdated" OnRowUpdating="WebGridCarton_RowUpdating">

   6:    <Columns>

   7:        <ig:BoundDataField DataFieldName="CartonViewModelGuid" Key="CartonViewModelGuid" Hidden="true" HtmlEncode="true">

   8:        <Header Text="Guid">

   9:        </Header>

  10:        </ig:BoundDataField>

  11:        <ig:BoundDataField DataFieldName="CartonInfoId" Key="CartonInfoId" Hidden="true" HtmlEncode="true">

  12:            <Header Text="Id">

  13:            </Header>

  14:        </ig:BoundDataField>

  15:        <ig:BoundDataField DataFieldName="SelectionColumn" Key="SelectionColumn" HtmlEncode="true" Width="15">

  16:        </ig:BoundDataField>

  17:        <ig:BoundDataField DataFieldName="Size" Key="Size" HtmlEncode="true">

  18:            <Header Text="Size">

  19:            </Header>

  20:        </ig:BoundDataField>

  21:        <ig:BoundDataField DataFieldName="Length" Key="Length" HtmlEncode="true">

  22:            <Header Text="Length (cm)">

  23:            </Header>

  24:        </ig:BoundDataField>

  25:        <ig:BoundDataField DataFieldName="Width" Key="Width" HtmlEncode="true">

  26:            <Header Text="Width (cm)">

  27:            </Header>

  28:        </ig:BoundDataField>

  29:        <ig:BoundDataField DataFieldName="Height" Key="Height" HtmlEncode="true">

  30:            <Header Text="Height (cm)">

  31:            </Header>

  32:        </ig:BoundDataField>

  33:        <ig:BoundDataField DataFieldName="Volume" Key="Volume" HtmlEncode="true">

  34:            <Header Text="Volume (cbm)">

  35:            </Header>

  36:        </ig:BoundDataField>

  37:        <ig:BoundDataField DataFieldName="Weight" Key="Weight" HtmlEncode="true">

  38:            <Header Text="Weight (kg)">

  39:            </Header>

  40:        </ig:BoundDataField>

  41:        <ig:BoundDataField DataFieldName="WeightLimit" Key="WeightLimit" HtmlEncode="true">

  42:            <Header Text="Weight Limit (kg)">

  43:            </Header>

  44:        </ig:BoundDataField>

  45:    </Columns>

  46:    <Behaviors>

  47:        <ig:Activation Enabled="true"></ig:Activation>

  48:        <ig:Selection CellClickAction="Row" RowSelectType="Single" Enabled="true"></ig:Selection>

  49:        <ig:EditingCore AutoCRUD="false">

  50:            <Behaviors>

  51:                <ig:CellEditing EditModeActions-EnableF2="true" EditModeActions-MouseClick="Single" Enabled="true">

  52:                    <ColumnSettings>

  53:                        <ig:EditingColumnSetting ColumnKey="CartonInfoId" ReadOnly="true" />

  54:                        <ig:EditingColumnSetting ColumnKey="SelectionColumn" ReadOnly="true" />

  55:                        <ig:EditingColumnSetting ColumnKey="CartonViewModelGuid" ReadOnly="true" />

  56:                        <ig:EditingColumnSetting ColumnKey="Size" EditorID="DropDownProvider_Size" ValidatorID="RequiredFieldValidator1" />

  57:                        <ig:EditingColumnSetting ColumnKey="Length" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  58:                        <ig:EditingColumnSetting ColumnKey="Width" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  59:                        <ig:EditingColumnSetting ColumnKey="Height" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  60:                        <ig:EditingColumnSetting ColumnKey="Volume" EditorID="WebTextEditor1" ReadOnly="true" ValidatorID="DecimalValidator" />

  61:                        <ig:EditingColumnSetting ColumnKey="Weight" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  62:                        <ig:EditingColumnSetting ColumnKey="WeightLimit" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  63:                    </ColumnSettings>

  64:                    <EditModeActions MouseClick="Single" />

  65:                </ig:CellEditing>

  66:                <ig:RowAdding Alignment="Top" EditModeActions-EnableF2="true" EditModeActions-EnableOnActive="true"

  67:                    EditModeActions-MouseClick="Single" Enabled="true">

  68:                    <EditModeActions EnableOnActive="True" MouseClick="Single" />

  69:                    <ColumnSettings>

  70:                        <ig:RowAddingColumnSetting ColumnKey="SelectionColumn" ReadOnly="true" />

  71:                        <ig:RowAddingColumnSetting ColumnKey="Size" EditorID="DropDownProvider_Size" ValidatorID="RequiredFieldValidator1" />

  72:                        <ig:RowAddingColumnSetting ColumnKey="Length" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  73:                        <ig:RowAddingColumnSetting ColumnKey="Width" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  74:                        <ig:RowAddingColumnSetting ColumnKey="Height" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  75:                        <ig:RowAddingColumnSetting ColumnKey="Volume" EditorID="WebTextEditor1" ReadOnly="true" ValidatorID="DecimalValidator" />

  76:                        <ig:RowAddingColumnSetting ColumnKey="Weight" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  77:                        <ig:RowAddingColumnSetting ColumnKey="WeightLimit" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  78:                    </ColumnSettings>

  79:                </ig:RowAdding>

  80:                <ig:RowDeleting Enabled="true" />

  81:            </Behaviors>

  82:        </ig:EditingCore>

  83:    </Behaviors>

  84:    <EditorProviders>

  85:        <ig:TextBoxProvider ID="WebTextEditor1">

  86:            <EditorControl ID="EditorControl1" ClientIDMode="Static" runat="server"></EditorControl>

  87:        </ig:TextBoxProvider>

  88:        <ig:DropDownProvider ID="DropDownProvider_Size">

  89:            <EditorControl ClientIDMode="Predictable" DropDownContainerMaxHeight="100px" EnableAnimations="False"

  90:                EnableDropDownAsChild="False" ID="CartonSizeDrdEditor" DropDownContainerWidth="185px" AutoPostBack="true" 

  91:                EnableAjaxViewState="true">

  92:            </EditorControl>

  93:        </ig:DropDownProvider>

  94:    </EditorProviders>

  95: </ig:WebDataGrid>

This grid has one drop down editor and others fields are text box editors. And it supports CRUD. The grid control has 6 CRUD events associated with it. They’re RowAdding, RowAdded, RowUpdating, RowUpdated, RowDeleting and RowDeleted.

This is the ViewModel class used for data binding.

   1: public class CartonInfoGridViewModel

   2: {

   3:     public string CartonViewModelGuid { get; private set; }

   4:  

   5:     public int CartonInfoId { get; set; }

   6:     public string Size { get; set; }

   7:     public string Length { get; set; }

   8:     public string Width { get; set; }

   9:     public string Height { get; set; }

  10:     public string Volume { get; set; }

  11:     public string Weight { get; set; }

  12:     public string WeightLimit { get; set; }

  13:  

  14:     public CartonInfoGridViewModel()

  15:     {

  16:         this.CartonViewModelGuid = Guid.NewGuid().ToString();

  17:     }

  18: }

CartonViewModelGuid property is used identify the objects in the Session collection.

 

Adding Rows

In the RowAdding event we create a new ViewModel object with the values entered in the grid. And add it to the Session collection then we bind the Session collection to the grid. Please note these property names are same as the ones in the ASPX file.

   1: protected void WebGridCarton_RowAdding(object sender, Infragistics.Web.UI.GridControls.RowAddingEventArgs e)

   2: {

   3:     var cartonRow = new CartonInfoGridViewModel();

   4:  

   5:     cartonRow.CartonInfoId = -1;

   6:     cartonRow.Size = e.Values["Size"].ToString();

   7:     cartonRow.Length = e.Values["Length"].ToString();

   8:     cartonRow.Width = e.Values["Width"].ToString();

   9:     cartonRow.Height = e.Values["Height"].ToString();

  10:     cartonRow.Volume = e.Values["Volume"].ToString();

  11:     cartonRow.Weight = e.Values["Weight"].ToString();

  12:     cartonRow.WeightLimit = e.Values["WeightLimit"].ToString();

  13:  

  14:     if (this.Session["GridGridCartonsInfo"] != null && this.Session["GridCartonsInfo"] is List<CartonInfoGridViewModel>)

  15:     {

  16:         var collection = Session["GridCartonsInfo"] as List<CartonInfoGridViewModel>;

  17:         collection.Add(cartonRow);

  18:     }

  19:     else

  20:     {

  21:         var collection = new List<CartonInfoGridViewModel>();

  22:         collection.Add(cartonRow);

  23:     }

  24:  

  25:     this.DataBindCaronInfoGrid();

  26: }

DataBindCaronInfoGrid method binds the Session data to the Grid.

Just to prevent any Javascript errors popping up during this process in the RowAdded event we suppress the exceptions.

   1: protected void WebGridCarton_RowAdded(object sender, Infragistics.Web.UI.GridControls.RowAddedEventArgs e)

   2: {

   3:     e.ExceptionHandled = true;

   4: }

 

Deleting Rows

To delete a row we get the Id of the object and remove it from the Session collection. And bind the Session data to the grid.

   1: protected void WebGridCarton_RowsDeleting(object sender, Infragistics.Web.UI.GridControls.RowDeletingEventArgs e)

   2: {

   3:     string id = e.Row.DataKey.First().ToString();

   4:  

   5:     if (this.Session["GridGridCartonsInfo"] != null && this.Session["GridCartonsInfo"] is List<CartonInfoGridViewModel>)

   6:     {

   7:         var collecion = this.Session["GridGridCartonsInfo"] as List<CartonInfoGridViewModel>;

   8:  

   9:         var removeItem = collecion.FirstOrDefault(c => c.CartonViewModelGuid == id);

  10:  

  11:         if (removeItem != null)

  12:             collecion.Remove(removeItem);

  13:     }

  14:  

  15:     this.DataBindCaronInfoGrid();

  16: }

  17:  

  18: protected void WebGridCarton_RowDeleted(object sender, Infragistics.Web.UI.GridControls.RowDeletedEventArgs e)

  19: {

  20:     e.ExceptionHandled = true;

  21: }

 

Updating Rows

Updating the rows happens the same way, we identify the object via the Id and update the values.

   1: protected void WebGridCarton_RowUpdating(object sender, Infragistics.Web.UI.GridControls.RowUpdatingEventArgs e)

   2: {

   3:     if (Session["GridGridCartonsInfo"] != null && Session["GridGridCartonsInfo"] is List<CartonInfoGridViewModel>)

   4:     {

   5:         var collection = Session["GridGridCartonsInfo"] as List<CartonInfoGridViewModel>;

   6:  

   7:         if (e.OldValues["CartonViewModelGuid"].ToString() == e.Values["CartonViewModelGuid"].ToString())

   8:         {

   9:             string id = e.Values["CartonViewModelGuid"].ToString();

  10:  

  11:             var editItem = collection.FirstOrDefault(c => c.CartonViewModelGuid == id);

  12:  

  13:             if (editItem != null)

  14:             {

  15:                 editItem.CartonInfoId = Convert.ToInt32(e.Values["CartonInfoId"]);

  16:                 editItem.Size = e.Values["Size"].ToString();

  17:                 editItem.Length = e.Values["Length"].ToString();

  18:                 editItem.Width = e.Values["Width"].ToString();

  19:                 editItem.Height = e.Values["Height"].ToString();

  20:                 editItem.Volume = e.Values["Volume"].ToString();

  21:                 editItem.Weight = e.Values["Weight"].ToString();

  22:                 editItem.WeightLimit = e.Values["WeightLimit"].ToString();

  23:             }

  24:         }

  25:  

  26:         this.DataBindCaronInfoGrid();

  27:     }          

  28: }

  29:  

  30: protected void WebGridCarton_RowUpdated(object sender, Infragistics.Web.UI.GridControls.RowUpdatedEventArgs e)

  31: {

  32:     e.ExceptionHandled = true;

  33: }

 

Note

In the ViewModel class CartonViewModelGuid property is used as the object identifier in the collection. (In this case collection is persisted in the ASP.NET Session) For the new rows we set the CartonInfoId to –1, because this is an identity column in the database.

When we update the objects which are stored in the database already we can’t change the CartonInfoId that’s the reason why we use a separate Id property for the ViewModels.

 

The code for the DataBindCaronInfoGrid method

   1: private void DataBindCaronInfoGrid()

   2: {

   3:     if (this.Session["GridCartonsInfo"] != null && this.Session["GridCartonsInfo"] is List<CartonInfoGridViewModel>)

   4:     {

   5:         this.WebGridCarton.DataSource = Session["GridCartonsInfo"] as List<CartonInfoGridViewModel>;

   6:         this.WebGridCarton.DataBind();

   7:     }

   8:     else

   9:         this.LblStatus.Text = "Session expired, Please reload the page.";

  10: }

HTTP Headers

This is a quick and a small post on reading the HTTP headers in ASP.NET. Code gets the HTTP header key – value pairs and write it to a text file.

   1: protected void Page_Load(object sender, EventArgs e)

   2: {

   3:     var headers = Request.Headers;

   4:     StreamWriter writer = new StreamWriter(Server.MapPath("~/data.txt"));

   5:  

   6:     foreach (var item in headers.AllKeys)

   7:     {

   8:         string data = item + "---" + headers[item];              

   9:         writer.WriteLine(data);

  10:     }

  11:  

  12:     writer.Flush();

  13:     writer.Close();

  14: }

Output text file as follows.

   1: Connection---Keep-Alive

   2: Accept---text/html, application/xhtml+xml, */*

   3: Accept-Encoding---gzip, deflate

   4: Accept-Language---en-US

   5: Host---localhost:24859

   6: User-Agent---Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)

UI Updates – Part 2

ASP.NET

ASP.NET UI updates are handles using Ajax. In this example it is described how to use the ASP.NET Ajax for the updates.

In ASP.NET Ajax we can have update panel and make the updates via Ajax calls. Prepare your ASPX markup like below.

   1: <body>

   2:     <form id="form1" runat="server">

   3:         <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>

   4:         

   5:         <div>

   6:             <asp:Button ID="BtnPostback" runat="server" Text="PostBack" OnClick="BtnPostback_Click" />

   7:         </div>

   8:  

   9:         <div>

  10:             <asp:UpdateProgress ID="UpdateProgress1" runat="server">

  11:                 <ProgressTemplate>

  12:                     <asp:Label Text="Working..." ID="LblBusyIndicator" runat="server"></asp:Label>

  13:                 </ProgressTemplate>

  14:             </asp:UpdateProgress>

  15:         </div>

  16:  

  17:         <div>

  18:             <asp:UpdatePanel ID="UpdatePanel1" runat="server">

  19:                 <ContentTemplate>

  20:                     <asp:Label ID="Label1" runat="server" Text=""></asp:Label>

  21:                 </ContentTemplate>

  22:                 <Triggers>

  23:                     <asp:AsyncPostBackTrigger ControlID="BtnPostback" />

  24:                 </Triggers>

  25:             </asp:UpdatePanel>

  26:         </div>

  27:  

  28:     </form>

  29: </body>

Code behind..

   1: private void PerformTask()

   2: {

   3:     // job time 4 - 10 seconds

   4:     int time = new Random().Next(4, 11);

   5:     Thread.Sleep(1000 * time);

   6:     Label1.Text = String.Format("Hello from worker - ran for {0} seconds", time);

   7: }

   8:  

   9:  

  10: protected void BtnPostback_Click(object sender, EventArgs e)

  11: {

  12:     PerformTask();

  13: }

 

Silverlight

Silverlight the platform itself is asynchronous. And we can have the same mechanism we used in WPF for the UI updates, but little different.

Read the Silverlight Dispatcher for the Silverlight UI updates

Calendar in ASP.NET

We all know that .NET provides us rich set tools for us. Calendar control is one of them. We can simply use the calendar control for date input. But it is more powerful and we can simply create a complete application using calendar control which shows the tasks that have been scheduled.

To use the calendar control in a such way we should have to know about, how the calendar control is rendered in the HTML. Calendar control is rendered in as a table in HTML and every day is rendered in the order.

And calendar control provides a DayRender event as well. That’s enough for us to create the application.

Simply drag drop the calendar control inside your ASP.NET page. Make it big enough to fit to the screen.

And put the code behind as described here. I hardcoded the tasks in a HashTable but if you want, you can keep them in a data source.

 

image

 

In the Page_Load we load some data into the HashTable. This scenario may vary based on where you keep your data source. (As I mentioned earlier here it is simply hard coded).

DayRender method is triggered for each day that is rendered for the month. First in the if condition we check whether the rendered date is in the HashTable. Here we are providing the key and check whether value of the key is null or not. If it is not null then the key is available in the HashTable so a task has been assigned on that day.

Then we proceed inside the if condition.

We create a Literal control and make gives the text as <br/> (I don’t need to explain this as we all know why ?) And I add the Literal Control to the specific cell of the calendar control. (because as I explained earlier calendar control is rendered as a table in HTML)

Then I add a Label and feed the task of that particular day. Here DayRenderEventArgs object is very useful in getting the rendered day.

image

The above is simple and yet powerful. 🙂

Starting a process from a Windows Service

If you have some knowledge about Windows services and their functionalities then go ahead. Otherwise you may not understand some of the points mentioned here.

Are you suffering from starting a process from your windows service ? I have a cunning solution for that. Normally we cannot start any process either on our local machine or from a remote machine through a windows service.

We can start the process by enabling the Desktop Interactive of the service, but the UAC system of Vista and Windows 7 is a problem, that every time we start the process from a service it asks whether to allow the desktop interactivity. We can’t ask our clients to stop the UAC system. So what is the solution ?

Now I think you have some idea why can’t we start a process from service. But you might have a question in your mind why we need a process for a service ? There can be several answers

  • We need to perform a CPU intensive operation
  • COM interoperability is clashing with some threading components of the service
  • Some actions can be performed more efficiently by a separate process rather than a service

I figured out this solution as I suffered from the bolded point. Here the method I have used.

Create an ASPX webpage (it should be hosted in a later version of IIS 4.0)

In the Page_Load method start the process.

Then Create the Windows Service to access the the web page. (Access it through normal WebRequest and WebResponse). Cool !

The real cool thing is you can almost start all the processes by using this method from a windows service. Even processes with arguments and Verbs.’

Here’s the code for the webpage

protected void Page_Load(object sender, EventArgs e)
{

Process p = new Process();

p.StartInfo.FileName = "path to your process (your .exe file)"

p.StartInfo.UseShellExecute = false;

p.StartInfo.CreateNoWindow = true;

p.Start();

p.WaitForExit();

p.Dispose();

}

 

In the service within the OnStart() put the following code

try

{

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("ASPX page path");

HttpWebResponse resp = (HttpWebResponse)req.GetResponse();

}

catch (Exception ex)

{

}