Sunday, July 5, 2009

Localization with MasterPages - The Final Cut

I'll try to be short and to the point. You may skip all the following text description and go ahead to the code example.

Intro:
Assuming you want your ASP.NET website/web application to have a multi-lingual interface. You'll find multiple resources on the internet on how to use LocalResources files or GlobalResources files to store specific language's data.
Nice Video Tour for Localization.
A specific language resource would be loaded according to the user's browser language preferences.
But most probably you'll need to give the user the option to switch between languages on the fly without going to the browser's preferences....makes sense.

Questions:

1- Where to store user language preference?
Some suggest using a Session variable to store the language preference across the pages. But shouldn't you be storing this for future uses?
Some suggest using a Cookie, but this will be browser specific and/or a machine specific.
IMHO, it would be perfect if the site can remember a user preference regardless of the browser or machine he uses to sign in. That's why storing it in the database might sound like the best option, and I use ASP.NET Profile for that.

Btw, Profile usage is not as easy to use in a Web-Application project as it is in a WebSite project, as no custom class gets created to hold your profile properties defined in the web.config. Please check the code below to know how to manage Profile properties in a Web-Application.

2- How to switch it on the fly?
Simply by setting 2 properties: CurrentThread.CurrentUICulture & CurrentThread.CurrentCulture

3- But where to set them?
Basic answer is to override the InitializeCulture() method on each and every page in your application....not the smartest thing to do.
Another suggestion is to let all your pages inherit from a BasePage class which in turn inherits from the Page class, and there you override the InitializeCulture() method.
The problem is that InitializeCulture() method is never called on cached pages. Ref: InitializeCulture and caching don't mix

I'm using a MasterPage, can I do this in the code behind of the MasterPage.... The answer is a striking NO. The MasterPage doesn't inherit from the Page class, and it has no idea what InitializeCulture() is.

If we want a global location to set the culture, then why not using the Global.asax class to set the culture in one of the events declared there.
That's why many people who don't like to use the BasePage approach sugests using the
Application_BeginRequest() as a good location for this code.
Good point, only that the Profile won't be read at this point, yet... so what's now.
The answer is: Application_PreRequestHandlerExecute(). It's not there by default, you should add it yourself.

Code example (might have a room for refactoring):
Here I use a couple of buttons on the master page for switching the language on the fly, you may use whatever approach you want to use.

public partial class Main : System.Web.UI.MasterPage
{
protected void English_Click(object sender, EventArgs e)
{
SwitchCulture("en-Us");
}
protected void Arabic_Click(object sender, EventArgs e)
{
SwitchCulture("ar-Eg");
}
private void SwitchCulture(string culture)
{
CultureHelper.SaveCulture(culture);
Response.Redirect(Request.Url.AbsolutePath);
}
}

As said before thie event is not there by default when you add the Global class to your application.

public class Global : System.Web.HttpApplication
{
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
CultureHelper.SetCulture();
}
}

I created a class to encapsulate the culture manipulation.

public class CultureHelper
{
public static void SaveCulture(string culture)
{
HttpContext.Current.Profile.SetPropertyValue("Culture", culture);
}
public static void SetCulture()
{
var culture = GetCulture();
if(string.IsNullOrEmpty(culture))
return;
var cultureInfo = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Thread.CurrentThread.CurrentCulture = cultureInfo;
}
private static string GetCulture()
{
var culture = string.Empty;
if (HttpContext.Current.Profile != null)
{
culture = (string) HttpContext.Current.Profile.GetPropertyValue("Culture");
}
return culture;
}

And don't forget the Profile property definition in the web.config

[system.web]
[anonymousIdentification enabled="true"/]
[profile]
[properties]
[add name="Culture" allowAnonymous="true" defaultValue="Auto" type="string"/]
[/properties]
[/profile]
...
[/system.web]

Tuesday, February 17, 2009

"Agile Simplified" is being showcased

I just received an e-mail from www.SlideShare.net saying my presentation Agile Simplified is currently being showcased on the 'Technology' page by their editorial team. It's likely to be there for the next 16-20 hours...

Most probably this happens for all new posted presentations, each might get showcased in its own category, and their so called editorial team might just be an electronic one :) I mean automatic selection.
So although it shouldn't mean any extra credit, it won't hurt to brag about it :) :)

Check my previous blog post about this presentation.

Saturday, February 14, 2009

An Introduction to Agile Software Development

Yesterday, I introduced the Agile way of Software Development to Egyptian University Students participating in Microsoft's Imagine Cup Local Competition.

