Welcome to MSDN Blogs Sign in | Join | Help
The Microsoft "Oslo" Base Domain Library (the BDL) -- now not completely in T-SQL!

The Microsoft "Oslo" repository "provides a robust, enterprise-hardened storage location for the data models. It takes advantage of the best features of SQL Server 2008 to deliver on critical areas such as scalability, security, and performance." The repository team took SQL Server 2008, and used SQL Server best practices to create a series of internal schemas that enabled extremely important features of any enterprise-scale database application -- particularly manageability and security, but also scalability and performance. In short, it's a "pre-designed" SQL Server 2008 application that you can use to kick-start enterprise database application design, development, and deployment. All well and good. If you're strong in SQL skills, you can go to the link in the first sentence of this graph and read all about how it works and the features it supports.

Although the "Oslo" repository can be used for any kind of data-driven application (because it's just SQL Server 2008, duh), it is especially functional when used with data structures that fit the basic schemas in the "Oslo" repository. However, while you can design your data structures in T-SQL to take advantage of the repository features using the documentation here, the question is: How do you do this in "M"?

If you're already familiar with "M" and "Oslo" a little bit, you might know that you can obtain all the features of the repository Base Domain Library (BDL) by compiling your "M" domains with the /t:Repository switch. What this does -- among other things -- is create matching SQL Server views for your tables (using the same name as the tables but without the "Table" on the end), associating your domain models with the appropriate repository Folders, and creating triggers that can be used to secure data access in any way SQL Server 2008 supports, among other things (you can read more about this here). This switch, I think, is a tremendous boon to developers who either don't want to focus on doing data modeling work in T-SQL (we are hoping "M" will be much more efficient and pleasant to use) or who are not especially familiar with T-SQL and SQL Server 2008. Almost all our current samples (How to: Populate a Model into the "Oslo" Repository, How to: Create data using "M", How to: Install the HumanResources Sample Model using "M", and How to: Use "M" to Create Custom Icons and Display Names for some examples) use the /t:Repository switch to turn on "Oslo" repository feature patterns. It works, and it's easy.

There is a problem with this switch for people who want to use "M" but who understand database design well enough to want to control the projection of "M" into T-SQL and into the "Oslo" repository: it's all or nothing. If you create 100 extents in "M" you'll get (most likely) 100+ tables -- it varies depending on the particular "M" statements -- and if you use the /t:Repository target switch you'll ALSO get 100+ views, all secured by their own insert triggers. However, it is often the case that the database schemas contain many more tables than views. Using views are, after all, a very good way to expose data to client applications in a way that is more efficient and understandable for them. Most client applications do not need to know all the internal storage details, and those developers may well not want to know themselves. (In addition, with views you have the ability to version the underlying data tables and modify the views to draw the data from the new tables so that client applications consuming views can continue functioning. Views are good.) No, instead of having everything done for you at all times, you'd often prefer to be able to specify which views are created yourself, and specify which of the "Oslo" repository features are to be applied to them. Right?

The May CTP release of "Oslo" is the beginning of your ability to do this repository "feature pattern" work in "M" and the "M" toolchain rather than having to do this work in T-SQL directly. To do so in the May CTP release, you must use the various "M"-based patterns to structure your data correctly and then add some specific T-SQL calls in the -postSQL parameter of M.exe -- and you do not specify /t:Repository in the m.exe command. The only trick is that there are a couple different ways to handle your "Oslo" repository Folder creation. The easiest way I have found and for which I wrote an example is to:

  1. Use the HasFolderAndAutoId type in the System module, which creates the Folder and Id patterns needed to enable "Oslo" repository management and security features and expose them in "Quadrant".
  2. Make sure your table ends in "Table". :-) (The answer to your question is given to you if you examine the AddForeignFolderKey procedure. For the complete list of pattern restrictions that may be necessary to take advantage of all repository features, see Using the AddStandardPatterns Procedure.)
  3. Use the PathsFolder() pattern when you declare the values for your extent.
  4. Create your own view that has the same name as the table in step 2 but without the "Table" at the end.
  5. Use the mx.exe createFolder option to create the Folder name in the repository that you specified in your extent value declarations.
  6. Create a .sql file that calls AddForeignFolderKey passing the module name and the view name.
  7. Compile your .m file to an image, using the -postSql parameter to add the .sql file to the image.
  8. Install the image into the repository using mx.exe. If you are doing iterative work, also pass the -f switch, which will delete and reinstall the module if it is the same.

If you want to see the sample that does this, scroll to the bottom of Folders and Ids, which will continue to be updated and corrected on a regular basis as we update that information. If you want to read about how all of this works in T-SQL, see "Oslo" Repository Schemas documentation that my colleague Jason Roth put together for the January CTP release.

by ralph.squillace | 1 Comments

Filed under:

"Oslo" -- SQL Server 2008 includes Express.... Just FYI
It has occurred in the forum (http://social.msdn.microsoft.com/Forums/en-US/oslo/threads) that people sometimes don't have access to SQL Server 2008 -- or they don't think they do. In point of fact, however, you can use SQL Server 2008 Express editions to satisfy the requirement. Get that here.

by ralph.squillace | 0 Comments

Filed under: ,

OK, this is dumb, but this Bing thing is really great.

Strangely enough, although I love the company I work for, I'm not interested in technologies that we put out that aren't really the best. Often, we end up making them the best through iterations, and that's wonderful. Sometimes we don't, and we have great competition in the world, and that benefits us and everyone. Anyway, I don't typically deal with too much hype, but it is **really** exciting to use bing.com as a search and discover that it works great. I mean, really great. By far the best results for the work and personal things I do than the competitor that I typically use. I'm very happy about this; it's a hard field to make progress in, and we've finally begun to do it.

 At least, at first glance. It'll be interesting to me to see whether it can keep up the good work as I start to use it more heavily.

by ralph.squillace | 0 Comments

Filed under: ,

Microsoft code name "Oslo" -- New Tutorials for the May CTP

I've spent quite a bit of the past two years working on "Oslo" and find it an amazing challenge to explain to people who aren't database gurus. Have a look -- it's an amazing set of tools to begin developing .NET applications with modeling techniques that don't make much sense if you haven't been interested in data modeling or advanced reusable libraries and so on. One thing we've done on the "Oslo" documentation team is to put together two main tutorials that you should know about, but which might be hard to find.

First, the Getting Started with "Oslo" tutorial walks you through a relatively simple -- but very real -- modeled application from start to finish.

Second, we didn't want anyone getting stalled out working with "Oslo" simply because we didn't give examples using your favorite data access technique. So we put together examples of four data access technologies that all access the model data inside SQL Server 2008. They are here

Hope they help, and we'll be adding more documentation soon. Feel free to bother me here or make any request in the forum.

by ralph.squillace | 1 Comments

Filed under:

Patterns & Practices: Extremely useful WCF Security Guidance Project How To topics available.

I was recently asked to have a look at some really nice How To topics on WCF security that the Patterns & Practices people have worked up, and am quite happy with them; so much so I wanna tell them to let us fold them back into the regular SDK documentation! The project's Web site is http://www.codeplex.com/WCFSecurity, but to have a quick list of the topics I had a chance to see (plus some nice walkthrough videos), check out J.D. Meier's blog entry on them at http://blogs.msdn.com/jmeier/archive/2008/03/27/patterns-and-practices-wcf-security-guidance-now-available.aspx.

Note: There are two very useful WCF security projects going on right now that I got a touch confused about because, unaware of them, I did not realize the distinction when I found them. The first is the documentation and videos provided by the project mentioned above. The second, WCF Security Guidance Package, is a Visual Studio based set of security automation tools -- and some documentation -- that help you apply the appropriate principles inside Visual Studio. Both are recommended, and I see them as complimentary.

 If you find them useful or woefully inadequate, do let the projects know. They'll do their best to make them as useful as possible. Back to what I'm really interested in.... Web Service Software Factory: Modeling Edition!

by ralph.squillace | 1 Comments

More about Descriptions

I haven't posted for a while because I've been working on other stuff, and much of that work has encouraged me to think hard about the concept of the ServiceDescription class, the root of what is called the Description Hierarchy that I posted about previously. The ServiceDescription object, when created, contains everything that is known about a WCF service, and is really all that is required by a ServiceHost to get one up and running. One could, if one wanted, override the ServiceHostBase.CreateDescription method and return a ServiceDescription object that has been created live, right there. Many people do in fact do exactly this, populating a ServiceDescription from information contained in a file, or a database, or obtained from another Web service, for example.

Note: If you do this, make sure to override ApplyConfiguration also to return immediately, disabling configuration files. :-)

