Nicholas Allen's Indigo Blog

Windows Communication Foundation From the Inside

December, 2007

  • Nicholas Allen's Indigo Blog

    Mapping Credentials to Authentication Schemes

    • 3 Comments

    You may have noticed that an HTTP binding is configured with an HttpClientCredentialType whereas an HTTP binding element is configured with an AuthenticationScheme. How are these two settings related? If you want to switch between a custom binding and a standard binding for HTTP, then you need to know how to do the translation.

    Here's how client credentials map to authentication schemes:

    • HttpClientCredentialType.None becomes AuthenticationSchemes.Anonymous
    • HttpClientCredentialType.Certificate also becomes AuthenticationSchemes.Anonymous. Certificates are used with HTTPS rather than as authentication credentials on top of HTTP. This will be caught in validation if you try to use certificates with the TransportCredentialOnly security mode.
    • HttpClientCredentialType.Basic becomes AuthenticationSchemes.Basic
    • HttpClientCredentialType.Digest becomes AuthenticationSchemes.Digest
    • HttpClientCredentialType.Ntlm becomes AuthenticationSchemes.Ntlm
    • HttpClientCredentialType.Windows becomes AuthenticationSchemes.Negotiate

    Translation works the same way with proxy credentials and the proxy authentication scheme except that proxies don't have to worry about the issue with certificates.

    Next time: Get a Real XML Parser

  • Nicholas Allen's Indigo Blog

    Controlling HTTP Connection Limits

    • 3 Comments

    I need to make many simultaneous HTTP calls to the same service from my client application. How do I increase the limit on the number of HTTP connections?

    This setting isn't available on any of the bindings or binding elements but the default limit can be set through the DefaultConnectionLimit property on System.Net.ServicePointManager. This will change the limit for every server you want to connect to. Alternatively, you can control the connection limit very granularly through configuration.

    <system.net>
    <connectionManagement>
    <add address="..." maxconnection="2" />
    </connectionManagement>
    </system.net>

    The address can be for a particular HTTP address or for a plain machine name. Use the wildcard character * for the address to set the default connection limit through configuration. If a request can match against more than one of the configuration entries, then the most specific entry is the one that will be used.

    Next time: Mapping Credentials to Authentication Schemes

  • Nicholas Allen's Indigo Blog

    Cleaning up Async

    • 1 Comments

    There needs to be some concept of cleanup that takes place when an asynchronous request can't be completed. For example, when a service is shut down or a socket is closed you know that any asynchronous operation waiting on that resource will never get a result. Something needs to happen to the asynchronous operations to prevent them from lingering forever.

    The asynchronous callback pattern has no built-in concept for cancelling a request that you made previously. Even if you build in the concept of cancellation, by its fundamental nature an asynchronous call is going to race to completion against any actions you take. It would be possible to examine a request, see that it hasn't been completed, attempt to cancel the request, and then find out that it completed anyway because completion occurred during that same interval of time. Consequently, cleanup has to be initiated by the request itself rather than by the caller.

    There are two typical patterns that you see asynchronous operations implement to perform cleanup. The fencepost pattern is where the operation cleans up by completing successfully but giving a distinguished value that indicates that the operation had no result, such as by returning a null object. The exception pattern is where the operation cleans up by storing an exception caught on the worker thread, signaling completion, and then throwing the stored exception on the user thread that goes to pick up the result.

    The fencepost pattern is typically used for expected cases, such as shutting down, whereas the exception pattern is typically used for unexpected cases, such as IO failures. As an example, consider a service waiting for incoming client connections on a socket. The service will keep several asynchronous requests ready at a time waiting to accept clients. When the socket is shut down, those asynchronous requests need to be cleaned up and they'll do that by returning null. On the other hand, if the socket had encountered a read error instead, then cleanup would have been done by throwing an exception for each outstanding request.

    Next time: Controlling HTTP Connection Limits

  • Nicholas Allen's Indigo Blog

    Sharing Contracts Across Services

    • 3 Comments

    I've deployed several services that share some of their data contracts. When I build a client application that calls more than one of the services, each service contributes a copy of the data contract during proxy generation. This causes a compile error because the same type is defined multiple times. How do I resolve the conflict?

    Assuming that you don't want to change the services, there are four obvious ways to fix the conflict. Three of the ways are mostly manual and one of the ways is mostly automatic.

    1. Manual resolution: split up the client application into separate compile units so that each of the compile units references only one of the services. This method is the most invasive and tedious solution.
    2. Manual resolution: edit the output of proxy generation to place each service proxy in its own namespace. This method requires very little work but makes writing the client application inconvenient because types need to be namespace qualified and passing objects between different services becomes harder.
    3. Manual resolution: edit the output of proxy generation to remove extra copies of types. This method requires the most work every time you want to regenerate the proxies but gives you the most freedom for building the client application the way you want.
    4. Automatic resolution: treat proxy generation as an incremental process and use svcutil /r to reference type assemblies generated by earlier processed services. This method is possible to fully automate but introduces complications into the proxy generation process. This method is the best long-term solution but requires infrastructure work that may be too much overhead for smaller projects.

    Next time: Cleaning up Async

  • Nicholas Allen's Indigo Blog

    2007 Year in Review

    • 1 Comments

    The year in review comes at the end of December rather than the beginning of January this year. Another year has gone by, with 250 articles posted so far (exactly as many as were posted during all of last year). Counting today and four more to come by the end of the year, the total for the year will be 255 articles and a grand total of 505 articles overall. 397 of those articles are directly about WCF. Due to holidays there will be no posts on Monday December 24th, Tuesday December 25th, or Tuesday January 1st.

    The top articles of all time are listed in the sidebar but here are the most read articles of 2007 and the most read articles written in 2007.

    Most Read Articles During 2007

    1. Configuring HTTP for Windows Vista
    2. Inside the Standard Bindings: NetTcp
    3. Net.Tcp Port Sharing Sample, Part 1
    4. Inside the Standard Bindings: BasicHttp
    5. How to: Enabling Streaming
    6. Preventing Anonymous Access
    7. Net.Tcp Port Sharing Sample, Part 2
    8. Making Sense of Transport Quotas
    9. MSMQ and Poison Messages
    10. Configuring HTTP

    Most Read Articles Written in 2007

    1. Preventing Anonymous Access
    2. MSMQ and Poison Messages
    3. Designing New Faults
    4. Orcas Beta 1 Released
    5. Client IP Address
    6. Enabling Kerberos in IIS
    7. Using XML Serialization with WCF
    8. WCF Case Studies
    9. A Trick with Faults (Discussion)
    10. Getting the Client Identity

    The feedback that the article on client IP addresses got directly contributed to that feature being added in Orcas. You have to speak up if you want things to change.

  • Nicholas Allen's Indigo Blog

    Session Security

    • 1 Comments

    How often does authorization occur?

    Authorization is typically scoped to either messages or sessions. When authorization is scoped to messages, then an authorization request occurs each time a message is sent. When authorization is scoped to sessions, then an authorization request occurs at the start of the session and all of the messages that are transmitted on the session share the authorization outcome. The exact timing of the authorization request can vary depending on whether authorization happens when open is called on the channel or when send is called on the channel for the first time.

    You can only have authorization scoped to sessions if you have a sessionful channel that incorporates session-based security. TCP is an example of a transport channel that is sessionful and supports session-based security through SSL. Message security is an example of a layered channel that is sessionful and supports session-based security through conversation mechanisms. There are many other channels that are either secure or sessionful but don't have session-based security.

    Next time: Sharing Contracts Across Services

  • Nicholas Allen's Indigo Blog

    Windows and UPN Format Credentials

    • 1 Comments

    There are many different formats for representing an identity. Some of the popular styles are distinguished names (CN=Name,OU=Users,DC=Domain), Windows (Domain\Name), and user principals (Name@Domain.com). The style you choose when setting the ClientCredentials is not necessarily going to be the same style selected for presenting the identity in ServiceSecurityContext. For example, you may provide credentials on the client using user principal names and then check the identity on the service to find it's got a Windows name.

    If you want to do something with the identity that requires a particular format, then you'll need to do some conversion to get the required format. Windows has a TranslateName function in secur32.dll that does this for you. Since you're probably using managed code, here's the imported function for TranslateName:

    enum EXTENDED_NAME_FORMAT
    {
    NameUnknown = 0,
    NameFullyQualifiedDN = 1,
    NameSamCompatible = 2,
    NameDisplay = 3,
    NameUniqueId = 6,
    NameCanonical = 7,
    NameUserPrincipal = 8,
    NameCanonicalEx = 9,
    NameServicePrincipal = 10,
    NameDnsDomain = 12
    }

    [return: MarshalAs(UnmanagedType.U1)]
    [DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool TranslateName(string lpAccountName, EXTENDED_NAME_FORMAT AccountNameFormat, EXTENDED_NAME_FORMAT DesiredNameFormat, StringBuilder lpTranslatedName, out uint nSize);

    The return type is marshaled as U1 because the declaration uses the 1 byte BOOLEAN rather than the 4 byte BOOL.

    Next time: Session Security

  • Nicholas Allen's Indigo Blog

    Concurrent Channel Performance

    • 1 Comments

    Being thread-safe is different than being concurrent. The channel interfaces are thread-safe so that multiple callers can use them at the same time without getting garbled messages. However, if multiple callers try to send messages on a single channel at the same time then a few different things might happen.

    • The channel might be fully concurrent and process the sends in an overlapped fashion.
    • The channel might be partially concurrent and process a limited number of the sends at a time. The other sends are blocked until one of the earlier requests completes.
    • The channel might be singly concurrent and process the sends in a sequential fashion.

    A connection-oriented channel like TCP tends to be singly concurrent. Interleaving messages requires having a sophisticated framing protocol and the performance cost is rarely worth handling this special case. Datagram channels on the other hand tend to be at least partially concurrent. For example, HTTP in theory is fully concurrent to any degree that you'd like but the HTTP specification recommends limiting clients to two connections to any particular server, resulting in partial concurrency.

    Whenever you have less than full concurrency you run the risk of deadlock with certain application designs. That's because the logical operation order of the application might say that caller 1 must complete a send before caller 2 can complete, but the physical operation order of the channel says that caller 1 cannot start a send before caller 2 completes.

    I've seen customers encounter this problem when using HTTP and callbacks. The original request is waiting for the callback operation to complete but the callback thread cannot make any progress because all of the connections are tied up by existing callers, such as the original request. This is a case where the performance gain of sharing connections has led to unsafe behavior. The solution is to increase the concurrency of the system so that at least one call from any of the ongoing chains of operations can complete (there can be callbacks on top of callbacks in complicated knots so the problem is not as simple to solve as increasing the concurrency factor to two). Concurrency can be increased directly at the networking level or by isolating networking resources into separate pools at the application level.

    Next time: Windows and UPN Format Credentials

  • Nicholas Allen's Indigo Blog

    Understanding MustUnderstand

    • 1 Comments

    The MustUnderstand attribute is frequently misunderstood because people assume that it must be much more complex and powerful than it really is. I explained the mechanics of using MustUnderstand last year but today's article is about some of the misconceptions for how the attribute is used.

    Annotating a message header with MustUnderstand creates a contract from the client to the service that the operation must fail if that part of the message is not able to be processed. This is not the same as validation because the service can understand what the header means even if the value of the header is wrong. For example, if a header is supposed to contain a certificate, then a service that knows about that header understands the message even if the client passes a certificate that is expired or malformed.

    Another key point of the description is that MustUnderstand is a contract from a client to a service. Decorating message contracts with MustUnderstand doesn't do anything on the service and doesn't show up in the metadata. You don't push understanding from the service to the client. The client chooses to decorate the message with a MustUnderstand attribute because the client knows in the message it is sending that a particular message header semantically affects other headers or content in the message. Some of the corollaries of that idea are that message headers with nil or null content can nevertheless have a MustUnderstand requirement and that the MustUnderstand mechanism only is useful if the message header actually gets written into the message. You can't require a service to support a specific capability with MustUnderstand unless that capability is actually being exercised.

    Next time: Concurrent Channel Performance

  • Nicholas Allen's Indigo Blog

    Printing Flexible Message Headers

    • 1 Comments

    When a message header supports multiple representations, which version is used when the message header is printed?

    A message header is a piece of data about the message that's carried on the wire somewhere apart from the message contents. When using an enveloped message format like SOAP, there's typically an explicit container for message headers and rules for what those message headers should look like. For other message formats, the message header may look different depending on the type of data that the message header represents and how the message header is transmitted. The same message header may have a different way of being expressed on the wire depending on the format of the message in which the header is contained.

    If the message header is printed together with a message, then the message header obviously should just use the same format as was used by the message. However, since message headers are independent objects, you can pull out a message header on its own and try to look at it. When you do that, the message header is queried for all of the message formats that it could be used with, and one of those message formats needs to be picked.

    Here's the order of preference for picking among the available message formats:

    1. SOAP 1.2 with WS-Addressing 1.0
    2. SOAP 1.2 with WS-Addressing August 2004
    3. SOAP 1.1 with WS-Addressing 1.0
    4. SOAP 1.1 with WS-Addressing August 2004
    5. SOAP 1.2 without addressing
    6. SOAP 1.1 without addressing
    7. No envelope or addressing

    Next time: Understanding MustUnderstand

  • Nicholas Allen's Indigo Blog

    Collections without CollectionDataContract

    • 3 Comments

    In the article about serialization conflicts, one of the points mentioned was that CollectionDataContract doesn't let you add non-default data members. How do I format a collection that contains data members? CollectionDataContract is automatically being added even if I don't want it.

    The trick is that CollectionDataContract is automatically applied to the default collection implementations rather than to all collections. That means that if your class is a List of T then you'll get the automatic CollectionDataContract behavior. If your class is an IList of T then you can choose to decorate the type with normal data contracts.

    This subtle distinction allows you to save the day. Convert your collection type to implement the corresponding interface and delegate all of the calls to a concrete implementation of the collection that you keep as a member variable. Then, you can figure out how to serialize that member variable, which gives you full control over how the list elements mix together with the other data members.

    Next time: Printing Flexible Message Headers

  • Nicholas Allen's Indigo Blog

    TCP Keep Alive

    • 9 Comments

    How do I detect when the other side of a TCP connection has gone away? Does TCP keep-alive take care of this for me?

    Although we take it for granted that change can be quickly detected for closely connected components, it turns out to be surprisingly difficult to detect change when two machines are isolated by more than a simple wire. Even a really big change to the system, like one of the machines disappearing, is hard to spot.

    Detecting that the other side has disappeared is a common request because, on a server, knowing that the client has dropped the connection allows you to clean up resources much faster. The TCP transport sometimes gives you quick notifications by aborting the session that the connection has been dropped. However, there's no guarantee that the transport will be able to detect that the other side has gone away. That's because notification of a TCP connection reset has to travel just like any other piece of data and can be lost or redirected along the way, if it was sent at all. The only sure thing is that the next time you attempt an IO operation, you'll find out if the channel was still good or not.

    If you're unhappy waiting for the next IO operation, then you can make IO operations happen faster. The basic concept is to have a cheap IO operation that does nothing but bounce between the two parties. This is sometimes called a heartbeat and is exactly what takes place when you talk about TCP keep-alive. However, the standard keep-alive interval for TCP is 120 minutes, which is probably worse than your current latency for detecting change. By default, a service gets bored waiting after about 10 minutes and gives up. The chance of a keep-alive happening between the time that a client disconnects and the service notices it is pretty small.

    If you want something faster but don't want to change the timeouts, then you can take the basic concept into your own hands. One approach is to create a keep-alive method on your service contract that does nothing but let you trigger IO operations at a frequency you desire. Another approach is if you control both ends and don't want to change your service contract, then you can do the same thing in a protocol channel and swallow those messages so that the service never has to see them.

    Next time: Collections without CollectionDataContract

  • Nicholas Allen's Indigo Blog

    Resolving Conflicts in Serialization

    • 2 Comments

    DataContractSerializer supports multiple serialization mechanisms. If more than one serialization mechanism is specified for the same type, which one gets used?

    Experimentation is the easiest way to figure out what happens. I'll look at different combinations of the XML serializer, the data contract serializer, and the "collection-based" data contract serializer, which is the default mechanism for the concrete collection classes.

    Let's start with a base interface that defines a member for the type.

    public interface IFoo
    {
    string data { get; set; }
    }

    Now, let's apply various combinations of serialization mechanisms to implementations of the base interface. I'll start with each of the serializers by themselves and then each of the legal combinations. Data contracts and collection data contracts can't be used at the same time so that pairing isn't possible as well as trying to use all three serialization mechanisms at once.

    Here's a quick test program to run through the combinations.

    public class Program
    {
    [DataContract]
    public class FooDataContract : IFoo
    {
    [DataMember]
    public string data { get; set; }
    }

    [Serializable]
    public class FooSerializable : IFoo
    {
    public string data { get; set; }
    }

    [CollectionDataContract]
    public class FooCollection : List<int>, IFoo
    {
    [DataMember]
    public string data { get; set; }
    }

    [Serializable]
    [DataContract]
    public class FooSerializableDataContract : IFoo
    {
    [DataMember]
    public string data { get; set; }
    }

    [Serializable]
    [CollectionDataContract]
    public class FooSerializableCollection : List<int>, IFoo
    {
    [DataMember]
    public string data { get; set; }
    }

    static void Main(string[] args)
    {
    using (XmlTextWriter writer = new XmlTextWriter(Console.Out))
    {
    foreach (Type type in typeof(Program).GetNestedTypes())
    {
    IFoo foo = (IFoo)type.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
    foo.data = type.Name;
    DataContractSerializer serializer = new DataContractSerializer(type);
    writer.WriteString(type.Name);
    writer.WriteWhitespace("\n");
    serializer.WriteObject(writer, foo);
    writer.WriteWhitespace("\n\n");
    }
    }
    Console.ReadLine();
    }
    }

    That gives us a set of outputs to inspect to see what serializer won.

    FooDataContract

    <Program.FooDataContract xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/"><data>FooDataContract</data></Program.FooDataContract>

    FooSerializable

    <Program.FooSerializable xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/"><_x003C_data_x003E_k__BackingField>FooSerializable</_x003C_data_x003E_k__BackingField></Program.FooSerializable>

    FooCollection

    <Program.FooCollection xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/" />

    FooSerializableDataContract

    <Program.FooSerializableDataContract xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/"><data>FooSerializableDataContract</data></Program.FooSerializableDataContract>

    FooSerializableCollection

    <Program.FooSerializableCollection xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/" />

    • FooDataContract gave us the data member with the nice format for properties.
    • FooSerializable gave us the data member with the ugly format for properties.
    • FooCollection failed to give us the data member (CollectionDataContract doesn't let you have non-default members).
    • FooSerializableDataContract is the same as FooDataContract. DataContract beats Serializable.
    • FooSerializableCollection is the same as FooCollection. CollectionDataContract beats Serializable as well.

    Next time: TCP Keep Alive

  • Nicholas Allen's Indigo Blog

    Deriving from Bindings

    • 1 Comments

    When packaging up a collection of settings, how do I know whether to use a CustomBinding, extend the Binding class, or extend one of the standard binding classes?

    In most cases this should be easily decidable by asking at most two questions. The first question is used to decide between creating a custom binding instance and creating a new class for your binding.

    • Is your binding generally reusable such that you'd expect it to be dropped into other applications with few changes?

    If your binding is not generally reusable or you don't care to make it so, then use a custom binding. Otherwise, create a binding class by extending Binding or by extending one of the standard bindings. The second question is used to decide between those two alternatives when creating a new class for your binding.

    • Can your binding be expressed as a simple derivative of one of the existing standard bindings?

    If you're changing some of the default settings of the standard binding, then your binding is probably a derivative. If you’re changing or exposing some of the settings of binding elements that the standard binding doesn't normally expose, then your binding is probably a derivative. If you're adding a layered channel that doesn't have any protocol impact, such as logging, then your binding is probably a derivative. If you're adding or removing layered or transport channels that do have an impact on the protocol, then your binding is probably not a derivative.

    A derivative of one of the standard bindings should be made by subclassing the standard binding class. A new binding is made by subclassing the base Binding class.

    Next time: Resolving Conflicts in Serialization

  • Nicholas Allen's Indigo Blog

    Localhost Common Name

    • 1 Comments

    What CN should I use when issuing a test certificate for a service?

    There are three types of names you'll commonly see:

    1. fully qualified domain names
    2. machine names (netbios)
    3. localhost

    These names are what work when connecting at internet, intranet, and local machine scales, respectively. The one most commonly gotten wrong is for localhost, possibly because you see it so often for test certificates but never use it elsewhere. Check your spelling of localhost and make sure there are no spaces, quotation marks, port numbers, or URIs in the name if you are getting an error validating the certificate. Also, make sure that you aren't trying to access a service with a certificate issued to localhost from a different machine.

    Next time: Deriving from Bindings

  • Nicholas Allen's Indigo Blog

    Silent Security Failures

    • 1 Comments

    I'm using reliable messaging and getting an exception that the reliable session has faulted. It's the first exception that I see so I don't know why the session faulted. How do I know what went wrong? Here's the full error message:

    The underlying secure session has faulted before the reliable session fully completed. The reliable session was faulted.

    The reliable messaging channel threw an exception because the reliable session was broken. The reliable session was broken because the security session was broken. In this case, you know that the security session was broken without seeing any other exceptions being thrown. That means the security channel must have faulted without encountering an exceptional event, which triggered all of the other visible results.

    The easiest way to debug a situation like this is to start removing extraneous pieces. Try removing the reliable messaging channel and see if that allows whatever went wrong with the security session to bubble up. If things immediately fail at startup, then try removing both the reliable messaging and security channels. There may be something basic about your configuration that's incorrect and removing protocol layers will help you find that faster.

    Next time: Localhost Common Name

  • Nicholas Allen's Indigo Blog

    Detecting Metadata

    • 1 Comments

    How do I figure out during dispatch whether a request is destined to be a metadata request or a normal application request?

    The reason you might care whether a request in flight is for metadata or not is because of security policy. You might want to permit lots of people to access your metadata but be very strict about who can call application methods. If you're a security routine that intercepts all sorts of different calls, then you need to know what type of call is currently happening to make the right decision. Otherwise, you don't know who to apply scrutiny to and who to just let in.

    In the context of the currently in flight operation, there are three pieces of data that will help you figure this out. If you see all three signs, then you know that the request is going to be for metadata retrieval.

    1. Check the contract name of the endpoint that the current operation is being dispatched to. It should be IMetadataExchange.
    2. Just in case someone defines a different contract with the same name, also check the contract namespace. It should be http://schemas.microsoft.com/2006/04/mex.
    3. Finally, check the action of the operation. If it's an operation that does metadata transfer, then the action should be http://schemas.xmlsoap.org/ws/2004/09/transfer/Get/.

    Next time: Silent Security Failures

  • Nicholas Allen's Indigo Blog

    Shutting Down Service Hosts

    • 5 Comments
    How should I shut down a running service host?

    Here's a variety of attempts at answering the same question, with varying levels of sophistication thrown into the mix.

    Way Too Simple

    • Always call abort, it's guaranteed to shut things down.

    This is the equivalent of shutting your computer down every day by pulling the plug out. It does what it promises but there's considerable damage as a side-effect.

    Bit Too Simple

    • Call abort if the service host has failed.
    • Call close if the service host is running.

    The second attempt alleviates some of the collateral damage but has flaws of its own. If the close fails, then we have no way to fall into the error case.

    Simple

    • Run your code inside of a catch block.
    • Call close inside the block on the service host.
    • Call abort if an error occurs inside the block.

    Advanced

    • Run your code inside of a catch block.
    • Filter out the exceptions from the service host that are recoverable and use them as a signal to restart the host.
    • Call abort if an error occurs inside the block.
    • If the error is recoverable, then start a new service host afterwards.
    • Run your code inside of another catch block.
    • Call close inside the block on the service host
    • Call abort if an error occurs inside the block.

    Next time: Detecting Metadata

  • Nicholas Allen's Indigo Blog

    Future of Silverlight

    • 3 Comments

    Late last week Scott Guthrie announced some of the plans for the next version of Silverlight. In addition to being renamed from Silverlight 1.1 to Silverlight 2.0 and getting a beta date for Q1 of next year, here were the big new features announced.

    • WPF UI Framework: The current Silverlight Alpha release only includes basic controls support and a managed API for UI drawing. The next public Silverlight preview will add support for the higher level features of the WPF UI framework. These include: the extensible control framework model, layout manager support, two-way data-binding support, and control template and skinning support. The WPF UI Framework features in Silverlight will be a compatible subset of the WPF UI Framework features in last week's .NET Framework 3.5 release.
    • Rich Controls: Silverlight will deliver a rich set of controls that make building Rich Internet Applications much easier. The next Silverlight preview release will add support for core form controls (textbox, checkbox, radiobutton, etc), built-in layout management controls (StackPanel, Grid, etc), common functionality controls (TabControl, Slider, ScrollViewer, ProgressBar, etc) and data manipulation controls (DataGrid, etc).
    • Rich Networking Support: Silverlight will deliver rich networking support. The next Silverlight preview release will add support for REST, POX, RSS, and WS* communication. It will also add support for cross domain network access (so that Silverlight clients can access resources and data from any trusted source on the web).
    • Rich Base Class Library Support: Silverlight will include a rich .NET base class library of functionality (collections, IO, generics, threading, globalization, XML, local storage, etc). The next Silverlight preview release will also add built-in support for LINQ to XML and richer HTML DOM API integration.
Page 1 of 1 (19 items)