What Was I Thinking?

Follies & Foils of .NET Development

  Home  |   Contact  |   Syndication    |   Login
  39 Posts | 0 Stories | 91 Comments | 0 Trackbacks

News

Archives

Post Categories

Check These Out

Gurus

Thursday, November 05, 2009 #

Looks like I’m heading to LA.  I’m really excited to be attending the PDC this year.  I went to the PDC in 03 (where they announced WCF, WF, WPF) and it turned my brain to mush by the middle of day two.  ANyone else going?  Anyone like to share a RSVP promo code to save me some $$$?  I’ll buy you a drink or two!

 

Hope to see you all there!


Friday, October 30, 2009 #

I’ve been spending a lot of time lately debugging through some web-hosted applications.  Some of these applications are hosted by Visual Studio’s own internal web server (Cassini), and others are hosted by my local instance of IIS.

 

Web projects hosted locally get automatic attached to the VS debugger when you press F5.  Projects hosted under IIS do not.  In order to debug IIS hosted projects, you must attach the Visual Studio debugger to the IIS worker process (aspnet_wp.exe or w3wp.exe) manually. 

 

Traditionally, you attach by selecting Attach To Process from the Debug menu, scrolling the list of processes until you find the worker process and then clicking “Attach”.

 

Thanks to this handy macro I found by HologramX at snipplr.com, I’m able to attach with a single keystroke:

To add this macro to your environment,  Tools > Macro IDE > Add new item to MyMacros > Select Module, Name 'AttachToWebServer' > Copy/paste code.

Imports System
Imports EnvDTE80
Imports System.Diagnostics

Public Module AttachToWebServer

Public Sub AttachToWebServer()

Dim AspNetWp As String = "aspnet_wp.exe"
Dim W3WP As String = "w3wp.exe"

If Not (AttachToProcess(AspNetWp)) Then
If Not AttachToProcess(W3WP) Then
System.Windows.Forms.MessageBox.Show(String.Format("Process {0} or {1} Cannot Be Found", AspNetWp, W3WP), "Attach To Web Server Macro")
End If
End If

End Sub

Public Function AttachToProcess(ByVal ProcessName As String) As Boolean

Dim Processes As EnvDTE.Processes = DTE.Debugger.LocalProcesses
Dim Process As EnvDTE.Process
Dim ProcessFound As Boolean = False

