Writing a Task-based IHttpAsyncHandler

by Tobias Hertkorn on October 17th, 2012

Download the source at: Com.Hertkorn.TaskBasedIHttpAsyncHandler

If you want to serve up files or other content that has not much in common with the webforms or mvc engine, you should serve them using http handlers. The corresponding interface is IHttpHandler. They are light-weight and give great performance, especially when combined with IIS 7.

The basics - registering a custom handler

Http handler can be registered on multiple levels within IIS. One of the most common one is within the confines of the web application. That is done using web.config. If you have a handler defined in the class Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www.Handlers.SampleAwaitHttpAsyncHandler which is in the dll Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www use the following syntax:

XML:
  1. <configuration>
  2.   <system.webServer>
  3.     <handlers>
  4.       <add name="testAwait" path="testAwait.axd" type="Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www.Handlers.SampleAwaitHttpAsyncHandler, Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www" verb="*"/>
  5.     </handlers>
  6.   </system.webServer>
  7. </configuration>

Make sure you understand the ramifications of the verb setting, before you copy and paste the above code.

Now the http handler listens under the URL testAwait.axd for incoming requests.

Taking it up a notch - async

Using async handlers, when done correctly, will offload IO bound work to IO completion ports and therefore will free up the http pipeline threads to work on other requests. In high-stress situations this is crucial in order to achieve high throughput. IHttpAsyncHandler though is not for every situation, a introduction into the advantages and disadvantages can be found on msdn.

The original interface for an async IHttpHandler is called IHttpAsyncHandler and is implementing like this:

C#:
  1. public abstract class HttpAsyncHandler : IHttpAsyncHandler
  2. {
  3.     public abstract bool IsReusable { get; }
  4.  
  5.     IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
  6.     {
  7.         // ...
  8.     }
  9.  
  10.     void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
  11.     {
  12.         // ...
  13.     }
  14.  
  15.     void IHttpHandler.ProcessRequest(HttpContext context)
  16.     {
  17.         // ...
  18.     }
  19. }

Of course this is not an easy and "modern" interface into the async world. Nowadays we are trying to expose async operations using the Task class. So ideally an asynchronous handler would look something like this:

C#:
  1. public class SampleTaskHttpAsyncHandler : HttpAsyncHandler
  2. {
  3.     public override Task ProcessRequestAsync(HttpContextBase context)
  4.     {
  5.         return new Task(() =>
  6.         {
  7.             var data = DateTime.Now.ToString("hh:MM:ss,fff");
  8.             Thread.Sleep(1000);
  9.             context.Response.Output.Write("<html><body><p>start: ");
  10.             context.Response.Output.Write(data);
  11.             context.Response.Output.Write("<br/>done: ");
  12.             context.Response.Output.Write("{0:hh:MM:ss,fff}", DateTime.Now);
  13.             context.Response.Output.WriteLine("</p>");
  14.             context.Response.Output.Write("</body></html>");
  15.         });
  16.     }
  17.  
  18.     public override bool IsReusable
  19.     {
  20.         get
  21.         {
  22.             return true; // Not keeping any state in this handler
  23.         }
  24.     }
  25. }

or even better using the new async await syntax introduced in .NET 4.5:

C#:
  1. public class SampleTaskHttpAsyncHandler : HttpAsyncHandler
  2. {
  3.     public override async Task ProcessRequestAsync(HttpContextBase context)
  4.     {
  5.         var data = DateTime.Now.ToString("hh:MM:ss,fff");
  6.         await Task.Delay(1000);
  7.         context.Response.Output.Write("<html><body><p>start: ");
  8.         context.Response.Output.Write(data);
  9.         context.Response.Output.Write("<br/>done: ");
  10.         context.Response.Output.Write("{0:hh:MM:ss,fff}", DateTime.Now);
  11.         context.Response.Output.WriteLine("</p>");
  12.         context.Response.Output.Write("</body></html>");
  13.     }
  14.  
  15.     public override bool IsReusable
  16.     {
  17.         get
  18.         {
  19.             return true; // Not keeping any state in this handler
  20.         }
  21.     }
  22. }

In order to achieve that we will implement an abstact HttpAsyncHandler base class that will handle all the ugly continuation handling for us and will present us with a simple Task based mechanism.

The implementation

