October 2006 - Posts

Working MasterPage Samples

A few people have asked me for a working web site with some of the code from my MasterPage: Tips, Tricks, and Traps article. This download is a web site based on the ASP.NET Personal Web Site Starter Kit and provides a number of the samples from the article.

Samples include a master page base class, and an HttpModule to assign the MasterPageFile property for .aspx web forms at runtime. There is an example of content page to master page interaction through a strongly typed Master property, and an example of master page to content page interaction via an event.

posted by scott with 0 Comments

What's Wrong With This Code? (#8)

Joe Developer is working with a simple struct:

struct Point
{
    
public int x;
    
public int y;
}

Joe's tech lead asked him to write a method that will return an array of 10,000 initialized points. Joe wrote following code.

Point[] CreatePoints()
{
    
Point[] points = new Point[10000];

    
for (int i = 0; i < points.Length; i++)
    {
        points[i] =
new Point();
    }

    
return points;
}

The code doesn't create any runtime errors, but Joe is worried because his tech lead looked at the code and frowned. What could provoke such a reaction?

Hint: Joe's lead is a performance nut.

posted by scott with 36 Comments

What's In a Workflow Queue Name?

There is a queuing infrastructure in Windows Workflow that facilitates communications between workflows and the outside world. Event related activities, like the HandleExternalEvent and Delay activities, provide a formalized layer of abstraction over this queuing infrastructure. You might never need to know that a queuing service exists if you can get 100% of the job done with components from the base activity library.

If you need more flexible messaging, or like Harry, want to work with a low level API, you can use the queuing service to create queues and en-queue messages for your own purposes. Before jumping in, it's worthwhile to study how the built-in activities make use of workflow queues, particularly workflow queue names.

If we look at the correlated local service example in the Windows SDK, we'll find a Parallel activity with two sequences inside. Both sequences call a CreateTask method and expect a TaskCompleted event to arrive. A local service we've implemented has to raise this TaskCompleted event, but the event isn't delivered directly to the running workflow. Instead, the workflow runtime catches the event. The workflow instance might be unloaded from memory and living as a serialized blob in a database table. The runtime will reload the proper workflow and deliver the event.

If there are multiple workflow instances active, it's easy for the runtime to find the right instance because of the InstanceId property on all incoming event arguments (local service communications might define event arguments that derive from the ExternalDataEventArgs class).

But how does the workflow know what activity is waiting for the event? This isn't hard if there is only one activity waiting for an event, but in this sample we have two activities both waiting for the same event from the same service.The secret is in the workflow queue names the individual activities create to wait for the events. If we use GetWorkflowQueueData on the workflow instance, we can inspect the queue names. A queue name in the this SDK sample will look like the following:

Message Properties
Interface Type:ITaskService
Method Name:TaskCompleted
CorrelationValues:
001

What we see is that the workflow queue "name" actually contains all of the information the runtime needs to deliver events to their proper destination. The name includes the interface and event name, as well as correlation values when the associated activities have correlation tokens.

Queue names in windows Workflow are more than just friendly identifiers. As Harry pointed out, they implement IComparable and are actually a key piece to how Windows Workflow works with data exchange services.

posted by scott with 1 Comments

The Fall Tour

I spent the summer as a hermit. Here are some events where I'll be presenting over the next three months.

VSLive! Boston (Oct 24 - Oct 27). On Wednesday afternoon, I'll show you how to bend master pages to do your bidding. There is some great ASP.NET 2.0 content in this show with the likes of Fritz Onion, Miguel Castro, Chris Kinsman, and more.

VSLive! Dallas (Nov 14 - Nov 17). One presentation on master pages, and one presentation to help make sense of web site projects, web application projects, and web deployment projects.

Central Penn Code Camp (Dec 2). I'll probably do one talk on an ASP.NET topic, and one introduction to Windows Workflow.

CMAP User Group (Dec 5). Every December, the central Maryland user group has a tips and tricks show. Different presenters each take 20 minutes and show you tools and time saving techniques you'll find useful in everyday development. Free pizza and door prizes, too!

BaltoMSDN User Group (Dec 13). If you are in Baltimore or Northern Maryland, come out and support the newest user group in the area. Topic TBA.

posted by scott with 0 Comments

Static Doesn't Mean Thread Safe

One misunderstanding I often see is "if I make this member static it will be thread safe". I think this misconception arises because of the boilerplate MSDN documentation that will often say: "Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe."

When a .NET framework class appears with the above documentation, it means a developer took the necessary precautions to make the static member thread safe. Perhaps they added a lock, or used some other synchronization mechanism to ensure thread safety. See: Statics and Thread Safety Part I and Part II.

Marking a class member static doesn't guarantee thread safety. It is up to you to make the member thread safe.

There is one exception to this rule. It's a special case where the static / Shared keyword will guarantee threading magic. Do you know what that is?

posted by scott with 13 Comments

What Can aspnet_compiler.exe Do For Me?

People often ask what the aspnet_compiler tool can do. The usual answer is "pre-compile an ASP.NET 2.0 web site". The follow up question is: "What good does that do me?". MSDN says the advantages to compilation in general are:

  • Performance
  • Security
  • Interoperability
  • Stability

Let's take these one at a time.

Performance

I say the performance benefits to pre-compiling a site are negligible. Pre-compilation does save some work, but ASP.NET applications in general are slow starters. Think about all the work that happens when the first request arrives. A process has to start. An AppDomain has to load. The cache is empty. The database connection pool is empty. Even if you've precompiled, the JIT compiler has to step in and create native code.

I'm not saying you shouldn't pre-compile a site to save some time. Just don’t' count on seeing a significant reduction in start up time for all applications.

Security

The MSDN article I linked to says "Compiled code is more difficult to reverse engineer than non-compiled source code…". Compilation makes the job more difficult, but all someone needs is the right tools. If you are trying to hide secrets in your source code, then encryption, not compilation, is the way to go. If you want to protect intellectual property then look for good obfuscation tools. Just remember you can't stop reverse engineering, you can only slow it down.

I don't think of pre-compilation as a security benefit, but if you pre-compile an ASP.NET application without the updateable option, you will lock down the application in production. Pre-compilation can strip out all the markup in template files, and no one can easily hand-tweak stuff outside of the configuration management process.

Interoperability

I never thought of interoperability as a benefit of compilation, per se. It seems interoperability between languages is a feature of the platform, not any compiler. I think the documentation is stretching this a bit.

Stability

ScottGu refers to using pre-compilation for "deep verification". A pre-compiled ASP.NET application is stable and verified in the sense that you won't see runtime errors because of syntax problems in markup. If someone checks in a malformed server control tag, you can catch the problem during the build with a compiler error, instead of finding out about the problem in test or production. Stability is arguably the greatest benefit of pre-compilation, and I recommend pre-compiling a web site for this reason alone. .

Summary

When I heard about pre-compilation in ASP.NET 2.0, my thoughts circled around startup performance. As it turns out, pre-compilation will save not only CPU cycles but test and development cycles as well. Pre-compilation is worth the effort, primarily because it requires so little work to setup.

If you want an interactive and user friendly interface for pre-compilation, check out Rick Strahl's ASP.NET 2.0 Compiler Utility.

Web Deployment Projects can do pre-compilation (and a whole lot more), using a standard MSBuild project file.

If you just want to add a quick pre-compilation step to your Team Foundation Server build, use the AspNetCompiler task in the AfterCompile override in TfsBuild.proj:

<Target Name="AfterCompile">
  <AspNetCompiler    
     PhysicalPath=
      
"$(BinariesRoot)\Release\_PublishedWebsites\MyWebSite"
     Debug="false"
     VirtualPath="/MyWebSite"
  />
</Target>

posted by scott with 3 Comments

SiteMap Macro v2.0

Last year I wrote a little macro to generate a web.sitemap file for ASP.NET 2.0. A few people have been asking about support for Web Application Projects, so I've updated the macro.

Download.

This macro walks through the directories of a web project and adds all the content pages to a ASP.NET 2.0 site map file. If you use URL re-writing or virtual path providers, it won't be able to see those URLs. Chances are you'll need to hand tweak the file afterwards as the macro isn't intelligent enough to devise a good title for each page, but it can save a lot of typing to get started.

To install:

  1. Open the macro explorer (Alt+F8).
  2. Right click a macro project and select "New Module…"
  3. Name the new module SiteMapMacro
  4. Right click the new module and select "Edit". Clear existing code.
  5. Paste the code from the download into the new file.
  6. In the Project Explorer, right click References and add a reference to System.Xml.dll. Close the editor.

Back in the macro explorer, you should be able to right-click the macro to execute it.

posted by scott with 4 Comments

Don't Try This At Home

I wasn't paying attention when I typed:

mstsc /v:name.domain.net /console

Unfortunately, I was already sitting in front of the very machine I was trying to reach with terminal services. I took over the console, all right - the screen blacked out. I couldn't get Ctrl-Alt-Delete to provoke a response. If it wasn't for the lights on the case I'd have thought the computer died.

It takes 30 seconds or so for the un-credentialed new connection to timeout and return control to the real console.

It's a very long, worrying 30 seconds, though. I just wanted to share my anxiety.

posted by scott with 3 Comments

MasterPage Issue With ViewState Disabled in web.config

Vivek points out some surprising behavior with master pages.

Let's say we want to disable ViewState for all pages by default. All we need is an entry in web.config:

<system.web>

   <
pages enableViewState="false"  />

</
system.web>      

When the ASP.NET runtime generates code for a web form, it will disable ViewState using generated code. The code in the Temporary ASP.NET Files directory for an .aspx will contain a snippet like the following:

private void @__BuildControlTree(some_aspx @__ctrl) {
  
  #line 1 "some.aspx"
  @__ctrl.EnableViewState =
false;
  
// ...
}

At some point, we may run into a page that requires ViewState. Arguably, we could say that something on the page should be re-written to use ControlState (required) instead of ViewState (optional), but it's easy to override the default configuration on a page by page basis in the @ Page directive:


<%@ Page EnableViewState="true" ...  %>

This works - the @ Page directive overrides web.config and EnableViewState=true appears in the generated code….

… but there is a catch.

From what I can tell - some of the same goo responsible for the parsing and code generation of .aspx files also parses and generates the code for .ascx, and .master files. This goo will read the web.config setting and place EnableViewState=false in the code generated for master pages (and user controls, too).

For ViewState to be available to a control, it's parent must have ViewState enabled. Since a master page injects itself as the direct child of the Page object, having ViewState disabled in the master page shuts off ViewState for everything inside the Page.

With ViewState effectively disabled, it appears as if the @ Page directive isn't overriding web.config. It turns out that the @ Page directive does work as we expect. What we don't expect is that the MasterPage would pick up a configuration settings from the <pages> section of web.config and kill ViewState. But should we? The documentation for the <pages> element says:

"Globally defines page-specific configuration settings, such as ASP.NET directives for pages and controls that are within the scope of the configuration file."

Perhaps ASP.NET is working as designed, but I'd say the behavior is not intuitive in this case.

posted by scott with 3 Comments

Web Client Software Factory - Drop 04

The Web Client Software Factory dropped a new build yesterday. This is the first build I've downloaded. I spent about 30 minutes perusing the documentation and reference implementation, and I am shocked!.

My expectations were to open this package and find yet-another-data-access-layer-approach. Ho hum.

Instead, this factory positions itself to address leading issues in today's web development practices.

Some highlights:

  • The reference implementation uses a model-view-presenter with controller (MVPC) pattern to maximize the testability of code, and centralize control flow and business logic.
  • The docs explain how to implement MVPC using the canonical definition of test-driven development. Write your tests first. Red - green - refactor.
  • The project uses "Page flow". Page flow models a user's path through a multi-page task using a Windows Workflow state machine and custom Page activities.

I plan to spend some more time with the factory and keep an eye on the weekly releases.

posted by scott with 4 Comments

What's Wrong With This Code? (#7)

