Monday, January 30, 2012

Microsoft.Web.Administration in IIS 7

While creating the new administration stack in IIS 7, we were looking into the different ways users could manipulate the server configuration as well as the new runtime information available in IIS 7 (Internally we call this RSCA-Runtime State and Control API) from managed code, and we realized we needed to provide a simpler and more straight forward API that developers could consume from managed code. Microsoft.Web.Administration is the answer to this problem. This API is designed to be simple to code against in an “intellisense-driven” sort of way. At the root level a class called ServerManager exposes all the functionality you will need.

To show the power and simplicity of this API, let’s look at some samples below. To try this samples just create a new Console Application in Visual Studio and add a reference to Microsoft.Web.Administration.dll that can be found at IIS directory (%WinDir%\System32\InetSrv).
Please note that the following code is based on Windows Vista Beta 2 code and will likely change for the release candidate versions of Windows Vista since we have planned several enhancements to simplify the API and expose more features into it.
The following picture shows the main objects (excluding Configuration related classes).

Microsoft.Web.Administration
Creating a Site

ServerManager iisManager = new ServerManager();
iisManager.Sites.Add("NewSite", "http", "*:8080:", "d:\\MySite");
iisManager.Update();

This basically creates an instance of the ServerManager class and uses the Add method in the Sites collection to create a new site named "NewSite" listening at port 8080 using http protocol and content files are at d:\MySite.
One thing to note is that calling Update is a requirement since that is the moment when we persist the changes to the configuration store.
After running this code you have now a site that you can browse using http://localhost:8080

Adding an Application to a site
ServerManager iisManager = new ServerManager();
iisManager.Sites["NewSite"].Applications.Add("/Sales", "d:\\MyApp");
iisManager.Update();


This sample uses the Sites collection Indexer to get NewSite site and uses the Applications collection to add a new http://localhost:8080/Sales application.

Creating a Virtual Directory
ServerManager iisManager = new ServerManager();
Application app = iisManager.Sites["NewSite"].Applications["/Sales"];
app.VirtualDirectories.Add("/VDir", "d:\\MyVDir");
iisManager.Update();


Runtime State and Control

Now, moving on to the new Runtime state and control information we also expose in this objects information about their current state as well as the ability to modify them. For example, we expose the list of W3WP processes running (Worker processes) and what I think is really cool, we even expose the list of requests currently running. Stopping a Web Site
ServerManager iisManager = new ServerManager();
iisManager.Sites["NewSite"].Stop();

Recyciling an Application Pool
ServerManager iisManager = new ServerManager();
iisManager.ApplicationPools["DefaultAppPool"].Recycle();

Getting the list of executing requests
ServerManager iisManager = new ServerManager();
foreach
(WorkerProcess w3wp in iisManager.WorkerProcesses) {
Console.WriteLine(
"W3WP ({0})", w3wp.ProcessId);

foreach
(Request request in w3wp.GetRequests(0)) {
Console.WriteLine(
"{0} - {1},{2},{3}",
request.Url,
request.ClientIPAddr,
request.TimeElapsed,
request.TimeInState)
;
}
}

Another big thing on this API is the ability to edit the “.config” files using a simple API, this includes the ability of modifying the main applicationHost.config file from IIS, web.config files from asp.net as well as machine.config and other config files (such as administration.config)

Basic Threading in C#

Threading is a great way to make your application smoother. A single thread in a C# application, is an independent execution path that can run simultaneously with the main application thread. Of course, C# supports multithreading. And to use the threading namespace, you can directly call it from System.Threading, or import it:

1
using System.Threading;

Without this, a basic HTTP request makes your application unavailable in a span of time. And if you are using a progress bar, you won’t see it updating (since the application is unresponsive at this state).

Single Thread

The most basic way to implement this, is by using the Thread class. An example:

1
2
Thread tMain = new Thread(new ThreadStart(someMethod));
tMain.Start();

Wherein someMethod is a void function that contains the code you want to execute. With this, the thread will execute in parallel with the main thread. The only drawback, is you cannot modify controls created in the main thread like you normally use to. This will result into a Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on exception. The solution is pretty simple:

1
2
3
4
5
// Won't Work
textBox1.Text = "Hello";
// Will Work
this.Invoke(new MethodInvoker(delegate { textBox1.Text = "Hello"; }));

This approach functions just the same as a BackgroundWorker instance.

Powertip: You can disable all the controls in your form painlessly without setting the Enabledproperty of each control (imagine having 20+ controls in your form).

1
2
3
4
5
6
7
private void controlsEnableToggle(bool val)
{
foreach (Control c in this.Controls)
{
c.Enabled = val;
}
}

Multiple Threads

Dealing with multiple repetitive tasks with a single line of execution is painful, especially in large numbers. C# supports multithreading. This means, we can initiate as many threads as we like, and it will do the job. So how to do that? A simple for-loop.

1
2
3
4
for (int i = 0; i < 10; i++) {
Thread tMain = new Thread(new ThreadStart(someMethod));
tMain.Start();
}

The above code works, but what if we need to supply parameters to our method?

01
02
03
04
05
06
07
08
09
10
11
// .NET 2
for (int i = 0; i < 10; i++) {
Thread tMain = new Thread(new ParameterizedThreadStart(someMethod));
tMain.Start(param);
}
// .NET 3.5 & 4
for (int i = 0; i < 10; i++) {
Thread tMain = new Thread(unused => someMethod(param));
tMain.Start();
}

Pausing and Stopping Threads

Whenever you close a form, the main thread will stop, but the threads you created are not. Thus, the process is still alive. To stop this, you can just go to the task manager, and end the process from there. But that’s not user-friendly.

You cannot actually stop or pause threads immediately. You can never tell what the thread is doing, and terminating them abnormally may cause side effects. For this, ManualResetEvent is used.

1
2
ManualResetEvent pauseEvent = new ManualResetEvent(true);
ManualResetEvent stopEvent = new ManualResetEvent(false);

The bool values passed to the constructor indicates whether the initial state of the instance is signaled or not. This is pretty easy to use.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
ManualResetEvent pauseEvent = new ManualResetEvent(true);
ManualResetEvent stopEvent = new ManualResetEvent(false);
private void someMethod(string param) {
// Stops the thread
if (stopEvent.WaitOne(0))
return;
// Pauses the thread
pause Event.WaitOne(Timeout.Infinite);
// Your code
}

To change the signaled state of these events, we will use Reset and Set methods. Here’s how to use them in relation to the above code:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
// Stops the threads
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
stopEvent.Set();
}
// Pauses the threads
private void Button1_Click(object sender, EventArgs e)
{
pauseEvent.Reset();
}
// Resumes the threads
private void Button2_Click(object sender, EventArgs e)
{
pauseEvent.Set();
}

You can choose how you will use the methods, the above is just an example.