C#:
  1. public abstract class HttpAsyncHandler : IHttpAsyncHandler
  2. {
  3.     // In very high performance situations you might want to change the argument to HttpContext
  4.     // and save one object allocation per request.
  5.     public abstract Task ProcessRequestAsync(HttpContextBase context);
  6.  
  7.     public abstract bool IsReusable { get; }
  8.  
  9.     IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
  10.     {
  11.         if (cb == null)
  12.         {
  13.             throw new InvalidOperationException("cb is null");
  14.         }
  15.  
  16.         var task = CreateTask(context);
  17.  
  18.         task.ContinueWith((ar, state) => (state as AsyncCallback)(ar), cb);
  19.  
  20.         if (task.Status == TaskStatus.Created)
  21.         {
  22.             // Got a cold task -> start it
  23.             task.Start();
  24.         }
  25.  
  26.         return task;
  27.     }
  28.  
  29.     void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
  30.     {
  31.         var task = result as Task;
  32.  
  33.         // Await task --> Will throw pending exceptions
  34.         // (to avoid an exception being thrown on the finalizer thread)
  35.         task.Wait();
  36.  
  37.         task.Dispose();
  38.     }
  39.  
  40.     void IHttpHandler.ProcessRequest(HttpContext context)
  41.     {
  42.         var task = CreateTask(context);
  43.  
  44.         if (task.Status == TaskStatus.Created)
  45.         {
  46.             task.RunSynchronously();
  47.         }
  48.         else
  49.         {
  50.             task.Wait();
  51.         }
  52.     }
  53.  
  54.     private Task CreateTask(HttpContext context)
  55.     {
  56.         var task = ProcessRequestAsync(new HttpContextWrapper(context));
  57.  
  58.         if (task == null)
  59.         {
  60.             throw new InvalidOperationException("ProcessRequestAsync must return a Task");
  61.         }
  62.  
  63.         return task;
  64.     }
  65. }

The trick is to use the ContinueWith in order to complete the request after the actual task is done.

Download the source with sample at: Com.Hertkorn.TaskBasedIHttpAsyncHandler

October 17th, 2012 8:48 pm | Comments Off

Signaling IIS that the app pool is still busy – Running Tasks in IIS

by Tobias Hertkorn on February 8th, 2012

Unfortunately IIS is really not at all nice during app pool shutdown to threads running in its context. They simply are killed hard when a shutdown is initiated e.g. during an app pool recycle.

Of course this is by no means a desired behavior and can lead to serious data corruption. Phill Haack already did an excellent blog post on this subject, so please go read The Dangers of Implementing Recurring Background Tasks In ASP.NET now. I’ll wait here.

Today I want to talk about an addition to RegisterObject. The IIS does check if an app pool is busy or idle and may initiate a shutdown simply because it wants to save resources by shutting down long idle app pools. So simply put, even though one correctly uses RegisterObject, the app pool still does not know that there is stuff going on, which is relevant and should mark the app pool as busy.

Like RegisterObject this is done using the System.Web.Hosting.HostingEnvironment class, specifically IncrementBusyCount() and DecrementBusyCount(). With a busy count larger than zero the app pool is marked as busy.

When running task I would recommend that any given tasks does two things before starting its work: Register an object as managed by the hosting environment using RegisterObject and increment the busy counter.

It goes without saying that it must be guaranteed that a task, even a failed one, does a proper cleanup, unregistering and decrementing the busy count.

Final side-note: Please do realize that the IIS will still initiate app pool shutdowns even on busy app pools. So there is still the need to use RegisterObject.

February 8th, 2012 11:53 am | Comments Off

Creating an ASP.NET MVC 3 Html.ActionLink that is an Image

by Tobias Hertkorn on December 5th, 2010

[UPDATE: There is an improved version of Html.ActionLink: Creating an improved ASP.NET MVC 3 Html.ImageActionLink]

Have I told you that I really enjoy ASP.NET MVC 3 Razor? Damn, that's nice to work with. I am doing a small, fun project for a friend of mine and I stumbled across the situation that I wanted to have an image as an ActionLink. And since the @Html.ActionLink syntax is really sexy, I wanted to write an Extension method that does the same with an image.

The solution is this quick hack:

[UPDATE: There is an improved version without the hacky replace: Creating an improved ASP.NET MVC 3 Html.ImageActionLink]