The presentation took place at Microsoft Egypt Premises.

The slides are available here, where you can view it online (full screen if you want).
Also downloading is enabled.
It's worth mentioning that Imagine Cup World Finals will be held in Egypt.
Finally, I'd like to wish all the teams the best of luck, in the competition.

Monday, July 7, 2008

Software Development Meme

Frans Bouma has passed this torch to me. Seems like a good opportunity to get back to blogging after some busy months of work load.

Here we go:

How old were you when you first started programming?
14 years old, that's when I bought a Yamaha MSX AX-170 (Arabized and sold by Sakhr).

I bought it for gaming, but then I discovered you can do some tricks with it by writing some lines of code called Basic J


How did you get started in programming?
There was a Basic book that came with the MSX unit, I used to try out some examples from the book and try to innovate on my own. Later on when I was at high school I was convinced by a relative of mine (Hossam Ali) who was few years older and was studying Computer Engineering and he was working as a programmer at the same time.

What was your first language?
MSX basic, but just for some silly trials, then it was Turbo C at college time.


What was the first real program you wrote?
It depends on how you define "real". Do you mean a commercial program or one that you can proudly share with others. I'll assume the later one.

In fact I can't remember which was first, but either a game to shoot invading space ships before they hit the earth, or implementing Ferrari's method to solve the Quartic equation.


What languages have you used since you started programming?
I list those used in my professional career: SQL, VC++6, VB6, C#, VB.NET, JavaScript, VBScript


What was your first professional programming gig?
A document management system called TAM Pro, I started working on it as soon as I joined Raya Software, just after my graduation back in 2000, and I quit working on it when I left the company at 2005 J

If you knew then what you know now, would you have started programming?
Definitely, yes

If there is one thing you learned along the way that you would tell new developers, what would it be?
Always be open to new ideas and to change in general. Keep yourself up-to-date with what's going on the market. Try to acquire a new experience in each project.

If you find yourself doing a repetitive work (donkey work), then most probably you are the one to blame. Either reuse or auto-generate (as in code generation).

A pop-quiz if you are a .NET developer: Have you ever heard about LLBLGen Pro?


What's the most fun you've ever had … programming?
Seeing your code being used in production; serving others as a useful tool.

So who's next?
I was going to name Frans Bouma, I've already forgotten he was the one who sent it to me.

  • Hosam Ali
  • Mohamed Nar
  • Sami Samir
  • Mourad Askar
  • Hazem Tourab
  • Bernard Savonet

Tuesday, October 30, 2007

All about ASP.NET 2.0 Security

Searching for something in the ASP.NET Membership model led me to this Scott Gu's post: ASP.NET 2.0 Membership, Roles, Forms Authentication, and Security Resources

It's an old post but worth referencing.
It's a never ending ASP.NET 2.0 river of resources, it contains loads of links and references of resources for:
Authorization & Authentication models.
Membership, Roles, Profiles, Personalization & Providers.
Security guidelines and how-to's.

Sunday, July 8, 2007

ASP.NET Session State Brief

I'm writing this post to answer some people's questions about Session State storage, while this information is well known for many developers, some can find it useful. I'll try to brief things up.

Introduction
HTTP is a stateless protocol, meaning that a Web server treats each HTTP request for a page as an independent request; the server retains no knowledge of variable values used during previous requests.

What's a Session?
A session is a time limited, user (browser) specific, logical connection to a web application.

In other words, a server may treat subsequent requests, made from a specific browser/user, to web pages of the same web application, as logically placed under the same umbrella (session).

This way user specific variables or properly called Session specific variables can be stored somewhere, to be available for subsequent requests.

How requests can be identified to belong to a specific session?
When a user starts a new session, or the browser sends the first request to the web application, ASP.NET starts a new session and the SessionID for that session is sent to the browser with the response.
Then the ASP.NET would expect the SessionID to be sent back from the browser in the subsequent requests, to tie them all under the same session.

A session is considered active as long as requests continue to be made with the same SessionID value. If the time between requests for a particular session exceeds the specified time-out value in minutes, then the session is considered expired. Requests made with an expired SessionID value result in a new session being started.

How is the SessionID maintained?
That's configurable in the web application, Session ID values are transmitted between the browser and the Web server in a cookie, or in the URL if cookieless sessions are specified, as shown in the following example.
http://www.anysite.com/s(lit3py55t21z5v55vlm25s55)/orderform.aspx