Of course, you could go the other way, overriding ApplyConfiguration in order to consume any arbitrary file or configuration system, doing the same thing. Imagine that you serialize an entire ServiceDescription object, and in ApplyConfiguration deserialize it and hand it off to the system. I'm using this thought game to point out that the totality of information necessary to execute a WCF service is **collected** from disparate sources and assimilated and validated into a ServiceDescription object, and then THAT is used to construct a service runtime and execute it (if possible). In the default case, the information lives in:

  • Attributes on the WCF service implementation. (ServiceContractAttribute, MessageContractAttribute, DataContractAttribute, ServiceBehaviorAttribute, and so on.)
  • The structure of the implementation determined by managed reflection over the structure of the service implementation. (For example, WCF throws exceptions if you try to return two MessageContractAttribute types.)
  • "Configuration" values contained in the configuration files, including behaviors and bindings and so on.

In fact, if the WCF implementation didn't require some attributes on the implementation at runtime, it would be possible to build a system in which you implement an implementation without any attributes at all, and specify a huge amount of precise information in a very large configuration file. Then you could build a UI tool to handle that file more easily (SvcSuperConfigMaster.exe, anyone?) And then you could make that tool handle these configuration tables almost anywhere in a Windows domain -- or other domain, for that matter. The only thing you'd need to do at runtime, then, would be to point at an implementation that could be loaded at runtime and then configured from the SuperFile.config.

In .NET 3.0, we did a good job of introducing a relatively straightforward way to both program and "model" -- another way of saying "specify" or "describe" -- the service and it's runtime information both in a managed assembly (attributes and structure) and in a description file (configuration file). I once mentioned to some of the product managers how all of this I saw as "metadata", almost the entire thing. They laughed and said, no, no, no. But I still think I'm right. :-)

by ralph.squillace | 1 Comments

The Description Tree Heirarchy

One of the things we didn't get a chance to write as much about as we wanted to by RTM -- but will fix soon -- is the description tree. The service-side description tree consists of the heirarchy of objects starting with ServiceDescription class. The client-side description tree heirarchy starts from the ServiceEndpoint class.

Uh-oh: The first thing about these classes that you should know -- and notice if you followed the link for ServiceDescription is that there are TWO ServiceDescription classes in .NET Framework 3.0. The System.Web.Services.Description.ServiceDescription class represents metadata for a service in XML (and is used by WCF in some metadata-exclusive situations. Mail me if you want details). The ServiceDescription class that I want to discuss today is the System.ServiceModel.Description.ServiceDescription class, which is the representation of everything that is known about a WCF service. This ServiceDescription is used by WCF to create:

  • A service runtime, including listeners and behaviors that configure ru ntime execution patterns when the runtime is built.
  • WSDL and XSD files (including policy assertions) returned by ?wsdl HTTP Get requests or WS-MetadataExchange requests.
  • Code that can represent compatible data types, contract interfaces, and WCF client classes in addition to configuration files containing binding information.

Typically, the word metadata in the Web services world means the WSDL/XSD and other associated XML files that describe the interoperable structures and signatures used to communicate -- that is, the stuff you get back from a ?wsdl querystring request. But of course, that kind of metadata is interoperable metadata because the language used to communicate it is public and any tool or platform can use it. Hopefully by now you know that this metadata describes a contract that an implementation must support in order to be usable by a client on another platform. If you take a step back, however, you should be able to see that an implementation -- for example, a WCF service implementation with configuration -- contains exactly the same information as its WSDL in addition to the information specific to the implementation.

What is the sum total of all information about a WCF service? Well, naturally the contract must be modeled in code, typically as a service contract interface. In addition, a configuration file typically contains endpoint information in addition to any behaviors expected to run when the service is constructed. Where are these items brought together? Assuming a standard service application, when ServiceHost.Open is called a ServiceDescription is created -- and validated for contradictions that would prevent it from running -- that contains the totality of all information known about the service. In WCFs applications, the ServiceDescription object is effectively the internal WCF metadata for a WCF service. It can be converted into other "metadata" forms, such as a running service itself, code+configuration, and WSDL and XSD output. If you understand the ServiceDescription heirarchy, you'll understand all sorts of fun things about how WCF applications are configured, how they execute, and the forms into which such information can be put. The team doesn't necessarily think of the description tree as "uber-metadata" but I think doing so really helps. It helps you make some sense out of the fact that downloaded WSDL information (some of which is represented as XML-based System.Web.Services.Description.ServiceDescription information!) cannot simply be put into a System.ServiceModel.Description.ServiceDescription object, added to a ServiceHost, and run: they aren't the same things at all.

Here's the entire tree, pretty much, in a single, large (~270KB) .png file. Have some fun with it. Remember, on the client side, the description tree starts with the ServiceEndpoint class.

by ralph.squillace | 1 Comments

Sessionless Duplex Services, Part Two: Lifetimes and Contexts

Two posts ago I wrote the following post about how to build a duplex service and client that does NOT use sessions. Once I wrote the sample, I wanted to extend it to provide some more flexibility. I've done that now, but in so doing, I ran across some issues that make this type of service useful in only a couple of circumstances -- IMHO. However, I want to throw the newer version out there to demonstrate some programmatic areas and to see whether any of you can figure out what scenarios this type of service is useful.

The first thing I wanted to do was to hook up custom extensions that tracked the lifetimes of the ServiceHost, the InstanceContext, and the IContextChannel when uses without sessions. To do this I used Extensible Objects (objects that implement IExtensibleObject<T> to which objects that implement IExtension<T> (where T is the specific extensible object) can be added). Michele Leroux Bustamante has a simple example clearly implemented here if you want to examine how this works very quickly.

I did the same thing. Here's the implementation for the ServiceHost tracker:

  class ServiceHostContext : IExtension<ServiceHostBase>, IDisposable
  {
    Guid id;

    public ServiceHostContext()
    { this.id = Guid.NewGuid(); }

    public string ID
    { get { return this.id.ToString(); } }
     
    #region IExtension<ServiceHost> Members

    public void Attach(ServiceHostBase owner)
    {
      Console.ForegroundColor = ConsoleColor.Red;
      Console.WriteLine("Attached to new ServiceHost.");
      Console.ResetColor();
    }

    public void Detach(ServiceHostBase owner)
    { throw new Exception("The method or operation is not implemented."); }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
      Console.WriteLine("Destroying service host: " + this.id);
    }

    #endregion
  }

This is made then used by a service behavior like so:

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
      serviceHostBase.Extensions.Add(new ServiceHostContext());
    }

 I addition, I have an instancecontext tracker:

  public class MyInstanceContextExtension : IExtension<InstanceContext>
  {

    //Associate an Id with each Instance Created.
    String instanceId;

    public MyInstanceContextExtension()
    { this.instanceId = Guid.NewGuid().ToString(); }

    public String InstanceId
    {
      get
      { return this.instanceId; }
    }

    public void Attach(InstanceContext owner)
    {
      Console.ForegroundColor = ConsoleColor.Red;
      Console.WriteLine("Attached to new InstanceContext.");
      Console.ResetColor();
    }

    public void Detach(InstanceContext owner)
    {
      Console.WriteLine("Detached from InstanceContext.");
    }
  }

And this is added by and endpoint behavior (so it can be used on both sides!) that itself adds an instance context initializer. So it looks like this:

 public class MyInstanceContextInitializer : IInstanceContextInitializer
  {
    public void Initialize(InstanceContext instanceContext, Message message)
    {
      MyInstanceContextExtension extension = new MyInstanceContextExtension();

      //Add your custom InstanceContex extension that will let you associate state with this instancecontext
      instanceContext.Extensions.Add(extension);
    }
  }