C#:
  1. public static MvcHtmlString ImageActionLink(this HtmlHelper helper,
  2.     string imageUrl,
  3.     string altText,
  4.     string actionName,
  5.     string controllerName,
  6.     object routeValues,
  7.     object linkHtmlAttributes,
  8.     object imgHtmlAttributes)
  9. {
  10.     var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(imgHtmlAttributes);
  11.  
  12.     var builder = new TagBuilder("img");
  13.     builder.MergeAttribute("src", imageUrl);
  14.     builder.MergeAttribute("alt", altText);
  15.  
  16.     foreach (var key in attributes.Keys)
  17.     {
  18.         var value = attributes[key];
  19.         string valueAsString = null;
  20.         if (value != null)
  21.         {
  22.             valueAsString = value.ToString();
  23.         }
  24.         builder.MergeAttribute(key, valueAsString);
  25.     }
  26.  
  27.     var link = helper.ActionLink("[placeholder]", actionName, controllerName, routeValues, linkHtmlAttributes);
  28.     var text = link.ToHtmlString();
  29.     text = text.Replace("[placeholder]", builder.ToString(TagRenderMode.SelfClosing));
  30.  
  31.     return MvcHtmlString.Create(text);
  32. }

We return a MvcHtmlString, so we can indicate that this sting is not supposed to be html encoded after we return it to the templating engine. Plus we use the handy AnonymousObjectToHtmlAttributes helper method in order to enable us to pass html attributes to the img tag using anonymous classes, just as we are used to by now.

Unfortunatelly we can not tell the helper.ActionLink call to not html encode what we pass as linkText - therefore we fall back on using a place holder that we replace after the fact.

So now we can do a

XML:
  1. @Html.ImageActionLink(
  2.    Url.Content("~/Content/Images/" + url),
  3.    alt,
  4.    "Details",
  5.    "Calender",
  6.    new { area = "", day = Model.Date.Day, month = Model.Date.Month, year = Model.Date.Year },
  7.    new { @class = "SelectDay", onClick = string.Format("DoCallbackBookingDetails({0},{1},{2}); return false;", Model.Date.Day, Model.Date.Month, Model.Date.Year) },
  8.    new { style = "border: 0px;" })

right inside Razor. Beautiful!

[UPDATE: There is an improved helper to create an image that has an actionlink url a href: Creating an improved ASP.NET MVC 3 Html.ImageActionLink]

December 5th, 2010 12:28 am | Comments (3)

How to bind SharePoint event receivers to a custom list definition

by Alexander Brütt on March 12th, 2008

In most cases you want to create your own list definition with your own event receiver that sould only react on events of your custom list.

SharePoint ships with alot of list definitions with unique list type numbers:

100: Generic list
101: Document library
104: Announcements list
and many more.

You can create event receivers that react on events of these list types.

To create a custom list definition you should use the visual studio template "List Definition" shipped with the VSeWSS 1.1. If you create a custom list with base type "document library" its id will also be 101. It's just a copy of the template you selected.

First line of schema.xml after creating the custom list definition project:

XML:
  1. <List xmlns:ows="Microsoft SharePoint"
  2.   Title="List Definition1"
  3.   Direction="$Resources:Direction;"
  4.   Url="MyDocumentLibrary"
  5.   BaseType="1"
  6.   Name="MyDocumentLibrary"
  7.   Id="cc1b7796-28cb-4eeb-9ca5-fdacfadddecc"
  8.   Type="101" 
  9.   xmlns="http://schemas.microsoft.com/sharepoint/">

So what you should do ist change the Type value to a non-used number:

XML:
  1. <List xmlns:ows="Microsoft SharePoint"
  2.   Title="List Definition1"
  3.   Direction="$Resources:Direction;"
  4.   Url="MyDocumentLibrary"
  5.   BaseType="1"
  6.   Name="MyDocumentLibrary"
  7.   Id="cc1b7796-28cb-4eeb-9ca5-fdacfadddecc"
  8.   Type="999" 
  9.   xmlns="http://schemas.microsoft.com/sharepoint/">

You also have to modify the ListDefinition.xml

