Welcome to MSDN Blogs Sign in | Join | Help

My .NET Rocks Episode

Recently I recorded a .NET Rocks episode which went live this week.  The episode is titled Ron Jacobs on Azure AppFabric.  Nice title, except that my intention was to discuss Windows Server AppFabric.  Of course it is a little confusing to have two things share the same name “AppFabric” but that is where we are in today’s world. 

Today the name “AppFabric” is applied to two things

They share the same name for a reason.  The reason is that in the future our intention is to make the same set of capabilities available in both the Windows Server environment and the Windows Azure environment.

Now I am not the evangelist for the Windows Azure Platform and so I am not an expert on it, but if you want to know more about the Windows Server AppFabric, just ask.

Posted by ronjacobs | 0 Comments

How to get rid of http://tempuri.org in a Workflow Service

Technorati Tags: ,

Somebody was asking earlier today how to eliminate the namespace http://tempuri.org from their WSDL with a WorkflowService.

I had no idea but soon enough somebody from the team came up with the answer.  It was something you would never guess in a million years so I decided this has to go on the blog.

When you create a WorkflowService the namespace of your service is declared as a part of the ServiceContractName.  For example, in the Introduction to Workflow Services Hands On Lab You create a service named SubmitApplication.xamlx and set the ServiceContractName to {http://contoso.com/hr/}IApplicationService

Everything inside the curly braces is the namespace.  Everything after is the contract name.  Fine – this looks good right?  I assumed that was all you would need but then…  Look at this WSDL.

<wsdl:definitions name="SubmitApplication" targetNamespace="http://tempuri.org/" >

Doh!  There it is, right in front of me…  Now other parts of the WSDL are using the correct namespace so maybe it is not that big of a deal but still, I want to get rid of http://tempuri.org!!

Ready for the solution?  It’s non-obvious but here goes...

The name of the WorkflowService is actually an XName which means it includes a namespace.  If you don’t provide a namespace, guess what you get?  http://tempuri.org!

If you want to get rid of it, you have to use a namespace prefix.  Earlier when I set the ServiceContractName it created an xmlns declaration in the XAML for me. It’s buried in the long list of xmlns declarations but you will find it if you search for p: (I deleted a bunch of stuff below so you can see it.

<WorkflowService Name="SubmitApplication" xmlns:p="http://contoso.com/hr/" >
 
Now to apply this namespace to the Name property you have to edit the XAMLX file in the XML editor (the designer doesn’t allow this).  The fix is so simple…
 
<WorkflowService Name="p:SubmitApplication" xmlns:p="http://contoso.com/hr/" >
 
Notice the namespace prefix?  Just put a p: in front of the name and you are all done!
 
Now look at the WSDL.
<wsdl:definitions name="SubmitApplication" targetNamespace="http://contoso.com/hr/" >

Wohoo!
Posted by ronjacobs | 1 Comments

How To Secure a Workflow Service in .NET 4

The other day somebody asked me how they could secure a Workflow Service.  Specifically what they wanted to do was to allow one or two Windows Accounts to be able to access the service on the Intranet.  The other requirement was to simply list the allowed identities in the configuration file.  So I decided to tackle this project and put it on endpoint.tv

After investigating this here is the solution I came up with.  It starts with a WCF Workflow Service project.  (Note: This code would work with any WCF service, not just Workflow Services)

I then added an appKey value to the web.config with the list of allowed identities.

<configuration>
<appSettings>
<add key="ServiceAllow" value="REDMOND\rojacobs,ROJACOBS-PC\Administrators"/>
</appSettings>

Next I added a class that is derived from ServiceAuthorizationManager and implemented a role check for the identity to see if I had a match with my list.

public class ServiceAuthz : ServiceAuthorizationManager
{
private String[] serviceAllows;

public ServiceAuthz()
{
String allowString = System.Configuration.ConfigurationManager.AppSettings["ServiceAllow"];
serviceAllows = allowString.Split(',');
}

protected override bool CheckAccessCore(OperationContext operationContext)
{
var authCtx = operationContext.ServiceSecurityContext.AuthorizationContext;
var identities = (List<System.Security.Principal.IIdentity>)(authCtx.Properties["Identities"]);

foreach (var ident in identities)
{
var windowsIdent = ident as System.Security.Principal.WindowsIdentity;
if (windowsIdent != null)
{
var windowsPrincipal = new System.Security.Principal.WindowsPrincipal(windowsIdent);
foreach (String allow in serviceAllows)
{
Boolean fInRole = windowsPrincipal.IsInRole(allow);
if (fInRole)
return true;
}
}
}
return false;
}
}

By default, the Workflow Service doesn’t include any configuration so it will use basicHttpBinding which will not pass credentials.  So I simply added a protocol mapping section to web.config <system.serviceModel> section to cause it to use wsHttpBinding instead which will pass the Windows credentials by default.

<protocolMapping>
<add scheme ="http" binding="wsHttpBinding"/>
</protocolMapping>
And then we have to add the <serviceAuthorization> behavior.  Since I am using “tagless” services, I added this behavior to the rest of the behaviors which will apply to all “tagless” services in the solution.
 
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceAuthorization serviceAuthorizationManagerType=
"WorkflowServiceWindowsAuthZ.ServiceAuthz, WorkflowServiceWindowsAuthZ" />

Now we’re all set to go.  You can test the service by using the RUNAS command to run the WCFTestClient from various user accounts to see the security take effect.
 
If you want to see this in action, be sure to check it out on endpoint.tv or just download the code and give it a go…
Posted by ronjacobs | 0 Comments

Uh Oh – Workflow Services Lab is Missing…

Just yesterday I told you that we released an update to the Visual Studio 2010 Training Kit.  The test version that I had did include the Workflow Services lab but somehow the version released to the web did not include it. 

So I posted the very latest version of the lab here if you want to try it out.

Posted by ronjacobs | 2 Comments

Visual Studio 2010 Training Kit – January Release now available

Finally the January release of the Visual Studio 2010 training kit is ready.  This updated version of the kit fully supports Visual Studio 2010 beta 2 and includes some new labs that I wrote such as the Introduction to Workflow, Workflow Services Lab and the What’s New in WCF4 Lab.

You can download the kit here.

Posted by ronjacobs | 0 Comments

Today on endpoint.tv – Windows Server AppFabric in action

Before PDC I went to the Channel 9 studio and recorded an episode of endpoint.tv with Ian and Adam two of the program managers on the AppFabric team.  At the time we were still using the code name “Dublin” because we didn’t know for sure what the official name would be.  I had some trouble getting the video uploaded to the site but it is finally there today.

Be sure to check it out here.

Posted by ronjacobs | 0 Comments

WCF Security Spike – Day 2

We learn what he have to learn.

Does that surprise anyone?  Some people have reacted with shock when I said that I don’t know WCF Security very well.  Others said that I was implying that evangelists are somehow inferior to their colleagues on the product teams.  Of course there are people with varying skills in different roles as a part of any organization.  Technical Evangelists must have deep technical skills just like any other developer.  However, the truth remains that when faced with an overwhelming amount of new technology every day we make choices and learn what we have to learn.

There are a few people who have taken the time to decipher WCF security.  To this point, I have not been one of them because I have not had to learn it.  The evangelist in me wants to focus on new product features and in the area of security there is nothing new in WCF.  Yet because many customers are struggling with it, I felt that it was time… I must take on the beast so here we go for day 2.  I’m writing these notes as I go so who knows what we will find.

Service Principal Name

Jesse mentioned to me that I needed to use setspn.exe to create a service principal name.  I immediately went to Bing :-) and searched for WCF setspn which lead me to the WCF Security Guidance wiki page on the application scenario Intranet – Web to Remote WCF Using Transport Security (Trusted Subsystem, HTTP)

This article discusses using setspn when hosting the WCF service in an ASP.NET app pool running under a custom domain account.  In my case the app pool is running under Network Service so I wasn’t sure if I needed it or not but I decided to give it a go so I ran the command

C:\>setspn -A http/rojacobs-vid rojacobs-vid
Registering ServicePrincipalNames for CN=ROJACOBS-VID,OU=Workstations,OU=Machines,DC=redmond,DC=corp,DC=microsoft,DC=com
        http/rojacobs-vid
Updated object

Test It

I’ve created a local group on my machine called “ServiceUsers”.  The plan is to add accounts to the “ServiceUsers” group and then use IsInRole to determine if the user is a member of the group.

The test will be to simply invoke the service with the WCF Test Client using two different accounts.  One test will run it under my domain account REDMOND\rojacobs and the other will run it under a local admin account ROJACOBS-VID\Ron.  Both of these accounts are members of ServiceUsers.

Here is the code for testing the user accounts.  For now I’m commenting out the PrincipalPermission attribute

//[PrincipalPermission(SecurityAction.Demand, Role = "BUILTIN\\Administrators")]
public string AdminOperation()
{
return string.Format("IsInRole(\"ServiceUsers\")={0}",
Thread.CurrentPrincipal.IsInRole("ServiceUsers"));
}
 
I’ve setup my project in Visual Studio to host the site in my local IIS as well.

Test #1 – using domain account

I launch the WCF Test Client and add a service reference to my service by giving it the address of my service.

After clicking Invoke I see that it works returning the following

<s:Body u:Id="_0">
<AdminOperationResponse xmlns="http://tempuri.org/">
<AdminOperationResult>IsInRole("ServiceUsers")=True</AdminOperationResult>
</AdminOperationResponse>
</s:Body>

This didn’t work yesterday so I think it was the setspn trick that fixed it.  Just to be sure I delete the spn and try it again

C:\>setspn -d http/rojacobs-vid rojacobs-vid
Unregistering ServicePrincipalNames for CN=ROJACOBS-VID,OU=Workstations,OU=Machines,DC=redmond,DC=corp,DC=microsoft,DC=com
        http/rojacobs-vid
Updated object

Well guess what – it still worked.  Strange, I’m sure this didn’t work yesterday…  Perhaps the reboot overnight did it?  Maybe the effects of setspn are still lingering in the worker process? 

Just to be sure, I do an iisreset command

C:\>iisreset

Attempting stop...
Internet services successfully stopped
Attempting start...
Internet services successfully restarted

Now to test it again… still works. 

Test #2 – using local admin account

Yesterday no matter what I did, the local admin account would never return true for IsInRole.  Wonder what will happen today?

<s:Body u:Id="_0">
<AdminOperationResponse xmlns="http://tempuri.org/">
<AdminOperationResult>IsInRole("ServiceUsers")=True</AdminOperationResult>
</AdminOperationResponse>
</s:Body>

Doh! I can’t believe it… 

Test #3 – using PrincipalPermission

Now I’m going to put PrincipalPermission back and try to get it to allow only users in the ServiceUsers group.

[PrincipalPermission(SecurityAction.Demand, Role = "ServiceUsers")]
public string AdminOperation()
{
return string.Format("IsInRole(\"ServiceUsers\")={0}",
Thread.CurrentPrincipal.IsInRole("ServiceUsers"));
}

I test it from both WCF Test Clients (one under REDMOND\rojacobs and the other running as ROJACOBS-VID\Ron) they both succeed. 

Now I test it from a third local account (ROJACOBS-VID\Andrew) which is not a member of ServiceUsers – this one should fail.

Yes! Success – it fails.

I’m still not sure about this – with things not working yesterday, then working today.  I think I need to reboot and run all the tests again just to be sure.

Test #4 – After Reboot

WCF Test client on the same machine running under domain account works

WCF Test client on the same machine running under local admin account works

Test #5 – Multiple Machines

Ok great, but I wonder will this work across machines? 

In order to test this using HTTP I need to enable the firewall rule World Wide Web Services (HTTP Traffic-In)

Once I enabled all the machines in my home office to see each other (by turning on Network Discovery) I could browse to the service and see the WSDL but then I ran into a problem.

The WSDL used the full name of my machine rojacobs-vid.redmond.corp.microsoft.com in the URIs.  My other machines in my home office could not resolve this address so the WCF Test Client failed.

Strange that I can navigate to http://rojacobs-vid but not http://rojacobs-vid.redmond.corp.microsoft.com

This is probably an issue because I’m testing this in my home office.  If I had all these machines physically plugged into the corporate network (rather than via DirectAccess) it might work.

Day 2 Spike Summary

What have we learned so far?

  • The easy way to restrict access by roles is with Windows Groups with PrincipalPermission demands
  • You can do the PrincipalPermission demand with an attribute or in code
  • You can use a local machine groups
  • You do have to use a binding that supports this Intranet scenario (wsHttpBinding or netTcpBinding.  basicHttpBinding wont’ work)
  • You must enable the firewall ports for inbound and outbound traffic when testing
  • Sometimes things just don’t work until you go home for the day and try again the next day :-)

