Contents tagged with AppFabric

  • How to check the version of AppFabric 1.1 aka the Distributed Cache

    Tags: AppFabric, SharePoint 2013

    Introduction

    The other day I posted about the patching procedure for the SharePoint 2013 Distributed Cache (Microsoft AppFabric 1.1) and on that post I got a great comment from Riccardo:

    Hi Wictor, is it possible to discover the patch level of the Distributed Cache without looking at control panel? Powershell?

    That is a great question Riccardo! But the answer is not that simple…

    Check the version using Installed Updates

    The easiest way to manually check what version of AppFabric you are using, or rather which CU that is applied to AppFabric 1.1, is to use the Program and Features tool in Windows Server and then click on View installed updates.

    Installed AppFabric updates

    As you can see in the screenshot above, they all have the same version 1.1.2106.32 and that is also the version you get when you query the registry with this:

    Get-ItemProperty HKLM:\SOFTWARE\Microsoft\AppFabric\V1.0\ -Name ProductVersion | select ProductVersion

    That version number is the version number of Microsoft AppFabric 1.1 and it doesn’t change when you are applying Cumulative Updates.

    Check the Distributed Cache version using PowerShell

    If you want to automate checking the patch level of the Distributed Cache then the only way I’ve found (if someone has a better way, please enlighten me) is to actually check the version of one specific file, the Microsoft.ApplicationServer.Caching.Configuration.dll file. This is one of the few files that is updated in all the current CU’s (1-5), but I cannot guarantee that it will be updated in future version. The check is is done using the following PowerShell script (remove the line breaks and paste as a single line):

    (Get-ItemProperty "C:\Program Files\AppFabric 1.1 for Windows Server\
    PowershellModules\DistributedCacheConfiguration
    \Microsoft.ApplicationServer.Caching.Configuration.dll" -Name VersionInfo)
    .VersionInfo.ProductVersion

    This script will give you different version numbers depending on your AppFabric 1.1 CU level as follows:

    CU level Product Version
    CU1 1.0.4639.0
    CU2 1.0.4644.0
    CU3 1.0.4652.2
    CU4 1.0.4653.2
    CU5 1.0.4655.2

    If this method is valid for future CU’s then please remind me to update this table.

    Summary

    So Riccardo, there you have the best answer I can give you. It is not an optimal solution but it works…

  • How to patch the Distributed Cache in SharePoint 2013

    Tags: SharePoint 2013, AppFabric

    Introduction

    In SharePoint 2013 the Distributed Cache plays a very important role, it is a key component for performance and caching. An incorrectly configured or managed Distributed Cache will cause issues, with your farm. I’ve even seen blogs recommending turning it off, most likely due to that they don’t manage the cache properly and get into a situation where it causes even worse performance problems. One of the good things with the Distributed Cache is that is not a SharePoint service, it is a standalone service called AppFabric 1.1 for Windows Server. Initially the guidance from Microsoft was that the Distributed Cache (DC) would be patched together with SharePoint and we should keep our hands off it. But that has over the time changed and allowed us to take advantage of the fixes and improvements that the AppFabric team does to the service. So, it is our responsibility to patch the Distributed Cache. But how do we do it?

    [Update 2014-07-16] Here's a link to the official statement that AppFabric/DC is independently updated from SharePoint: KB2843251

    Proper patch instruction

    First of all there are currently five released Cumulative Updates (CU) for AppFabric 1.1, CU1 to CU5. An AppFabric Cumulative Update is exactly the same as a CU for SharePoint, it contains all previous CUs. So if you install CU5 you also get CU1 to CU4 installed. The important thing with this is that you as an administrator should not just read the latest CU Knowledge base article, but also the previous ones (you will see one reason in a minute).

    Let’s assume that we have a SharePoint farm with more than one servers running the Distributed Cache service instance. To patch these we need to download the AppFabric CU that we would like to use. I’d recommend using the latest (CU5) right now and I’ve not yet seen any issues with it, only positive effects. If you are using Windows Server 2012 you definitely should go with at least CU4.

    Here’s a link to the different CU’s and their KB article respectively

    In this case let’s apply CU5.

    The first thing before patching that is really important to do is to properly shut down the Distributed Service instance. The reason that you would like to do this is that some items in the Distributed Cache is only living in the cache and is not backed to a persistent storage, such as some items in the SharePoint Newsfeed. If you do not properly shut down the service instance you will loose that data. Fortunately we have a great PowerShell cmdlet that does this job for us. The process here is that you need to patch one machine at a time according to these steps:

    1. Shut down the service instance on one machine
    2. Patch AppFabric 1.1
    3. Post-patch operations
    4. Start the service instance
    5. Restart from 1 on the next machine

    Do not do servers in parallel unless you have a massive amount of servers and can handle that extra load/redundancy!

    Step (1) is done in PowerShell using one of the built-in SharePoint cmdlets:

    asnp *sharepoint*
    Stop-SPDistributedCacheServiceInstance -Graceful

    This command will gracefully shut down the service instance on the local machine. A graceful shutdown means that all the cache items will be distributed to the other service instances in the cache cluster. Make sure that you have enough overhead to do this. This is yet another example of my “3 is the new 2” rule is important. When you patch one server you don’t want just one extra machine! Once the service instance is stopped, I normally wait a couple of extra minutes to make sure that all the cached items has properly propagated to the other servers.

    Then it is time to apply the actual AppFabric patch, step (2). Run the patch executable and follow the instructions. It’s basically a next, next, finish procedure.

    Step (3). When the patch is applied you should have read through the KB articles, and if you are applying CU3 or later you should have seen that in order to improve performance you need to modify the Distributed Cache configuration file. The CU3 KB article mentions a new feature added to the AppFabric service that takes advantage of the non-blocking background garbage collection feature in .NET 4.5, which is important for machines with large amounts of RAM – and that is exactly the description of a SharePoint server. So modify the DistributedCacheService.exe.config file as below to enable the background garbage collection:

    <configuration>
      ...
      <appSettings>
        <add key="backgroundGC" value="true"/>
      </appSettings>
    </configuration>

    The final thing we need to do on the machine is to start the service instance again, step (4). The AppFabric Windows Service will be disabled when it is shut down and you should NOT try to start that one manually, you must use the following PowerShell (or corresponding action in Central Administration if you’re a n00b).

    $instance = Get-SPServiceInstance | ? {$_.TypeName -eq "Distributed Cache" -and $_.Server.Name -eq $env:computername}
    $instance.Provision()

    This PowerShell snippet will get the Distributed Cache service instance on the machine where it is executed and provision it and start the AppFabric Windows Service.

    Once this is done and you are ready to move on to the next machine, step (5), give it a couple of extra minutes so that the newly patched (and empty) cache service instance has time to catch up.

    Not patching properly…

    A very common issue is that in order to apply the patch you just run the patch executable. Two things will happen when you do this. First of all the service will be shut down, but not gracefully and you will loose data. Secondly it will not start the service instance properly or at all. The patch contains a script that waits for the service to come online, but since this is not a normal AppFabric cache, it’s controlled by SharePoint, so this script will wait forever until the service comes up. If this happens all you have to do is kill the script window and start the service properly as shown above (warning: this is not a recommendation and there might be side effects)

    Summary

    I hope this short post cleared up some confusion on how to patch the Distributed Cache service in SharePoint 2013 and gave you an example on how to do this in a production environment without loosing any data. Of course, you should always test the procedure in a test/staging environment. Cache on!

  • SharePoint 2013: A look at hardware, software and other requirements

    Tags: SharePoint 2013, AppFabric, SQL Server

    SharePoint 2013As usual a new version of a product has new requirements of all different kinds; especially when it comes to resource usage. With SharePoint 2013 there is no difference. The Hardware and Software requirements for SharePoint 2013 Preview is published and I thought I should walk through the new and updated requirements and compare them with SharePoint 2010. And also talk about some other key changes that you need to be aware of when planning your SharePoint 2013 installations.

    Note: this is written for the SharePoint 2013 Preview and stuff will/can be changed for RTM.

    Server Roles

    Let's start with some key changes in the physical topology for SharePoint 2013. In SharePoint 2010 we basically had three different server roles; Web Servers, App Servers and SQL Servers. These roles are still valid for SharePoint 2013 but we also have two new roles; Office Web Apps Servers and Workflow Servers. Some roles can be combined and some cannot - for instance it is not supported to run any other role or application on servers that has Office Web Apps installed.

    Update 2012-07-21: A clarification on Office Web Apps Server. You cannot install Office Web Apps Server on a SharePoint 2013 Server Preview. This is currently a hard block.

    Of course we can still split out specific service applications on dedicated servers; for instance we can use specific search servers or cache servers, but I categorize these into the App Servers role.

    Software Requirements

    Let's start with the Software requirements. Software requirements are the minimum stuff you need on your (virtualized) metal before installing SharePoint 2013 or Office Web Apps. I'm not going to go through all the details - since they are well documented in the official documentation. Instead let's focus on some key things.

    Operating Systems

    SharePoint 2013 only supports two operating systems; Windows Server 2008 R2 Service Pack 1 and Windows Server 2012, 64-bit editions of course. There is no support for client OS such as Windows 7 any longer.

    For a Office Web Apps Server it looks like Service Pack 1 for 2008 R2 is not needed, but I strongly suggest that you install that anyways.

    Database Servers

    In SharePoint 2013 the support for SQL Server 2005 has been dropped and you need to use SQL Server 2008 R2 Service Pack 1 or higher (which includes SQL Server 2012).

    .NET Framework

    For SharePoint 2013 Web and App servers you need Microsoft .NET Framework 4.5 (RC) installed and for Office Web Apps Servers you need .NET Framework 4.0. If you have dedicated Workflow servers you can use 4.5 (RC) or 4.0 PU3.

    Windows Server AppFabric

    One new addition to the pre-requisites of SharePoint 2013 is the usage of Windows Server AppFabric, which is a requirement (you actually need to bump it to version 1.1). The AppFabric component is used for the Distributed Cache service (aka Velocity) which is used for the "Social stuff" and token caching.

    Distributed Cache

    Windows Azure Workflow Server specifics

    The servers hosting the Windows Azure Workflow server (WAW) has some specific requirements. The SQL Server must have Names Pipes and TCP/IP enabled and you must have the Windows Firewall enabled. Note that Windows Azure Workflow is an optional component of SharePoint 2013

    Other required software and things to note

    You also need a set of hotfixes, WCF Data Services, WIF 1.0+1.1. By using the pre-requisite installer you get these things configured for you automagically. The pre-req installer downloads them for you but if you would like to automate your installations you can pre download them using Paul Storks excellent PowerShell scripts.

    If you're leveraging the Exchange 2013 and SharePoint 2013 integration features, you need to install the Exchange Web Services Managed API, version 1.2, on your SharePoint boxes.

    Hardware Requirements

    So let's take a look at the hardware requirements, which has been stirring up the community to really weird proportions the last few days. Your hardware requirements really depends on how you intend to use SharePoint 2013, which Service Applications, which customizations etc etc, so you need to take this with a pinch of salt. For development purposes you can adjust these values as it fits you and your projects, but consider the "minimum values" below as something to start with. I'm going to add my personal "minimum values" to this discussion.

    Processors

    VMWare Processor configurationThe specified minimum requirements for the processor is 4 cores (64-bit of course). I've been setting up 2-core machines on my laptop and it has been working fine. But if you're doing development with Visual Studio you likely need a few more. For Production you of course add at least follow the "minimum requirements".

    For Database servers the recommendation is 4 cores for smaller deployments and at least 8 for medium and large deployments.

    Memory, RAM

    This one really hit the fan, when the requirements was published. And the reason for that is that the Requirements document states that you need 24 Gb of RAM for Server development on a single box. In my opinion it's far more than you need for average development, unless you're firing up all service applications, or using Search or Social features heavily. Having 24GB snapshots, will drain your disk space quite fast :).

    For my development boxes with SQL Server 2012 and Visual Studio 2012 installed I've so far found that the sweet spot is somewhere between 6 and 8 GB which is a good trade-off for laptop disk space and performance, but your mileage may vary.

    For production the minimum recommended RAM is 12GB, basically what I recommended for 2010.

    If we take a look at the SQL side you should have at least 8GB RAM for smaller deployments and 16GB RAM for medium ones. In production I would seriously consider bumping that to at least 64GB.

    Browser Support

    When the guessing game of SharePoint 15 features was in full motion a lot of people expected native HTML5 support for SharePoint vNext. Thankfully that did not happen and the Browser support is therefore basically the same as for SharePoint 2010; no Internet Explorer 6, use 32-bit IE's, support for latest versions of Chrome, Safari and Firefox.

    Summary

    This was a quick walkthrough of the hardware and software requirements for SharePoint 2013 Preview. A few new requirements and increased hardware resources compared to SharePoint 2010. But this is all about planning - you cannot just take the requirement and apply that onto your SharePoint 2013 farm, you need to evaluate your farm design and test it. Over the next few months I expect to see some great design samples including metrics from MSIT and of course gain experience from the 2013 engagements starting now...

  • Recap of the European SharePoint Conference 2011

    Tags: Windows 7, SharePoint 2010, Presentations, AppFabric

    European SharePoint Conference 2011Back home after a few days in Berlin for the European SharePoint Conference 2011. It was a great conference with good speakers and really nice attendees. It was three days full of sessions, expert panels, shoot-outs and SharePoint fun! Thanks to everyone who was there (especially those who came to my sessions :-) and the team behind the conference! And as always it great to meet up with the SharePoint MVP's, MCM's and now even MCA's!

    During the attendee dinner the conference team announced the dates for the next European SharePoint Conference, which is set to the first week in December 2012. This one will not be in Berlin, but more likely in Barcelona, Paris or Copenhagen (Barcelona is the one everyone cheered for, so my guess is that the other two are out of question :-)

    For this conference I did two development presentations. I've done similar sessions before but this time I changed them into be even more based on best practices and experiences from the field, since we're quite a bit into the product cycle. I got good feedback on it and in some of the sessions I attended I noticed the same approach from the speakers. For you who either didn't attend my sessions or didn't have time to write down all the code samples in your notebooks - you can find the presentations on the conference web site and you can find the code samples here:

    I'm planning (note planning - no promises) on doing a couple of follow up posts on specifics in the demos.

    Now it's only two more conferences this year; Singapore and Stockholm...

  • Improve performance of your SharePoint 2010 applications using Windows Server AppFabric caching

    Tags: SharePoint 2010, AppFabric

    Besides SharePoint my very dear topics is performance optimizations and new technologies, so here's a post mixing all these together.

    Background

    Caching is one way to improve the performance of any application, there are several ways to do it in-memory, disk etc etc. SharePoint 2010 has a set of caching capabilities, most of them are in-memory caches and some involve disk or even SQL based. One problem with (especially) in-memory caching is that if you have a farm different servers may display different results, which is due to the fact that the different servers cached information at different times. Another problem with in-memory caching is that it's per process, that is that you have different caches for different web applications and application pools.

    Windows Server AppFabric is an extension to Windows Server. There's one cloud version of AppFabric in Windows Azure but there's also an on-premise installation of AppFabric that contains hosting and caching capabilities. AppFabric is a middle-tier service platform (the real middle-tier, not the other middle-tier :-) and can be used in all kinds of scenarios. The caching features of AppFabric is based on the Velocity project, which was one of my favorite acts for the PDC08 conference. It contains an easy to set up and configure distributed caching framework that can use SQL Server or disk as storage. In this post I will show you how to leverage the caching features of the on-premise version of AppFabric.

    Using a distributed cache such as AppFabric can solve many problems. I've already mentioned a few such as the problems with in-memory caching. Other interesting things is that you can easily set up cross farm caching or even cross domain caching. You can have the caching on separate servers in the application tier. You can share the cache between web applications and service applications. The cache isn't tied to the application pool so any recycles of that will not clear the cache and so on...

    Setting up and configuring AppFabric v1.1 CTP

    AppFabric v1.1 CTP is really easy to setup. You install the binaries (download 1.0 via Web Platform installer or 1.1 directly here) on the first machine to host the distributed cache and configure the cache. Then you add new servers to that cache cluster. Very much like you do when setting up a SharePoint farm. Follow these instructions on MSDN, you can't fail (which you can't say with SharePoint).

    Installing AppFabric

    Configuration is used either using the configuration application or using PowerShell (what else did you expect). In this case I configured it to use SQL Server as backend. Using the disk based would ideally be used in cross farm/domain scenarios (think about that - having data up to date cross farms, that's cool!). Once AppFabric is installed you need to start the AppFabric caching  using the following PowerShell commands. First use Use-CacheCluster to set the context and then just Start-CacheCluster to start the cache. There is one final configuration that is needed and that is to allow the application pool account to access the cache. This is done using the Grant-CacheAllowedClientAccount cmdlet. If you forget to set this you will see an ERRCA0017 error. AppFabric contains tons of configuration options. You could setup different clusters, named caches etc etc.

    Using the cache in a Web Part

    Now on to the SharePoint bits. In this scenario I'll build a Web Part that fetches quotes from Yahoo, that is doing an HTTP request to get a quote. This is a common scenario where you would build some kind of cache to avoid HTTP latencies. Another problem with HTTP requests is that they might time out or take a long time. You need to call it at least once so the poor bastard that's loading your page first might suffer from it. In this case we could actually create a timer job that makes the requests and put the data in the AppFabric cache before it's requested. But for this simple demo let's settle with a Web Part.

    Preparing your solution

    Before building the actual Web Part we need to set up the project to use the AppFabric cache. First two references must be added; Microsoft.ApplicationServer.Caching.Core and Microsoft.ApplicationServer.Caching.Client. They are found in the c:\Windows\System32\AppFabric\ folder (even though some documentation says otherwise). I could not directly add them as reference in Visual Studio, it could not find the directory, so I copied the file from that folder to a custom folder first.

    After adding the references we need to set up the configuration of our custom cache. Normally you do this using the application configuration file, but in SharePoint scenarios this doesn't work that well, so I'll do it all in code. In this sample I use hardcoded values for servers and ports - in a real world scenario you should of course make this configurable using for instance the P&P SharePoint Guidance Hierarchical Object Store. To access the cache we need to create a cache factory and retrieve our cache from that. Creating the cache factory object is an expensive operation so I'll implement this in a singleton thread-safe object to avoid creating the factory that many times. It looks as follows:

    sealed class CacheUtility {
        private static DataCacheFactory _factory;
        private static DataCache _cache;
        private static readonly object _lock = new object();
    
        CacheUtility() { }
    
        public static DataCache CurrentCache     {
            get {
                using (SPMonitoredScope scope = new SPMonitoredScope("DataCache.CurrentCache")) {
                    lock (_lock) {
                        if (_cache == null) {
                            List<DataCacheServerEndpoint> servers =                             new List<DataCacheServerEndpoint>();
                            servers.Add(new DataCacheServerEndpoint("SP2010", 22233));
                            DataCacheFactoryConfiguration config =                             new DataCacheFactoryConfiguration() {
                                Servers = servers
                            };
                            _factory = new DataCacheFactory(config);
                            _cache = _factory.GetDefaultCache();
                        }
                        return _cache;
                    }
                }
            }
        }
    }

    As you can see in this class I have the DataCacheFactory object and the actual cache object (DataCache) as static variables. They are only created on the first request to the CurrentCache property. The DataCacheFactory object is instantiated with a configuration, in this case I'll just add one AppFabric cache server using the server name and the cache port.

    Then let's add the Web Part to the project. In the Web Part class implement the function to get the stock quotes as follows:

    public class Quote {
        public string Name;
        public double LastTrade;
        public DateTime DateTime = DateTime.Now;
    }
    
    public Quote GetQuote(string ticker) {
        using (SPMonitoredScope scope = new SPMonitoredScope("GetQuote")) {
            using (WebClient client = new WebClient()) {
                string tmp = client.DownloadString(                String.Format("http://finance.yahoo.com/d/quotes.csv?s={0}&f=nl1", ticker));
                string[] arr = tmp.Split(new char[] { ',' });
                if (arr.Length == 2) {
                    return new Quote() {
                        Name = arr[0].Trim(new char[] { '"' }),
                        LastTrade = double.Parse(arr[1])
                    };
                }
            }
            return new Quote() {
                Name = "Not found"
            };
        }
    }

    It's a simple method, GetQuote(), that retrieves the stock quote using the Yahoo Finance API. If the quote is found it returns a Quote object with correct values and if not an "empty" Quote object.

    Also add a property to the Web Part so that we can turn on and off the caching:

    [Personalizable(PersonalizationScope.Shared)]
    [WebBrowsable]
    [WebDisplayName("Use Cache")]
    public bool UseCache { get; set; }

    Now it's time to implement the actual Web Part, and as always it's done in the CreateChildControls, like this:

    protected override void CreateChildControls() {
        using (SPMonitoredScope scope = new SPMonitoredScope("CachedWebPart.CreateChildControls")) {
            Quote quote = null;
            string ticker = "MSFT";
            if (UseCache) {
                DataCacheItem cacheItem = CacheUtility.CurrentCache.GetCacheItem("quote" + ticker);
                if (cacheItem != null) {
                    quote = (Quote)cacheItem.Value;
                }
            }
            if (quote == null) {
                quote = GetQuote(ticker);
                if (UseCache) {
                    CacheUtility.CurrentCache.Add("quote" + ticker, quote, new TimeSpan(0, 1, 0));
                }
            }
            this.Controls.Add(new LiteralControl(
                String.Format("Last trade for {0} is {1}, retrieved at {2}", 
                    quote.Name, quote.LastTrade, quote.DateTime)));
        }
    }

    This method is also pretty straight forward. It checks if the cache is enabled and if it is it tries to retrieve the quote from the cache using the GetCacheItem method. If it's not found it uses the GetQuote method to retrieve the quote and if the cache is enabled it stores the quote in the cache. Finally it just prints out the quote value. That's it!

    Test and evaluate

    So let's take a look at this Web Part now using the Developer Dashboard (notice the SPMonitoredScopes I added to the code). Here's how it looks without the cache enabled. The call to the Yahoo Finance API takes about half a second - a real performance killer.

    No caching

    Once we turn on the caching, the first call to the CacheUtility class will initialize the cache and for subsequent calls use that cached factory and cache. Notice how the first hit which creates the cache actually takes some time and in this case the HTTP request takes a really long time!

    Caching started

    Then once the cache is up and running, retrieving the quote takes almost no time.

    Caching in full effect

    Improve performance a little bit more...

    But it still takes about 10ms! This 10ms comes from that this is not an in-memory cache, it is actually stored in SQL Server. But hey, it's better than a couple of seconds. But I want to save an extra couple of cycles here. Ok, let's enable the client cache! Add the following row when configuring the cache factory and you will also have an in-memory cache that has a maximum number of object, a timeout and an invalidation policy.

    config.LocalCacheProperties = 
        new DataCacheLocalCacheProperties(
            100, 
            new TimeSpan(0, 3, 0), 
            DataCacheLocalCacheInvalidationPolicy.NotificationBased);

    In this case I set the invalidation policy to use notifications. To get this up and running you need to shut down the cache cluster and configure notifications for the cluster as follows:

    Stop-CacheCluster 
    Set-CacheConfig -CacheName "default" -NotificationsEnabled $true 
    Start-CacheCluster

    And now retry the Web Part and we'll see that this is more efficient:

    Client cache enabled

    Monitoring

    The AppFabric cache also contains a lot of features for monitoring and logging. The simplest command is the Get-CacheStatistics that show you stats of the cache:

    Cache stats

    Summary

    Next time you are thinking about caching in SharePoint (or any other .NET application for that matter) consider using AppFabric caching. It's a great and versatile middle-tier caching framework with near endless configuration possibilities. I know that this simple yet powerful framework can make a big difference in any of your projects. And why invent some own caching framework when we have something beautiful as this!

  • Presentations and code for Office 365 and Windows Azure sessions from TechDays 2011

    Tags: Windows Azure, SharePoint 2010, Presentations, Office 365, AppFabric

    Back in the saddle from another TechDays event here in Sweden. This year it was all about the cloud! It was as always a great show and an awesome party. Thank you Microsoft, all presenters, all attendees and sponsors.

    I did two sessions - or actually one session divided into two segments about Office 365 and Windows Azure. I tried to squeeze in as much cloud technology as I could in a one big demo. For those who attended - I hope you enjoyed it. If you want to go back to the slides or take a look at the demo code you can find them below. The sessions were also recorded so you can enjoy them in full glory (keep an eye on the TechDays site for more information).

    Presentations and code

    Here's the code.

    Interview

    I was also interviewed after my sessions by Peter Nicks from Microsoft - and here's the video of that:

    TechDays 2011 Interview

    See ya next year...

About Wictor...

Wictor Wilén is a Director and SharePoint Architect working at Connecta AB. Wictor has achieved the Microsoft Certified Architect (MCA) - SharePoint 2010, Microsoft Certified Solutions Master (MCSM) - SharePoint  and Microsoft Certified Master (MCM) - SharePoint 2010 certifications. He has also been awarded Microsoft Most Valuable Professional (MVP) for four consecutive years.

And a word from our sponsors...

SharePoint 2010 Web Parts in Action