Maarten Balliauw {blog}

ASP.NET MVC, Microsoft Azure, PHP, web development ...

NAVIGATION - SEARCH

LINQ for PHP (Language Integrated Query for PHP)

Perhaps you have already heard of C# 3.5's "LINQ" component. LINQ, or Language Integrated Query, is a component inside the .NET framework which enables you to perform queries on a variety of data sources like arrays, XML, SQL server, ... These queries are defined using a syntax which is very similar to SQL.

There is a problem with LINQ though... If you start using this, you don't want to access data sources differently anymore. Since I'm also a PHP developer, I thought of creating a similar concept for PHP. So here's the result of a few days coding:

PHPLinq - LINQ for PHP - Language Integrated Query

A basic example

Let's say we have an array of strings and want to select only the strings whose length is < 5. The PHPLinq way of achieving this would be the following:

[code:c#]

// Create data source
$names = array("John", "Peter", "Joe", "Patrick", "Donald", "Eric");

$result = from('$name')->in($names)
            ->where('$name => strlen($name) < 5')
            ->select('$name');

[/code]

Feels familiar to SQL? Yes indeed! No more writing a loop over this array, checking the string's length, and adding it to a temporary variable.

You may have noticed something strange... What's that $name => strlen($name) < 5 doing? This piece of code is compiled to an anonymous function or Lambda expression under the covers. This function accepts a parameter $name, and returns a boolean value based on the expression strlen($name) < 5.

An advanced example

There are lots of other examples available in the PHPLinq download, but here's an advanced one... Let's say we have an array of Employee objects. This array should be sorted by Employee name, then Employee age. We want only Employees whose name has a length of 4 characters. Next thing: we do not want an Employee instance in our result. Instead, the returning array should contain objects containing an e-mail address and a domain name.

First of all, let's define our data source:

[code:c#]

class Employee {
    public $Name;
    public $Email;
    public $Age;

    public function __construct($name, $email, $age) {
        $this->Name     = $name;
        $this->Email     = $email;
        $this->Age        = $age;
    }
}

$employees = array(
    new Employee('Maarten', 'maarten@example.com', 24),
    new Employee('Paul', 'paul@example.com', 30),
    new Employee('Bill', 'bill.a@example.com', 29),
    new Employee('Bill', 'bill.g@example.com', 28),
    new Employee('Xavier', 'xavier@example.com', 40)
);

[/code]

Now for the PHPLinq query:

[code:c#]

$result = from('$employee')->in($employees)
            ->where('$employee => strlen($employee->Name) == 4')
            ->orderBy('$employee => $employee->Name')
            ->thenByDescending('$employee => $employee->Age')
            ->select('new {
                    "EmailAddress" => $employee->Email,
                    "Domain" => substr($employee->Email, strpos($employee->Email, "@") + 1)
                  }');

[/code]

Again, you may have noticed something strange... What's this new { } thing doing? Actually, this is converted to an anonymous type under the covers. new { "name" => "test" } is evaluated to an object containing the property "name" with a value of "test".

This all sounds intuitive, interesting and very handy? Indeed! Now make sure you download a copy of PHPLinq today, try it, and provide the necessary feedback / feature requests on the CodePlex site.

Preview Word files (docx) in HTML using ASP.NET, OpenXML and LINQ to XML

Since an image (or even an example) tells more than any text will ever do, here's what I've created in the past few evening hours:

image

Live examples:

Want the source code? Download it here: WordVisualizer.zip (357.01 kb)

Want to know how?

If you want to know how I did this, let me first tell you why I created this. After searching Google for something similar, I found a Sharepoint blogger who did the same using a Sharepoint XSL transformation document called DocX2Html.xsl. Great, but this document can not be distributed without a Sharepoint license. The only option for me was to do something similar myself.

ASP.NET handlers

The main idea of this project was to be able to type in a URL ending in ".docx", which would then render a preview of the underlying Word document. Luckily, ASP.NET provides a system of creating HttpHandlers. A HttpHandler is the class instance which is called by the .NET runtime to process an incoming request for a specific extension. So let's trick ASP.NET into believing ".docx" is an extension which should be handled by a custom class...

Creating a custom handler

A custom handler can be created quite easily. Just create a new class, and make it implement the IHttpHandler interface:

[code:c#]

/// <summary>
/// Word document HTTP handler
/// </summary>

public class WordDocumentHandler : IHttpHandler
{
    #region IHttpHandler Members

    /// <summary>
    /// Is the handler reusable?
    /// </summary>

    public bool IsReusable
    {
        get { return true; }
    }

    /// <summary>
    /// Process request
    /// </summary>
    /// <param name="context">Current http context</param>

     public void ProcessRequest(HttpContext context)
    {
        // Todo...

        context.Response.Write("Hello world!");
    }

    #endregion
}

[/code]

Registering a custom handler

For ASP.NET to recognise our newly created handler, we must register it in Web.config:

image

Now if you are using IIS6, you should also register this extension to be handled by the .NET runtime:

image

In the application configuration, add the extension ".docx" and make it point to the following executable: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll

This should be it. Fire up your browser, browse to your web site and type anything.docx. You should see "Hello world!" appearing in a nice, white page.

OpenXML

As you may already know, Word 2007 files are OpenXML packages containg WordprocessingML markup. A .docx file can be opened using the System.IO.Packaging.Package class (which is available after adding a project reference to WindowsBase.dll).

The Package class is created for accessing any OpenXML package. This includes all Office 2007 file formats, but also custom OpenXML formats which you can implement for yourself. Unfortunately, if you want to use Package to access an Office 2007 file, you'll have to implement a lot of utility functions to get the right parts from the OpenXML container.

Luckily, Microsoft released an OpenXML SDK (CTP), which I also used in order to create this Word preview handler.

LINQ to XML

As you know, the latest .NET 3.5 release brought us something new & extremely handy: LINQ (Language Integrated Query). On Doug's blog, I read about Eric White's attempts to use LINQ to XML on OpenXML.

LINQ to OpenXML

For implementing my handler, I basically used similar code to Eric's to run query's on a Word document's contents. Here's an example which fetches all paragraphs in a Word document:

[code:c#]

using (WordprocessingDocument document = WordprocessingDocument.Open("test.docx", false))
{
    // Register namespace

    XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

    // Element shortcuts

    XName w_r = w + "r";
    XName w_ins = w + "ins";
    XName w_hyperlink = w + "hyperlink";

    // Load document's MainDocumentPart (document.xml) in XDocument

    XDocument xDoc = XDocument.Load(
        XmlReader.Create(
            new StreamReader(document.MainDocumentPart.GetStream())
        )
    );

    // Fetch paragraphs

    var paragraphs = from l_paragraph in xDoc
                    .Root
                    .Element(w + "body")
                    .Descendants(w + "p")
         select new
         {
             TextRuns = l_paragraph.Elements().Where(z => z.Name == w_r || z.Name == w_ins || z.Name == w_hyperlink)
         };

    // Write paragraphs

    foreach (var paragraph in paragraphs)
    {
        // Fetch runs

        var runs = from l_run in paragraph.Runs
                   select new
                   {
                       Text = l_run.Descendants(w + "t").StringConcatenate(element => (string)element)
                   };

        // Write runs

        foreach (var run in runs)
        {
            // Use run.Text to fetch a text string

            Console.Write(run.Text);
        }
    }
}

[/code]

Now if you run this code, you will notice a compilation error... This is due to the fact that I used an extension method StringConcatenate.

Extension methods

In the above example, I used an extension method named StringConcatenate. An extension method is, as the name implies, an "extension" to a known class. In the following example, find the extension for all IEnumerable<T> instances:

[code:c#]

public static class IEnumerableExtensions
{
    /// <summary>
    /// Concatenate strings
    /// </summary>
    /// <typeparam name="T">Type</typeparam>
    /// <param name="source">Source</param>
    /// <param name="func">Function delegate</param>
    /// <returns>Concatenated string</returns>

    public static string StringConcatenate<T>(this IEnumerable<T> source, Func<T, string> func)
    {
        StringBuilder sb = new StringBuilder();
        foreach (T item in source)
            sb.Append(func(item));
        return sb.ToString();
    }
}

[/code]

Lambda expressions

Another thing you may have noticed in my example code, is a lambda expression:

[code:c#]

z => z.Name == w_r || z.Name == w_ins || z.Name == w_hyperlink.

[/code]

A lambda expression is actually an anonymous method, which is called by the StringConcatenate extension method. Lambda expressions always accept a parameter, and return true/false. In this case, z is instantiated as an XNode, returning true/false depending on its Name property.

Wrapping things up...

If you read this whole blog post, you may have noticed that I extensively used C# 3.5's new language features. I combined these with OpenXML and ASP.NET to create a useful Word document preview handler. If you want the full source code, download it here: WordVisualizer.zip (357.01 kb).

kick it on DotNetKicks.com

ASP.NET MVC framework - Security

Some posts ago, I started playing with the ASP.NET MVC framework. In an example I'm creating, I'm trying to add Forms-based security.

"Classic" ASP.NET offers a nice and easy way to set security on different pages in a web application, trough Web.config. In the example I'm building, I wanted to allow access to "/Entry/Delete/" only to users with the role "Administrator". So I gave the following snip a try:

[code:c#]

<location path="/Entry/Delete">
   <system.web>
     <authorization>
       <allow roles="Administrators"/>
       <deny users="*"/>
     </authorization>
   </system.web>
</location>

[/code]

This seems to work in some occasions, but not always. Second, I think it is very confusing to define security in a different place than my route table... Since the ASP.NET MVC framework is built around "dynamically" changing URL schemes, I'm not planning to maintain my Web.config security for each change...

In an ideal world, you would specify permissions for a route at the same location as you specify the route. Since the ASP.NET MVC framework is still an early CTP, perhaps this might be added in future versions. For now, the follwing strategies can be used.

Code Access Security

Luckily, the .NET framework offers a nice feature under the name "CAS" (Code Access Security). Sounds scary? Perhaps, but it's useful in the MVC security context!

The idea behind CAS is that you specify security requirements using attributes. For example, if authentication is required in my EntryController (serving /Entry/...), I could use the following code snippet:

[code:c#]

[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
public class EntryController : Controller {
    // ...
}

[/code]

Now let's try my example from the beginning of this post. The URL "/Entry/Delete" is routed to my EntryController's Delete method. So why not decorate that method with a CAS attribute?

[code:c#]

[ControllerAction]
[PrincipalPermission(SecurityAction.Demand, Role="Administrator"]
public void Delete(int? id) {
   ...
}

[/code]

This snippet makes sure the Delete method can only be called by users in the role "Administrator"

Exception handling

Problem using the CAS approach is that you are presented an ugly error when a security requirement is not met. There are two possible alternatives for catching these Exceptions.

Alternative 1: Using Global.asax

In Global.asax, you can specify an Application_Error event handler. Within this event handler, you can catch specific types of Exceptions and route them to the right error page. The following example redirects each SecurityException to the /Login, my LoginController:

[code:c#]

protected void Application_Error(object sender, EventArgs e) {
    Exception ex = Server.GetLastError().GetBaseException();
    if (ex is SecurityException) {
        Response.Redirect("/Login");
    }
}

[/code]

Alternative 2: Use more attributes!

Fredrik Normén has posted an ExceptionHandler attribute on his blog, which allows you to specify which type of Exception should be handled by which type of view. Hope this makes it into a future ASP.NET MVC framework version too!

Alternative 3: Use in-line CAS

Another option is to use in-line CAS. For example, you can do the folluwing in your ControllerAction:

[code:c#]

try {
    PrincipalPermission permission = new PrincipalPermission(User.Identity.Name, "Administrators", true);
    permission.Demand();
} catch (SecurityException secEx) {
    // Handle the Exception here...
    // Redirect to Login page, for example.
}

[/code]

ASP.NET DataPager not paging after first PostBack?

A few posts ago, I mentioned that I am currently giving a classroom training on ASP.NET. People attending are currently working on a project I gave them, and today one of them came up to me with a strange problem...

Here's the situation: in VS 2008, a web page was created containing 2 controls: a DataList and a DataPager. This DataPager serves as the paging control for the DataList. Databinding is done in the codebehind:

[code:c#]

protected void Page_Load(object sender, EventArgs e) {
    ListView1.DataSource = NorthwindDataSource;
    ListView1.DataBind();
}

[/code]

This works perfectly! When the page is rendered in a brwoser window, data is shown in the DataList control. Now, when testing the DataPager, something strange happens: when a page number is clicked, ASP.NET will process a PostBack, rendering... the same page as before! Clicking the DataPager again is the only way to really go to a different page in the result set.

Let's have a look at the ASP.NET page lifecycle... The page Load event is actually not the best place to call the DataBind() method. PreRender is a better place to call DataBind():

[code:c#]

protected void Page_Load(object sender, EventArgs e) {
    ListView1.DataSource = NorthwindDataSource;
}

protected void Page_Render(object sender, EventArgs e) {
    ListView1.DataBind();
}

[/code]

kick it on DotNetKicks.com

Thank you, ISP!

Living in Belgium sometimes feels like living in the desert. Over the past few years, my ISP has always offered the same: a cable modem subscription with 10Mbit downstream speed, and 256Kbit upstream speed. Great! Except for that data transfer limit of 12 GB per month and the ridiculous price of 42 EUR (that is 61 US$). And no, there are few better alternatives in this center of Europe...

Good news though! My ISP has been teasing its customers with a marketing campaign, focussing on great improvements in every Internet access subscription they offer. Rumours came along on different user forums: price drops, no more data transfer limits, ... Now here's the real improvement: everything stays the same, except you get a faster upload speed (512Kbit, yes!). Data transfer limit stays... (press release)

12 GB of data transfer is filled quite fast at 10Mbps... Here's what I think, and many others too...

 
(For the record: the information on this website represents my personal opinion. My opinion may differ with other people's opinion and my employer's opinion. This website is by no means related to other people nor my employer. )

ASP.NET MVC Framework - Basic sample application

ASP.NET MVC FrameworkYou might have noticed that I'm quite enhousiast about the new ASP.NET MVC framework.

What are you talking about?

Basically, this new ASP.NET MVC framework is an alternative to standard ASP.NET webforms, with some advantages:

  • No more postbacks or viewstate, no more page lifecycle trouble: all communication is done using a REST pattern
  • Separation of concerns: no more pages containing cluttered business logic inside view logic (MVC)
  • Testable model and controller: you can now create uinit tests which communicate with your model as if a user is browsing your website

Is there a tutorial available?

For more information and a step-by-step tutorial, check Scott Guthrie's blog:

My own sample project

For an article I'm working on, I am writing a sample application using this framework. This sample application is a very basic photo album website, listing some albums and photo's. Anyone who's interested in a sample MVC application (no data entry yet!) can download it.

Current shortcomings...

There are some shortcomings in the current CTP... Current databound controls can not be used easily. There are some ways around, but using a simple <% foreach ... %> is currently the easiest way to display data on your web page. Another way around is the MVCToolkit project, which adds support for some helper methods and classes.

ASP.NET 3.5 Extensions CTP preview released

Just over the weekend, Microsoft has released the ASP.NET 3.5 Extensions CTP. This download includes several additions to ASP.NET 3.5:

  • ASP.NET AJAX Improvements contains some new AJAX features like browser history support (back-forward), improvements to the JavaScript library, ...
  • The new ASP.NET MVC framework which I blogged about last week
  • ASP.NET Dynamic Data Support contains some new features for building a data-driven application much faster.
  • ASP.NET Silverlight support
  • ADO.NET data services, also known under the name "Astoria".

I'll be doing some testing during the week, and keep you all informed.

LINQ to filesystem

The past few hours, I've been experimenting with LINQ. As a sample application, I'm trying to create a small photo album website, which shows me all images in a specific folder on my webserver.

What does LINQ have to do with that? Everyone has used a loop over all files in a folder, and I decided to try LINQ for that matter. Here's how:

[code:c#]

var rootFolder = "C:\\";
var selectedImages = from file in Directory.GetFiles(rootFolder, "*.jpg")
                             select new { Path = file,
                                          Name = new FileInfo(file).Name,
                                          CreationDate = new FileInfo(file).CreationTime,
                                          DirectoryName = new FileInfo(file).DirectoryName
                                    };

[/code]

There you go! A collection named "selectedImages", filled with anonymous class instances containg a file Path, Name, CreationDate and DirectoryName. This collection can now be bound to, for example, a GridView:

[code:c#]

this.gridView1.DataSource = selectedImages;
this.gridView1.DataBind();

[/code]

EDIT: (mental note to myself: add LINQ keywords to syntax highlighter...) - done!

ASP.NET MVC framework preview to be released next week

Half the world has been focussing on the release of the new Visual Studio 2008 and .NET 3.5 last week. That is good, as .NET 3.5 offers lots of nice new features and improvements. In the blogosphere, I haven't read much about an extension I've been waiting for anxiously: the new ASP.NET MVC framework.

Luckily, Scott Guthrie posted some examples on it, and I can't wait for a preview to be released next week. I'll keep you informed!

ASP.NET load balancing and ASP.NET state server (aspnet_state)

At one of our clients, we used to have only one server for ASP.NET applications (including web services). Since this machine is actually business-critical and load is constantly growing, the need for a second machine is higher than ever.

This morning I was asked to set up a simple demo of a load-balanced ASP.NET environment. I already did this in PHP a couple of times, but in ASP.NET, this question was totally new to me. Things should not be very different, I thought. And this thought proved right!

A bit later, we had a load balancer in front of 2 web server machines. We got everything configured, fired up our webbrowser and saw a different page on each refresh (stating the server's hostname). Load balancing mission succeeded!

Next thing: session state. In our PHP environment, we chose to centralize all session data in a database. ASP.NET provides the same functionality, but we chose to use the ASP.NET state server for this demo. This proved to be a difficult yourney... But we managed to get things running! Here's how.

1. Set up the ASP.NET state service

Pick a server which will serve as the session state server. Fire up the services control panel (services.msc). Select the "ASP.NET State Service" item and make it start automatically. Great! Our state service is running.

Caveat 1: state server will not listen on any public IP address. So fire up your registry editor, change the following key and restart the ASP.NET state service:

HKLM\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\AllowRemoteConnections

Eventually change the port on which the state server will be listening:

HKLM\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\Port (default: 42424)

Caveat 2: after changing the AllowRemoteConnections directive, make sure the server's port 42424 is NOT open for the Internet, just for your web servers!

2. Make both ASP.NET servers use the state server

Every Web.config file contains a nice configuration directive named "sessionState". So open up your Web.config, and make it look like this:

[code:xml]

<?xml version="1.0"?>
<configuration>
    <system.web>
        <!-- ... -->
        <sessionState
            mode="StateServer"
            stateConnectionString="tcpip=your_server_ip:42424"
            cookieless="false"
            timeout="20" />
        <!-- ... -->
    </system.web>
</configuration>

[/code]

3. So you think you are finished...

...but that's not the case! Our load balancer did a great job, but both servers where returning different session data. We decided to take a look at the session ID in our cookie: it was the same for both machines. Strange!

Some research proved that it was ASP.NET's <machineKey> configuration which was the issue. Both web servers should have the same <machineKey> configuration. Let's edit Web.config one more time:

[code:xml]

<?xml version="1.0"?>
<configuration>
    <system.web>
        <machineKey
          validationKey="1234567890123456789012345678901234567890AAAAAAAAAA"
          decryptionKey="123456789012345678901234567890123456789012345678"
          validation="SHA1"
          decryption="Auto"
        />
        <!-- ... -->
        <sessionState
            mode="StateServer"
            stateConnectionString="tcpip=your_server_ip:42424"
            cookieless="false"
            timeout="20" />
        <!-- ... -->
    </system.web>
</configuration>

[/code]

(more on the machineKey element on MSDN)

Also check MS KB 325056, this was an issue we did not meet, but it might save your day.

4. Great success!

Our solution now works! Only problem left is that we have a new single point of failure (SPOF): the ASP.NET state service. But we might just set up 2 of those and fail over both session service machines.

UPDATE 2008-01-23: Also check out my blog post on Session State Partitioning!

kick it on DotNetKicks.com