XML:
  1. <Elements Id="cc1b7796-28cb-4eeb-9ca5-fdacfadddecc" xmlns="http://schemas.microsoft.com/sharepoint/">
  2.   <ListTemplate Name="BskhDocumentLibrary"
  3.                 DisplayName="My Dokument Library"
  4.                 Description="Custom document library with custom event receiver doing cool things."
  5.                 BaseType="1"
  6.                 Type="999"
  7.                 OnQuickLaunch="TRUE"
  8.                 SecurityBits="11"
  9.                 Sequence="110"
  10.                 Image="/_layouts/images/itdl.gif"
  11.                 DocumentTemplate="101" />
  12. </Elements>

and the ItemEvntReceiver.xml

XML:
  1. <Elements Id="faca01fe-8478-4116-8b72-1e243bda6268" xmlns="http://schemas.microsoft.com/sharepoint/">
  2.   <Receivers ListTemplateOwner="cc1b7796-28cb-4eeb-9ca5-fdacfadddecc" ListTemplateId="999">
  3.     <Receiver>
  4.       <Name>ItemDeleted</Name>
  5.       <Type>ItemDeleted</Type>
  6.       <SequenceNumber>1</SequenceNumber>
  7.       <Class>faca01fe-8478-4116-8b72-1e243bda6268</Class>     
  8.     </Receiver>
  9.     <Receiver>
  10.       <Name>ItemAdded</Name>
  11.       <Type>ItemAdded</Type>
  12.       <SequenceNumber>1</SequenceNumber>
  13.       <Class>faca01fe-8478-4116-8b72-1e243bda6268</Class>
  14.     </Receiver>
  15.     <Receiver>
  16.       <Name>ItemUpdated</Name>
  17.       <Type>ItemUpdated</Type>
  18.       <SequenceNumber>1</SequenceNumber>
  19.       <Class>faca01fe-8478-4116-8b72-1e243bda6268</Class>
  20.     </Receiver>
  21.   </Receivers>
  22. </Elements>

Now your event receiver will be called only by MyDocumentLibrary lists and no longer by all document libraries.

March 12th, 2008 4:01 pm | Comments Off

Visual Studio 2005 Project Template for SharePoint 2007 Timer Jobs

by Alexander Brütt on February 27th, 2008

What are SharePoint Timer Jobs?
SharePoint timer jobs are tasks executed on a scheduled basis by the SharePoint Timer service.

SharePoint comes by default with a lot of timer jobs, like profile synchronisation between Active Directory and SharePoint. To see all currently installed timer jobs go to Central Administration > Operations > Timer Job Definitions.

Developing your own SharePoint Timer Job
To create your own SharePoint 2007 timer job you have to perform the following steps:

  1. Create a class library project.
  2. Create a class derrived from SPJobDefinition that contains the business logic.
  3. Create a class derrived from SPFeatureReceiver that can be deployed and functions as an installer for the timer job.
  4. Create a feature description file: feature.xml
  5. Create a solution description file: manifest.xml
  6. Create build description files for makecab.exe to build a .wsp (cab) file: .ddf and .Targets
  7. Build the wsp file.
  8. Add the solution to SharePoint.
  9. Deploy the solution.
  10. Activate the feature.

Nobody wants to do all these steps for each new timer job. So in order to make it simpler to start a new timer job project, I created a visual studio template containing all the neccessary stuff you need.

Visual Studio 2005 Project Template
Andrew Connell wrote a good article on his blog on how to create a SharePoint timer job and to modify the visual studio project to get a deployable .wsp file after building the project. Based on his article I created a reusable visual studio template. In addition I included a setup.bat file that can deploy and undeploy the .wsp file.

How to use the template

  1. Download the template and save it in MyDocuments\Visual Studio 2005\Templates\Visual C#.
  2. Create a new project using the "Custom SharePoint Job" template (Be careful to not use a dot in the project name).
  3. Write your logic inside the Execute method.
  4. Build the project.
  5. Run the setup.bat in your project folder to install, deploy and activate your job (Do not copy it into your bin folder, just run it. Everything is taken care for you. :) ).

If no errors are shown your timer job is listed in the Timer Job Definition list of the central administration. Navigating to Timer job status you can find out if your job was executed and if there were any errors. In case of an error exception messages are written to the EventLog by SharePoint.

Tip: Before you start customizing your project (namespace, assembly version, file names) look inside every file to get an overview on how everything fits together.

Good luck on making good (timer-)jobs,
Alexander Brütt

Download: CustomSharePointJob.zip