And finally let's track channels by creating an IContextChannel extension:

  public class ChannelTrackerExtension : IExtension<IContextChannel>
  {
    <snipExtraneousIdentifyingStuff />

    #region IExtension<IContextChannel> Members

    public void Attach(IContextChannel owner)
    {
      Console.ForegroundColor = ConsoleColor.Red;
      Console.WriteLine("Attached to new IContextChannel {0}.", owner.GetHashCode());
      this.channel = owner;
      Console.ResetColor();
    }

    public void Detach(IContextChannel owner)
    {
      Console.WriteLine("Detached from IContextChannel {0}.", owner.GetHashCode());
    }

  }

And we use an endpoint behavior (again for both sides) that implements IChannelInitializer to detect the creation of channels.

  public class ChannelInitializer : IChannelInitializer
  {
    #region IChannelInitializer Members

    public void Initialize(IClientChannel channel)
    {
      Console.WriteLine("IClientChannel.Initialize called.");
      channel.Extensions.Add(new ChannelTrackerExtension());
    }
    #endregion
  }

The service and endpoint behaviors all implement System.ServiceModel.Configuration.BehaviorExtensionElement so that they can be wired up using a configuration file. The other thing I've done is to update both the contract and the client. First, the contract now specifies one request/response service and has one one-way callback operation, like so:

  [ServiceContract(
    Name = "SampleDuplexHello",
    Namespace = "http://microsoft.wcf.documentation",
    CallbackContract = typeof(IHelloCallbackContract),
    SessionMode = SessionMode.NotAllowed
  )]
  public interface IDuplexHello
  {
    [OperationContract]
    string Hello(string greeting);
  }

  public interface IHelloCallbackContract
  {
    [OperationContract(IsOneWay = true)]
    void Reply(string responseToGreeting);
  }

