SharePoint Dragons

Nikander & Margriet on SharePoint

Tag Archives: c#

C# in Depth

Over the years, we’ve been technical reviewers of dozens of books. Occasionally, we like to give a thumbs up to the most interesting ones we see, because we feel they deserve the attention.

The book “C# in Depth” by Jon Skeet is definitely among this group. When we saw the first draft version it was, from the perspective of a technical reviewer, disappointing as there was so little to comment on: the book already looked very complete at an early stage. We’ll go one step further: this book currently is the de facto standard for books covering the essence of C# (such as generics, nullable types. lambda expressions, extension methods, LINQ, DLR, and code contracts). It’s the best one in it’s kind, and we feel that every C# developer should have a copy at home.

If you want to find out if we’re exaggerating, check it out for yourself: http://www.amazon.com/C-Depth-Second-Jon-Skeet/dp/1935182471/ref=sr_1_1?s=books&ie=UTF8&qid=1322814011&sr=1-1

Advertisements

Parallel programming in SharePoint 2010: the back to the future pattern

We certainly don’t consider ourselves to be lazy, but we’ll admit we had some hesitations writing this article because of the amount of work it involved. Eventually we went ahead and wrote it, and since we weren’t planning to write an article the size of a book chapter we tried to keep it as succinct as possible. As a down side, this does mean that you, the reader, must possess basic SharePoint 2010 knowledge as well as some WCF knowledge. This article is about using .NET 4 and up (in particular the TPL, the Task Parallel Library) in combination with SharePoint 2010 and it’s object model by invoking several WCF services, so if you’re interested in doing that you should read on.

Laziness