Deploying the timer on a SharePoint Farm:
I had problems deploying the timer job on a farm. The following commands deployed the timer on the farm correctly:

1. Add the solution
stsadm -o addsolution -filename {WSPFILENAME}
2. Deploy the solution
stsadm -o deploysolution -name {WSPFILENAME} -url {SITEURL}
3. Install the feature
stsadm -o installfeature -filename {FeatureFolder}\feature.xml
4. Activate the feature
stsadm -o activatefeature -id {FEATUREID} -url {SITEURL} -force

kick it on DotNetKicks.com

February 27th, 2008 8:44 pm | Comments Off

Weird behaviour when binding to (the programmatic construct) interface

by Tobias Hertkorn on December 21st, 2007

Well, a couple of days ago a coworker and I came across a very strange behaviour concerning Expression Blend while binding to a generic list of an interface. And we were able to reproduce the same behaviour in Visual Studion 2008 as well.

In our example ObservableCollection, where Mitarbeiter is the German word for employee. Sorry about the German throughout the example, but I am currently waiting to board my plane and I did not have enought time to reproduce the screenshot with a purely English example. Well, treat it as your first lesson in German. ;)

Anyway, so IMitarbeiter is the interface describing an employee and here is the important part: it is derived from another interface IPerson (where you are in luck, because German Person = English person. Great stuff, hugh?)

So basically the interface is:

C#:
  1. public interface IMitarbeiter : IPerson
  2. {
  3.   string Email { get; }
  4.   IArbeitsplatz Arbeitsplatz { get; }
  5. }

EMail = email (dugh) Arbeitsplatz = working space

And here is the definition for IPerson:

C#:
  1. public interface IPerson
  2. {
  3.   IPersonName Name { get; }
  4.   byte[] Bild { get; }
  5.   string Beschreibung { get; }
  6. }

Name = name (double dugh) Bild = picture Beschreibung = description

So. As I said the interface IMitarbeiter is now used to define a generic list ObservableCollection which we now want to bind to a WPF interface using Expression Blend. We do not worry about where this ObservableCollection comes from (since I know you are going to as: it come from some distant, well capsulated business tier, going through a GUI specific processing layer using MVP and blablabla... ;) ), we are stricktly concerned with interface matters.

All that is done by the developer is create this codebehind:

C#:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14. using System.Collections.ObjectModel;
  15. using System.Drawing;
  16.  
  17. public partial class MainForm : Window
  18. {
  19.   public ObservableCollection<IMitarbeiter> MitarbeiterCollection
  20.   {
  21.     get { return m_MitarbeiterCollection; }
  22.   }
  23.  
  24.   ...
  25.  
  26. }

So we are firing up Expression Blend and start doing the object binding, go to the "Create Data Template" Dialog and see this:

Create Data Template Dialog in Expression Blend with Bug

So we ONLY see the stuff defined in IMitarbeiter. No Fields coming from IPerson! Why that is is absolutely not clear. Most likely the deverloper of the code that does the reflection to analyse the interface did pass the wrong value that controlled the walking of the inheritance tree. The fun part is: This behaviour is totally reproducable in ASP.NET's databinding as well.

I have to emphasis, this is absolutely a GUI bug. When handcoding against the interface's properties everything works as expected.

I am sorry about this hurried post - but actually we started boarding and I am the last person sitting out her - and the nice lady behind the counter is starting to give me some nasty looks. Gotta go.

December 21st, 2007 8:47 pm | Comments (1)

Trying to enhance ObjectDataSource

by Tobias Hertkorn on June 1st, 2007

Edit: Please be sure to check out the comments! Eilon Lipton the author of ObjectDataSource was so nice and did talk to me about my problem. Awesome! He even went as far as creating a blog post Using ObjectDataSource to do the dirty work for your custom data source over at his blog about it. But first enjoy the original story posted on Sept. 9th 2006:

The ObjectDataSource is a great new tool in ASP.NET 2.0. But unfortunatelly its primary intended way of using it, is by using static methods or methods of objects that get created using the default constructor. But this is very limiting when building complex websites. Especially complex websites that should be maintainable. ;) That means it is not easy using the ODS when employing software cells, a Model View Presenter approach, capsulation of logic, etc. Especially MVP suffers from a considerable amount of work-around-uglyness, when using the standard ObjectDataSource. Sure, it is possible to override the default object creation process by attaching to the ObjectCreating event.

