Tuesday, 1 December 2009

Expression Web SuperPreview

Recently I've discovered a relatively new product from Microsoft for web development (Asp.Net or PHP) called Expression Web. The product itself looks more or less like Visual Studio with less options available ;)

The functionality that caught my eye was SuperPreview option. It allows developers to render the pages your working on using chosen browser engine within your development area. Available are all popular versions of IE and any other browser installed on your machine. It actually uses the real IE6 engine instead of any emulators. It also allows you to display the same page on multiple layers, each rendered using different engine. This allows you to find the differences much quicker.

You can read about this cool option in this article.

There is also a separate version of SuperPreview which doesn't require Expression Web but it only supports IE. You can download it from here. Here is how my blog looks like when IE6 & IE8 overlay:



PS. I just wish that SuperPreview functionality was available in VS2010. The Expression Web seems as a new product only from marketing perspective. Its designer/developer area offers exactly the same options as in VS (code/design/split views), except the SuperPreview of course. Since VS already supports ASP.NET development, it would be logical to simply add PHP support to it instead of releasing a new product. When I asked tech guys from Microsoft they couldn't really explain this, which I think proves they think the same :)

Monday, 30 November 2009

PHP on Windows

According to Microsoft 70% of PHP developers are working on Windows, although the majority of PHP services are hosted on Linux (in my case this was actually true so maybe they are right about that). This leads to situation where most of the apps are being developed in an environment different than they are deployed to. This is obviously not the best way to that.

Microsoft claims there are 2 main reasons for that:

  1. Installation process

  2. Although there are many packages available for one-click installation of all necessary PHP components on Windows (e.g. WAMP) they are all configured to work with Apache server. Although IIS is recommended server to be used for PHP on Windows (to get most of what the system offers), developers are afraid of configuring/using it with their apps.

    Microsoft decided to change it by introducing Web Platform Installer. This free tool will download, install and configure all components required to run web applications on your machine for you (both Asp.net and PHP). It also offers installation of popular OpenSource scripts/engines (e.g. for blogs, galleries, etc.) so you can start development faster.

    This seems to be a step in a good direction and makes configuration of PHP and IIS easy like never before. However, there are still some some weak points in my opinion:

    • Developer is limited to one version of PHP (what if you need to install older version?)

    • The installer supports mainly Microsoft technologies e.g. drivers/extensions for MsSql. I haven't seen any option for installation of MySql extensions.

    • Some very popular php apps are missing e.g. phpBB (probably MS promotes an equivalent supported by them)


  3. Performance

  4. This one is actually more important than installation. Microsoft admits that PHP did not performed well on older versions of IIS. However, together with introduction of IIS7 most of the performance issues should have been resolved. The key aspects for improving performance are:

    • Microsoft contributed to creation process of PHP build for Windows e.g. rewritten parts of the code to make better use of the system

    • Implementation of FastCGI for Windows (can by installed with Web Platform Installer)

    • Windows Cache Extensions for accelerating PHP

    • Ability to work in a cloud (thanks to Windows Azure)


    I asked the speaker about any reliable benchmarks comparing performance of PHP in Windows vs. Linux environment that would prove that it is worth considering deployment of my PHP apps to Windows. Unfortunately this strongly depends on the app itself so there is no simple answer to that question.

Soo, who's convinced?

BTW. When I asked the Microsoft guy about examples of large, commercial PHP applications deployed on Windows and performing well he answered that there are some but he's not allowed to name them ;)

Tuesday, 17 November 2009

Code comments generators

Writing comments for your code is not the most interesting part of development. I admit that sometimes it's hard to follow company/project guidelines for code comments in a restrictive way, mostly because the lack of time (I know that's a pure excuse).

Some time ago somebody recommended me to use GhostDoc, which would take care of that for me. Tried it today - simple Shift+Ctrl+D combination and your code is fully commented. Some of the generated comments were even quite descriptive. After quick investigation I found out that all the descriptive comments were copied from base classes (e.g. when method overrides the one in the base class, implements interface member etc). For the new members the comments were generated basing on their names.

In my opinion this could be a handy tool if used correctly i.e. only for generating draft versions that would be corrected/extended manually. However, there is a great danger that most of generated comments would be left unchanged (especially if time is limited). Although it would cause all possible StyleCop rules to pass it would still affect the code quality. I will uninstall it right now because I don't want to be tempted when a deadline is close ;) We don't really need comments that don't introduce any new knowledge.

I'm wondering if there are any positive aspects of using them that I didn't think of? Are there any developers who admit that they're using them and can give some examples proving that this can be very useful?

Friday, 30 October 2009

Asp.Net: Validating data type using CompareValidator

CompareValidator, as the name says, is mainly used to compare value of specified control against value of another control, constant, etc. You can specify the operator that can be used in comparison so you are not limited to checking equality. The following example demonstrates simple comparison of int values (max > min):