For the first option, it won't work if the user disables cookies in his browser.
For the second option, session will be lost if the user re-writes the URL, removing the SessionID.

Where is the Session State stored?
That's configurable in the web application too.

If enabled, Session State or Session Specific variables can be stored in the following locations:
- InProc: stores values in the memory of the ASP.NET worker process. It offers the fastest access to these values. However, the session data is lost when the ASP.NET worker process recycles or the IIS is restarted.
It's not appropriate for Server Farms, where more than one web server is used, since subsequent requests can be directed to different servers.

- StateServer: uses a stand-alone Microsoft Windows service (aspnet_state.exe) to store serialized session variables. This service may run on another machine, thus be shared among multiple web servers in a web farm.
This is somehow slower than the InProc mode, due to serialization and deserialization. Especially if run on a separate machine.

- SQLServer: although this is the slowest solution of all, this is used for highest reliability, since a failover clustering can be used. Serialization is used here as in the StateServer mode.

- Custom: stores session state data using a custom session state store provider. You must specify the type of the session state store provider using the providers sub-element of the sessionState configuration element. See Implementing a Session-State Store Provider for more details.

For Out-Of-Proc options (anything but the InProc):
- Make sure session variables are serializable. See KB 312112 for details.
- Using a web farm, the Application Path of the website (For example \LM\W3SVC\2) in the IIS Metabase should be identical in all the web servers in the web farm. See KB 325056 for details.

The following is an example of the configuration section written under [system.web] section:
[sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;user id=[username];password=[strong password]" cookieless="false" timeout="20" /]

More on using the SQL Server option:
To install the session state database on SQL Server, run the Aspnet_regsql.exe tool located in the following folder on your web server:
[systemroot]\Microsoft.NET\Framework\versionNumber

Supply the following information with the command:
-The name of the SQL Server instance, using the -S option.
-The logon credentials for an account that has permission to create a database on SQL Server. Use the -E option to use the currently logged-on user, or use the -U option to specify a user ID along with the -P option to specify a password.
-The -ssadd command-line option to add the session state database

The Aspnet_regsql.exe tool will create a database named ASPState containing stored procedures called from the ASP.NET to save and retrieve session state to and from the database.

Session data itself is stored in the tempdb database by default, which might not be the best option, since tempdb is cleared when the SQL Server restarts.

You can optionally use the -sstype option to change the storage location of session data.

The following are the possible values of the -sstype option:
t data will be stored in the SQL Server tempdb database.
p data will be stored in the ASPState database instead of in the tempdb database.
c Stores session data in a custom database. You must also include the name of the custom database using the -d option.

For example, the following command creates a database named ASPState on a SQL Server instance named "SampleSqlServer" and specifies that session data is also stored in the ASPState database: aspnet_regsql.exe -S SampleSqlServer -E -ssadd -sstype p


P.S. this turned out to be anything but a brief article, isn't it?

References:
.NET Framework Developer's Guide: Session State
ASP.NET: Session State Overview
Nothin' but ASP.NET: ASP.NET Session State
ASP.NET: Session-State Modes
.NET Framework General Reference: sessionState Element
INFO: ASP.NET State Management Overview
Peter A. Bromberg: ASP.NET Session State FAQ
ASP.NET Technical Articles: Session State Providers
ASP.NET: Securing Session State

Sunday, June 24, 2007

Deploying Crystal Reports - Visual Studio 2005

As you already might know, a free version of Crystal Reports is installed/bundled with Visual Studio 2005.

Now to deploy an application that uses Crystal Reports, the resources on the internet suggests using one of the following methods to deploy the Crystal Reports redistributable package.

1- Using Crystal Reports for .NET Framework 2.0 Windows Installer:
Which I failed to find online, even on the Business Objects online downloads section, but doing a complete search for "CR*.msi" on my hard disk, I managed to find it on the following path:
[Microsoft Visual Studio 8 Install Folder]\SDK\v2.0\BootStrapper\Packages\CrystalReports\CRRedist2005_x86.msi

2- Using Merge Modules:
For which you should create a setup project for your application and in there, you should right-click the setup project node in the Solution Explorer, and select "Add Merge Module", and browse to the following path:
[Windows Root Drive]\Program Files\Common Files\Merge Modules\CrystalReportsRedist2005_x86.msm
This most probably won't be found on your machine!!
I wonder why this merge module wasn't installed with Crystal Reports.
So what you need to do is to go to the Business Objects download site, download this file and put it where it should have been put (in the above path).
Now build your setup project, and Crystal Reports run-times will be installed with your application.