What have we not figured out?

  • How to test from another machine using the WCF Test Client when it cannot access the machine using the full DNS name (for some strange reason)
  • or how to generate WSDL that does not use the full DNS name of the machine in the URI

Some other things to investigate

  • What about Windows Identity Foundation?  Does this make it easier or harder?  Is it appropriate for this scenario?

Next Steps

  • Review Windows Identity Foundation Whitepaper
  • Do the Windows Identity Foundation WCF Services hands on lab
Posted by ronjacobs | 0 Comments

WCF Security Spike – Day 1

My first goal was to secure a service.  I’m happy to say I managed to get a service that allowed one user and didn’t allow another by using a PrincipalPermission with wsHttpBinding (I gave up on netTcpBinding for now – one monster at a time right?)

I created a simple service and declared a service with an endpoint as you see in the web.config (from .NET 4)

<system.serviceModel>
<services>
<service name="Security">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration=""
contract="ISecurity" />
</service>

Then I implemented my service with a security demand

[PrincipalPermission(SecurityAction.Demand, Role="BUILTIN\\Administrators")]
public string AdminOperation()
{
return GetSecurityInfo("AdminOperation");
}

Easy huh? Wait a minute… not so fast.  I started testing this.

The machine is joined to the REDMOND domain and when I tested using my domain account it worked just fine.

