Wednesday, September 29, 2010

IIS 7.5 – different handling of errors for local vs non-local requests

There are few issues which can be discovered during the migration from IIS 6 to 7.5 (example). One of the issues we’ve been unable to identify for quite some time is connected with “virtual instances” built on top of the url rewriting.

The problem stems from the fact that by default IIS does not handle non-ASP.NET requests using ASP.NET pipeline. This means that while all requests to virtual ASP.NET resources (like http://myhost.com/virtualinstance/theresource.aspx) are handled by ASP.NET (and thus can be rewritten to http://myhost.com/theresource.aspx), all requests to virtual non-ASP.NET resources (like http://myhost.com/virtualinstance/thestyle.css) are not handled by ASP.NET (IIS happily returns 404 since there’s no physical resource under such virtual address).

As I’ve pointed out in the article on virtual instances, our way to overcome this problem was to attach an *.aspx page as a 404 page in the IIS (screenshot taken on IIS 6.0).

This way all requests for non-ASP.NET resources were passed to ASP.NET pipeline as http://myhost.com/ErrorHandler.aspx?404;http://myhost.com/virtualinstance/thestyle.css. This only changes slightly the regular expression you can use to handle “virtual” requests but it’s not a big issue.

Unfortunately, it’s been this trick which turned out to not to work on IIS 7.5. Or, at least, work partially. It turned out that all remote requests were handled correctly, while local requests were handled incorrectly – all “virtual” requests to non-ASP.NET resources were still completely ignored by ASP.NET pipeline. We bravely accepted such behavior for quite some time - business clients send only remote requests so there’s been only an inconvenience for our IT guys who were deploying apps onto a server and trying to quickly verify whether or not the app is running by launching a local browser and pointing it to an app.

But since developers also run & test their applications on their local IISes, they needed a simple solution.

Yes, they could install a virtual machine and send requests between their host machine and the virtual machine (so that all requests would be recognized as remote by IIS).

Another option was to force IIS 7.5 to handle all requests with ASP.NET pipeline:

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true">
     ...
  </modules>
</system.webServer>

but this changes the way ASP.NET sees all virtual requests to non-ASP.NET resources – instead of http://myhost.com/ErrorHandler.aspx?404;http://myhost.com/virtualinstance/thestyle.css you just get http://myhost.com/virtualinstance/thestyle.css. Should IIS be responsible for handling all requests, even to static resources? The question is open but surely, handling all requests with ASP.NET would change the semantics when comparing to IIS 6 so there’s always a chance of some subtle issues and extensive testing would be needed.

Luckily, yet another solution exists. It turns out that the errorMode attribute of the httpErrors section of system.webServer in the web.config which is responsible for storing the mapping of 404 to /ErrorHandler.aspx in IIS 7.5 can be configured in one of three ways:

  • DetailedLocalOnly
  • Detailed
  • Custom

And the problem with IIS 7.5 is that the DetailedLocalOnly is the default setting. This changes the way IIS 7.5 handles errors regarding local requests.

So, to conclude, setting the errorMode to Custom makes the semantics equal for local and remote requests and – to us – also means that the error handling under IIS 7.5 works exactly the same as it did under IIS 6.0.

<system.webServer>
  <httpErrors errorMode="Custom">
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" prefixLanguageFilePath="" path="/ErrorHandler.aspx" responseMode="ExecuteURL" />
  </httpErrors>
</system.webServer>

Thursday, September 16, 2010

The identity of an application pool in IIS

When creating a new application pool, it’s often convenient to be able to identify the identity the pool is using. The issue arises usually on IIS 7/7.5 where default application pools use identities of a form IIS APPPOOL\identityname. And when you grant access to databases/file system you have to provide the identity name in an exact form.

Usually I tend to use this simple script. Just copy it into a text file, name it test.aspx and point your browser to it. The application pool’s identity name will be presented to you in a clear way.

<%@ Page Language="C#" %>
 
Current user: <%= System.Security.Principal.WindowsIdentity.GetCurrent().Name %>

Friday, September 10, 2010

Easy log4net integration into .NET applications

There are plenty of possibilities to integrate log4net logging into your application. You can declare log4net configuration declaratively or imperatively for the whole project, in each separate assembly or even in each separate class. The pattern I describe below assumes that:

  • you want to have a single and possibly the simplest configuration of the log4net for your whole application
  • you do not want to call explicitely any complicated initialization code
  • you do not want to create loggers explicitely (people tend to do that however it’s rather cumbersome)

The idea is as follows – you create a simple proxy class for the logging purposes. The class will have a static constructor responsible for configuring the log4net and two proxy methods for actual logging. It’s up to you to declare this class in the of your assemblies which is then referenced by other solution assemblies.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using log4net.Config;
 
namespace BusinessLibrary.Common
{
    public class Log
    {
        static Log()
        {
            XmlConfigurator.Configure();
        }
 
        public static ILog For( object LoggedObject )
        {
            if ( LoggedObject != null )
                return For( LoggedObject.GetType() );
            else
                return For( null );
        }
 
        public static ILog For( Type ObjectType )
        {
            if ( ObjectType != null )
                return LogManager.GetLogger( ObjectType.Name );
            else
                return LogManager.GetLogger( string.Empty );
        }
    }
 
}

From now on the only way you log anything is to use the proxy class:

Log.For( this ).Info( "Hello from BusinessClass" );

Then, in the application’s configuration file you provide a single configuration for log4net. I usually delegate the configuration to a separate file so that the web.config/app.config contains only:

<configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
    ...
</configSections>
<log4net configSource=".\Configuration\log4net.config"/>

and a separate file, located under \Configuration\log4net.config contains the actual configuration:

<?xml version="1.0" standalone="yes"?>
<log4net>
    <appender name="TestLogFileAppender" type="log4net.Appender.RollingFileAppender">
        <file value="c:\000\test.log" />
        <appendToFile value="true" />
        <rollingStyle value="Size" />
        <filter type="log4net.Filter.LevelRangeFilter">
            <acceptOnMatch value="true" />
            <levelMin value="DEBUG" />
            <levelMax value="FATAL" />
        </filter>
        <maxSizeRollBackups value="10" />
        <maximumFileSize value="10MB" />
        <staticLogFileName value="true" />
        <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%newline%date [%thread] %-5level - %message" />
        </layout>
    </appender>
    <root>
        <level value="ALL" />
        <appender-ref ref="TestLogFileAppender" />
    </root>
</log4net>

(this is only an example configuration)

That’s all. No other code is required to either configure log4net itself or create logging objects. You’ll find an example, consisting of a web application and an auxiliary assembly here.

Friday, June 25, 2010

C# Puzzle No.21 (beginner)

Is it possible to call a method on a null object reference? Of course, not!

static void Main( string[] args )
{
    Foo _foo = null;
 
    // will throw NullReferenceException
    Console.WriteLine( _foo.Bar() );
 
    Console.ReadLine();
}

Oh, really?

Your goal is to provide all necessary auxiliary definitions and implementations so that the code above not only does not throw NullReferenceException but also writes “Hello world” at the console.

Wednesday, June 16, 2010

C# Puzzle No.20 (intermediate)

Suppose you are writing a class for a generic vector and you want to naively implement a method to add two vectors:

public class Vec<T>
{
    public T x { get; set; }
    public T y { get; set; }
 
    public Vec() { }
    public Vec( T x, T y ) { this.x = x; this.y = y; }
 
    public static Vec<T> Add( Vec<T> v1, Vec<T> v2 )
    {
        return new Vec<T>( v1.x + v2.x, v1.y + v2.y );
    }
}

This won’t work. The compiler is unable to interpret the “+” for type T so v1.x + v2.x cannot be compiled.

One of possible approaches could be to restrict possible Ts with a type constraint so that “+” can be replaced with a method of an interface implemented by T. Such approach, however, does not take you anywhere since you cannot impose an interface on an existing type and thus, Vec<int> or Vec<double> would still fail to compile.

Another common approach is to define an auxiliary interface:

public interface IAdder<T>
{
    T Add( T t1, T t2 );
}

so that Vec::Add can be now implemented as:

public static Vec<T> Add( Vec<T> v1, Vec<T> v2, IAdder<T> Adder )
{
    return new Vec<T>( Adder.Add( v1.x, v2.x ), Adder.Add( v1.y, v2.y ) );
}

and used:

public class IntAdder : IAdder<int>
{
    public int Add( int t1, int t2 )
    {
        return t1 + t2;
    }
}
 
class Program
{
    static void Main( string[] args )
    {
        Vec<int> v1 = new Vec<int>( 4, 5 );
        Vec<int> v2 = new Vec<int>( 6, 7 );
 
        Vec<int> v3 = Vec<int>.Add( v1, v2, new IntAdder() );
 
        Console.ReadLine();
    }
}

While this works, there are obvious drawbacks – an extra implementation of the auxiliary interface has to be provided for any T you want to use your Vec class with. Also, the signature of the Add method is now rather clumsy.

Let’s then just start again:

public class Vec<T>
{
    public T x { get; set; }
    public T y { get; set; }
 
    public Vec() { }
    public Vec( T x, T y ) { this.x = x; this.y = y; }
 
    public static Vec<T> Add( Vec<T> v1, Vec<T> v2 )
    {
       // ???
    }
}

Your goal is to provide valid implementation of the Add method so that the returning vector is a sum of two provided vectors and the generic class works for built-in numeric types. You are not allowed to use reflection (or any BCL API) but rather depend solely on the features of the C# language.

Thursday, June 10, 2010

DevExpress XPO – a support for LINQ’s Skip is finally there

A lack of support for LINQ’s Skip has been a huge drawback of the XPO implementation. I am glad to confirm that Skip is finally there. I test it with Microsoft SQL Server 2008 where Skip is evaluated using row_number. For example, a simple query:

new XPQuery<D_Grupa_Roli>( Global.Model )
    .Where( o => o.Oid > 0 )
    .OrderBy( o => o.Nazwa )
    .Skip( 2 )
    .Take( 3 )
    .ToList()
    .ForEach( rola => Console.WriteLine( rola.Nazwa ) );

is evaluated as

exec sp_executesql 
   N'select resultSet.F0, resultSet.F1, resultSet.F2, resultSet.F3, resultSet.F4, resultSet.F5, resultSet.F6 
     from(select N0."OID" as F0, N0."ModifyDate" as F1, N0."ModifyUser" as F2, N0."ID_GLOBAL_DICT" as F3, 
                 N0."Nazwa" as F4, N0."OptimisticLockField" as F5, N0."GCRecord" as F6, 
                 row_number() over(order by N0."Nazwa" asc) as ''rowNumber'' 
         from "dbo"."D_Grupa_Roli" N0
         where (N0."GCRecord" is null and (N0."OID" > @p0))) resultSet 
     where resultSet.rowNumber > 2 and resultSet.rowNumber <= 2 + 3',N'@p0 int',@p0=0

What’s interesting is that skipping throws an exception for an unordered query:

new XPQuery<D_Grupa_Roli>( Global.Model )
    .Where( o => o.Oid > 0 )
     //.OrderBy( o => o.Nazwa ) ordering is missing
    .Skip( 2 )
    .Take( 3 )
    .ToList()
    .ForEach( rola => Console.WriteLine( rola.Nazwa ) );
and the exception says “Can not skip records without ORDER BY clause.

If you think about it, that sounds reasonable as the row_number requires an explicit order.

How’s that possible then that Linq2Sql will not throw an exception for such query? Well, it seems that if the ordering is missing from the query tree, Linq2Sql will …………… .

I am sure the curious reader will find the answer on his/her own.

Wednesday, June 9, 2010

DevExpress XPO – Deferred Deletion

One of the interesting features of XPO is the Deferred Deletion. Basically, instead of physically removing rows from your database, XPO marks them as deleted. Not only this can speed up the deletion but also helps to avoid some subtle concurrency issues.

One of our critical applications uses XPO as the ORM engine. As rows are deleted from the database, I never run a Purge mechanism which is designed to physically remove rows marked as deleted. My intention is simple – because all rows are kept in my database, I can always track any changes made by an end user. Even when a row is deleted, I can always peek into the database and conclude “you see, it’s not that our application deletes your data, in fact it’s been Mr.X/Mrs.Y who deleted it” (I mark every single row with the name of the last user who modifies the record).

There’s unfortunately an inconvenient issue with Deferred Deletion. Suppose you have a class (the class code has been simplified):

public class Person {
public int Oid { get; set; } // primary key
public string Name { get; set; }
public int ID_CITY { get; set; } // foreign key
}
and you delete an instance from the database.
Instead of just marking the GCRecord (a marking column in the database) as deleted, XPO also clears all foreign keys for such row! This is explained in the Remarks section of the documentation:

When a child object of a one-to-many association is deleted, XPO clears its reference to a parent object. This object remains orphan when restored. This behavior is by design, because the Deferred Deletion feature is not intended for the object restoration, but for overcoming complex foreign index constraints at the database level.


What does it mean?

Well, suppose you have a row in your database (NULL, 1, ‘The name’, 1) : (GCRecord, Oid, Name, ID_CITY). When XPO “deletes” it, the row becomes (11442123, 1, ‘The name’, NULL). The GCRecord column marks the row as deleted (with a random not null value) but in the same time the reference to the parent row is cleared.

How that’s possible – you ask – if the ID_CITY has the int type? Well, it does not matter whether you have int or int? in your model, XPO will always create a reference column as INT NULL (sic!).

Ok. So far, so good. Since Deferred Deletion is not designed to restore rows, it may not matter that the record has been altered in a way that it becomes an orphan (parent reference is gone!).

But now, you have a problem if you ever try to access such deleted record (and XPO allows that!) – your model insists that the ID_CITY has an int type, while the database holds a NULL value. XPO is then unable to create an instance of your business class.

Possible solutions?

First solution could be to alter your business classes so that all reference fields would always be of type int?. This does not seem acceptable, since it’s you to decide wheter a reference is required (int) or can be empty (int?) and XPO should not force you to stick with the only possibility.

Second, which works for me is to change the way you handle such reference properties in your business classes. It seems that before a query is sent to the database to update the GCRecord, XPO automatically invokes the setters of all reference properties (and sets them to null). Let’s then change the setter to handle this:

public class Child {
public Parent TheParent
{
get { ... }
set
{
// add this to prevent setting NULL value for your reference property
if ( this.IsDeleted ) return;
...
}
}
}

A remark - XPO claims that the Skip operator is implemented now. I’ll confirm that and write a note.