Min: <asp:TextBox ID="TextBox1" runat="server"/><br />
Max: <asp:TextBox ID="TextBox2" runat="server"/>
<asp:CompareValidator
ID="CompareValidator1"
runat="server"
ControlToCompare="TextBox1"
ControlToValidate="TextBox2"
ErrorMessage="Max must be greater than Min"
Operator="GreaterThan"
Type="Integer">
</asp:CompareValidator><br />
<asp:Button ID="Button1" runat="server" Text="Submit" />

Which results in:
Results of comparison against different control

Validating data type
Except the basic functionality CompareValidator supports validation of data types. Except the regular operators (Equal, GreaterThan, GreaterThanEqual, LessThan & LessThanEqual) CompareValidator supports an additional one called "DataTypeCheck". It can be used to validate the type of provided value against the type specified in validator. The supported types are: String, Integer, Double, Currency & Date. The validator mainly checks the format of the provided value. In case of Date type it also checks the range (e.g. to ensure that 31st of April is not valid). I like this feature, especially that I've already seen a quite complex custom validator written to do exact this task. Example:

Date: <asp:TextBox ID="TextBox1" runat="server" />
<asp:CompareValidator
ID="CompareValidator1"
runat="server"
ControlToValidate="TextBox1"
ErrorMessage="Invalid date"
Type="Date"
Operator="DataTypeCheck"/>
<asp:Button runat="server" Text="Submit"/>

Which results in:Invalid date provided

Using right format
When validating the type Asp.Net uses the current Page Culture. If you'd like only to accept inputs in specified format you can set the Page Culture property manually. The same date "11.30.2009" will be validated differently for UK and US Cultures (in UK "DD.MM.YYYY" is used, in US it's "MM.DD.YYYY"):

UK:
<%@ Page ... Culture="en-GB"%>

Date invalid for Culture en-GB

US:
<%@ Page ... Culture="en-US"%>

Date valid for Culture en-US

You can also set the page Culture from your code by overriding InitializeCulture() method:

protected override void InitializeCulture()
{
Culture = "en-US";
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("en-US");

base.InitializeCulture();
}

Monday, 19 October 2009

The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate,NTLM

I've deployed a WCF service to IIS with security mode set to "Message":





When I tried to called it from my client app I got the following error:

The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate,NTLM'.

I googled out that this is caused by security settings of the service virtual directory. I configured it to use Integrated Windows Authentication rather than allowing Anonymous access. I did so because I wanted to restrict the access to my service.

Solution:
It came out that using Anonymous Access in this particular case (WCF service in Message security mode) doesn't mean that anyone is allowed to use the service. The authentication is performed by WCF rather than IIS, but it still takes place.

Answer found here.

Tuesday, 28 July 2009

ASP.NET: How to trigger the client-side validation manually

By default the client-side validation is triggered when submitting forms using buttons. However, sometimes you may want to trigger client-side validation on your ASP page manually from custom Javascript. You can achieve that by calling Javascript validation functions provided by the ASP.Net framework directly from your custom code.

The following page source example displays a TextBox and its validation controls (RequiredFieldValidator & ValidationSummary). The validation controls have the same ValidationGroup defined, which allows us to validate different page elements independently. The page displays also a DIV element that will cause the Validation action when clicked:
<!-- Validation Summary -->
<asp:ValidationSummary ID="ValidationSummary1" runat="server"
HeaderText="Validation errors:" ValidationGroup="Group1"/>

<!-- TextBox and its validator -->
Name: <asp:TextBox ID="TextBox1" runat="server" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server"
ErrorMessage="Name is required" Text="*"
ControlToValidate="TextBox1" ValidationGroup="Group1"> />

<!-- Div that causes client-side validation when clicked -->
<div onclick="Validate();" >Validate Form</div>

The code above should should produce smth like that when validation is triggered:

Validation form
Now let's take a look at the custom JS code that triggers the validation. There are couple ways to do that:
  • Easy way - works for all validators from the same ValidationGroup:
    function Validate()
    {
    // If no group name provided the whole page gets validated
    Page_ClientValidate('Group1');
    }
  • If you want to validate only specific validators:
    function Validate()
    {
    // Get the specific validator element
    var validator = document.getElementById('RequiredFieldValidator1');

    // Validate chosen validator
    ValidatorValidate(validator);

    // Update validation summary for chosen validation group
    ValidatorUpdateIsValid();
    ValidationSummaryOnSubmit(validationGroup);
    }

Useful links:

Friday, 3 July 2009

C# - Array to Dictionary