However when I fired up the WCF Test Client using a local machine account that is a member of the Administrators group I get “Access Denied”.

In fact, if I allow the call and then test for role membership using IsInRole(“SomeGroup”) with any local group, all of them returned false.  The only time I got IsInRole(“Administrators”) to return true was when I used my domain account to call the service.

Oh the mysterious ways of Active Directory…  Who can plumb the depths of kerberos?  Perhaps I could (should?) look at Windows Identity Foundation for help…

Posted by ronjacobs | 4 Comments

WCF Security Is Hard

Ok… I said it.  Let’s just admit the truth for a second.  It is hard.  Recently somebody contacted me about a security problem and asked for me help.  I’m probably the wrong guy to ask.  After all, I’m an evangelist.  That means I create demos, labs etc. none of which use security (typically).  What that means is that while I know a great deal about some aspects of WCF when it comes to security I actually don’t know much.  I know, I know… it’s embarrassing.

My new years resolution for 2010 is to write some articles, samples and create some videos to help you deal with security.  After all it is vital that we help you to get this right.  I’m tired of waiting for somebody else to fix this. 

[edit: 12/4] By this comment I don’t mean to say that all evangelists are less technically competent that any other programmer.  I work with some very smart guys who are great developers.  I only meant to imply that we tend to learn things that our work requires and mine doesn’t require security very often