The client application now creates a new object for each client proxy. This is an artifact of the fact that it's a console application; if it were a Windows Form or WPF application I wouldn't need to do this. But while I was building it out, I stumbled across some interesting behavior that illuminates what is going on. The host creates a new Client object for each connection it wants to make:

  public class ClientApp
  {
    public static void Main()
    {
      Client client = new Client();
      client.Run();
      client = new Client();
      client.Run();

And each Client object creates an AutoResetEvent to hold open the client until all callbacks are received and then in Run() creates a duplex client, invokes the request/reply operation and waits for the callbacks.

   public void Run()
    {
      // Picks up configuration from the config file.
      InstanceContext callbackInstanceContext = new InstanceContext(this);
      SampleDuplexHelloClient wcfClient = new SampleDuplexHelloClient(callbackInstanceContext);
      try
      {
        using (OperationContextScope opScope = new OperationContextScope(wcfClient.InnerDuplexChannel))
        {
          // add replyto
          Console.WriteLine(wcfClient.InnerDuplexChannel.LocalAddress.ToString());
          OperationContext.Current.OutgoingMessageHeaders.ReplyTo
            = wcfClient.InnerDuplexChannel.LocalAddress;
          Console.ForegroundColor = ConsoleColor.White;
          Console.WriteLine("Enter a greeting to send and press ENTER: ");
          Console.Write(">>> ");
          Console.ForegroundColor = ConsoleColor.Green;
          string greeting = Console.ReadLine();
          Console.ForegroundColor = ConsoleColor.White;
          Console.WriteLine("Called service with: \r\n\t" + greeting);
          Console.ForegroundColor = ConsoleColor.Cyan;
          Console.WriteLine(wcfClient.Hello(greeting));
          Console.ResetColor();
          this.waitHandle.WaitOne();
          Console.WriteLine("Set was called.");
        }
      }
      catch (TimeoutException timeProblem)
      {
        Console.WriteLine("The service operation timed out. " + timeProblem.Message);
        wcfClient.Abort();
      }
      catch (CommunicationException commProblem)
      {
        Console.WriteLine("There was a communication problem. " + commProblem.Message);
        wcfClient.Abort();
      }
    }

The callback (Reply()) remains the same. Now we can talk. As soon as I figure out how to post .zips I will. Anyway, the service now looks like this:

   public string Hello(string greeting)
    {
      Console.ForegroundColor = ConsoleColor.Green;
      Console.WriteLine("Caller sent: " + greeting);
      Console.ResetColor();
      Console.WriteLine("Session ID: " + OperationContext.Current.SessionId);
      string response = "Service object " + this.GetHashCode().ToString() + " received: " + greeting;
     
      // Generate five callbacks
      CallbackArguments args = new CallbackArguments();
      args.numCallbacks = 5;
      args.to = OperationContext.Current.IncomingMessageHeaders.ReplyTo.Uri;
      args.callerClient
        = OperationContext.Current.GetCallbackChannel<IHelloCallbackContract>();
      args.incomingHeaders
        = OperationContext.Current.IncomingMessageHeaders.ReplyTo.Headers;
      // Do this on another thread.
      System.Threading.ThreadPool.QueueUserWorkItem(
        new WaitCallback(DuplexHello.GenerateCallbacks), args
      );
      Thread.Sleep(1000);
      //DuplexHello.GenerateCallbacks(args);
      return String.Format("Hi there. You sent {0}.", greeting); 
    }

Note that I have a GenerateCallbacks method that is supposed to invoke five callbacks to the caller (you can do this either on this thread or another). Note, also, that the request/reply portion of the call is handled simply by returning. The only thing of interest here is the callbacks generated by the service. For this, recall that we needed to add the inbound ReplyTo value to the outbound To header. But -- and this is one of those interesting things, when we did TWO separate clients, the second client failed to see the response and the service did send the messages. What happened?

What happened is that the first request does not need to append any address information to the callback To header. But subsequent callbacks DO append information, and that extra information does need to be appended. So here's the simple way to do this in GenerateCallbacks:

    private static void GenerateCallbacks(object parameters)
    {
      CallbackArguments args = parameters as CallbackArguments;
      IHelloCallbackContract callerClient
        = args.callerClient;
      using (
        OperationContextScope callbackOpContext
          = new OperationContextScope((IContextChannel)callerClient)
      )
      {
        OperationContext.Current.OutgoingMessageHeaders.To = args.to;
        foreach (AddressHeader ah in args.incomingHeaders)
        {
          // deal with ref params
          OperationContext.Current.OutgoingMessageHeaders.Add(ah.ToMessageHeader());
        }

We assign the To and then we take any extra information coming in from the client instance and make sure to add that information as well. In this case, the extra information is that on subsequent calls the client creates listeners on addresses that make use of reference parameters and these, too, must be added to the outbound headers collection. Whoohooo!

Now, let's make our callbacks.

     for(int i = 0; i < args.numCallbacks; ++i)
      {
        try
        {
          Console.WriteLine("callback {0} to: {1}", i, args.to);
          callerClient.Reply(String.Format("Notification {0}.", i.ToString()));
        }
        catch (TimeoutException timeout)
        {
          Console.WriteLine("There was a timeout exception on a callback.");
        }
        catch (CommunicationException commException)
        {
          Console.WriteLine("CommunicationException: {0}", commException.Message);
        }
        catch (Exception ex)
        {
          Console.WriteLine("General exception: {0}.", ex.Message);
        }
      }

Great, right? Um, no. Here's another interesting thing. The default service InstanceContextMode is PerSession. But we aren't using a session, remember? The behavior we get in this case ends up being "PerCall". So if we return from the operation prior to the completion of the callbacks you can guess what happens: The callbacks don't get there because the service infrastructure has cleaned up the operation context and service channel from underneath us.

One fix is to do what I did here: Throw a Thread.Sleep in the operation to enable the callbacks to reach their destination successfully. Obviously this is just a mitigation for the example. Try it yourself without the Sleep and see what you get. The other way to handle this is to realize that the service InstanceContextMode could be single, in which case the underlying service channel stays around for all clients. Mix and match solutions and enjoy yourself.

Let's get back to the extensions that track lifetimes. One of the things that building the sample this way accomplishes is to establish what happens on each side when multiple clients run. First of all, each client callback address is different, so clients are still separate entities for the purposes of duplex calls. We can see that for each new Client object, you get a brand new channel. For the service, however, there is only ever one channel for the lifetime of the service host with the exception of any timeouts. When the InstanceContextMode is functionally PerCall, you get the following:

Attached to new ServiceHost.
Channel tracker added.
The service is ready.
Press <ENTER> to terminate service.

IClientChannel.Initialize called.
Attached to new IContextChannel 42132014.
Attached to new InstanceContext.
Service object created: 45155606
Caller sent: Hello.
Session ID:
callback 0 to: http://localhost:8081/Callback/cea9fe7f-c3a6-41cf-9151-c54b1404d5b5
callback 1 to: http://localhost:8081/Callback/cea9fe7f-c3a6-41cf-9151-c54b1404d5b5
callback 2 to: http://localhost:8081/Callback/cea9fe7f-c3a6-41cf-9151-c54b1404d5b5
callback 3 to: http://localhost:8081/Callback/cea9fe7f-c3a6-41cf-9151-c54b1404d5b5
callback 4 to: http://localhost:8081/Callback/cea9fe7f-c3a6-41cf-9151-c54b1404d5b5
Service object destroyed: 45155606
Attached to new InstanceContext.
Service object created: 29447802
Caller sent: Hello again.
Session ID:
callback 0 to: http://localhost:8081/Callback/99928a92-2d3c-42cc-bd1b-34af14cc737b
callback 1 to: http://localhost:8081/Callback/99928a92-2d3c-42cc-bd1b-34af14cc737b
callback 2 to: http://localhost:8081/Callback/99928a92-2d3c-42cc-bd1b-34af14cc737b
callback 3 to: http://localhost:8081/Callback/99928a92-2d3c-42cc-bd1b-34af14cc737b
callback 4 to: http://localhost:8081/Callback/99928a92-2d3c-42cc-bd1b-34af14cc737b
Service object destroyed: 29447802

You can see that we get a new IntanceContext and service object for each call. But only one service channel is ever created. It's this channel that must be available for the callbacks. This means, also, that you cannot call Close on it when you're done with your callbacks. Go ahead: Close it and see what happens -- you're going to need it again later. ;-)

The client, however, has channels appearing all over. OK, for each new client:

Channel tracker added.
IClientChannel.Initialize called.
Attached to new IContextChannel 44419000.
http://localhost:8081/Callback/cea9fe7f-c3a6-41cf-9151-c54b1404d5b5
Enter a greeting to send and press ENTER:
>>> Hello first.
Called service with:
        Hello first.

        Notification 0.

        Notification 1.

        Notification 2.

        Notification 3.

        Notification 4.
Hi there. You sent Hello first..
Set was called.
Channel tracker added.
IClientChannel.Initialize called.
Attached to new IContextChannel 59109011.
http://localhost:8081/Callback/99928a92-2d3c-42cc-bd1b-34af14cc737b
Enter a greeting to send and press ENTER:
>>> Hello second.
Called service with:
        Hello second.

        Notification 0.

        Notification 1.

        Notification 2.

        Notification 3.

        Notification 4.
Hi there. You sent Hello second..
Set was called.
Press ENTER to exit...

Now, I think that based on what we've discovered that this a) isn't the application structure you'd want if you were building a sessionless duplex service and client, and b) that if you built it correctly it would still only be useful in limited scenarios given how hard you have to work to correlate things AND managed service infrastructure lifetimes. Because Christian Weyer had asked me about the sessionless duplex possibility before, now I'm going to challenge him to take a stance: How should this application be built, and once it's built correctly, what scenarios can make use of it? Christian?

 UPDATE: I forgot one other critical little piece. Note that in the callback section I generate a new OperationContext that is not the one used with request/reply. Why? Because if I use the same one I'll set the To values for the request/reply operation as well as the oneway callback -- and that would muck with the response to the operation. Therefore I only need to set the callback headers for the callback context. That's why I do that.....

by ralph.squillace | 1 Comments

Quick WCF Metadata Publication Walkthrough

I've been having a conversation with Scott Klein, who is busy writing a book on WCF (http://www.amazon.com/Professional-WCF-Pr%20ogramming-Development-Communication/dp/0470089849/sr=1-2/qid=1161195651/ref=sr_1_2/102-4898838-8936%20108?ie=UTF8&s=books). He'd been reading and following the documentation about publishing service metadata and had figured out how to do this in code, but for some reason he just didn't understand the errors resulting from his attempt to use the configuration file to do the same thing. Even the book writers don't get things first time! (But that's likely because I need to do a better job writing about it. ;-)

We got in a conversation about this, and it seemed to me that a couple of things made it difficult for him to pick up how this works.

  • In the programmatic version, you don't usually have to think about the base addresses; in config, you usually do.
  • The fact that HTTP/GET publication just "happens" but WS-MetadataExchange is a real contract with real endpoints.

With Scott's permission, I'm republishing the majority (minus the embarrasing dumb stuff I wrote here and there and with some small edits for sensibility) of a mail to Scott that walks through the process of taking a standard application configuration file and adding different sorts of metadata publication as we go along until at the end we're publishing service metadata at a HTTP/GET address using the ?wsdl convention AND over both HTTP and TCP using WS-MetadataExchange requests. The critical things to watch for:

  • All metadata publication using relative addressing requires supporting base addresses for the ServiceHost; otherwise, you can use absolute addresses.
  • HTTP/GET publication is an "artifact" of the ServiceMetadataBehavior and the HttpGetEnabled property.

As a side note, because the ServiceMetadataBehavior is a service behavior, it must be not only specified in the configuration file but also referenced by the \service@behaviorConfiguration attribute.

Here we go (and if you walk through this and catch me in a typo, let me know and I'll fix it!):

From: Ralph Squillace
Sent: Tuesday, October 17, 2006 10:42 AM
To: 'Scott Klein'
Subject: RE: ServiceMetadata -- the big picture and walkthrough

<snip stupid stuff I wrote/>

 

Automatic metadata publication must have an address at which to publish. A ServiceHost is not tied to any particular application domain. This means that unlike ASMX files, which always reside at an IIS virtual directory (for example, http://computer/vDirectory), ServiceHost when opened does not know where it is. It must therefore be passed (or infer from absolute addressing) a “base” or “root” address to which all relative addresses used by the host are appended.

 

When a Service.svc file is hosted in IIS/WAS, the ServiceHost acquires the base address from IIS/WAS, and is therefore automatically whatever the virtual directory is, just like ASMX, plus the “Service.svc” relative address that points to the service implementation.

 

In all other cases, you must provide a base address in order for automatic metadata publication to work unless you supply an absolute address in your endpoints or for the httpGetUrl property.

 

Let’s walk through this. Let’s say you haven’t configured any metadata for your service yet and it looks like the following. What is the address for this service?

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service

        name="Microsoft.WCF.Documentation.SampleService"

        >

        <endpoint

          address=""

          binding="wsHttpBinding"

          contract="Microsoft.WCF.Documentation.ISampleService"

        />

      </service>

    </services>

  </system.serviceModel>

</configuration>

The answer is that this throws an exception on Open because no base address exists to create this class unless hosted in IIS/WAS, in which case the service is hosted at the virtual directory. In all other cases, this service throws.

 

We **could** do this:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service

        name="Microsoft.WCF.Documentation.SampleService"

        >

        <endpoint

          address="http://computer/Services/SampleService"

          binding="wsHttpBinding"

          contract="Microsoft.WCF.Documentation.ISampleService"

        />

      </service>

    </services>

  </system.serviceModel>

</configuration>

Now the service WILL be published, but at http://computer/Services/SampleService because we specified an absolute address. Trick question: what is the base address? The answer is that there isn’t one here. There may be one passed to the ServiceHost programmatically, but let's assume that's not the case here.

 

OK, this runs, now let’s add metadata support for HTTP/GET. We do this:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service

        name="Microsoft.WCF.Documentation.SampleService"

        >

        <endpoint

          address="http://computer/Services/SampleService"

          binding="wsHttpBinding"

          contract="Microsoft.WCF.Documentation.ISampleService"

        />

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="metadataSupport">

          <serviceMetadata httpGetEnabled="true" httpGetUrl=""/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

What has changed? The ServiceMetadataBehavior is loaded and we’ve told it to publish HTTP/GET metadata at the address baseHttpAddress + httpGetUrl (because the address we’ve specified there is relative) + “?wsdl”. There are two problems here. First, we haven’t specified this behavior for any service in the configuration file yet, so it’s loaded but not invoked. Second, even when we specify the “metadataSupport” behavior in <service behaviorConfiguration=”metadataSupport”/> property, we’ll throw because – of course – the host does not HAVE an HTTP-based base address. How can we make this work? Well, we can specify an absolute address. Like so:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service

        name="Microsoft.WCF.Documentation.SampleService"

        behaviorConfiguration=”metadataSupport”

        >

        <endpoint

          address="http://computer/Services/SampleService"

          binding="wsHttpBinding"

          contract="Microsoft.WCF.Documentation.ISampleService"

        />

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="metadataSupport">

          <serviceMetadata httpGetEnabled="true" httpGetUrl="http://computer/Services/SampleService/Metadata"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

 

Note that here we now provide an absolute address for the HttpGetUrl property (and the behavior is wired up to the service) so that you can view the metadata at “http://computer/Services/SampleService/Metadata?wsdl”. But this is fairly fragile. What we WANT to do is to specify the base addresses so that we can use relative ones. Now we use the base addresses stuff. We do this:

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service

        name="Microsoft.WCF.Documentation.SampleService"

        behaviorConfiguration="metadataSupport">

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8080/SampleService" />

          </baseAddresses>

        </host>

        <endpoint

          address=""

          binding="wsHttpBinding"

          contract="Microsoft.WCF.Documentation.ISampleService"

        />

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="metadataSupport">

          <serviceMetadata httpGetEnabled="true" httpGetUrl=""/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Here we have specified that there is a base address of http://localhost:8080/SampleService. Note that the service endpoint HTTP-based address is empty and that it’s relative (because it’s not absolute), so the service is available at the base address.

  1. The ServiceMetadataBehavior is there...
  2. ...AND it’s wired up to the service
  3. ...AND the HttpGetUrl property is empty (and therefore relative)
  4. ...AND because the http/GET protocol requires an HTTP-transport based base address
  5. ...AND the base address IS an Http-based address

THEREFORE we can now see the metadata using a browser at http://localhost:8080/SampleService?wsdl. Whew. Lot's of ANDs.

 

Can we use WS-MetadataExchange yet? No. Can we retrieve metadata from any other transport? No. At this point we only support HTTP/GET at the previously mentioned address. Now let’s add WS-MetadataExchange support. We now add a metadata endpoint, one in which the contract is IMetadataExchange, the binding supports HTTP, and a relative (or absolute!) address. Here we go:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service

        name="Microsoft.WCF.Documentation.SampleService"

        behaviorConfiguration="metadataSupport">

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8080/SampleService" />

          </baseAddresses>

        </host>

        <endpoint

          address=""

          binding="wsHttpBinding"

          contract="Microsoft.WCF.Documentation.ISampleService"

        />

        <endpoint

           address="mex"

           binding="mexHttpBinding"

           contract="IMetadataExchange"

        />

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="metadataSupport">

          <serviceMetadata httpGetEnabled="true" httpGetUrl=""/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

We have added a metadata endpoint with the relative address “mex” (therefore we must have a supporting base address -- and we do), specified the mexHttpBinding (for basic HTTP support -- and the base address IS an HTTP-based address, so we're OK there), and referenced the IMetadataExchange contract. You can now (in addition to using HTTP/GET as before) point svcutil at http://localhost:8080/SampleService/mex and it will retrieve the metadata content in a WS-MEX message.

Now, do we have HTTP/GET support? Yes. Do we have WS-Mex support over HTTP? Yes. Do we have WS-Mex support over TCP? No. To do that, we’ll add another endpoint that uses TCP, like so:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service

        name="Microsoft.WCF.Documentation.SampleService"

        behaviorConfiguration="metadataSupport">

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8080/SampleService" />

          </baseAddresses>

        </host>

        <endpoint

          address=""

          binding="wsHttpBinding"

          contract="Microsoft.WCF.Documentation.ISampleService"

        />

        <endpoint

           address="mex"

           binding="mexHttpBinding"

           contract="IMetadataExchange"

        />

        <endpoint

           address="tcpmex"

           binding="mexTcpBinding"

           contract="IMetadataExchange"

        />

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="metadataSupport">

          <serviceMetadata httpGetEnabled="true" httpGetUrl=""/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Now then, what happens when we run svcutil against net.tcp://localhost:8080/SampleService/tcpmex? We should get 300 exceptions, as you did (OK, it’s really just one). Why? You can guess: The service host does not have a TCP-transport base address AND the TCP-based metadata endpoint uses a relative address. Had we specified an absolute address there:

        <endpoint

           address="net.tcp://localhost:8081/SampleService/tcpmex"

           binding="mexTcpBinding"

           contract="IMetadataExchange"

        />

We would have been able to point svcutil at net.tcp://localhost:8081/SampleService/tcpmex and it will work fine. But again, absolute addresses are bad. So how do we use relative addresses? We add a tcp-transport based base address, like so:

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8080/SampleService" />

            <add baseAddress=”net.tcp://localhost:8081/SampleService” />

          </baseAddresses>

        </host>

NOW we can point to net.tcp://localhost:8081/SampleService/tcpmex and it will work fine. From here you should be able to add any other WS-MEX endpoint AND you should be able to understand what to check when it doesn’t work. Let’s look at the exception you got in your example:

 

"Could not find a base address that matches scheme net.tcp for the endpoint with binding MetadataExchangeTcpBinding. Registered base address schemes are []."

 

<snip from Scott's mail with permission> 

So, I added the following section:

 

<host>

          <baseAddresses>

            <add baseAddress = "net.pipe://localhost/"/>

            <add baseAddress ="net.tcp://localhost:8000/"/>

          </baseAddresses>

</host>

 

Now I get the following on sh.Open()

 

“The HttpGetEnabled property of ServiceMetadataBehavior is set to true and the HttpGetUrl property is a relative address, but there is no http base address.  Either supply an http base address or set HttpGetUrl to an absolute address.”

</snip from Scott's mail with permission> 

 

 

The answer here is that you’ve set the HttpGetEnabled property to true BUT you have no <add baseAddress=/> property that supports HTTP. We have no transport to use for HTTP! You can either:

  1. Set the HttpGetUrl property to an absolute address OR
  2. add a HTTP-based base address to the <baseAddresses> element.

This put everything in perspective for Scott, who immediately went on to bigger and better things, but I wanted to make sure that no one else got stalled out by the simple service requirement. Two other notes. First, if you're wondering where the implementation of the IMetadataExchange contract is (because you know you didn't build one!) it's in the ServiceMetadataExtension, which the behavior adds to the ServiceHost. Second, here we added HTTP and TCP metadata support. But if you're following the abstract point, you should see that an IME endpoint can be any endpoint. You can secure it; auth it; encrypt it; whatever you want. We provide four basic supporting metadata bindings, but you can create custom bindings, too. Hope this all helps! I'll be folding this information back into the documentation as soon as I can...

 

 

by ralph.squillace | 3 Comments

Sessionless duplex services? No problem. Small issues, yes; problems, no.

Duplex is neato, definitely, because among other things it allows a service to push information at clients as it sees fit. You could just have two services, and one service throws an endpoint at the other and then listens for stuff coming back, too, but there are scenarios for this and scenarios for that. Sometimes a duplex client is the perfect thing but you don't really want the session that comes with the system-provided duplex-supporting bindings used to wire up the two-way communication system. For example, WsDualHttpBinding uses a secure conversation session by default and a reliable session if you turn it on; and the NetTcpBinding surfaces a session associated with the underlying TCP connection.

The problem with these is that there are some session lifetime and creation issues that you have to manage when you use sessions. What's the timeout period? What do I do when Abort happens? And so on. Wouldn't it be nice of you could do duplex without session overhead? Well, you can. But you may want to consider why you want to before you do it. I'll wait to see when Christian thinks it might be helpful. ;-)

A duplex contract is merely a service contract in which there are some operations that specify outbound calls from the service to the client. And a duplex client, then, is merely a WCF application that also hosts a client-side listener for return calls from the service to which it is connected. A client runtime is exposed for use by the -- surprise -- ClientRuntime class. If the client is a duplex client, however, it also hosts a service runtime, exposed for inspection or modification as a DispatchRuntime object available from the ClientRuntime.CallbackDispatchRuntime property. This exposes the entire service side runtime and can be thought of as a service in your client application but for one major exception: It cannot be activated by a call from a service. It must connect to a service first, and then it listens for callbacks from that service. In the WCF system-provided duplex bindings, the information about the callback listener is transferred to the service (so that the service knows where to direct the callback invocations) using the provided session support.

 Therefore, when -- for your own twisted reasons -- you want to build a sessionless duplex application, you have to do a little extra lifting. But not that much. Here's how to build a simple duplex application that does not establish a session using the HTTP transport and a few extra gadgets. First, let's get you up and running, and then we'll post more later about how this works and how to modify it. Or maybe someone will beat me to that.

Big Fat Warning. This sample does not do security. You'd be a fool to think it does, so don't think that.

OK, onward. First, a simple duplex contract.

  [ServiceContract(
    Name = "SampleDuplexHello",
    Namespace = "http://microsoft.wcf.documentation",
    CallbackContract = typeof(IHelloCallbackContract),
    SessionMode = SessionMode.NotAllowed
  )]
  public interface IDuplexHello
  {
    [OperationContract(IsOneWay = true)]
    void Hello(string greeting);
  }

  public interface IHelloCallbackContract
  {
    [OperationContract(IsOneWay = true)]
    void Reply(string responseToGreeting);
  }

Note that the ServiceContractAttribute.SessionMode property is set to SessionMode.NotAllowed. This means that if we make a mistake and supply a sessionful binding, the application throws an exception. Here is the service implementation.

  public class DuplexHello : IDuplexHello, IDisposable
  {
    public DuplexHello()
    {
      Console.WriteLine("Service object created: " + this.GetHashCode().ToString());
    }

    public void Hello(string greeting)
    {
      Console.WriteLine("Caller sent: " + greeting);
      Console.WriteLine("Session ID: " + OperationContext.Current.SessionId);
      Console.WriteLine("Waiting two seconds before returning call.");
      // Put a slight delay to demonstrate asynchronous behavior on client.
      Thread.Sleep(2000);
     
      // Get the callback client object.
      IHelloCallbackContract caller
        = OperationContext.Current.GetCallbackChannel<IHelloCallbackContract>();

      // Fetch out the client ReplyTo and place it in the outbound To header to
      // direct the callback message.
      Uri to = new Uri(OperationContext.Current.IncomingMessageHeaders.ReplyTo.ToString());
      OperationContext.Current.OutgoingMessageHeaders.To = to;
     
      string response = "Service object " + this.GetHashCode().ToString() + " received: " + greeting;
      Console.WriteLine("Sending back: " + response);
      caller.Reply(response);
    }

    #region IDisposable Members

    public void Dispose()
    {
      Console.WriteLine("Service object destroyed: " + this.GetHashCode().ToString());
    }

    #endregion
  }

If you've built a duplex application before, the only interesting code here is the section in which we find the incoming ReplyTo header and assign that value to the outgoing To header.

      // Fetch out the client ReplyTo and place it in the outbound To header to
      // direct the callback message.
      Uri to = new Uri(OperationContext.Current.IncomingMessageHeaders.ReplyTo.ToString());
      OperationContext.Current.OutgoingMessageHeaders.To = to; 

With the default sessionful bindings, you don't have to take this step. But without those sessions to help out, you do. If this application structure is important to you, you'd probably handle this wiring procedure in a combination extensible object and IDispatchMessageInspector. Here we simply do this in code.

 Now the tricky part. I'll just tell you how to build the binding and get back to discussing this later. We're going to layer a CompositeDuplexBindingElement over a OneWayBindingElement on top of a HttpTransportBindingElement. Oh yeah, we need an encoder, too. OK, let's use the default TextMessageEncodingBindingElement for that. I find config easier for this than code, so we'll assemble this binding using the CustomBinding. It looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
        name="Microsoft.WCF.Documentation.DuplexHello"
        behaviorConfiguration="mex">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8080/DuplexHello"/>
          </baseAddresses>
        </host>
        <endpoint
          address=""
          binding="customBinding"
          bindingConfiguration="duplexNoSession"
          contract="Microsoft.WCF.Documentation.IDuplexHello"
         />
        <endpoint
          address="mex"
          binding="mexHttpBinding"
          contract="IMetadataExchange"
        />
      </service>
    </services>
    <bindings>
      <customBinding>
        <binding name="duplexNoSession">
          <compositeDuplex />
          <oneWay/>
          <textMessageEncoding />
          <httpTransport />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="mex" >
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Note that here I enable metadata using the ServiceMetadataBehavior. Downloading and creating the client remains pretty simple. Now, create a host:

      // Create a ServiceHost for the service type and use the base address from configuration.
      using (ServiceHost serviceHost = new ServiceHost(typeof(DuplexHello)))
      {
        try
        {
          // Open the ServiceHostBase to create listeners and start listening for messages.
          serviceHost.Open();

and you're running. Let's get to the client, and then I'll sign off for now. Use Svcutil.exe to generate client code. Once you do that, the trick to the client is that you must create a new OperationContextScope to set the outbound ReplyTo header. Then, pass the local address of the underlying channel to the ReplyTo header and invoke the client.

      // Picks up configuration from the config file.
      InstanceContext callbackInstanceContext = new InstanceContext(this);
      SampleDuplexHelloClient wcfClient = new SampleDuplexHelloClient(callbackInstanceContext);
      try
      {
        using (OperationContextScope opScope = new OperationContextScope(wcfClient.InnerDuplexChannel))
        {
          // Add explicit replyto for the other side to pick up.
          OperationContext.Current.OutgoingMessageHeaders.ReplyTo
            = wcfClient.InnerChannel.LocalAddress;
 

In duplex clients you need to hold the application domain open to listen for callbacks. Without a session to fault, if a client vanishes the service will not know about it until a callback fails. The upside, however, is that the channel is not like to fault. Typically, if a callback fails, your service can simply ignore that result or retry the call on the same channel later. This is a handy way of getting services to support eventish behavior for client applications without worrying unduly about the channel lifetime and infrastructure. The complete client application follows. I learned most of this to write documentation for duplex applications but I was guided mainly by Dr. Nick and Kenny Wolf, who have written in their blogs about pieces and helped me understand how they go together to make this work. If I have to repost to clarify or modify what I've said, it's not their fault. :-) More soon.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;

namespace Microsoft.WCF.Documentation
{
  public class Client : SampleDuplexHelloCallback
  {
    AutoResetEvent waitHandle;

    public Client()
    {
      waitHandle = new AutoResetEvent(false);
    }

    public void Run()
    {
      // Picks up configuration from the config file.
      InstanceContext callbackInstanceContext = new InstanceContext(this);
      SampleDuplexHelloClient wcfClient = new SampleDuplexHelloClient(callbackInstanceContext);
      try
      {
        using (OperationContextScope opScope = new OperationContextScope(wcfClient.InnerDuplexChannel))
        {
          // Add explicit replyto for the other side to pick up.
          OperationContext.Current.OutgoingMessageHeaders.ReplyTo
            = wcfClient.InnerChannel.LocalAddress;

          Console.ForegroundColor = ConsoleColor.White;
          Console.WriteLine("Enter a greeting to send and press ENTER: ");
          Console.Write(">>> ");
          Console.ForegroundColor = ConsoleColor.Green;
          string greeting = Console.ReadLine();
          Console.ForegroundColor = ConsoleColor.White;
          Console.WriteLine("Called service with: \r\n\t" + greeting);
          wcfClient.Hello(greeting);
          Console.WriteLine("Execution passes service call and the client waits.");
          this.waitHandle.WaitOne();
          Console.ForegroundColor = ConsoleColor.Red;
          Console.WriteLine("Set was called.");
        }
      }
      catch (TimeoutException timeProblem)
      {
        Console.WriteLine("The service operation timed out. " + timeProblem.Message);
        // Sessionful channels should abort the channel unless it is a
        // specified fault. This duplex sample uses a datagram channel, which should be
        // reusable.
        wcfClient.Close();
      }
      catch (CommunicationException commProblem)
      {
        Console.WriteLine("There was a communication problem. " + commProblem.Message);
        // Sessionful channels should abort the channel unless it is a
        // specified fault. This duplex sample uses a datagram channel, which should be
        // reusable.
        wcfClient.Close();
      }
      finally
      {
        Console.ResetColor();
        Console.Write("Press ");
        Console.ForegroundColor = ConsoleColor.Red;
        Console.Write("ENTER");
        Console.ResetColor();
        Console.Write(" to exit...");
        Console.ReadLine();
      }
    }
   
    public static void Main()
    {
      Client client = new Client();
      client.Run();
    }

    public void Reply(string response)
    {
      Console.WriteLine("Received output.");
      Console.WriteLine("\r\n\t" + response);
      this.waitHandle.Set();
    }
  }
}

by ralph.squillace | 2 Comments

Last note on the return of WCF client objects.
It would best be said, in the end, that a client object returns from an operation call (including a one-way operation) when the outbound message is sent by the transport. This can be a network call, but need not be. The most obvious example is handing the message off to a message queue. Sigh. I think this one is done now.

by ralph.squillace | 0 Comments

OperationContractAttribute.IsOneWay: One more blocking scenario.

As I mentioned in the previous post, there are scenarios in which one-way operations can block a client. After investigation I've discovered yet another, shown to me by John Justice, Michael Marucheck, and Ed Pinto.

The scenario is quite specific. The client can block on the service when the ConcurrencyMode is set to ConcurrencyMode.Single and the binding has established a session of any type (that is, it implements ISessionChannel<ISession>). The reason is that in this case, the dispatcher enforces ordering on such messages. For example, in the case of HTTP-based one-way operations (remember that WSHttpBinding is the recommended default binding), a first message comes in from the client and while the service is processing the message the transport sends back an HTTP 202, enabling the client transport to return and unblocking the WCF client object. The WCF client object can then make the call again.

If the second call arrives while the service is still processing the first message, the second call is blocked until the service frees up. This, in turn, prevents the HTTP 202 message from being sent back until the dispatcher begins to process the second message. Should those calls come in too fast, the client will begin to block. 

If I come up with any more, I'll let you know. Cheers, me.

by ralph.squillace | 0 Comments

OperationContractAttribute.IsOneWay operations aren't simply fire-and-forget.

Although I am responsible for writing a good portion of the programmer documentation for WCF, there are always little things that you "realize" suddenly that you didn't quite understand. Today, I learned from Shy Cohen and Maheshwar Jayaraman (thanks guys!) about OperationContractAttribute.IsOneWay -- the external documentation for which is here and here and is wrong, or certainly not right enough. Sigh. Shall I try again?

The second link typifies my misunderstanding. It says:

A one-way operation is one in which a client invokes an operation and continues processing after WCF queues the message for sending by the client.

Ah... not quite. What I now understand is that IsOneWay operations are really only for the case when you do not require a return message. However, the client object call does not return until the data is succesfully written to the wire -- which means that if the service cannot read the data from the wire that the client object blocks. Let me say this again: clients can block on one-way operations because the client object only returns once channel.Send() returns.

For the most part, the scenario in which a client can block on a oneway call is when a client object makes a large number of one-way calls in a tight loop. For example in the following code the Print() method is a one-way operation.

      PrintClient client = new PrintClient();
      for (int i = 0; i < 3000; i++)             // <= 300 will not block, 3000 will
      {
        client.Print("String #" + i.ToString());
      }
      Console.WriteLine("Client is done");

In this case, the service's ability to retrieve data off the wire using the default settings is quickly reduced; suddenly the client.Print call begins blocking on the return from the Send() call at the transport level. A loop like the above but with 300 or fewer calls will print out "Client is done" usally before the service can print out that it has received a message. With 3000, however, the service can print out most messages before the client is able to complete the loop.

This behavior has several ramifications that I need to put into the documentation, but I'll write them out here first.

  1. One-way methods will block in any situation in which the transport cannot send the message. Fire too many one-ways at a service and it could block your client.
  2. It follows, then, that exceptions can be thrown on a one-way call any time Send does not return. For example, EndpointNotFoundException and SendTimeoutException can easily occur. Test it yourself! :-)
  3. RM implements its own message buffer, so clients can return for a longer period -- until the RM buffer fills up, in which case the client blocks.

More generally, the way around this problem is to insert buffers that separate the client call from the transport send action. The problem is that any such buffer will have its own limits that you must be aware of. Options include the following.

  1. Use asynchronous calls against the client object. (Limit: ThreadPool capacity.)
  2. Use RM. (Limit: RM message buffer.)
  3. Implement a memory message queue to call against and then make the calls from there. (Limit: queue size.)

In all cases, the buffer you use will still have some limit, especially to protect against denial of service attacks. The big takeaway is:

  1. If you want to flood out one-way messages, the only way for you to get the proper client behavior in this scenario (a flood of oneway calls) is to test the kind of load you expect and balance both the client and service buffers to properly balance the load.

Of course, if the transport were to change and support a truer fire-and-forget model, such as UDP, you would not encounter this problem because the transport would return from Send once the data was sent regardless of the ability of the service to read the data. There it is. Now, to rewrite the documentation....

by ralph.squillace | 0 Comments

Extending WSDL and Policy, Part 2. Importing custom policy assertions.

Yesterday I outlined how to export custom policy assertions and why you would do such a thing. One thing I failed to note explicitly, but that should be obvious, is that policy assertions are merely XML elements. At a fundamental level, that's all they are. So in addition to conveying binding-related information, they can also convey pretty much any other information as well. That's not what it's for; but still. :-)

Tonight I'll discuss importing custom policy. Recall that policy assertions are to convey information about binding implementations. Therefore the point of importing policy assertions is to detect a certain policy and respond by adding a binding element, a binding, or configuring a binding or binding element to support the policy assertion. However, unlike in the service application, the importer does not need to be implemented on any particular binding element. In fact, it can be implemented on any ol' object at all. And to implement it, all you do is implement -- it should be clear -- System.ServiceModel.Description.IPolicyImportExtension. The external documentation fairly accurately describes the process, although I note that the example is missing (I'm here to oblige) and it looks like some text has vanished. I'll get right on that, just after I finish writing the topic on how to understand sessions, the documentation for which is currently just plain wrong in many ways.

The algorithm is

  1. Locate the assertion you are going to respond to at the scope you are interested (binding-wide assertion, operation-wide assertion, or a message-wide assertion).
  2. Add or modify binding elements by using the BindingElements property on the PolicyConversionContext.
  3. Remove the policy assertion from the assertion collection.

If you don't remove the assertion, the binding you were trying to modify will not be imported. All your hard import work will have been done in vain.

Here's an example implementation that does NOT add a binding. We could also retrieve a binding and modify it in some way, too. Note the remove step in red.

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.Text;
using System.Xml;

namespace Microsoft.WCF.Documentation
{
  public class CustomPolicyImporter : IPolicyImportExtension
  {
   #region IPolicyImporter Members
    public const string name1 = "acme";
    public const string ns1 = "
http://Microsoft/WCF/Documentation/CustomPolicyAssertions";

    /*
     * Importing policy assertions usually means modifying the bindingelement stack in some way
     * to support the policy assertion. The procedure is:
     * 1. Find the custom assertion to import.
     * 2. Insert a supporting custom bindingelement or modify the current binding element collection
     *     to support the assertion.
     * 3. Remove the assertion from the collection. Once the ImportPolicy method has returned,
     *     any remaining assertions for the binding cause the binding to fail import and not be
     *     constructed.
     */
    public void ImportPolicy(MetadataImporter importer, PolicyConversionContext context)
    {
      Console.WriteLine("The custom policy importer has been called.");
      // Locate the custom assertion.
      XmlElement customAssertion = context.GetBindingAssertions().Find(name1, ns1);
      if (customAssertion != null)
      {
        context.GetBindingAssertions().Remove(customAssertion);
        Console.WriteLine(
          "Removed our custom assertion from the imported "
          + "assertions collection and inserting our custom binding element."
        );
        // Here we would add the binding modification that implemented the policy.
        // This sample does not do this.
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(customAssertion.NamespaceURI + " : " + customAssertion.Name);
        Console.WriteLine(customAssertion.OuterXml);
        Console.ForegroundColor = ConsoleColor.Gray;
     }
   }
  #endregion
 }
}

Note that that this is just a class. It's not a binding element or anything special. And to wire it up in configuration, you simply do this:

        <client>
            <endpoint
              address="http://localhost:8080/StatefulService"
              binding="wsHttpBinding"
              bindingConfiguration="CustomBinding_IStatefulService"
              contract="IStatefulService"
              name="CustomBinding_IStatefulService" />
          <metadata>
            <policyImporters>
              <extension type="Microsoft.WCF.Documentation.CustomPolicyImporter, PolicyExtensions"/>
            </policyImporters>
          </metadata>
        </client>
 

And that's it. Tomorrow I'll move on to customizing WSDL export and import.

Hope it helps, me.

by ralph.squillace | 0 Comments

Extending WSDL and Policy, Part 1. Exporting custom policy assertions.

I've been busy for a while trying to update the documentation for all the changes that have happened since the February release. That meant, definitely, that I hadn't blogged for a while. But now I've got some things that it's important to get on about.

The first is how to extend the policy system and import custom policy. The second is how to do the same with WSDL.

The external SDK documentation is not completely up to date (see http://windowssdk.msdn.microsoft.com/en-us/library/system.servicemodel.description.ipolicyexportextension.aspx) so I thought I'd try to correct that.

Policy is, in general, a statement about runtime binding support at a particular scope. For example, you might say that although the contract requires encryption and digital signing, your policy might say that we want to sign things with X.509. The contract discusses the requirement; the binding implements a supporting version of that requirement; the policy asserts the implementation that the binding uses. For very specific discussion of policies, see Dan Roth's article Understanding Web Services Policy.

By default, WCF supports security policy assertions, transaction policy assertions, and several others. However, for a first version, this is a reasonable but small subset of what will eventually be needed, and there is every likelihood that you or someone like you will want to sign your messages (or do something else with them) that requires support on both sides of a conversation. Custom policy extensions are then required.

In the normal case, a bindingelement implements IPolicyExportExtension, because the policy exporter is going to be inserting policy assertions about that binding into the WSDL that clients will consume. It is not, therefore, unsurprising that in WCF, IPolicyExportExtensions must be implemented by custom binding elements. This is done by extending System.ServiceModel.Channels.BindingElement. For example, here's a simple version:

  public class ExporterBindingElement : BindingElement, IPolicyExportExtension
  {

    public const string name1 = "acme";
    public const string ns1 = "
http://Microsoft/WCF/Documentation/CustomPolicyAssertions";

    static XmlDocument doc = new XmlDocument();

    public ExporterBindingElement()
    {
      Console.WriteLine("Exporter created.");
    }

    #region IPolicyExporter Members

    public void ExportPolicy(MetadataExporter exporter, PolicyConversionContext policyContext)
    {
      if (exporter == null)
        throw new ApplicationException("The MetadataExporter object passed to the ExporterBindingElement is null.");
      if (policyContext == null)
        throw new ApplicationException("The PolicyConversionContext object passed to the ExporterBindingElement is null.");
     
      XmlElement elem = doc.CreateElement(name1, ns1);
      elem.InnerText = "My custom text.";
      XmlAttribute att = doc.CreateAttribute("MyCustomAttribute", ns1);
      att.Value = "ExampleValue";
      elem.Attributes.Append(att);
      XmlElement subElement = doc.CreateElement("MyCustomSubElement", ns1);
      subElement.InnerText = "Custom Subelement Text.";
      elem.AppendChild(subElement);
      policyContext.GetBindingAssertions().Add(elem);
      Console.WriteLine("The custom policy exporter was called.");
    }

    #endregion

    public override BindingElement Clone()
    {
      // Note: All custom binding elements must return a deep clone
      // to enable the run time to support multiple bindings using the
      // same custom binding.
      return this;
    }

    // Call the inner property.
    public override T GetProperty<T>(BindingContext context)
    {
      return context.GetInnerProperty<T>();
    }
  }

The next step is to wire your binding element into the binding that your service uses. The easiest way to do this is to create a System.ServiceModel.Configuration.BindingElementExtensionElement returns the ExporterBindingElement above, like so:

  public class ExporterBindingElementConfigurationSection : BindingElementExtensionElement
  {

    public ExporterBindingElementConfigurationSection()
    {
      Console.WriteLine("Exporter configuration section created.");
    }

    public override Type BindingElementType
    {
      get { return typeof(ExporterBindingElement); }
    }

    protected override BindingElement CreateBindingElement()
    {
      return new ExporterBindingElement();
    }
  }

Then you can hook this element into the binding by using a configuration file, like so:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
        name="Microsoft.WCF.Documentation.StatefulService"
        behaviorConfiguration="addMetadata"
      >
        <host>
          <baseAddresses>
            <add baseAddress="
http://localhost:8080/StatefulService"/>
          </baseAddresses>
        </host>
        <endpoint
          address="
http://localhost:8080/StatefulService"
          binding="customBinding"
          bindingConfiguration="exporter"
          contract="Microsoft.WCF.Documentation.IStatefulService"
        />
        <endpoint
          address="mex"
          binding="mexHttpBinding"
          contract="IMetadataExchange"
        />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="addMetadata">
          <serviceMetadata
             httpGetEnabled="true"
             httpGetUrl=""
           />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <customBinding>
        <!--
          Use the name attribute of the binding element as
          the value of the bindingConfiguration attribute in
          your endpoint.
        -->
        <binding name ="exporter">
          <!-- Use the name attribute of your binding element extension specified below. -->
          <!-- Be certain the order of your custom binding elements is correct. -->
          <exporterBinding />
          <reliableSession/>
          <textMessageEncoding messageVersion="Default" />
          <httpTransport/>
        </binding>
      </customBinding>
    </bindings>
    <extensions>
      <bindingElementExtensions>
        <!-- Use the add element to associate your bindingelement configuration handler and give it a name to use. -->
        <add
          type="Microsoft.WCF.Documentation.ExporterBindingElementConfigurationSection,PolicyExtensions"
          name="exporterBinding" />
      </bindingElementExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

Note that in this configuration file, we use a custom binding because we haven't created a single custom binding that includes our custom policy exporter as a binding element. This is easy to do, but perhaps I'll discuss this another time.

When this code is executed, the following WSDL policy statements are generated:

<wsp:Policy wsu:Id="CustomBinding_IStatefulService_policy">
  <wsp:ExactlyOne>
    <wsp:All>
      <acme a:MyCustomAttribute="ExampleValue" xmlns="http://Microsoft/WCF/Documentation/CustomPolicyAssertions" xmlns:a="http://Microsoft/WCF/Documentation/CustomPolicyAssertions">
  My custom text.
        <MyCustomSubElement>Custom Subelement Text.</MyCustomSubElement>
  </acme>
      <wsrm:RMAssertion xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm/policy">
        <wsrm:InactivityTimeout Milliseconds="600000" />
        <wsrm:AcknowledgementInterval Milliseconds="200" />
  </wsrm:RMAssertion>
      <wsaw:UsingAddressing />
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
 
At this point, we have a binding element that exports some custom policy at the binding level, in addition to the standard RM policy assertion that the ReliableSessionBindingElement exports by default. This sample does not implement an actual bindingelement that has behavior; for clarity, I've only used the binding element to export custom policy information.
Tomorrow we'll import this policy statement and do something with it. Then we'll move on to customizing WSDL itself.
 
Hope it helps, me.

by ralph.squillace | 1 Comments

More Posts Next page »
© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker