Thursday, August 09, 2007

I just spent the past few days pulling my hair out trying to get a custom principal to work in WCF.

Google returned all sorts of interesting, but often outdated and/or overly complex results. I kept looking at the techniques people were using, thinking this can't be so hard!!!

Well, it turns out that it isn't that hard, but it is terribly obscure... Fortunately I was able to get help from various people, including Clemens Vasters, Juval Lowy and (in this case most importantly) Christian Weyer. Even these noted WCF experts provided an array of options rather than a unified, simple answer like I'd expected.

My conclusion: while WCF really is cool as can be, it is also a deep plumbing technology that begs for abstraction for use by "normal" people.

Anyway, as a result of my queries, Christian got one of his colleagues to write the blog post I wish I had found a few days ago: www.leastprivilege.com - Custom Principals and WCF.

One of my motivations in researching this issue was for the WCF chapter in my upcoming Using CSLA .NET 3.0 ebook. There's now a comprehensive discussion of the topic in that chapter, starting with the creation and use of X.509 certificates and walking through the whole process of implementing custom authentication and using a custom principal in a WCF service. Dominick's blog post is great, but only covers about a third of the overall solution in the end.

The ebook should be out toward the end of September, for those who are wondering.

Thursday, August 09, 2007 2:28:24 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

 Tuesday, August 07, 2007

Aaron Erickson, a fellow Magenic consultant, has done a lot of research and work on how to index LINQ queries over objects. He just published an article on the topic: Indexed LINQ.

While indexing these queries has a cost, due to building the index, it can be very beneficial if you are doing a lot of queries over a large set of data.

Tuesday, August 07, 2007 10:13:56 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  | 

 Wednesday, August 01, 2007

For those that don't know, I live in the Twin Cities area: Minneapolis and St. Paul, Minnesota.

This evening one of the major bridges crossing the Mississippi River collapsed. It is a horrible disaster. The kind of thing that you simply hope never to see.

My family and I are fine. We've called our friends who might have been affected and they are fine.

My heart goes out to the people who were caught in this disaster, and their families and friends.

Wednesday, August 01, 2007 9:18:16 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  | 

I wrote the following for the Using CSLA .NET 3.0 ebook, but I don't think I'm going to use it now, because I've wrapped most of this into a new class in CSLA .NET. Rather than letting this go to waste though, I thought I'd post it here. Remember that it is just draft content, so it may have typos or errors, but perhaps it will be useful to someone:

Basic Workflow Execution

Executing a workflow is a little tricky, because workflows default to running on a background thread. That means you must take steps to ensure that the workflow completes before the host process terminates.

One way to solve this issue is to always execute a workflow synchronously. Another is to use a thread synchronization object to prevent the process from terminating until the workflow completes.

Note: It is also possible to suspend and resume workflows, and even to unload them from memory so they store their state in a database. Later you can reload that workflow instance and resume it. These advanced scenarios are outside the scope of this book

Synchronous Execution

The code to synchronously execute a workflow follows a standard pattern:

1.      Create an instance of the WorkflowRuntime.

2.      Create a synchronization object.

3.      Set up event handlers.

4.      Create workflow instance.

5.      Ensure you have a valid principal object.

6.      Start the workflow.

7.      Wait for the workflow to complete.

The only step unique to CSLA .NET is number 5, and that is only required if you are using custom authentication. The WF runtime will automatically ensure that the background thread that executes the workflow has the same principal as the thread that calls the workflow’s Start() method, but you must ensure that the principal is set on the current thread before calling Start().

The following code implements these steps to execute the ProjectWorkflow implemented earlier in this chapter:

      using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())

      {

        Exeception error = null;

 

        AutoResetEvent waitHandle = new AutoResetEvent(false);

        workflowRuntime.WorkflowCompleted +=

          delegate(object sender, WorkflowCompletedEventArgs e)

          {

             waitHandle.Set();

          };

        workflowRuntime.WorkflowTerminated +=

          delegate(object sender, WorkflowTerminatedEventArgs e)

          {

            error = e.Exception;

            waitHandle.Set();

          };

 

        // create workflow instance

        Dictionary<string,object> parameters = new Dictionary<string,object>();

        parameters.Add("ProjectId", projectId);

        WorkflowInstance instance =

          workflowRuntime.CreateWorkflow(

            typeof(PTWorkflow.ProjectWorkflow),

            parameters);

 

        // login before starting WF instance

        ProjectTracker.Library.Security.PTPrincipal.Login("pm", "pm");

 

        // execute workflow

        instance.Start();

 

        // wait for workflow to complete

        waitHandle.WaitOne();

 

        // throw any workflow exception

        if (error != null)

          throw error;

      }

Creating the workflow instance involves setting up a Dictionary<string, object> that contains name/value pairs for any parameters to be passed into th workflow instance:

        // create workflow instance

        Dictionary<string,object> parameters = new Dictionary<string,object>();

        parameters.Add("ProjectId", projectId);

        WorkflowInstance instance =

          workflowRuntime.CreateWorkflow(

            typeof(PTWorkflow.ProjectWorkflow),

            parameters);

The name in the dictionary must correspond to the name of a dependency property defined by the workflow, and of course the type of the value must match the dependency property type.

Keep in mind that creating an instance of a workflow does not start the workflow. The workflow won’t start executing until the Start() method is called later in the code.

The waitHandle synchronization object is the key to making this process synchronous. The waitHandle object starts out unset because false is passed to its constructor as its initial state:

        AutoResetEvent waitHandle = new AutoResetEvent(false);

At the bottom of the code is a line that calls WaitOne(), thus blocking the current thread until waitHandle is set:

        // execute workflow

        instance.Start();

 

        // wait for workflow to complete

        waitHandle.WaitOne();

While the current (starting) thread is blocked, the workflow is busy executing on a background thread. In other words, the Start() call returns immediately, having just started the workflow instance executing on a background thread. Without the WaitOne() call, the current thread would exit the code block, which would dispose the WF engine instance while it is executing the workflow. The result would be an exception.

Notice how the event handlers for the WorkflowCompleted and WorkflowTerminated events both call waitHandle.Set(). These events are raised by the WF engine when the workflow either completes or terminates unexpectedly. Either way, by calling the Set() method, the current thread is released so it can continue running.

In the case of a workflow terminating unexpectedly, the exception from the workflow is made available to the WorkflowTerminated event handler. You can choose what to do with this information as appropriate for your application. One technique is shown here, which is to store the Exception object in a field:

          delegate(object sender, WorkflowTerminatedEventArgs e)

          {

            error = e.Exception;

            waitHandle.Set();

          };

And then have the current thread throw the exception once it is unblocked:

        // wait for workflow to complete

        waitHandle.WaitOne();

 

        // throw any workflow exception

        if (error != null)

          throw error;

The result of this code is that the workflow appears to run synchronously, even though it really executes on a background thread.

Asynchronous Execution

Allowing a workflow to run asynchronously is just a slightly more complex version of running the workflow synchronously. The important thing is to ensure that your process doesn’t exit until the workflow is complete. This means that the synchronization object must be available at a broader scope so you can write code to prevent the application from closing if the workflow is still running.

You also must come up with a way to deal with any exception object in the case that the workflow terminates unexpectedly. One solution is to elevate the error field from the previous example to a broader scope as well.

Finally, the WorkflowRuntime instance must remain in memory until the workflow completes.

This means that you must define these fields so they exist at an application level, for instance using static fields:

  private static AutoResetEvent _waitHandle = null;

  private static Exception _workflowError = null;

  private static WorkflowRuntime _workflowRuntime = null;

