I have written an ItemAttachmentAdded event receiver. The business rule was that any attachment added to a list other than the document library had to be moved to the document library and a link to that moved file was to be placed in a field on the related list item so the partner could follow the link to the file in the document library. Having not found this information put together anywhere, I felt it was important to put this little tidbit of code out there. YMMV.
ItemAttachmentAdded, as its name implies, is an event that occurs when an attachment has been uploaded to a list with a list item. It is crucial to understand that when you have more than one attachment being added to a list item the server will run the uploading of these files in threads.
Let's say you are uploading 10 files on one list item. The uploading process may take 1, 2, 3, or more of the files at a time and upload them in a thread. The next ones will be added to other threads until all of the files have been copied into their folders under that list item. This caused me a headache because I could not debug the code. As I would step through the debugger I would find my current debugger line bouncing around to other spots because of the other threads hitting my breakpoints. At any given moment I could be brought back to the same variable but have different values in it each time.
I took a crash-course (i.e. the GOOG) on threads and found this handy little piece of code called "lock()." I am not a .NET wizard so I can't fully explain what it is doing but this is an old one: you have to give lock() an object. Apparently ANY object will do. As you can see from my code I just gave it a new object(). Lock apparently allows execution of one thread at a time. The other threads are queued up to run after the current thread is unlocked. In this way you can see from my code that I am running the work of appending the link to each file in a string builder, copying the file and then adding it to a list of files to delete from the current list item. After each run I can loop back and take in the next thread.
I added my code for debugging this monster in the catch statement. I actually removed all the other calls I had peppered throughout this code to examine each variable as I went through. I had about 20 calls to DebugIt(). That was what told me that the values were changing a WHOLE lot. For example, I could see that my attachments collection only had a few of my attachments in it. This was my first tip-off that I was dealing with threading...
I don't claim that this code is pretty. I don't claim that this code is refactored correctly. I don't claim that it is even a correct use of lock(). I can only claim that this bit of code now allows me to get all of my attachments into the document library and write a link back in the original item. (O;
using Microsoft.SharePoint;
using Constants;
using System.Collections.Generic;
using System.Text;
using System;
using System.Diagnostics;
namespace Intake_Handler
{
class Attachments_Handler : SPItemEventReceiver
{
static readonly object _object = new object();
public override void ItemAttachmentAdded(SPItemEventProperties properties)
{
lock (_object)
{
using (SPWeb web = properties.ListItem.Web)
{
try
{
this.DisableEventFiring();
base.ItemAttachmentAdded(properties);
SPList docLib = web.Lists[ListNames.CASE_DOCUMENTS];
SPFolder rootFolder = docLib.RootFolder;
string docLibURL = web.Url + "/" + rootFolder.ToString();
SPListItem item = properties.ListItem;
SPAttachmentCollection attachments = item.Attachments;
List<string> filesToDelete = new List<string>();
StringBuilder sb = new StringBuilder();
var attachmentLink = item["AttachmentLink"] == null ? string.Empty : item["AttachmentLink"].ToString();
sb.Append(attachmentLink);
if (attachments.Count != 0)
{
foreach (string fileName in attachments)
{
SPFile file = item.ParentList.ParentWeb.GetFile(item.Attachments.UrlPrefix + fileName);
byte[] fileBytes = new byte[0];
fileBytes = file.OpenBinary();
string destURL = rootFolder.Url + "/" + file.Name;
SPFile destFile = rootFolder.Files.Add(destURL, fileBytes, true);
sb.Append(docLibURL + "/" + fileName + " | ");
filesToDelete.Add(fileName);
}
}
item["AttachmentLink"] = sb.ToString();
foreach (string fileToDelete in filesToDelete)
{
attachments.Delete(fileToDelete);
}
item.SystemUpdate();
}
catch (Exception ex)
{
DebugIt(web, string.Format("Catch: {0}", ex.ToString()));
}
finally
{
this.EnableEventFiring();
}
}
}
}
public static void DebugIt(SPWeb web, string body)
{
SPList list = web.Lists["debug"];
SPListItem item = list.Items.Add();
item[SPBuiltInFieldId.Title] = DateTime.Now.Millisecond.ToString();
item["body"] = body;
item.Update();
}
}
}