XML:
  1. <body>
  2.     <form id="form1" runat="server">
  3.     <div>
  4.       <asp:ObjectDataSource ID="ODS1"
  5.             runat="server"
  6.             OnObjectCreating="ODS1_ObjectCreating" />   
  7.     </div>
  8.     </form>
  9. </body>

and in the code behind:

C#:
  1. protected void ODS1_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
  2. {
  3.     e.ObjectInstance = presenter.getAdapter();
  4. }

Sure, that could be a solution - but I'd rather do something like this:

XML:
  1. <body>
  2.     <form id="form1" runat="server">
  3.     <div>
  4.       <myDS:AdapterDataSource ID="ADS1"
  5.             runat="server"
  6.             AdapterProvider="presenter"
  7.             AdapterProviderMethod="getAdapter"
  8.             AdapterSelectMethod="Select"
  9.             AdapterTypeName="TestAdapter" />
  10.     </div>
  11.     </form>
  12. </body>

Ha, EAAAAAAASY to do, right? Just override the method onObjectCreating of the base class ObjectDataSource in the new class AdapterDataSource. Well, if you look at the specs you will realize that the Event is not actually raised in ObjectDataSource - just made public there. The actual event is raised inside ObjectDataSourceView. But still no problemo, right:

C#:
  1. public class AdapterDataSourceView : ObjectDataSourceView
  2. {
  3.   private AdapterDataSource m_owner;
  4.  
  5.   public AdapterDataSourceView(AdapterDataSource owner, string name, HttpContext context)
  6.     : base(owner, name, context)
  7.   {
  8.     m_owner = owner;
  9.   }
  10.   protected override void OnObjectCreating(ObjectDataSourceEventArgs e)
  11.   {
  12.     // Let the ADS decide if we have to do some magic here.
  13.     if (m_owner.HasCustomDataBinding)
  14.     {
  15.       e.ObjectInstance = getCustomBinding();
  16.     }
  17.     base.OnObjectCreating(e);
  18.   }
  19.  
  20.   private object getCustomBinding()
  21.   {
  22.     throw new System.Exception("The method or operation is not implemented.");
  23.     // e.g. return m_owner.CustomDataBinding;
  24.   }
  25. }

So now, all we have to do is override the one method of ObjectDataSource in the AdapterDataSource that generates those ObjectDataSourceView instances, right... WRONG. No method seems to be responsible for creating the view.

Using the reflector I found out what was really happening behind the scenes.

C#:
  1. private ObjectDataSourceView GetView()
  2. {
  3.   if (this._view == null)
  4.   {
  5.     this._view = new ObjectDataSourceView(this, "DefaultView", this.Context);
  6.     if (base.IsTrackingViewState)
  7.     {
  8.       ((IStateManager) this._view).TrackViewState();
  9.     }
  10.   }
  11.   return this._view;
  12. }

The method used is called getView() inside the .NET Framework provided by Microsoft - and it is private! Yeah, baby. :) All those protected methods inside the ObjectDataSource class are just for show - because the main step in the process of creating the actual view can not be overridden.

But I wasn't going to give up that easily. How about overriding all methods that depend on GetView(). That way I could retire the private GetView() and great my own view creation algorithm. So full of hope I fired up the Analyser. But when I saw the following I started sobbing quietly:

ObjectDataSource GetView Disaster

Well, to make a long story short - I did try to do the overriding and stuff - but unfortunatelly those methods sometimes use Attributes that are internal and all kind of crazy stuff that makes you want to beat the programmer that did put that little innocent "private" in front of GetView(). ;)

Because it is 3 in the morning and I am really tired I did use this shortcut:

C#:
  1. public AdapterDataSource()
  2.   : base()
  3. {
  4.   Type baseType = typeof(ObjectDataSource);
  5.   FieldInfo fi = baseType.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic);
  6.   AdapterDataSourceView sourceView = new AdapterDataSourceView(this, "DefaultView", this.Context);
  7.   fi.SetValue(this, sourceView);
  8.   if (base.IsTrackingViewState)
  9.   {
  10.     ((IStateManager)sourceView).TrackViewState();
  11.   }
  12. }