This time, Joe Developer is building a web application for the company intranet. Most of the site is available to anonymous users, but one directory - the adminPages directory, should only be accessible to users in the machine's local administrators group. Joe added the following to the bottom of his web.config, and is feeling pretty secure.

<configuration>
  <location path="adminPages">
    <system.web>
      <authorization>
        <allow roles=
"BUILTIN\Administrators" />
        <deny users=
"?" />
      </authorization>
    </system.web>
  </location>  
</configuration>

Should Joe be worried?

posted by scott with 12 Comments

Celebrities in MSDN Documentation

I imagine the MS legal department frowns on the appearance of recognizable names in documentation. Nevertheless, there are a few out there. For instance, the documentation for IXmlSerializable.WriteXml contains a celebrity from Bedrock:

// Create a person object.
Person fred = new Person("Fred Flintstone");

Over in the Visual FoxPro world, we find a local Microsoft celebrity:

IF UPPER(userID) = 'BILLG' && only one user may add tables
   RETURN .T.
ENDIF
   RETURN .F.
ENDIF

Finally, perhaps someone had a particular GeorgeW in mind when describing sp_denylogin to aspiring DBAs:


EXEC sp_denylogin 'Corporate\GeorgeW'

If only it were that easy…

So, know of any others?

posted by scott with 2 Comments

Workflow Policy, Rules, and Collections

Q: Great article on the WWF Rules engine. I am curious how one would implement a rule that is applied to a collection of items, something akin to "IF request.Payments[n].Amount > 10000 THEN request.Payments[n].RequiresApproval = true"

A: There are quite a few approaches to choose from. As usual - it depends on the application!

One solution is to use a WhileActivity and loop through the collection. This explicit approach would run a PolicyActivity for each item in the collection.

The Windows SDK contains an interesting alternative approach in the "Processing Collections in Rules" entry. This is approach is worthy of note because it uses the features and flexibility of the rules engine to perform the iteration without any outside help. The approach specifically relies on the following features:

  • Every rule in a Windows Workflow rule set has an associated priority number. The priority number determines when the rule will execute relative to other rules.
  • The rules engine in WF analyzes the dependencies between rules. When a rule updates a dependency, the rules engine can re-evaluate previous rules that used the dependency.

With a Payment class defined in your domain model, the workflow class can accept an array of Payment references as a parameter:

public partial class Workflow1 : SequentialWorkflowActivity
{      
  
public Payment[] Payments
   {
      
get { return _payments; }
      
set { _payments = value; }
   }
  
  
Payment[] _payments;
  
IEnumerator _enumerator;
  
Payment _currentPayment;
}

Next comes the RuleSet:

Name Priority Rule
GetEnumerator 3 IF 1=1 THEN
  this._enumerator = this._payments.GetEnumerator()
MoveEnumerator 2 IF this._enumerator.MoveNext() THEN
  this._currentPayment = (Payment)this._enumerator.Current
One Or More Biz Rules 1 this._currentPayment.Amount > 10000 THEN
  this._currentPayment.RequiresApproval = True
ELSE
  this._currentPayment.RequiresApproval = False
Force re-eval 0 IF this._currentPayment == this._currentPayment THEN
  Update("this/_enumerator/")

GetEnumerator runs first because it has the highest priority. MoveEnumerator runs second - again because of priority. After these two rules finish, _currentPayment will reference the first Payment object in the array. All the "business" rules could now execute on that payment and decide on an outcome.

The interesting piece happens in the last rule, which always evaluates to true and performs an explicit "Update" on the _enumerator field. "Update" is a rule action that explicitly tells the engine about a side effect. In this rule, we are telling the engine that we've changed _enumerator. Even though we haven't actually changed _enumerator, we've forced the engine to look for previous rules with a dependency on _enumerator.

The rules engine knows the MoveEnumerator rule has a dependency on _enumerator, so it re-executes this rule. If MoveNext returns true in this rule, we update the _currentPayment field. The rules engine also detects this implicit side effect, and will reevaluate all the business rules that depend on _currentPayment. If MoveNext returns false, there are no more side effects and the Policy activity can close.

This is a clever pattern which keeps collection processing inside of a single activity.

posted by scott with 1 Comments