My First Spike

Here is the first security spike.  I’m going to create a WCF service hosted in IIS.  The service will use wsHttpBinding and Windows Credentials.  The goal is to restrict access by role as defined by group membership in active directory.  This should be a no-brainer right?

Yesterday I searched around looking for resources to help.  I didn’t find much.  Most things I found showed how to secure self-hosted WCF services with console apps.  The security picture with an IIS hosted service is significantly more complex.  You have IIS settings to deal with, plus ASP.NET stuff in web.config and then the myriad of WCF security settings and options.

I have often referred people to the WCF Security Guide from patterns and practices which is good but even after reading it I found myself confused.

My first attempt yesterday was to create a service hosted in IIS and then to use netTcpBinding as recommended by the WCF Security Guide to access it.  I didn’t get very far.  Once I hosted the service in IIS I was not able to access it with the netTcpBinding from the client.  I kept getting one of those “machine actively refused the connection” messages.

Today I’m going to go with the wsHttpBinding so I can simplify things a bit.  I’ll let you know how it goes.

Posted by ronjacobs | 4 Comments

Where can I get the Workflow Services Lab?

If you saw the walkthrough of the Workflow Services lab on Channel 9 you might be wondering where you can get the lab code.  This lab will be in the next update of the VS2010 training kit.  Until then I’ve posted a beta version of the lab here.

Posted by ronjacobs | 0 Comments

How do I clear my AppFabric Databases?

Once you start to develop apps with Windows Server App Fabric you will quickly find that your AppFabric monitoring and persistence databases become cluttered with stuff.  From time to time you will want to clear out these databases.

Here is the PowerShell script I use to get the job done.  Just copy these into a file like AppFabricReset.ps1

(Watch out for line breaks)