UUUUUUUUUUUUUUUUUGLY! And it breaks when using Mono - because there it's a private property called DefaultView. And the best part about this: There actually is a PROTECTED method called GetView(string viewName). But that does not get used, probably because it returns simply a DataSourceView instead of a ObjectDataSourceView...

C#:
  1. protected override DataSourceView GetView(string viewName)
  2. {
  3.   if ((viewName == null) || ((viewName.Length != 0) && !string.Equals(viewName, "DefaultView", StringComparison.OrdinalIgnoreCase)))
  4.   {
  5.     throw new ArgumentException(SR.GetString("DataSource_InvalidViewName", new object[] { this.ID, "DefaultView" }), "viewName");
  6.   }
  7.   return this.GetView();
  8. }

I just wonder why nobody considered using:

C#:
  1. protected readonly string DEFAULT_VIEW = "DefaultView";
  2.  
  3. protected override DataSourceView GetView(string viewName)
  4. {
  5.   if ((viewName == null) || ((viewName.Length != 0) && !string.Equals(viewName, DEFAULT_VIEW, StringComparison.OrdinalIgnoreCase)))
  6.   {
  7.     throw new ArgumentException(SR.GetString("DataSource_InvalidViewName", new object[] { this.ID, DEFAULT_VIEW }), "viewName");
  8.   }
  9.   if (this._view == null)
  10.   {
  11.     this._view = new ObjectDataSourceView(this, DEFAULT_VIEW, this.Context);
  12.     if (base.IsTrackingViewState)
  13.     {
  14.       ((IStateManager) this._view).TrackViewState();
  15.     }
  16.   }
  17.   return this._view;
  18. }
  19.  
  20. private ObjectDataSourceView GetView()
  21. {
  22.   if (this._view == null)
  23.   {
  24.     return GetView(DEFAULT_VIEW) as ObjectDataSourceView;
  25.   }
  26.   return this._view;
  27. }
  28.  
  29. private ObjectDataSourceView _view;

*SOB*

Update:
Marc Brooks pointed out that there is a bugreport on this issue. Check the comments to find out how that "worked out".

June 1st, 2007 7:01 pm | Comments Off

BLINQ – an automated ASP.NET website creator

by Tobias Hertkorn on September 20th, 2006

Download it over at www.asp.net's Toolbox.

Blinq is a tool for generating ASP.NET websites for displaying, creating, and manipulating data based on database schema. Just point Blinq at a SQL database and it will create a website with pages that display sorted and paged data, allow you to update or delete records, create new records, and follow relationships between tables in your database. You don't need to write SQL queries to use Blinq; LINQ will generate optimized queries for you that request just the data you want to show. Blinq uses the May LINQ Community Tech Preview to access data. The code Blinq creates is simple and easy to customize to fit your needs. Everything in the website Blinq creates is meant as a starting point for a website that meets your needs perfectly, so have fun customizing the pages, experimenting with the code, and making it yours!

Also watch the creator Polita Paulus over on channel9 explaining her tool.

September 20th, 2006 1:31 pm | Comments Off

Another multiple WAPs Howto

by Tobias Hertkorn on July 28th, 2006

Scott just sent me a pointer to another great WAPs Howto. Omar over at webdevtools did a great job on making my little post about multiple WAPs look a bit ... well, you be the judge: Part 1

July 28th, 2006 10:35 am | Comments Off

Using multiple Web Application Projects (WAP) in one Solution

by Tobias Hertkorn on July 27th, 2006

If you don't want to read about why I tried to use the Web Application Project model, please "fast forward" to How to set up multiple WAPs in one solution.

Preface

What is the Web Application Project? Plus there are some good pointers in Scott Guthrie's blog.