Recently I needed to convert an Array of DictionaryEntry<object,object> items into a Dictionary<string,string> object. Here is simple code using LINQ and ToDictionary method for doing that with 1 command:
DictionaryEntry<object,object>[] props = GetSomeProperties();
Dictionary<string,string> dict =
props.ToDictionary(de => (de.Key != null) ? de.Key.ToString() :
string.Empty);
de => (de.Value != null) ? de.Value.ToString() :
string.Empty);

Isn't that simple? :)

Friday, 26 June 2009

Windows could not start the IIS Admin Service on Local Computer

Sometime when starting IIS I'm getting the following error:

Windows could not start the IIS Admin Service on Local Computer. For more information, review the System Event Log. If this is a non-Microsoft service, contact the service vendor, and refer to service-specific error code -2147417831.

Event log is not much help:

The IIS Admin Service service terminated with service-specific error 2147549465 (0x80010119).

Solution:
Kill the inetinfo.exe process and start IIS again - that's it :)

Thursday, 18 June 2009

Handling exceptions from multiple UpdatePanels

In one of my previous posts I've already mentioned how to handle exceptions that occur within UpdatePanel on client side (see post). However, this did not cover server side error handling nor multiple UpdatePanels on one page.

Sometimes you may want to have on your ASP page multiple update panels or a large one containing multiple controls. The problem is that when an error occurs on update action you may find hard to localize the actual control that generated the error causing event. This may be required in some situations e.g. to display error message next to the control responsible for the exception.

Let's take a look at some code. Here is a sample ASP page with a ScriptManager and 2 UpdatePanels. Each of the panels contains a button and a label for displaying errors:
<asp:ScriptManager ID="ScriptManager1" runat="server" 
onasyncpostbackerror="ScriptManager1_AsyncPostBackError">
</asp:ScriptManager>

<!-- First Update Panel -->
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Button ID="B1" runat="server" Text="B1" onclick="B1_Click" />
<asp:Label ID="Label1" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>

<!-- Second Update Panel -->
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<asp:Button ID="B2" runat="server" Text="B2" onclick="B2_Click" />
<asp:Label ID="Label2" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
As you can see the ScriptManager has an error handler defined (line 2) and both buttons have click handlers assigned. The error labels are empty on entry.
Here is Code Behind for that page:
  protected void B1_Click(object sender, EventArgs e)
{
throw new Exception("Exception from Button1");
}

protected void B2_Click(object sender, EventArgs e)
{
throw new Exception("Exception from Button2");
}

protected void ScriptManager1_AsyncPostBackError(
object sender, AsyncPostBackErrorEventArgs e)
{
// Do smth with the error e.g log it
}
Clicking on any of the buttons will cause an Exception. The exception message varies for each button. In my example the error handler for script manager does nothing but you can do whatever you need with the caught error e.g. log it.

Now, let's handle the error on client side. The following code will register client side error handler, which will display a message from the thrown exception next to appropriate button:
// Register EndRequest handler for ScriptManager
Sys.WebForms.PageRequestManager.getInstance().
add_endRequest(EndRequestHandler);

function EndRequestHandler(sender, args)
{
// If there is an unhandled error
if (args.get_error() != undefined)
{
// Get id of the control that fired the error-causing action
var senderControlId = sender._postBackSettings.sourceElement.id;

// Get Label next to the source control
var errorLabel = document.getElementById(senderControlId).
nextSibling.nextSibling;

// Set error message in appropriate control.
// You may want to remove the user-unfriendly part of the
// message that was added by Script Manager
errorLabel.innerHTML =
args.get_error().message.replace
('Sys.WebForms.PageRequestManagerServerErrorException: ', '');

// Mark the error as handled
args.set_errorHandled(true);
}
}

That's it. To make it work just place the JS code on your ASP page.

Thursday, 11 June 2009

Centering Adsense Ads on blogger pages

Recently I decided to add Adsense ads on my blogger page to earn zillions of $$$$. Blogger dashboard offers easy to use Adsense integration (Monetize tab). It helps you to create Adsense account and automatically places the ads on your blog page. The problem is that it doesn't offer much control over the way the ads are displayed, soe you can't e.g. center your ad, align it in to the right etc. To achieve that you'll need to create the ad manually through the Adsense page (see Adsense help) and then paste the code into your blogger template:
  1. From blogger dashboard got to:  Layout > Edit Html

  2. Check the "Expand Widget Templates" checkbox to see the detailed view of your template

  3. Find the place in the template where you'd like to place the ad.

  4. Create a div element that would store your ad and style it as much as you want (center it, add padding, etc.)

  5. Paste the code generated by Adsense into the div

  6. Change the following characters in the generated Adsense code:

    • < change to &lt;

    • > change to &lt;

    • " change to &quot;

    so the code for your Ad looks somehow like this (changed lines: 1, 2, 3, 5 & 8):
    <script type='text/javascript'>&lt;!--
    google_ad_client = &quot;pub-5***************&quot;;
    google_ad_host = &quot;pub-1***************&quot;;
    /* name of your ad */
    google_ad_slot = &quot;7*********&quot;;
    google_ad_width = 160;
    google_ad_height = 600;
    //--&gt;
    </script>
    <script src='http://some_google_linkto_js_file'
    type='text/javascript'>
    </script>
    It is important that you do this, otherwise you'll get a JS error 'pub is undefined' (or something similar) on the page and the ad will not be displayed.