# Bring in the App Fabric module
import-module applicationserver
$ConfirmPreference = "None"
# Remove the persistence db
Remove-ASPersistenceDatabase -Force -Server ".\SQLExpress" -Database "ApplicationServerExtensions" -Confirm:$false
# Create the persistence DB
Initialize-ASPersistenceDatabase -Admins $env:computername\AS_Administrators -Readers $env:computername\AS_Observers -Users "BUILTIN\IIS_IUSRS" -Database "ApplicationServerExtensions" 
-Server ".\SQLExpress" -Confirm:$false
# Clear the monitoring DB
Clear-ASMonitoringDatabase -Database "ApplicationServerExtensions" -Server ".\SQLExpress" -Confirm:$false
Posted by ronjacobs | 0 Comments

WF4: How To Access Out Arguments

To get a value from your workflow you must access the dictionary of output arguments returned from the workflow.

Note: This example is based on Visual Studio 2010 Beta 2

Consider a workflow that accepts two arguments

Name Direction Argument Type
UserName In String
Greeting Out String

The workflow uses an assign activity to set the result of the expression "Hello " & UserName & " from Workflow 4" to the Greeting out argument.  The hosting program can access the Greeting out argument from the dictionary of output values returned from the WorkflowInvoker or WorkflowApplication.

Because the values in the output dictionary are stored as objects with a string as the key, there are three possible outcomes when you try to access an argument value.

  1. The key does not exist in the dictionary
  2. The key exists but the type of the value is not what you expected
  3. The key exists and the type is compatible with what you expected

Whenever you access an out argument you must consider these outcomes. In the event that the key does not exist or the type of the value is not what is expected you may encounter a KeyNotFoundException or InvalidCastException

You can choose to

  • Let these exceptions propagate possibly terminating the host with an unhandled exception
  • Catch the exceptions
  • Code defensively using TryGetValue to access the key from the collection or the C# keyword “as” or VB function TryCast to avoid the invalid cast

Accessing Out Arguments with WorkflowInvoker

The following example shows a method that Invokes the SayHello workflow passing in the UserName and accessing the out argument named “Greeting” after the workflow completes. 

Watch Out
This example is not coding defensively or catching exceptions so if the key did not exist or the type of the value was not compatible the exception would propagate to the caller.

C#

private static void GetArgumentsFromWorkflowInvoker()
{
IDictionary<string, object> output = WorkflowInvoker.Invoke(
new SayHello() { UserName = "Test" });

string greeting = (string)output["Greeting"];

Console.WriteLine("WorkflowInvoker said {0}", greeting);
}

Visual Basic

Shared Sub GetArgumentsFromWorkflowInvoker()
Dim output As IDictionary(Of String, Object) =
WorkflowInvoker.Invoke(New SayHello() With {.UserName = "Test"})

Dim greeting As String = output("Greeting")

Console.WriteLine("WorkflowInvoker said {0}", greeting)
End Sub

Accessing Out Arguments with WorkflowApplication

WorkflowApplication invokes the workflow on a thread from the CLR threadpool. To capture the outputs you must assign a delegate to the WorkflowApplication.Completed property. Keep in mind that your delegate is called whenever the workflow completes successfully or not. You can check the CompletionState property to find out if the activity closed or faulted. Because accessing the Outputs dictionary may result in an exception you should wrap the access with a try/catch/finally block as shown.

C#

private static void GetArgumentsFromWorkflowApplication()
{
AutoResetEvent sync = new AutoResetEvent(false);
string greeting = null;
Exception argException = null;

WorkflowApplication wfApp = new WorkflowApplication(
new SayHello()
{
UserName = "Test"
});

wfApp.Completed = (e) =>
{
// Did the workflow complete without error?
if (e.CompletionState == ActivityInstanceState.Closed)
{
try
{
// Accessing the output arguments dictionary
// might throw a KeyNotFoundException or
// InvalidCastException
greeting = (string)e.Outputs["Greeting"];
}
catch (Exception ex)
{
argException = ex;
}
finally
{
// Must be sure to unblock the main thread
sync.Set();
}
}
};

wfApp.Run();
sync.WaitOne();

// Show the exception from the background thread
if (argException != null)
Console.WriteLine("WorkflowApplication error {0}", argException.Message);
else
Console.WriteLine("WorkflowApplication said {0}", greeting);
}

Visual Basic

Shared Sub GetArgumentsFromWorkflowApplication()
Dim sync As AutoResetEvent = New AutoResetEvent(False)
Dim greeting As String = Nothing
Dim argException As Exception = Nothing
Dim wfApp As WorkflowApplication =
New WorkflowApplication(New SayHello() With {.UserName = "Test"})