For a client of mine, I am currently working out a major best-practice guideline for developing reusable .NET 2.0 programs. Their situation calls for a program design that makes it as easy as possible to reuse modules on multiple websites and on multiple rich clients. So I chose an approach that is a mixture between MVP and software cells to make it easier to reuse a lot of code between a Web App and a Windows Forms App. Unfortunatelly that still calls for duplicated code between multiple web apps, since the Views (Custom Controls) will reside inside the Web Site Project. Therefore it will get increasingly cumbersum to share modules between different web solutions. I thought back to when I used VS 2003 and thought it was really unfortunate that Microsoft changed the web project's look and feel. Why couldn't I create multiple web projects, each representing all of one module's views. I remembered having blogged about WAP a while back - even though for a different reason. It is an optional project style released by Microsoft that provides the same project, build and compilation semantics as the web project model known from VS 2003. Using WAP it is possible to use multiple projects each containing custom controls. Using WAP, it should be possible to use a (simplified) project tree like this:

  • Com.Hertkorn.Infrastructure.Cells.Forum (Class Library)
    containing Presenter, BLL and DAL, the M and P part of MVP
  • Com.Hertkorn.Website.Views.Forum (Web Application Project)
    containing Custom Controls, the V part of MVP for websites
  • Com.Hertkorn.Forms.Views.Forum (Windows Control Library)
    containing the V part of MVP for windows forms

Yeah, I know this a very crude representation of the actual assembly structure needed for real world MVP and Cells, but I made it simple enough to stress the main point: Now the multiple web solutions only need to reference Com.Hertkorn.Infrastructure.Cells.Forum and Com.Hertkorn.Website.Views.Forum in order to use the forum module. And Windows Applications use Com.Hertkorn.Infrastructure.Cells.Forum and Com.Hertkorn.Forms.Views.Forum. Therefore the duplicate code is reduced to a minimum.

But I had some trouble setting it up using VS 2005. I contacted Scott and he helped me through the process and I got it to work. For anybody else who is interested in the process, I decided to write it down. Even though right now I will not be able to use it. Unfortunatelly the May release of WAP does not render Custom Controls inside the designer that are contained in a different project. This issue will be fixed in the SP1 of VS 2005. That means there will still be no way around WSP and duplicated code til then.

How to set up multiple WAPs in one solution 

Now let's get to the good stuff. This is mostly from an email Scott was kind enough to forward me. And even better - he allowed me to blog about it using his stuff. Thanks again for that, Scott!

As I said before - but I'll repeat it here for all of you who skipped the preface. Setting up multiple WAPs can be done with the current version (WAP May 2006 + VS 2005 no SP), but there are some limitations:

  • It only works for IIS webs.
  • Design view does not render controls outside the WAP project (to be fixed in SP1)

If you’d like to setup this up to see how it works here are the steps:

  1. Create a new Web Application Project e.g. C:\MyProjects\MainWeb
  2. Create a virtual directory pointing to this location.
    Project Properties/Web Tab/Server Section
    Check “Use IIS Web server”
    Edit Project URL to http://localhost/MainWeb
    Push the “Create Virtual Directory” button
    (these steps will create the VDIR for http://localhost/MainWeb and map it to C:\MyProjects\MainWeb)
  3. Add a new Web Application Project to the solution
    C:\MyProjects\MainWeb\SubWeb1
    (note this will not be a true IIS sub-web, it’s just a folder in the MainWeb)
  4. Change the build output path to be the bin folder of the MainWeb
    Project Properties/Build Tab/Output section.
    Multiple WAPs Output Path
  5. Change the subweb to be a folder under the same VDIR as the MainWeb
    Project Properties/Web Tab/Server Section
    Check “Use IIS Web server”
    Edit Project URL to http://localhost/MainWeb/SubWeb1
    Multiple WAPs Project Url
  6. Save All, close and re-open the project properties
    You should see application root URL set to http://localhost/MainWeb
    This tells you that WAP is correctly detecting the subweb relationship.

There is only one IIS Web application in this model. http://localhost/MainWeb mapped to c:\MyProjects\MainWeb. However with this configuration the site is factored into several WAP projects that build into a common bin in the MainWeb.

To build and run a SubWeb project you must have the latest assemblies from the other projects in the MainWeb bin. Building the solution will take care of this for you.

The main advantage of this configuration is that it allows you to build the SubWeb folders independently into separate assemblies. This works great until you start creating dependencies by consuming controls from other SubWebs. Some sites I’ve seen have created a separate project with all the Controls to avoid this issue.

Depending on your scenario this maybe a good model. The alternative is to create this as a single project, which also works quite well.

The major disadvantage of using this approach is that the designer will not render controls outside it’s immediate project so the Control Project design above won’t render in design view. Unfortunately the WAP developers won’t be able to fix this scenario until Visual Studio SP1 later in the fall.

July 27th, 2006 9:48 am | Comments (4)
Tobi + C# = T# - Blogged blogoscoop