But first this: While we’re on the subject of laziness, for your amusement we took the effort to do some research to find out who in fact are at the top and the bottom of the laziness chain on this earth. As it turns out, the brother of Tammy G is the laziest person in the world because he is 40 years old, has two kids, still lives with his mommy, lets her pay all the bills, and also lets her take him anywhere because he has no drivers license (http://answers.yahoo.com/question/index?qid=20060918224104AAOSnHr). Albeit posthumously, it wouldn’t feel right to give the title of “hardest working person” to anybody else but James Brown.

In SharePoint world, it was a bit easier for us to decide. Sahil Malik (http://blah.winsmarts.com/) has repeatedly stated that he’s the laziest person you’ll ever meet (that is, if you stay away from Tammy G’s brother), so he full well deserves the honorary title of being the laziest person in SharePoint world (that, as well as being one of the most talented guys around when it comes to SharePoint). The hardest working individual may just be Mindsharp’s Todd Bleeker (http://sharepoint.mindsharpblogs.com/todd/default.aspx). For SharePoint 2010, we contributed to some courseware he wrote and in order to get things done in time he adopted a US commando life style for a couple of months that only allowed him 2 hours of sleep every night which enabled him to get more work done. It’s this kind of controlled craziness that makes us nominate him for the honorary title of being the hardest working person in SharePoint world.

On with the story…

To us, the most irritating thing about SharePoint 2010 is the fact that it doesn’t play nice with the .NET 4 framework. The main problem is that you can’t call the object model from a .NET 4 assembly although there are other problems as well, such as the inability to build SharePoint workflows using WF 4. It’s easy to demonstrate the problem that arises when you try to call the SharePoint object model from a .NET 4 assembly:

using (SPSite site = new SPSite(“http://moon/sites/test1″))
{

}

This results in the following error message:

“Microsoft SharePoint is not supported with version 4… of the Microsoft .Net Runtime.”

So there you have it. One good thing though, the error message is clear enough, you can’t say the same when you’re building a .NET 3.5 assembly with the wrong (default) target platform of x86. If you run the same code, once you hit the line that opens the site collection, you get the following exception:

The web application at [url] could not be found. Verify that you have typed the URL correctly…

Not the best way of letting you know that the target platform should be x64 instead of x86. In our experience, this is a common mistake for developers new to SharePoint 2010, and although this exception is not really the kind of help one would be looking for, it’s a mistake one probably only makes once.

Please note: As our gentle reader will no doubt understand, we would never make such a foolish mistake, we’ve just heard rumors about other people who did it wrong.

Although we totally appreciate and understand the fact that because of timelines and stuff it was impossible for the SharePoint team to include .NET 4 support in the object model, it’s annoying nonetheless. What’s more, already this has been a point of confusion for many developers. With the developer release of Windows 8 and .NET 4.5, things become even worse. Let’s just take the improvements for parallel programming in .NET 4.5 (see http://blogs.msdn.com/b/pfxteam/archive/2011/09/17/10212961.aspx): .NET technology keeps evolving at a rapid pace and the developer possibilities in SharePoint 2010 already start to fall behind the curve.

I don’t want to be left behind, give me the “Back to the Future” pattern!

.NET 4 has lots of features that, at some point, you may desperately want to use. If you really have a compelling case, there is always the option of building a WCF 4 service and call that from a SharePoint 2010 client. This way, you let the WCF 4 service handle the .NET 4 stuff.

For fun, let’s call this the “Back to the Future” pattern, based on one of our favorite 80s movies where Doc Brown invents time travel and Marty McFly (Michael J. Fox) gets stuck in the past and has to find a way to get back to the future. This is kind of the situation where SharePoint 2010 forces us in so we thought the name appropriate. backtf

In the rest of the article we’ll be using .NET 4 parallel programming in a way that based on our performance tests looks to be turning out really well. In this scenario we’ve created a web part that calls a WCF 4 service (which we’ll call the middle tier service) that uses parallel programming to invoke a WCF 3.5 service that runs in the context of SharePoint (we’ll call this one the end tier service).

Please note: You can download Reactive Extensions for .NET 3.5 SP1 from http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx. It contains the System.Threading.dll assembly that contains TPL for .NET 3.5. If you’re happy with that version, you could use that instead, but that’s not really the point of this article. What we need is a way that allows us to leverage all TPL stuff, including the new .NET 4.5 features.

What will we be doing?

Currently we’re working on a very interesting project that requires us to load large amounts of log files into memory, which allows us to perform advanced statistical analysis on this information. For this specific article, we’ve tweaked the requirement a bit and we’ll only be uploading a couple of files to a SharePoint document library. In order to do this in as little time as possible we’ll be leveraging the .NET 4 TPL. To demonstrate our “Back to the Future” pattern, the solution will consist of the following pieces:

  1. A visual web part that contains a button that triggers the upload of a predefined set of files located on the server.
  2. A WCF 4.0 service (the middle tier service) that leverages the .NET 4 TPL to make sure all files will be uploaded in a way that is much faster than running sequential code.
  3. A WCF 3.5 service running in SharePoint context (the end tier service). This is the service that eventually is responsible for uploading a file to a SharePoint document library.

We’ll discuss these list items starting backwards, ending with the visual web part piece.

How to upload and download stuff

Since we’re eventually planning to upload files to a SharePoint doc library, we’ll get that part out of the way by showing a method that can be incorporated in a console application that uploads stuff:

static void Upload()
{

List<string> files = new List<string> { “1.log”, “2.log”, “3.log”, “4.log”, “5.log”, “6.log”, “7.log”, “8.log” };

foreach (string file in files)
{
string path = String.Format(@”c:\temp\temp\{0}”, file);
string url = default(string);

using (SPSite site = new SPSite(“http://moon/sites/test1″))
{

using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[“TestLib”];

Stream s = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
SPFolder folder = list.RootFolder;
bool overwrite = true;
url = String.Format(“{0}/{1}/{2}”, web.Url, list.RootFolder.Url, folder.Files.Add(file, s, overwrite).Name);
}
}
}
}

Because we’re into symmetry we’ll also be showing code that allows you to download stuff from SharePoint. Try something like this:

public void Download(string url, string fileName)
{
WebClient client = new WebClient();

NetworkCredential creds = new NetworkCredential(“[user name]”, “[password]”, “[domain]”);
client.Credentials = creds;
//client.Credentials = CredentialCache.DefaultCredentials;

string dest = @”c:\temp\download\” + fileName;
client.DownloadFile(url, dest);
}

Building the end tier service

Let’s start with the creation of the end tier service: this is the service that runs in a SharePoint context and is responsible for uploading log files. Basically, this part is a synopsis of MSDN article “Creating a Custom WCF Service in SharePoint Foundation” (http://msdn.microsoft.com/en-us/library/ff521581(d=printer).aspx). We recommend that you read the article and do the following:

  1. Create an empty SharePoint project and call it ParallelServiceHost instead of RevertService.
  2. Create the interface for the end tier WCF service running in SharePoint context. This interface will only contain a single upload method that receives the name of a file located on the server that will be uploaded to a SharePoint document library. Add a file called IParallelService.cs that looks like this:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    namespace WcfService
    {
    [ServiceContract]
    public interface IParallelService
    {
    [OperationContract]
    string Upload(string file);

    }
    }

  3. Implement the parallel service. Create an Upload method that runs in SharePoint context, and uploads a file from a predefined location on the server. Add a file called ParallelService.cs that looks like this:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    namespace WcfService
    {
    using Microsoft.SharePoint.Client.Services;
    using System.ServiceModel.Activation;
    using Microsoft.SharePoint;
    using System.IO;
    using System.Net;

    [BasicHttpBindingServiceMetadataExchangeEndpointAttribute]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class ParallelService : IParallelService
    {
    public string Upload(string doc)
    {
    string path = String.Format(@”c:\temp\temp\{0}”, doc);
    string url = default(string);

    SPList list = SPContext.Current.Web.Lists[“TestLib”];

    Stream s = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
    SPFolder folder = list.RootFolder;
    bool overwrite = true;
    url = String.Format(“{0}/{1}/{2}”, SPContext.Current.Web.Url, list.RootFolder.Url, folder.Files.Add(doc, s, overwrite).Name);

    return url;
    }

    }
    }

  4. Add a registration file for this service called Parallel.svc to the SharePoint ISAPI folder. Parallel.svc looks like this:

    <%@ServiceHost Language=”C#” Debug=”true”
    Service=”WcfService.ParallelService, $SharePoint.Project.AssemblyFullName$”   Factory=”Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>

  5. Make sure token replacement for .svc files is enabled (step 12 in the article).
  6. Deploy everything which causes the .svc file to be copied to the ISAPI folder of the SharePoint root folder. It also copies the dll of ParallelService to the GAC.

Please check that the MEX endpoint can be reached at:

http://moon/TestLib/_vti_bin/parallel.svc/mex

If not, it’s back to the drawing board for you. Chances are that your server isn’t named “moon”, so just use your server name or “localhost” instead.

Building the middle tier service

We have the end tier service in place, a service that runs in the context of SharePoint and knows how to upload files to document libraries. Now it’s time to create the middle tier service that leverages the TPL to upload files in parallel.

  • Start with creating a WCF 4 service. We’ve just named it Upload4Service to indicate that this is the .NET 4 service.
  • Later on, we’ll create an Upload method that receives a single input parameter that contains a collection of all the files we want uploaded. The definition of this input parameter looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Upload4Service
{
[DataContract(Namespace = “http://schemas.loisandclark.eu/2011/9/Parallel/Upload/”)]
public class UploadRequest
{
[DataMember(IsRequired = true)]
public List<string> Files { get; set; }
}
}

  • The aforementioned Upload method returns an UploadResponse containing all new SharePoint locations that looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Upload4Service
{
[DataContract(Namespace = “http://schemas.loisandclark.eu/2011/9/Parallel/Upload/”)]
public class UploadResponse
{
[DataMember(IsRequired = true)]
public List<string> SharePointLocations { get; set; }
}
}

  • If exceptions arise, we’ve created our custom exception that’s suitable for WCF. Please note that because we’re using parallel programming it contains a list of error messages, because multiple tasks might encounter errors. The exception class looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Upload4Service
{
[DataContract(Namespace = “http://schemas.loisandclark.eu/2011/9/Parallel/”)]
public class FaultInfo
{
[DataMember(IsRequired = true)]
public List<string> Messages { get; set; }
}
}

  • The contract of the .NET 4 upload service looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace Upload4Service
{
[ServiceContract]
public interface IUpload
{
[OperationContract]
[FaultContract(typeof(FaultInfo))]
UploadResponse Upload(UploadRequest request);

}
}

  • Our *.svc file, Upload4Service.svc, uses a code-behind file instead of a DLL and looks like this:

<% @ServiceHost Language=”C#” Debug=”true” Service=”Upload4Service.UploadService” CodeBehind=”~/App_Code/UploadService.cs” %>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Net;
using System.Collections.Concurrent;

namespace Upload4Service
{
public class UploadService : IUpload
{
public ConcurrentQueue<string> UploadResults { get; set; }
public UploadResponse Upload(UploadRequest request)
{
try
{
UploadResults = new ConcurrentQueue<string>();

Parallel.ForEach(request.Files, ParallelUpload);
UploadResponse response = new UploadResponse();
response.SharePointLocations = UploadResults.ToList();

return response;

}
catch (AggregateException ae)
{
FaultInfo fi = new FaultInfo();
fi.Messages = new List<string>();
fi.Messages.Add(“Failed to upload files to SharPoint”);

foreach (Exception err in ae.InnerExceptions)
{
fi.Messages.Add(err.Message);
}

throw new FaultException<FaultInfo>(fi);
}
}

private void ParallelUpload(string file)
{
string websiteUrl = “http://moon”;

// Set up proxy.
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
EndpointAddress endpoint =
new EndpointAddress(websiteUrl + “/_vti_bin/Revert.svc”);
ServiceReference1.RevertClient proxy = new ServiceReference1.RevertClient(binding, endpoint);
proxy.ClientCredentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;

string result = proxy.Upload(file);

UploadResults.Enqueue(result);
}
}
}

As it turns out, adding a new application to a SharePoint web site and assigning it it’s own .NET 4 application pool is just not worth the effort. This results in conflicting web.config settings (with on the one hand, settings inherited from SharePoint, and on the other hand, settings automatically generated by .NET 4). Therefore, we advise to create a new web site dedicated for your WCF 4 services and add a new application to that web site. Then, do the following:

  • Place the Upload4Service.svc in the application folder.
  • Add a web.config file, this is ours (forget the named pipe stuff as it’s not relevant for this article):

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>

<system.serviceModel>

<trust level=”Full” originUrl=”” />
<services>
<service name=”Upload4Service.UploadService”>
<endpoint address=”” binding=”netNamedPipeBinding”
contract=”Upload4Service.IUpload” />
<endpoint address=”mex” binding=”mexNamedPipeBinding” contract=”IMetadataExchange” />
<host>
<baseAddresses>
<add baseAddress=”net.pipe://moon.lc.corp:8081/MyService/” />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!– To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment –>
<serviceMetadata httpGetEnabled=”True”/>
<!– To receive exception details in faults for debugging purposes,
set the value below to true.  Set to false before deployment
to avoid disclosing exception information –>
<serviceDebug includeExceptionDetailInFaults=”True” />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<customErrors mode=”Off”/>
</system.web>
</configuration>

  • Create an App_Code folder and add IUpload.cs and UploadService.cs in it (or create a bin folder and add the DLL to it).

There you have it, a WCF 4 service that leverages the TPL to perform some uploading. At this point, a very important take away is that the end tier service operation that is invoked by the WCF 4 middle tier service runs under the identity of the application pool of the WCF 4 service. In our case, this is lc\administrator, which maps to the special SharePoint System Account (SHAREPOINT\system). Make sure that, whatever account you choose to use, it has sufficient rights to upload files to the document library.

Building the visual web part

Now, we need a SharePoint client that communicates with the middle tier service. We’ve chosen to create a visual web part. That is interesting, because even sandboxed solutions are able to communicate with WCF services. Do the following:

protected void Button1_Click(object sender, EventArgs e)
{
UploadClient upload4 = new UploadClient();
//upload4.ClientCredentials.UserName.UserName = @”lc\administrator”;
//upload4.ClientCredentials.UserName.Password = “mysecret”;

UploadRequest uploadRequest = new UploadRequest()
{
Files = new List<string> { “1.log”, “2.log”, “3.log”, “4.log”, “5.log”, “6.log”, “7.log”, “8.log” }
};
UploadResponse uploadResponse = upload4.Upload(uploadRequest);
foreach (string loc in uploadResponse.SharePointLocations)
{
Label1.Text += loc;
}

Because you’ve added a service reference, the app.config file contains everything you need to know to call the middle tier service.The problem is that SharePoint doesn’t have this info. Therefore, you need to open the web.config file of your SharePoint web application and locate the <system.serviceModel> section. If you haven’t altered it before, it looks like this:

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
</system.serviceModel>

Add the stuff from your app.config file to the SharePoint web.config file. In our case, this looks like:

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
<bindings>
<wsHttpBinding>
<binding name=”WSHttpBinding_IUpload” closeTimeout=”00:01:00″ openTimeout=”00:01:00″ receiveTimeout=”00:10:00″ sendTimeout=”00:01:00″ bypassProxyOnLocal=”false” transactionFlow=”false” hostNameComparisonMode=”StrongWildcard” maxBufferPoolSize=”524288″ maxReceivedMessageSize=”65536″ messageEncoding=”Text” textEncoding=”utf-8″ useDefaultWebProxy=”true” allowCookies=”false”>
<readerQuotas maxDepth=”32″ maxStringContentLength=”8192″ maxArrayLength=”16384″ maxBytesPerRead=”4096″ maxNameTableCharCount=”16384″ />
<reliableSession ordered=”true” inactivityTimeout=”00:10:00″ enabled=”false” />
<security mode=”Message”>
<transport clientCredentialType=”Windows” proxyCredentialType=”None” realm=”” />
<message clientCredentialType=”Windows” negotiateServiceCredential=”true” algorithmSuite=”Default” />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address=”http://moon.lc.corp:8081/MyService/Upload4Service.svc” binding=”wsHttpBinding” bindingConfiguration=”WSHttpBinding_IUpload” contract=”ServiceReference1.IUpload” name=”WSHttpBinding_IUpload”>
<identity>
<userPrincipalName value=”administrator@lc.corp” />
</identity>
</endpoint>
</client>
</system.serviceModel>

Now you can go ahead and add the visual web part to a page and happily call the middle tier service to do some parallel task processing.

That’s a wrap!

The good news is that if you really need/want to use .NET 4 in combination with SharePoint 2010, you can. The bad news is that it initially requires you to set up an infrastructure to allow for this.

Revisiting the System.Transactions resource manager for SharePoint

And: Is Google really better than Bing?

Written by: Margriet Bruggeman, Nikander Bruggeman.

16 September, 2011

Heads up, a slight warning, the technical good stuff comes later, we’ll start indulging ourselves with a little “amuse cerveau” (amusement for the brain, if you’ve never heard of the term that’s okay, we just mixed two French words together).

We’ve been writing blog posts about SharePoint for years now, and the most popular one we’ve ever written was “Exploring the Data View Web Part”, currently located on our new web site at http://www.loisandclark.eu/Pages/dvwp.aspx . This apparently was and still is a popular topic among SharePoint aficionado’s and it helps that this particular blog post turns up high in search results.

Let’s see what Google does when searching for “data view web part”:

image

It’s the first spot, no surprise there because this blog post is a brilliant piece of literature! Please note, http://www.lcbridge.nl was our old web site, we’re not using it anymore. Now let’s see what happens if we execute the same search query with Bing:

image

Aha, if you forget the advertising, you’ll agree that our blog post shows up as the fourth result. Therefore, our temporary scientifically proven conclusion is that Google is 4 times better than Bing, we’ll see later if it stays that way.

We have written other blog posts that we consider to have been very successful because they gained us new customers offering interesting projects. An example of such a blog post is “Velocity: Solving a Spider-Man/Human Torch dilemma”, currently located at http://www.loisandclark.eu/Pages/Velocity.aspx . Being free contractors, such posts are kind of a big thing and they certainly don’t happen all of the time.

Then there are (luckily a few) blog posts that backfired in unexpected ways. In “Fast Search Server 2010 for SharePoint: brief discussion”, currently located at http://www.loisandclark.eu/Pages/FastSearch.aspx , we talked briefly about SharePoint Search and the acquisition of Microsoft of Fast. Soon after it’s publication on May 1st 2010 we started seeing referrer URLs in our web logs leading to http://arnoldit.com/wordpress/2010/05/03/fast-search-server-2010-vision/ . Being curious, we read it and it was not exactly the kind of fan mail we were hoping for. Don’t you think it’s just nicer when people just get along? Anyway, if we ever get to rural Kentucky, we’ll make sure to find that Arnold geezer in his high-tech nerve center and give him a good kicking! As a side note, we have high hopes that the blog post we’re writing now might be a potential candidate for a bit of unexpected backfiring too.

And then there is the blog post that we’re revisiting today, “Building a System.Transactions resource manager for SharePoint”, currently located at http://www.loisandclark.eu/Pages/transactions.aspx . This blog post explains how to build transaction support into SharePoint for list items. Being 100% serious, this is hands down and by far the best thing currently written about that specific topic. However, this blog post has two problems. The first one is that almost no one bothers to read it!

Why is that? There’s nothing wrong with it’s visibility. Look, if you go to Google and search for “sharepoint transactions” it shows up as the first result (still pointing to lcbridge.nl, a web site we’ll be discontinuing in a couple months).

image

If you Bing it something terrible happens. It’s the first result, but of the 3rd page! Is this a terrible injustice? We were wondering, so we went ahead and opened the first result. This is what we saw:

Question: “Programmtically i am Using List Webservice to Update the Document Meta data once document get uploaded.I want to implement Transaction in this such that if any exception then rollback it.”

Answer: “This question has come up before and the answer has been neither WSS nor MOSS provide any transactional support via the standard object model or web services.  Someone may know different, but I think you will need to roll your own.”

So, we put in the time and effort and write an 11,000+ words article (for comparison, a large MSDN article is up to 4000 words) that discusses exactly how to build transactional support using the standard SharePoint object model into SharePoint, and Bing thinks that that is less relevant than this little Q & A? We can go ahead and discuss the other results listed ahead of our poor neglected blog post, but the results would be very similar and our sadness would increasingly increase. Oh boy, if we ever come to Seattle again we’ll make sure to go to the Microsoft campus, find the Bing team in their high-tech nerve center and give them too a good kicking!

This brings us to two conclusions:

  1. Although we absolutely love Microsoft technology and recommend it to people whenever we can (did you know the Windows 8 pre-beta was released two days ago?), it pains us to say that we do feel that Google is better for searching than Bing.
  2. Transaction support for SharePoint list items is an esoteric topic that nobody is really interested in and that’s probably why Microsoft hasn’t bothered to build it in yet.

Strangely enough, conclusion two doesn’t stop us from writing about transactions in SharePoint again.

We said earlier that “Building a System.Transactions resource manager for SharePoint” had a second problem, and that’s the reason we’re revisiting this topic today. If you made it this far, it’s probably a good idea to browse through the original article at http://www.loisandclark.eu/Pages/transactions.aspx , otherwise the rest of this blog post won’t make much sense.

Our code worked great within a console application or service, but not within a web context. Usage resulted in invalidated SPRequests. Because of that, we propose an altered version that does support a web context. The most interesting change is in the Rollback method. The durable file manager then looks something like this:

  public class DurableFileManager : VolatileMossResourceManager, IEnlistmentNotification
  {

    private SPFile _objFile;

    public SPFile File
    {
      get { return _objFile; }
      set { _objFile = value; }
    }

    public Guid FileId { get; set; }

    public Guid SiteId { get; set; }

    public Guid WebId { get; set; }

    public void CheckIn(string strComment)
    {
      File.CheckIn(strComment);
    }

    public void CheckOut()
    {
      File.CheckOut();
    }

    public override void Rollback(Enlistment enlistment)
    {
      using (SPSite objSite = new SPSite(SiteId))
      {
        objSite.AllowUnsafeUpdates = true;

        using (SPWeb objWeb = objSite.OpenWeb(WebId))
        {
          objWeb.AllowUnsafeUpdates = true;

          SPFile objFile = objWeb.GetFile(FileId);

          if (objFile.CheckOutStatus == SPFile.SPCheckOutStatus.None)
          {
            objFile.CheckOut();
          }

          foreach (string strKey in UndoLog.Keys)
          {
            objFile.Properties[strKey] = UndoLog[strKey];
          }

          objFile.Update();
          objFile.CheckIn(“rollback because of a failed transaction”);

          objWeb.AllowUnsafeUpdates = false;
        }

        objSite.AllowUnsafeUpdates = false;
      }

      enlistment.Done();
    }

    public void SetValue(string strKey, string strValue)
    {
      EnlistTransaction();
      SaveOrgValue(strKey, File.Properties[strKey].ToString());
           
      File.Item.Properties[strKey] = strValue;
    }

    public void Update()
    {     
      File.Item.UpdateOverwriteVersion();
    }

    public DurableFileManager(SPFile objFile)
    {
      File = objFile;
      FileId = objFile.UniqueId;
      WebId = objFile.Item.Web.ID;
      SiteId = objFile.Item.Web.Site.ID;
    }
  }

The volatile file manager looks something like this (again, the most interesting change is in the Rollback method):

  public class FileManager : VolatileMossResourceManager, IEnlistmentNotification
  {
    public void CheckIn(string strComment)
    {
      File.CheckIn(strComment);
    }

    public void CheckOut()
    {
      File.CheckOut();
    }

    public override void Commit(Enlistment enlistment)
    {
      //CheckIn(“System.Transaction manager commits transaction”);
      enlistment.Done();
    }

    public Guid SiteId { get; set; }
    public Guid WebId { get; set; }
    public Guid FileId { get; set; }

    public override void Rollback(Enlistment enlistment)
    {
      using (SPSite objSite = new SPSite(SiteId))
      {
        objSite.AllowUnsafeUpdates = true;

        using (SPWeb objWeb = objSite.OpenWeb(WebId))
        {
          objWeb.AllowUnsafeUpdates = true;

           SPFile objFile = objWeb.GetFile(FileId);

           if (objFile.CheckOutStatus == SPFile.SPCheckOutStatus.None)
           {
             objFile.CheckOut();
           }

           foreach (string strKey in UndoLog.Keys)
           {
             objFile.Properties[strKey] = UndoLog[strKey];
           }

           objFile.Update();
           objFile.CheckIn(“rollback because of a failed transaction”);

           objWeb.AllowUnsafeUpdates = false;
        }

        objSite.AllowUnsafeUpdates = false;
      }
      enlistment.Done();
    }

    public void SetValue(string strKey, string strValue)
    {
      EnlistTransaction();
      SaveOrgValue(strKey, File.Properties[strKey].ToString());
      File.Item.Properties[strKey] = strValue;
      File.Item.UpdateOverwriteVersion();

    }

    public void Update()
    {
    }
    private void LockFile()
    {
      if (!MetadataIsDirty)
      {
        if (File.CheckOutStatus != SPFile.SPCheckOutStatus.None) throw new Exception(“Can’t lock file”);
        File.CheckOut();
        MetadataIsDirty = true;
        LockedFile = true;
      }
    }

    public FileManager(SPFile objFile)
    {
      File = objFile;
      FileId = objFile.UniqueId;
      WebId = objFile.Item.Web.ID;
      SiteId = objFile.Item.Web.Site.ID;
    }

    private SPFile _objFile;
    public SPFile File
    {
      get
      {
        return _objFile;
      }
      set
      {
        _objFile = value;
      }
    }

    private bool _blnLockedFile;
    public bool LockedFile
    {
      get { return _blnLockedFile; }
      set { _blnLockedFile = value; }
    }
  }

That’s all folks!