wfApp.Completed = Function(args)
If (args.CompletionState =
ActivityInstanceState.Closed) Then
Try
' Accessing the output arguments dictionary
' might throw a KeyNotFoundException or
' InvalidCastException
greeting = args.Outputs("Greeting")
Catch ex As Exception
argException = ex
Finally
' Must be sure to unblock the main thread
sync.Set()
End Try
End If

' VB requires lambda expressions to return a value
Return Nothing
End Function

wfApp.Run()
sync.WaitOne()

' Show the exception from the background thread
If (argException Is Nothing) Then
Console.WriteLine("WorkflowApplication said {0}", greeting)
Else
Console.WriteLine("WorkflowApplication error {0}", argException.Message)
End If
End Sub


Accessing Out Arguments with Defensive Coding Style

In this example, we are accessing the Out Argument with a defensive coding style that will insure no exceptions are thrown.

ContainsKey() vs. TryGet()
You should use TryGet instead of ContainsKey() to first check for the key and then Get() to access the key. The reason for this is that you will iterate over the collection twice, once to determine if the key is present and again to access the value.

C#

private static void GetArgumentsFromWorkflowInvokerDefensive()
{
IDictionary<string, object> output = WorkflowInvoker.Invoke(
new SayHello() { UserName = "Test" });

object obj = null;
string greeting = null;

if (!output.TryGetValue("Greeting", out obj))
{
Console.WriteLine("Greeting not found");
}
else
{
greeting = obj as string;
if (greeting == null)
Console.WriteLine("Greeting could not be converted to a string");
else
Console.WriteLine("WorkflowInvoker said {0}", greeting);
}
}

Visual Basic

Shared Sub GetArgumentsFromWorkflowInvokerDefensive()
Dim output As IDictionary(Of String, Object) =
WorkflowInvoker.Invoke(New SayHello() With {.UserName = "Test"})

Dim obj As Object = Nothing

Dim greeting As String

' TryGetValue will not throw an exception
If (Not output.TryGetValue("Greeting", obj)) Then
Console.WriteLine("Greeting not found")
Else
' Not sure what type it is, try to convert it
greeting = TryCast(obj, String)
If (greeting Is Nothing) Then
Console.WriteLine("Greeting could not be converted to a string")
Else
Console.WriteLine("WorkflowInvoker said {0}", greeting)
End If
End If
End Sub

Posted by ronjacobs | 0 Comments

WF4: Passing Arguments to Activities

In Windows Workflow Foundation .NET 4 (WF4) everything is an Activity.  That is a class that inherits from System.Activities.Activity.  Activities can have in, out or in/out arguments and do their work then the Activity.Execute() method is called.  You could think of them as being similar to a static method except for the fact that a static method can have only 1 return argument where an Activity can have more than one. 

Passing arguments to an activity can be done one of two ways.  You can use Initialization syntax when creating the activity to set the input arguments (which are just public properties of type InArgument<T>) or you can pass a Dictionary<string, object> which will be used to initialize the arguments.

Passing Arguments with Initialization syntax

   1: [TestMethod]
   2: public void ShouldReturnGreetingWithName()
   3: {
   4:     var output = WorkflowInvoker.Invoke(
   5:         new SayHello()
   6:         {
   7:             UserName = "Test"
   8:         });
   9:     Assert.AreEqual("Hello Test from Workflow 4", output["Greeting"]);
  10: }

This works well except that out arguments (OutArgument<T>) will also show up in the Intellisense window as well as other public properties such as the DisplayName.  In this example from the beta 2 release of the “Introduction to Workflow 4” hands on lab (look for it at the PDC) you will see three properties from Intellisense but only one of them is an in argument suitable for initialization.

OutArg

As you can see initializing an OutArgument looks possible, but if you try it…

image

What’s going on here?  InArgument<T> will implicitly construct from an argument of type T but OutArgument<T> won’t do this so even though it appears like you can do this, you can’t because it won’t compile.  But this is a good thing because you shouldn’t initialize an OutArgument anyway.

In practice, I think most people will wrap activity initialization and (possibly) invocation inside of other methods so that developers who are unfamiliar with workflow can just call those methods to do the work. 

Posted by ronjacobs | 0 Comments

WCF Essentials – Great Stuff now online

Michelle Leroux Bustamante has written some great new papers for MSDN that you really should check out.  They are in the WCF Essentials series.