Then you can create a method to start the workflow:

    public static void BeginWorkflow(Guid projectId)

    {

      _workflowRuntime = new WorkflowRuntime();

      _waitHandle = new AutoResetEvent(false);

      _workflowRuntime.WorkflowCompleted +=

        delegate(object sender, WorkflowCompletedEventArgs e)

        {

           _waitHandle.Set();

        };

      _workflowRuntime.WorkflowTerminated +=

        delegate(object sender, WorkflowTerminatedEventArgs e)

        {

          _workflowError = e.Exception;

          _waitHandle.Set();

        };

 

      // create workflow instance

      Dictionary<string,object> parameters = new Dictionary<string,object>();

      parameters.Add("ProjectId", projectId);

      WorkflowInstance instance =

        _workflowRuntime.CreateWorkflow(

          typeof(PTWorkflow.ProjectWorkflow),

          parameters);

 

      // login before starting WF instance

      ProjectTracker.Library.Security.PTPrincipal.Login("pm", "pm");

 

      // execute workflow

      instance.Start();

    }

Notice that the WorkflowRuntime object is no longer in a using block, so it can remain in memory, not disposed, while the workflow instance is running on the background thread.

The workflow instance is created the same as before, and its Start() method is called. At that point this method simply ends, returning to the caller.

Once you call BeginWorkflow() the workflow is started on a background thread, but your current thread (often the UI thread) is free to continue working.

The final piece to the puzzle is a method your application can call before it exits, or when it otherwise can’t continue without the workflow having completed:

    public static void EndWorkflow()

    {

      // wait for workflow to complete

      _waitHandle.WaitOne();

 

      // dispose runtime

      _workflowRuntime.Dispose();

 

      if (_workflowError != null)

        throw _workflowError;

    }

It is important to realize that this method will block the current thread until _waitHandle is set. If the workflow completes before this method is called, then _waitHandle is already set, and this method runs immediately, but if the workflow is still running, this method will block until the workflow completes or terminates.

For this to work, you must call EndWorkflow() before your process terminates to properly dispose the runtime and to determine if the workflow terminated unexpectedly.

 

Wednesday, August 01, 2007 4:15:26 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

 Tuesday, July 31, 2007

Srinivas Surampalli, a fellow Magenic consultant, just wrote the following article:

Using XML with Stored Procedures Effectively in SQL Server 2005

One common question that comes up relative to CSLA data access is how to efficiently save an object graph where there's a parent with children or even grandchildren.

The typical answer is to make an INSERT/UPDATE/DELETE call for the parent and each child. This article shows a different approach, where you could put the business object data into a DTO object graph, serialize that object graph into XML and make a single database call.

What surprised me, looking at the article's code, is that the SQL in the stored procedure is so straightforward.

As always, you should test different approaches to find the one that works best for your application, based on performance, maintainability and other requirements.

Tuesday, July 31, 2007 8:15:00 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  | 

 Thursday, July 26, 2007

Visual Studio 2008 Beta 2, along with Microsoft .NET 3.5 Beta 2, is available for download. Here's Soma's official announcement.

I find that downloading such huge sets of files requires a bit of help. My recommendation: Free Download Manager. This tool is awesome - indispensable in fact - if you do any downloads beyond small text files :) It does queued downloads, resumed downloads and throttling. Perhaps best of all, it does multi-threaded downloads, so it maximizes the use of your bandwidth when running at full throttle.

Update: Apparently there are some things you must do/fix before using VS 2008!! Read ScottGu's blog post about it!

Update 2: According to Juval Lowy, the svcutil.exe program in Beta 2 is broken. A workaround is to copy an older (Beta 1?) version of svcutil.exe over the top of the Beta 2 version.  Instead, Justin Smith says that you need to run "sn.exe -Vr svcutil.exe" - apparently then you don't need to copy an older verison over the new one.

Thursday, July 26, 2007 3:21:37 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  | 

Every time I go to override the Text property in a Windows Forms UserControl I have to go find this one little attribute that I can never remember...

It is easy enough to remember to make the property Browsable(true), but it is that other attribute that's hard to remember: DesignerSerializationVisibility(DesignerSerializationVisibility.Visible).

This web page not only answers the question, but includes a nice summary of all the System.ComponentModel attributes. 

At least next time I know I can search my own blog to find the answer :)

Thursday, July 26, 2007 8:47:34 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

 Wednesday, July 25, 2007

Today I received a letter from a large (the largest?) US bank, offering me a special discount rate on charges made to my credit card.

To my knowledge, I have no account at this bank at all, including no such credit card. That’s never good!

So I called the number on the letter to find out what’s going on. Of course I got to the bank’s credit card service center, where they asked me my credit card number, let’s pick up there:

“That’s why I’m calling. I don’t have such a credit card” said I.

“I can look it up using your social security number” said she.

And this is when my brain finally kicked in. I had dialed the number from the suspicious letter!! While the letter looked authentic, and the automated answering system on their end sounded authentic, how did I actually know I was talking to this large bank?

“I’m not sure I want to provide that” I answered.

“Can I have you spell your name then?” she asked.

I did that, as my name is easily found, and the letter already had that. She then confirmed that I had an account with them, and asked “Can you confirm your birth date?”

“Umm, I’m not sure I want to provide that either. I need to look on the web site and see if your phone numbers match.”

“OK, can I put you on hold?”

“Sure.”

So I did. I went to their web site, clicked “Contact Us” and found different phone numbers. In the meantime she came back on the line.

“Did you find what you needed?” she asked.

“No, the numbers don’t match.”

“Well you have reached ____, and we do have an account in your name. If you provide your birthdate I can give you the account details.”

“Yeah, see that’s the problem. You can confirm my identity, but there’s nothing you can give me that can confirm your identity. I’m going to have to call the number on the web site to be sure.”

“This really is ____” she said in an exasperated tone.

“Well, I can’t be sure” I replied.

“Then do what you need to” she said, and hung up rather abruptly.

So I did call the number from the web site. I did have an account there. Some credit card I haven’t used since the middle of 2000. I was able to find that out without even talking to a human: their automated system handled the whole process, including my canceling the card.

But it sure goes to show just how complex bi-directional authentication can be. Makes a person really appreciate the work done to design Kerberos, SSL, Windows Card Services and all the other authentication schemes out there we take for granted every day…

Wednesday, July 25, 2007 2:45:17 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  | 

 Thursday, July 12, 2007

Bil Simser tagged me with this meme, thanks Bil.

As I tend to do with these things, I'll answer, but I'm not going to pass it on.

Apparently the idea is to list 5 things that'll make me a better developer over the next 6 months. The problem is, I'm not sure being a better developer is my primary focus just at the moment. But here's my list:

Keep my head above water

Microsoft has recently, and continues into the foreseeable future, to come out with new technology releases at a ridiculous pace. By "head above water" all I really mean is that I want, at least, to have a general clue about the purpose and real capabilities of all the stuff they are throwing at us. Not just the marketing hype, but what it really means. This, to me, has always been one of my primary strengths: the ability to grok most of the technologies available, and to figure out how they fit into some overarching architecture/worldview/philosophy.

But the current rate of change, and the amount of redundancy (ADO.NET, DataSets, LINQ, ADO.NET EF all solving the same problem? Come on!) makes this increasingly challenging. So over the next 6 months I'll be reading, talking to experts (and anyone else with something to offer) and experimenting in the hopes of keeping my head above water.

CSLA .NET

I used to learn new technologies by writing games - specifically MUDs and then low-end MMORPGs (well, I wrote and rewrote parts of one that never really came together). Someday I'd like to get back to doing that.

But in the meantime, I have CSLA .NET, and it has grown enough over the years that it affords me the opportunity to explore most of the key technologies I really want to learn. Having just released version 3.0, I got to dig into the plumbing behind WPF data binding, and explore the details around how WCF supports client/server (which it does well btw). Now my sights are turned toward two targets: .NET 3.5 and Silverlight.

The new language features, and of course lINQ itself, are key areas of focus for CSLA .NET 3.5. Fun stuff, no doubts there!

And if Silverlight 1.1 gets just a few more features (most notably data binding to objects), I believe I can create a CSLA Light that will provide a meaningful subset of CSLA .NET functionality within the Silverlight environment. If that pans out, it will be quite exciting :)

Magenic

Working for Magenic is great! I really enjoy the company and the people I work with. Over the next few months I'll be conciously spending more time and energy working internally with Magenic. As noted above, Microsoft's technology release schedule is grueling, and I want to make sure Magenic's consultants have the opportunity to stay on top of everything. This not only means doing some internal training, but more importantly working to build up a base of expertise and the infrastructure so other people can do some of that training (formally or informally).

It is (in my view) entirely unrealistic for one person to be an expert on everything that's going on. How can you credibly span .NET, Windows, Web, AJAX, SOA, Silverlight, SharePoint, Biztalk, SQL, Windows Server 2008 and everything else? You can't. But Magenic can, because we have a large enough pool of people that there's a decent level of expertise in all these technologies spread across the company. My challenge, then, is to coordinate that expertise such that the experts are in a position to share their knowledge with everyone else who needs it.

Of course Magenic has five offices across the country, and so there's the interesting sub-challenge of enabling the sharing of expertise across geographic distance. Actually it isn't the knowledge sharing that's such a big deal (we have great forums, etc), but it is the sharing if passion that is harder - and yet is at least as important.

Don't burn out

I've been feeling seriously burned out of late. That's not good. Part of it is due to the way travel grates on me more all the time, but most of it is that I haven't spent time doing truly exploratory things for a very long time now. CSLA has become quite consuming, and so my spare time goes into the framework, when I could be working on a .NET MUD engine or something really fun like that (I have these cool prototypes for truly dynamic world behavior that I'd love to pull together into something real).

It is also the case that I'm terrible about taking vacations. If I vacation at home, I work. That's the primary drawback with having a home office - work is always right there. But with Smartphones and laptops with wireless and/or cell modems the "office" can go almost anywhere, so even with a traditional office, escape is almost impossible.

Except... In August I'm spending a full week (nearly 10 days) in or near the Boundary Waters in far northern Minnesota. No meaningful cell coverage, no Starbucks or anywhere like it that would have wireless. Just untold miles and miles of wilderness, canoing, camping and fishing. Yea!

Remember the passion

Have you ever played that "what if" game? The one where you ask youself what job you'd do if you didn't do your current job?

I always draw a blank. If I had to do something else it would actually be work, rather than what I do now, which is to get paid for my hobby.

This, by the way, is what bothers me most about outsourcing, or even the software factory concept. It is true that economic needs shift over time, and steel workers needed to get new skills to find new jobs (as an example). I suppose it is possible that many of those steel workers had a burning passion for their job. That working with steel was their hobby as well as their job. But I doubt it. Most people I know view work as work: as a way to make money so they can do what they really want to do in their off hours. On the other hand, most developers I know view work as fun - and a way to make money while having fun.

Economists are often dismissive toward us, saying that if our jobs go offshore that we can just learn new skills and find different jobs. What they fail to understand is that we'd be doing far more than switching jobs: we'd be giving up an entire way of life. We'd be moving from a world of creative inspiration to a world of soul-crushing tedium. Yes, I realize I'm probably being very condenscending toward people with "normal" jobs, but this is how I see the world... (Then again, maybe economists do realize the deep love we have for what we do, and they envy us and so feel no remorse in talking about the destruction of our way of life?)

In any case, I love aspects of technology (especially building distributed and/or quasi-autonomous systems). And I love teaching about development. These things have been my passion since long before I officially started my career (when I was just a teenager, I was teaching elementary school kids how to use those new-fangled computers (Apples with no floating point support)). And I love writing - I really am what JMS describes as an "author" (one cursed to write because they can't not write). I can't imagine not doing most of the things I do today.

I think that's the key: remember the passion. Remember the fun. Yes, work hard, but more importantly code well, and perhaps above all: have fun!

Thursday, July 12, 2007 10:38:00 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  |