Solution and automatic converter found here.

Friday, 29 May 2009

JS: Display tooltip over page element

To display a tooltip over a page element:
  1. Create hidden tooltip element:
  2. Create JS scripts for showing/hiding tooltip:
    function show()
    {
    var tooltip = document.getElementById('tooltip');
    tooltip.style.top = event.clientY;
    tooltip.style.left = event.clientX + 10;
    tooltip.style.display = 'block';
    }

    function hide(){
    var tooltip = document.getElementById('tooltip');
    tooltip.style.display = 'none';
    }
  3. Set Javascript events for the chosen page element:

    Element content
Tested on IE7.

Monday, 4 May 2009

The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

A piece of code that I've been working on lately started to throw exceptions when calling a web service using a generated client. The log file contained only the exception message:

"The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state."

Most of the suggestions found in Web blamed the invalid code, giving examples similar to the following one:

using (MyWebServiceClient service = new MyWebServiceClient())
{
service.DoSomeOperation();
service.Close();
service.DoAnotherOperation();
}
Since I was sure this was not the case I had to discover the real exception reason by myself.

Solution:

The using statement in the form presented above will mask any errors coming from the web service client and throw the "faulted state" exception instead, which may be irrelevant to the actual problem. To find out the real issue you can examine the InnerException property of the exception thrown (e.g. by logging it or debugging your app).

Alternatively you can add an additional try/catch to ensure that the real exception is thrown by your application rather than the "faulted state" one:

try
{
MyWebServiceClient service = new MyWebServiceClient()
service.DoSomeOperation();
service.Close();
}
catch (CommunicationException e)
{
service.Abort();
}
catch (TimeoutException e)
{
service.Abort();
}
catch (Exception e)
{
service.Abort();
throw;
}

You can read more about the reason for the "faulted state" exception on MSDN page (Thanks to Carl for the link).

Tuesday, 14 April 2009

Join the Java Black Belt community

I'm recently working mainly on .NET projects but would like to stay on top of Java technology stack as well. That's why I've just joined the Java Black Belt community.
It offers many free java exams created by community users. In order to get the black belt you need to contribute to the portal so the amount of testing material is still growing.
It's a great way to refresh your java knowledge. I hope that this belt-system will keep me motivated so I not only prove my current skills but also learn something new.
After I get to the certain level (let's say the blue belt) I'll update my blogger profile to display the belt icon so you can see my progress directly from this page.

Tuesday, 24 March 2009

Debugging .NET applications

Recently I attended C2C conference in Warsaw. I signed up for the .NET track. The session I was really looking forward to was “Hardcore Production Debugging” by Ingo Rammer. I hoped to learn something that I could use right away in my every day work. Remote debugging may be very painful and it is not easy to find the best approach. I was keen to learn about new easy-to-use tools that would make my life much easier. Unfortunately, the word “Hardcore” in session’s title was there for a reason…

The biggest part of the session was dedicated to WinDBG tool. Its interface is very poor and user-unfriendly. However, Ingo demonstrated that once you learn how to use it, it can be very helpful in finding the reason for application failures.

At first Ingo simulated situation where the developer has access to production machine (with no Visual Studio installed) where they can run WinDBG. Once he started this tool he attached it to the process of sample application that was throwing unhandled exceptions. With a few commands he was able to find out the exception message and the reason for failure. With the same tool Ingo was able to find out the reason for unexpected application exit by attaching breakpoint at the end of app’s process.

Ingo showed us also that remote debugging can be achieved with the WinDBG installed on developers machine and its console version – CDB – on client’s machine. After you configure the port for CDB you can access it from outside using WinDBG (option “Connect to remote session”).

Next, Ingo explained what to do when WinDBG and CDB are also not available on the production machine. He proved that we can find the same error as in the first part by studying the memory dump created for that application. To get the memory dump we can ask the client to create it manually using a simple program (Adplus) or OS built-in functionality (only on Vista) immediately after the error occurs. Then, all we need to do is load the dump into WinDBG (option “Open crush dump”) on the developer’s machine and examine it using the same commands as in the first case.

Finally, Ingo presented simple program GFlags for Windows registry keys modifications that would allow launching CMD/WinDBG immediately after specified process is started and attaching the debugger to this process. This can be handy when an error does not occur regularly and it’s not easy to reproduce. He warned us that you have to be very careful when using that tool and change system’s default settings only if you’re absolutely sure what you’re doing. Registry modifications can cause a lot of damage to your OS if the are not done correctly.

To sum up - the pase of presentation was high so it was not possible to learn all the commands that Ingo was using. The goal of this session was to show to the audience what is possible if we use the presented tools and that it doesn't require much work once you learn how to use them.

The session itself came out to be the highlight of the conference. Mixture of Ingo’s knowledge and charm proved to be the formula for perfect presentation. I just wish Ingo had some more time so we could study the examples more carefully.

Some WinDBG commands used by Ingo:

.hh Debugger Help
sxe clr Break on all CLR Exceptions
sxi clr Ignore CLR Exceptions
sxn clr Notify on CLR Exception

.loadby sos mscorwks
Loads SOS Debugging Extension;
Enables use of following options:

!help
!printexception (!pe)
!clrstack

~ list threads
!threads list managed code threads

Some Useful links:
All presented debugg tools for Windows
Ingo's page at Thinktecture
Tess Fernandez blog “If broken it is, fix it you should”

Ingo also recommended book "Debugging .NET" by John Robins.

Wednesday, 4 March 2009

Syntax highlighter toolbar covers first code line

As mentioned in my previous post I'm using Syntax Highlighter now to display code snippets. Appart slow loading it's working fine and it's doing its job. The only thing that was annoying to me was that the highlighter's toolbar was covering part of the first code line if it was too long:


Solution:
Solution is quite simple: I decided to lift it up a bit by overwritting toolbar's style:

Place this style definition anywhere in <head> and the result should be similar to that:

To be honest, I would like the toolbar to be displayed below the code snippet, aligned to the bottom border but this would require bit more styling. Let me know if you achieve that!

Monday, 2 March 2009

Code snippets on blogger

I decided to adjust the way I display code snippets on my blog. The old way of displaying wasn't very good and required some additional formatting. I've found a perfect solution for that: Syntax Highlighter. This allows you to easily display code snippets in a nice and accessible way. The code is colorised according to its type: e.g.

Xml:


sample

Java:
class HelloWorld
{
public static void main(String args[])
{
System.out.println("Hello World!");
}
}

The usage is really simple. In addition all required CSS an JavaScript files are available online so you can refer them from your blogger page! Can't wait to submit some more code ;)

Quick Note: When using this on blogger you'll need to turn the bloggerMode on. You can do it in JS:
SyntaxHighlighter.config.bloggerMode = true;
.

Sunday, 1 March 2009

PHP: Redirection fails after moving to new server

Recently one of my old php apps was moved to different server.
After the move it came out that some pages didn't work as expected i.e. some redirections were failing. The redirections were implemented using header function e.g.
header('Location: http://www.targetpage.com/');

When I checked the php logs I found common error "Headers already sent". Don't want to explain here what does it mean - there's plenty of answers here ;) The only thing that made me wonder was why it was working on the old server. When I compared the php.ini files from both servers I found out that the new one was configured not to useoutput buffering. The temporary fix was enabling this by setting in php.ini:
output_buffer=On

This is not the cleanest solution and may affect application's performance but works immediately. The long-term solution is to ensure that your app is not sending any output before calling the header method.

Friday, 20 February 2009

MsBuild: How to copy/delete all files from subfolders

Copying entire folder content (with subfolders) using standard MsBuild is not as straightforward as I'd imagine this to be. The only solution I've found uses recursion. Following example copies all files from source to dest folder:

C:\source
C:\dest


<AllFolderFiles Include="$(SourceDir)\**\*.*" />

<Copy SourceFiles="@(AllFolderFiles)"
DestinationFiles="@(AllFolderFiles -> $(DestinationDir)\
%(RecursiveDir)%(Filename)%(Extension)')" />

You can also use recursion to delete all files in folder and its subfolders:

C:\source


<FilesToDelete Include="$(ToDelete)\**\*.*" />

<Delete Files="@(FilesToDelete)" />

Anybody knows easier solution?

Thursday, 19 February 2009

Automatic webapp to IIS deployment using MsBuild

Sometimes, when buidling/deploying your projects standard MsBuild tasks are not enough. I've found a very handy SDC tasks library which allows you to do much more from your build script.

The following example demonstrates creation of application pool and website using this AppPool on IIS. Normally this would need to be done manually. Thanks to SDC we can automate the whole process to eliminate possible errors that can occure during manual deployment.

<!-- Use SDC tasks -->
<UsingTask AssemblyFile="Microsoft.Sdc.Tasks.dll" />

<!-- Define website folder -->
<PropertyGroup>
<WebSiteDestDir>
C:\Inetpub\wwwroot_mywebsite
</WebSiteDestDir>
</PropertyGroup>

<!-- Main task -->
<Target Name="Deploy_Webapp">
<CallTarget Targets="CleanIIS" />
<CallTarget Targets="CreateWebSite" />
<CallTarget Targets="CopyContent" />
</Target>

<!-- Clean IIS: remove existing AppPool and Website -->
<Target Name="CleanIIS">
<Web.WebSite.Delete Description="myWebapp" />
<Web.AppPool.Delete AppPoolName="myAppPool" />
</Target>

<!-- Create application pool -->
<Target Name="CreateAppPool" >
<Message Text="Creating my AppPool" />
<Web.AppPool.Create AppPoolName="myAppPool"
WorkerProcesses="1"
IdentityType="NetworkService"
ContinueOnError="false"/>
</Target>

<!-- Create website -->
<Target Name="CreateWebSite" DependsOnTargets="CreateAppPool">
<Message Text="Creating WebSite" />
<Web.WebSite.Create Description="myWebapp"
Path="$(WebSiteDestDir)"
HostName="myWebapp"
AppPoolId="myAppPool"
ContinueOnError="false" />
</Target>

<Target Name="CopyContent" DependsOnTargets="CreateWebSite">
<!-- Copy the app content to WebSiteDestDir here -->
</Target>
Using this library you can also create ActiveDirectory user, virtual directories and a whole lot more!

Wednesday, 11 February 2009

Losing session data on IIS

I had a problem with asp.net app losing session data. The same app was working like a charm on my local machine but after deploying to dev server it was throwing exceptions from time to time caused by missing session variables.

My webapp was using default sessionState settings with mode set to 'InProc' and it was configured to use default application pool. The problem was caused by the application pool using Web Garden with 5 worker processes. When using InProc mode the worker processes DO NOT share session data!

2 possible solutions:
  1. Configure application pool to use 1 worker process: in IIS Manager right click yout application pool > Properties > Performance > Web Garden section
  2. Use different session state mode e.g. state server or sql server (set in web.config)

Tuesday, 10 February 2009

Defining IBatis connection string in web.config

If you are using Ibatis you may want to move the connection string from the SqlMap.config file to the Web.config e.g. to enable its encryption (see previous post).

Solution:
Define your database connection in SqlMap.config as follows:

<database>
<provider name="sqlServer2.0"/>
<dataSource name="DB" connectionString="${dbConnStr}"/>
</database>

Define connection string in Web.config:
<configuration>
...
<connectionStrings>
<add name="dbConnStr" connectionString="some conn str"/>
</connectionStrings>
...
</configuration>

Then, manually configure the builder when initializing the mapper:

// Create builder
DomSqlMapBuilder builder = new DomSqlMapBuilder();

// Get connection string from web.config
ConnectionStringSettings dbConnStr =
ConfigurationManager.ConnectionStrings["dbConnStr"];

// Set dbConnStr property to
// populate in sqlmap.config
NameValueCollection properties = new NameValueCollection();
properties.Add("dbConnStr", dbConnStr.ConnectionString);
builder.Properties = properties;

// Create mapper
ISqlMapper mapper = builder.Configure("SqlMap.config");
IBatis framework is quite handy in use but the documentation is very poor. For more advanced questions you'll need to digg through fora and blogs. I've found this tip here.

Monday, 9 February 2009

Encrypting sections in web.config

Sometimes you want to ensure that your settings (e.g. connection strings) in web.config file are encrypted so nobody except the app iteslf can read/understand them. ASP.Net offers tool called aspnet_regiis which allows that. It can be found in the %WINDOWSDIR%\Microsoft.Net\Framework\version directory.

Solution:
Let's say we have a web app deployed on IIS called 'MyApp'. The app uses connection string defined in web.config:
<configuration>
...
<connectionStrings>
<add name="myConnectionString"
connectionString="some connection string"/>
</connectionStrings>
...
</configuration>

The easiest way to encrypt presented connection string is to invoke following command:
aspnet_regiis
-pe "connectionStrings" -app "/MyApp"

It is also possible to encrypt web.config providing physical path to the application folder rather than app name (e.g. if app is not deployed on IIS):
aspnet_regiis 
-pef "connectionStrings"
"physical path to app root folder"

The encrypted information in the web.config can still be accessed by your app without any explicit decoding. Aspnet_regiis tool can be also used to descrypt information, encrypt different sections etc. You can learn more about it here.

Warning
If the encoding succeeds but tha app cannot read the encrypted section because of following error:
"Failed to decrypt using provider 'RsaProtectedConfigurationProvider'. Error message from the provider: The RSA key container could not be opened."

you have to add following parameter to your encryption command:
aspnet_regiis 
-pe "connectionStrings" -app "/MyApp"
-prov DataProtectionConfigurationProvider

Thursday, 5 February 2009

Adding tooltips to list items in DropDownList

I needed to add tooltips to the items on the standard DropDownList asp control. I'm using custom data source to retrieve the list of items rather than static list defined in html.

Solution:
Add a ondatabound event handler to your DropDownList control:
<asp:DropDownList runat="server"
DataSourceId="MyCustomDatasource"
DataTextField="SomeName"
DataValueField="SomeValueCode"
ondatabound="ApplyOptionTitles"
... />
Then implement handler:
protected void ApplyOptionTitles(object sender, EventArgs e)
{
DropDownList ddl = sender as DropDownList;
if (ddl != null)
{
foreach (ListItem item in ddl.Items)
{
item.Attributes["title"] = item.Text;
}
}
}
Answer found here.

Wednesday, 4 February 2009

Handling exceptions in UpdatePanel & UpdatePanelAnimationExtender

UpdatePanelAnimationExtender allows you to add eye-catching animations to your UpdatePanel indicating that update is in progress. However, it doesn't directly support error handling.

The following code defines UpdatePanelAnimationExtender for UpdatePanel MyUP. The panel will fade out each time the update starts and fade in after successful update.
<cc1:UpdatePanelAnimationExtender
ID="MyUPAE"
BehaviorID="UpdateAnimation"
runat="server"
Enabled="True"
TargetControlID="MyUP">
<Animations >
<OnUpdating >
<Parallel duration="0">
<FadeOut minimumOpacity=".5" />
</Parallel>
</OnUpdating>
<OnUpdated>
<Parallel duration="0">
<FadeIn minimumOpacity=".5" />
</Parallel>
</OnUpdated>
</Animations>
</cc1:UpdatePanelAnimationExtender>
If the code executed during update causes an error the browser may warn you about JS error (caused by actual server side error) but the panel will remain faded out. Browser's warning is usually hard to notice or even not displayed at all.

Solution:
The following JS code allows you handling update errors on client side. It assumes that there is a span element defined on that page for displaying error messages (with ID=ErrorMsg).
<script type="text/javascript">

// Create EndRequest handler for update panel
Sys.WebForms.PageRequestManager.getInstance().
add_endRequest(EndRequestHandler);

function EndRequestHandler(sender, args)
{
// If there is an unhandled error
if (args.get_error() != undefined)
{
// create error message and display it
var errorLbl = $get('<%=ErrorMsg.ClientID%>');
errorLbl.innerHTML = 'An error occurred while completing update.';

// end update panel animation
var upAnimation = $find('UpdateAnimation');
upAnimation._postBackPending = false;
upAnimation.get_OnUpdatingBehavior().quit();
upAnimation.get_OnUpdatedBehavior().play();

args.set_errorHandled(true);
}
}
</script>

Monday, 2 February 2009

"Maximum request length exceeded" error

I have an aspx page using standard FileUpload control. When I tried to upload a file bigger than 4MB the page was not displayed or the following error message appeared: "Maximum request length exceeded". This is caused by maximal allowed size of request accepted by the server. Since the uploaded file is sent in request's body the whole request is even bigger than the file itself.

I've seen some solutions using Application_Error handler defined in Global.asax. This approach is quite straightforward and seems to be a way to go but unfortunately it didn't work for me - the page was still not displayed and the error was not handled.

Solution:
First, set the size of request accepted by the server to maximum (1GB) in web.config. Then, define a custom HttpModule that would check request's length (also in web.config):
<system.web>
<httpRuntime maxRequestLength="102400" />
<httpModules>
<add name="RequestLengthCheck"
type="MyNamespace.RequestCheckModule, MyLibraryName" />
</httpModules>
</system.web>
Now you have to implement the HttpModule that would check request's size and redirect to an error page if it's too big:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace MyNamespace
{
public class RequestCheckModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(app_BeginRequest);
}


void app_BeginRequest(object sender, EventArgs e)
{
HttpContext context = (HttpApplication)sender).Context;

if (context.Request.ContentLength > 4096000)
{
IServiceProvider prov = (IServiceProvider)context;
HttpWorkerRequest wr =
(HttpWorkerRequest)prov.GetService(typeof(HttpWorkerRequest));

// Check if body contains data
if (wr.HasEntityBody())
{
// get the total body length
int reqLength = wr.GetTotalEntityBodyLength();
// Get the initial bytes loaded
int initBytes = wr.GetPreloadedEntityBody().Length;

if (!wr.IsEntireEntityBodyIsPreloaded())
{
byte[] buffer = new byte[512000];
// Set the received bytes to initial
// byted before start reading
int recBytes = initialBytes;
while (reqLength - recBytes >= initBytes)
{
// Read another set of bytes
initBytes = wr.ReadEntityBody(buffer, buffer.Length);
// Update the received bytes
recBytes += initBytes;
}
wr.ReadEntityBody(buffer, reqLength-recBytes);
}
}
// Redirect the user to an error page.
context.Response.Redirect("Error.aspx");
}
}

public void Dispose()
{
}
}
}
I've found this piece of code here. It works perfect. The only thing I don't understand is why the whole request needs to be read for this to work? I've tried to make the redirection right after wr.GetTotalEntityBodyLength(); but it didn't work. Anybody knows why?

Disable all page elements with transparent div

Sometimes you want to disable all page elements at one time. There are many ways to achieve that. I'm using an additional div displayed on the top layer of the page. It covers the whole page content and makes clicking elements on lower layers impossible. The div can be totally transparent so the user still sees the content of the lower layers.

First, you need to place the additional div on your page, right behind the <body> tag:
...
</head>
<body>
<div id="disablingDiv" ></div>
...
Then, you need to create appropriate style for that div. This element needs to be displayed on the top layer:

#disablingDiv
{
/* Do not display it on entry */
display: none;

/* Display it on the layer with index 1001.
Make sure this is the highest z-index value
used by layers on that page */
z-index:1001;

/* make it cover the whole screen */
position: absolute;
top: 0%;
left: 0%;
width: 100%;
height: 100%;

/* make it white but fully transparent */
background-color: white;
opacity:.00;
filter: alpha(opacity=00);
}
Now it is ready to use but not displayed. If you want to enable the div (to disable all underlaying page elements) just invoke the following JS code:

document.getElementById('disablingDiv').style.display='block';
To disable it again:
   document.getElementById('disablingDiv').style.display='none';
You can also play with background color and opacity to create different effects. The div itself can contain some additional elements e.g. button allowing enabling, pictures, etc.
I've created this simple style basing on more complex example of lightbox described here.

Thursday, 29 January 2009

Free Java training courses

At http://www.sun.com/training/glassfish_login.html you can find 6 free 90-days trial courses on follwiong topics:

  1. GlassFish Application Server: Introduction (WMT-SAS-1536)

  2. Designing Java Web Services (WJ-4112-EE5)

  3. Adding Quality of Service and .NET Interoperability to Web Services (WMT-SAS-1543)

  4. Creating Reliable and Secure Interoperable Web Services (WMT-SAS-2544)

  5. Creating Transactional Web Services (WMT-SAS-2545)

  6. Working With the Web Services Policy (WMT-SAS-2546)

Sounds interesting to me - you better check that out before the promotion ends!

Thursday, 22 January 2009

DropDownList with OptGroup support

Strangely guys from Microsoft did not implement (forgot???) support for optgroup tag in their standard DropDownList asp control so it's not possible to group items on the list.

Most of the workarounds found in Web suggest use of Control Adapter, like this one. However, there are couple things I don't like in this solution:
  • Doesn't work correctly when DataSource, AutoPostBack or OnSelectedIndexChanged attributes are set
  • Overrides regular regular behaviour & rendering of DropDownList items
  • Works for all DropDownList controls on the page (cannot be switched off for some lists)

Instead, I propose to create our own custom control that extends the standard DropDownList class. We need to override the method RenderContents inherited by DropDownList from System.Web.UI.WebControls.ListControl. When overriding it is very useful to base on original implementation so the default behaviour is preserved. To get the original RenderContents method's code you can use .NET Reflector:


So, your custom list control needs to look as follows:
namespace MyNamespace
{
public class MyList : System.Web.UI.WebControls.DropDownList
{
protected override void RenderContents(
System.Web.UI.HtmlTextWriter writer)
{
// Custom implementation goes here
}
}
}
Once you create your custom DropDownList you can use it on a page just like any other control. You only need to register it explicitly using following directive:
<%@ Register assembly="MyAssembly"
namespace="MyNamespace"
tagprefix="ma" %>
...
<ma:MyList ... />

Advantages of this appraoch:
  • Original behaviour is preserved.
  • You can use custom control only if you need grouping without affecting the rest of drop-downs on page
  • You can implement several custom drop downs with different customizations
  • Should work with AutoPostBack & OnSelectedIndexChanged
I've found sample implementation using this approach here and here.

Saturday, 17 January 2009

Width of FileUpload control in Firefox

I'm using standard ASP.NET FileUpload control and had some troubles changing its width. For almost all browsers the regular width attribute does the job (as expected). However, for some strange reasons it is completely ignored by FF.

Solution:
To change the width of FileUpload control in FF you have to use size attribute instead. Example:
<asp:FileUpload ... size="50" />
The units used by size attribute are characters. Remember to use both size (for FF) and width (for all other browsers) to make the change visible for all users. Sometimes it may be tricky to make this field have the same width on all browsers.

I'm not sure why Mozilla did it this way but I don't like it at all. Any Ideas?

I found solution here.