What I like the most about these articles is that she gives really great practical advice.  For example check out this sentence from Getting Started with WCF

“The default is PerSession which is typically not the desired setting.”

Wow – you won’t read that in the MSDN documentation about the InstanceContextMode Enumeration but it is probably something that you should know.  It is the kind of advice you can expect from a seasoned WCF developer.

Are you doing WCF Services or SOA in your organization?

If so, I’d like to talk with you about your solution as a part of some research we are doing for future releases.  Please contact me through my blog if you would be willing to have a short phone discussion with me about your solution and needs.

Posted by ronjacobs | 0 Comments

Testing a SendMail activity with WF4

Many applications send email.  After all it is the preferred binding for async app to human communication.  I noticed that many of my colleagues were creating samples that simulate email by dropping a text file into a directory.  Then they would open the file and copy and paste a GUID into a form.  There is a much better way of course.  In the real world people would send an HTML mail with a link that brings you back to the website with the GUID in place.

Nearly everyone has done this with a web site where you register with the site, they send you an email to confirm and then you click a link in the email to complete the registration process.  I decided to build out this scenario using a workflow to manage the registration process but then I ran into the issue of how to test the email.

At first I thought I would just use Hotmail.  This would work for a demonstration but you have to have network connectivity and verifying that the message arrived would require coding to open the mailbox with POP3 and look for a particular message.  Then I found an easier way.  You can configure the SmtpClient to drop mail messages into a folder rather than send them.

Of course, many of you will say “Duh! I can’t believe Ron didn’t know about this…”.  Well it was news to me so I’m sure that some people didn’t know about it either so here is how it works.

First off, here is the very simple SendMail activity (using .NET 4 Beta 1)

   1: public class SendMail : CodeActivity
   2: {
   3:     public InArgument<string> From { get; set; }
   4:     public InArgument<string> Recipients { get; set; }
   5:     public InArgument<string> Subject { get; set; }
   6:     public InArgument<string> Body { get; set; }
   7:     public InArgument<bool> IsBodyHtml { get; set; }
   8:     protected override void Execute(CodeActivityContext context)
   9:     {
  10:         SmtpClient client = new SmtpClient();
  11:         MailMessage message = new MailMessage(From.Get(context),
  12:             Recipients.Get(context), Subject.Get(context), Body.Get(context));
  13:         message.IsBodyHtml = IsBodyHtml.Get(context);
  14:         client.Send(message);
  15:     }
  16: }

Now I want to test my SendMail class.  So I created a Unit Test project and added an app.config file with the necessary configuration magic.

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <system.net>
   4:     <mailSettings>
   5:       <smtp deliveryMethod="SpecifiedPickupDirectory">
   6:         <specifiedPickupDirectory pickupDirectoryLocation="c:\maildrop"/>
   7:       </smtp>
   8:     </mailSettings>
   9:   </system.net>
  10: </configuration>

Now all that I need to do is write the test.  I want to be sure that when I send a mail that a file ends up in the c:\maildrop directory.  The file will have a random GUID as the name.  I’m not worried about reading the file to be sure that it contains the correct text.  That would be testing the SmtpClient class which I assume works properly.

   1: [TestMethod]
   2: [DeploymentItem("MyActivities.dll")]
   3: public void ShouldSendMail()
   4: {
   5:  
   6:     // Remove all files from the drop location
   7:     if (Directory.Exists(mailDrop))
   8:         Directory.Delete(mailDrop, true);
   9:     Directory.CreateDirectory(mailDrop);
  10:  
  11:     // Create and send the message
  12:     WorkflowInvoker.Invoke(new MyActivities.SendMail()
  13:     {
  14:         From = "test@tempuri.org",
  15:         Recipients = "foo@bar.org, baz@foo.com",
  16:         Subject = "Test HTML message",
  17:         IsBodyHtml = true,
  18:         Body = messageBody
  19:     });
  20:  
  21:     // Find the message in the drop location
  22:     var messages = Directory.EnumerateFiles(mailDrop);
  23:     Assert.AreEqual(1, messages.Count());
  24: }

And the really cool thing is that you can open this file with Windows Live Mail and click the link if you want to demonstrate how it works.

image

I decided to screencast this code check it out
endpoint.tv Screencast - Spike on Workflow Managed Email Verification

Posted by ronjacobs | 0 Comments
More Posts Next page »
 
Page view tracker