For Each Process In Processes
If (Process.Name.Substring(Process.Name.LastIndexOf("\") + 1) = ProcessName) Then
Process.Attach()
ProcessFound = True
End If
Next

AttachToProcess = ProcessFound

End Function

End Module

 

To set as a keyboard shortcut go to Tools > Options > Keyboard, and search for the name of the macro (AttachToWebServer) and then assign a keyboard shortcut.

IIS-Attached Debugging is now one keystroke away.


Monday, October 19, 2009 #

When adding an item to a dictionary, I always thought you had to use the Add() method, like this:

string key = "MyKey";
int value = 20;
var myDictionary = new Dictionary<string, int>();
myDictionary.Add(key, value);

Apparently, you can directly reference it in the collection, and if the key doesn’t exist, its auto-added to the collection

string key = "MyKey";
int value = 20;
var myDictionary = new Dictionary<string, int>();
myDictionary[key] = value;

most likely you already knew this.  I did not.  Its the little things I guess.


Monday, October 12, 2009 #

I’m a big fan of Resharper.  Its loaded with refactoring and best practice guidance that makes me a better developer.  I’ve also started working with .NET RIA services.  The verdict is still out on RIA services, but I think I like them. Unfortunately RIA has an interesting approach to code sharing between the web and Silverlight project that's incompatible with Resharper.  It used generated code that is technically excluded from the project.   As a result, any code in my Silverlight project that references any of the shared classes won’t resolve for Resharper and valid code ends up looking like this:

 

image

 

This is likely to be addressed in a future version of Resharper (if RIA services takes hold), until then you can still get Resharper to resolve your shared code by:

  1. Select your Silverlight project in the Solution Explorer
  2. Select the Show All Files option from the Solution Explorer toolbar.
  3. Right-Click on the Generated Code folder and select Include In Project

As part of the project, Resharper will pick up the generated shared code and resolve your shared classes appropriately.  During compilation you may receive an warning about modifying a project file during compilation.  Just continue with the warning (in my case, I simply suppress the warning dialog so I’m not bothered with it).

 

With Generated Code include in my project, Resharper is back in full effect.

image


Sunday, October 11, 2009 #

Silverlight bundles its executables into a single file with a XAP extension.  This is really just a zip file.  You can open the XAP and modify the contents just as you would any ZIP file.  By default windows explorer doesn’t know how to open XAP files.

 

The following reg file adds the metadata to tell Explorer to treat XAP files like compressed folders.  I found this file somewhere on the internet, my apologies for not being able to site the source.

 

Windows Registry Editor Version 5.00 

[HKEY_CLASSES_ROOT\.xap]
"PerceivedType"="compressed"
"Content Type"="application/x-silverlight-app"
@="CompressedFolder"

[HKEY_CLASSES_ROOT\.xap\CompressedFolder]

[HKEY_CLASSES_ROOT\.xap\OpenWithProgids]
"CompressedFolder"=""

[HKEY_CLASSES_ROOT\.xap\PersistentHandler]
@="{098f2470-bae0-11cd-b579-08002b30bfeb}"

The file is available for download here

After apply the registry changes, you’ll be able to open XAP files directly in Windows Explorer.


Saturday, October 10, 2009 #

Recently I had the need to implement support for Dynamic Linq queries in my framework project.  For the uninitiated, Dynamic Linq allows me to write statements like this:

var query =
               db.Customers.Where("City == @0 and Orders.Count >= @1", "London", 10).
               OrderBy("CompanyName").
               Select("New(CompanyName as Name, Phone)");

where I can specify the predicates and selectors as string values.   Microsoft has published a helper class that enables DynamicLinq support here -->C# Dynamic Query Library (included in the -LinqSamples-DynamicQuery directory).

 

Buried in this code is a class Called Dynamic.cs.  Include this code file into your project and you’ll have Dyanmic Linq support, Unless…

 

Unless, you’re developing a silverlight application.  The dynamic class makes use of a ReaderWriterLock class that isn’t available in the Silverlight implementation of System.Threading.

Using Reflector, I extracted the disassembled code for ReaderWriterLock and made a version that will compile and run under silverlight.  The only adjustment was commenting out the HostProtection attribute since it wasn’t available due to its protection level.  In my testing I haven’t run into any problems as the result of removing this attribute.

Here’s the code you’ll want to add to your silverlight libraries in order to allow the compilation of Dynamic.cs and support Dynamic Linq in your silverlight project:

 

ReaderWriterLock.cs

using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Security.Permissions;
using System;
//[ComVisible(true), HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
[ComVisible(true)]
public sealed class ReaderWriterLock : CriticalFinalizerObject
{
    // Fields
    private int _dwLLockID;
    private int _dwState;
    private int _dwULockID;
    private int _dwWriterID;
    private int _dwWriterSeqNum;
    private IntPtr _hObjectHandle;
    private IntPtr _hReaderEvent;
    private IntPtr _hWriterEvent;
    private short _wWriterLevel;
 
    // Methods
    public ReaderWriterLock()
    {
        this.PrivateInitialize();
    }
 
    public void AcquireReaderLock(int millisecondsTimeout)
    {
        this.AcquireReaderLockInternal(millisecondsTimeout);
    }
 
    public void AcquireReaderLock(TimeSpan timeout)
    {
        long totalMilliseconds = (long)timeout.TotalMilliseconds;
        if ((totalMilliseconds < -1L) || (totalMilliseconds > 0x7fffffffL))
        {
            throw new ArgumentOutOfRangeException("timeout", "ArgumentOutOfRange_NeedNonNegOrNegative1");
        }
        this.AcquireReaderLockInternal((int)totalMilliseconds);
    }
 
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void AcquireReaderLockInternal(int millisecondsTimeout);
    public void AcquireWriterLock(int millisecondsTimeout)
    {
        this.AcquireWriterLockInternal(millisecondsTimeout);
    }
 
    public void AcquireWriterLock(TimeSpan timeout)
    {
        long totalMilliseconds = (long)timeout.TotalMilliseconds;
        if ((totalMilliseconds < -1L) || (totalMilliseconds > 0x7fffffffL))
        {
            throw new ArgumentOutOfRangeException("timeout", "ArgumentOutOfRange_NeedNonNegOrNegative1");
        }
        this.AcquireWriterLockInternal((int)totalMilliseconds);
    }
 
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void AcquireWriterLockInternal(int millisecondsTimeout);
    [MethodImpl(MethodImplOptions.InternalCall)]
    public extern bool AnyWritersSince(int seqNum);
    public void DowngradeFromWriterLock(ref LockCookie lockCookie)
    {
        this.DowngradeFromWriterLockInternal(ref lockCookie);
    }
 
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void DowngradeFromWriterLockInternal(ref LockCookie lockCookie);
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void FCallReleaseLock(ref LockCookie result);
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void FCallUpgradeToWriterLock(ref LockCookie result, int millisecondsTimeout);
    ~ReaderWriterLock()
    {
        this.PrivateDestruct();
    }
 
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void PrivateDestruct();
    [MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    private extern bool PrivateGetIsReaderLockHeld();
    [MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    private extern bool PrivateGetIsWriterLockHeld();
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern int PrivateGetWriterSeqNum();
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void PrivateInitialize();
    public LockCookie ReleaseLock()
    {
        LockCookie result = new LockCookie();
        this.FCallReleaseLock(ref result);
        return result;
    }
 
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    public void ReleaseReaderLock()
    {
        this.ReleaseReaderLockInternal();
    }
 
    [MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    private extern void ReleaseReaderLockInternal();
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    public void ReleaseWriterLock()
    {
        this.ReleaseWriterLockInternal();
    }
 
    [MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    private extern void ReleaseWriterLockInternal();
    public void RestoreLock(ref LockCookie lockCookie)
    {
        this.RestoreLockInternal(ref lockCookie);
    }
 
    [MethodImpl(MethodImplOptions.InternalCall)]
    private extern void RestoreLockInternal(ref LockCookie lockCookie);
    public LockCookie UpgradeToWriterLock(int millisecondsTimeout)
    {
        LockCookie result = new LockCookie();
        this.FCallUpgradeToWriterLock(ref result, millisecondsTimeout);
        return result;
    }
 
    public LockCookie UpgradeToWriterLock(TimeSpan timeout)
    {
        long totalMilliseconds = (long)timeout.TotalMilliseconds;
        if ((totalMilliseconds < -1L) || (totalMilliseconds > 0x7fffffffL))
        {
            throw new ArgumentOutOfRangeException("timeout","ArgumentOutOfRange_NeedNonNegOrNegative1");
        }
        return this.UpgradeToWriterLock((int)totalMilliseconds);
    }
 
    // Properties
    public bool IsReaderLockHeld
    {
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        get
        {
            return this.PrivateGetIsReaderLockHeld();
        }
    }
 
    public bool IsWriterLockHeld
    {
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        get
        {
            return this.PrivateGetIsWriterLockHeld();
        }
    }
 
    public int WriterSeqNum
    {
        get
        {
            return this.PrivateGetWriterSeqNum();
        }
    }
}
 

and LockCookie.cs

using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential), ComVisible(true)]
public struct LockCookie
{
    private int _dwFlags;
    private int _dwWriterSeqNum;
    private int _wReaderAndWriterLevel;
    private int _dwThreadID;
    public override int GetHashCode()
    {
        return (((this._dwFlags + this._dwWriterSeqNum) + this._wReaderAndWriterLevel) + this._dwThreadID);
    }
 
    public override bool Equals(object obj)
    {
        return ((obj is LockCookie) && this.Equals((LockCookie)obj));
    }
 
    public bool Equals(LockCookie obj)
    {
        return ((((obj._dwFlags == this._dwFlags) && (obj._dwWriterSeqNum == this._dwWriterSeqNum)) && (obj._wReaderAndWriterLevel == this._wReaderAndWriterLevel)) && (obj._dwThreadID == this._dwThreadID));
    }
 
    public static bool operator ==(LockCookie a, LockCookie b)
    {
        return a.Equals(b);
    }
 
    public static bool operator !=(LockCookie a, LockCookie b)
    {
        return !(a == b);
    }
}
 
 

Thursday, October 01, 2009 #

Manish Dalal wrote an excellent post on how to effectively data bind a silverlight combobox for foreign key scenarios.  Using his approach, you create a ListProvider class that receives a DomainContext, internally loads the data into the context, and exposes the loaded data through an easily bound property.

Having written a few of these, I’ve refactored the list provider functionality to a generic base class.  The heavy lifting is done via reflection and it should be noted that my code assumes you take the default naming convention of the entities and entity collections in the DomainContext class.

 

Defining the class

Here’s my base class:

public abstract class ListProviderBase<T, DC> : INotifyPropertyChanged where T : Entity, new()where DC : DomainContext
  {
      protected DC context;
      readonly Type itemType = typeof(T);
      public event Action<LoadOperation<T>> PickListLoadingComplete;
      public DC DomainContext
      {
          set
          {
              context = value;
       
              // Get the query object for the specified item Type
              MethodInfo queryMI = context.GetType().GetMethod("Get" + itemType.Name + "Query");
              EntityQuery<T> query = (EntityQuery<T>) queryMI.Invoke(context, null);

              // Load the data into the context, upon completion, call back to PickListDataLoad_Complete
              context.Load<T>(query, PickListDataLoad_Complete, itemType);
              onPropertyChanged("DomainContext");
          }
          get
          {
              return context;
          }
      }

      /// <summary>
      /// call back that fires when the data list loading is complete
      /// </summary>
      /// <param name="obj"></param>
      private void PickListDataLoad_Complete(LoadOperation<T> obj)
      {

          string msg = String.Format(((Type)obj.UserState).Name + " Pick List loaded with {0} Entries",
                                     obj.Entities.Count());
          Debug.WriteLine(msg);
          if (PickListLoadingComplete != null)
              PickListLoadingComplete(obj);
          onPropertyChanged("PickList");
      }

      /// <summary>
      /// The list of entities to appear in the list.  Bind to this element.
      /// </summary>
      public EntityList<T> PickList
      {
          get
          {
              // return the corresponding EntityList property.
              // Note:  This assumes the ItemList uses the default naming convention 
              //        in the domain context (ie the list of customer objects is accessed via the Customers property)
              return (EntityList<T>)readProperty(itemType.Name+"s", context);
          }
      }

      /// <summary>
      /// using reflection, reads the specified property from the specified object
      /// </summary>
      /// <param name="propertyName"></param>
      /// <param name="obj"></param>
      /// <returns></returns>
      private static object readProperty(string propertyName, object obj)
      {
          PropertyInfo propInfo = obj.GetType().GetProperty(propertyName);
          object propValue = propInfo.GetValue(obj, null);
          return propValue;
      }

      private void onPropertyChanged(string propertyName)
      {
          if (PropertyChanged != null)
              PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
      public event PropertyChangedEventHandler PropertyChanged;
  }

Within my application, I sub class the base class and specify my application’s DomainContext.

public abstract class NBOListProviderBase<T> :ListProviderBase<T,NBODomainContext> where T:Entity,new(){}

when I want to populate a combo with a list of related departments, its a single line of code to create the ListProvider;

public class DepartmentListProvider : NBOListProviderBase<Department> { }

Defining the Control

Add the definition as static resource in my XAML
   <Grid.Resources>
       <app:DepartmentListProvider x:Name="departmentListProvider" />
   </Grid.Resources>
 and define the combo box as follows:
                            <ComboBox  ItemsSource="{Binding PickList, Source={StaticResource departmentTypeListProvider}}"
                                       SelectedItem="{Binding Department, Mode=TwoWay}"
                                       DisplayMemberPath="DepartmentName"  />

This binds the control’s source to the PickList property of the instance of the DepartmentTypeListProvider object, instructs the combobox to show the DepartmentName values as the list content, and store the selected item in the Department property of the data context.

Loading the Data

In the Loaded event of the form hosting the combobox, be sure to set the DomainContext property of the list provider.  This will populate the data into the combobox and expose it via the PickList property.  I’ve written a helper method (again using some reflection) to make this easier.

 

public static class ViewHelper
   {
       public static void SetListProviderContext(FrameworkElement ResourceContainer, string ListProviderResourceName, DomainContext Context)
       {
           var listProvider = ResourceContainer.Resources[ListProviderResourceName];
           var propInfo = listProvider.GetType().GetProperty("DomainContext");
           propInfo.SetValue(listProvider, Context, null);
       }
   }

 To Load our combobox data, place the following code in the loaded event of the form that hosts the combobox:

 

ViewHelper.SetListProviderContext(LayoutRoot, "departmentListProvider", this.dds.DomainContext);

When the form renders, the combobox list will be loaded.  See Manish’s comments on overriding the equality operator to ensure the correct value is automatically selected editing existing data.


Monday, September 07, 2009 #

It appears Microsoft has removed the designer for XAML files from the latest release of Silverlight 3 Tools.  The designer isn’t really gone, its just moved to the bottom of the editor and is hidden by default.

 

To restore the designer view:

1. Open your .xaml file in the default vs editor.

2. Move your cursor to the bottom of the edit window (right underneath the horizontal scroll) and it should change to a split-style cursor

3. Click and drag up to reveal a pane that looks like the following:

image

4. Click on the “Resume Loading the Designer” link.  The designer should now be present

 

image

I haven’t found a way to tell VS to remember this layout preference, and to auto-load the designer yet, but I’ll keep looking.

Its my understanding Microsoft will be replacing it with a better designer when VS 2010 ships.


Thursday, July 09, 2009 #

I am considering writing a policy based error handler for WCF.  The Error handler would be registered with the service host, and would automatically log all exceptions.    WCF Services would “opt-in”/register for this error handler by a custom attribute.

Deciding how to handle the unhandled exceptions is the policy part.  The policy could state which exception types to convert to handled WCF faults, which exception types to map to different fault/exception types, which exceptions to obfuscate and which ones to allow to return to the caller (faulting the service channel).

I’m thinking the policy would be handled via configuration, so the rules could be changed without requiring code drops.

The policy-driven approach is similar to the enterprise library exception handling approach, but I think I could deliver 90% of the functionality with sufficiently less effort than is required by the entlib approach.

Would anyone find value in this approach and solution?


Wednesday, February 11, 2009 #

At PDC this year, Microsoft announced a major release of the Windows Workflow Foundations (WF); WF4.0

WF4.0 promises better performance, scalability, control, visibility, and usability than its predecessor.

Gone are code-asides and the dreaded code activities, replaced with 100% XAML markup and custom activities.  MFST has seriously stepped up its tooling to make custom activities easier (including defining a custom WF syntax which gets “compiled” to XAML), an improved designer and debugging support.

Interop support is available to run your soon-to-be-legacy 3.x Workflows under 4.0, but it sounds as if a refactoring of current workflows and activities is the best way to leverage the 4.0 improvements.  Looks like I’ll be releasing a new version of the Custom Activity Generator

The new WF runtime allows WF’s in app, data, or UI tiers. WF articulation is gaining momentum in the other MFST initiatives too, including Azure, office and of course Sharepoint.

Here’s a link to some session screencasts relating to WF 4.0.

 

Update:

Here’s a link to an article by David Chappell, which explains the high level interactions between some new MS technologies including WF 4.0, Oslo, and Dublin (Server extensions)