Tuesday, June 17, 2008

Simple LINQ Projection Example

Here is a simple example using LINQ projection. It takes a List<string> that include space-delimited strings, queries them and then project into an IEnumerable<Person>. I then loop through the output sequence and write the results to the console.

class Program
    {
        static void Main(string[] args)
        {
            List<string> people = new List<string>
                { "Joe Smith 20", "Jim Jackson 30", "Jason Suzuki 40" };
            IEnumerable<Person> query =
                from p in people
                select new Person
                {
                    FirstName = p.Split(' ')[0],
                    LastName = p.Split(' ')[1],
                    Age = Convert.ToInt32(p.Split(' ')[2])
                };
            foreach (Person p in query)
            {
                Console.WriteLine("First Name:\t{0}\r\nLastName:\t{1}\r\nAge:\t\t{2}",
                    p.FirstName, p.LastName, p.Age);
            }
        }
    }

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
    }

The Albahari Brothers Rock!

I recently ordered two books and wanted to give a quick plug to the Albahari brothers. They have provided a great service to C# programmers by writing two excellent books that cover an incredible amount of material with examples in a very few pages. Unlike many technical books these are thorough, concise, and believe it or not, portable! I have many other books that have far more pages and simply don't measure up in explaining or providing good examples. I should also mention that they have also written the new O'Reilly C# In A Nutshell book. I own the older version, but find it more of a reference book and am less likely to throw it in my backpack.

In addition, they have written a lightweight tool that allows you to learn and write ad-hoc LINQ expressions. You can even connect to a SQL Server and write SQL queries as well. What's more, they have included all of the code samples from their books. (See screenshot below)

CSharp3PocketReference

C# 3.0 Pocket Reference: Instant Help for C# 3.0 Programmers

Authors: Joseph Albahari and Ben Albahari

Publisher: O'Reilly

Publish Date: 02/26/2008

Pages: 172

Current Amazon Price: $10.19 (Used - $7.79)

LINQPocketReference

LINQ Pocket Reference: The Concise Reference to LINQ via C# 3.0


Authors: Joseph Albahari and Ben Albahari

Publisher: O'Reilly

Publish Date: 02/26/2008

Pages: 242

Current Amazon Price: $10.19 (Used - $8.00)

linqpadlogo

LINQPad

Authors: Joseph Albahari and Ben Albahari

Free Download!

LINQPad

Hi Mom, I'm on MSDN - System.Collections.IEnumerable

I was recently implementing the IEnumerable and IEnumerator interface and needed to check out the documentation on MSDN. I thought the example could use some sprucing up since it used an array that has a fixed size and had two separate classes for the interfaces. Here are the updates:

  1. Implemented both IEnumerable and IEnumerator interfaces into one class.
  2. Useed a generic list instead of an array which has a fixed size.
  3. Added an indexer.

IEnumerable Interface

http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx

Source

using System;
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Person> peopleArray = new List<Person>()
{
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};

People peopleList = new People(peopleArray);
foreach (Person p in peopleList)
Console.WriteLine(p.firstName + " " + p.lastName);

Person p2 = peopleList[1]; //Jim Johnson
Console.WriteLine(p2.firstName + " " + p2.lastName);
}
}

public class Person
{
public Person(string firstName, string lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}

public string firstName;
public string lastName;
}

public class People : IEnumerable, IEnumerator
{
private List<Person> people;
int position = -1;
public People(List<Person> list)
{
people = list;
}

public Person this[int indexer]
{
get { return people[indexer]; }
set { people[indexer] = value; }
}

public bool MoveNext()
{
position++;
return (position < people.Count);
}

public void Reset()
{
position = -1;
}

public object Current
{
get
{
try
{
return people[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}

public IEnumerator GetEnumerator()
{
return new People(people);
}
}
}

Friday, June 6, 2008

Upload File to SharePoint Document Library Using the ASP.NET FileUpload Control

Recently I needed to create a Web Part that included the following:

  • A Master / DetailsView form that integrates with an external database.
  • A way to show existing documents and add new documents (and column metadata) to an existing SharePoint Document Library.

After some research I figured out how to do this by converting the FileUpload control and a ASP.NET Button control which does the actual uploading. In a nutshell you convert the posted file to a byte array and then use the SharePoint OM to upload the file. I have included the following functionality:

  • Overwrite File - You have the option of overwriting existing files or creating a new version if you have versioning turned on for the Document Library. 
  • Include Subfolders - This is done by passing a slash delimited string relative to the Document Library.
  • Update Column Metadata - This is done by passing a Dictionary<string, string> object that includes a key which is the column name and value as the value.

There is a probably a little more work I need to do to make this more bullet-proof, but it should give you a start. You can also combine it to do multiple files using my previous post: Upload Multiple Files With ASP.NET. Note: I have abbreviated the code below for brevity.

ASP.NET

<asp:FileUpload ID="fileBrowse" runat="server" />
<asp:Button ID="fileUpload" runat="server" Text="Upload Files" OnClick="fileUpload_Click" />

Code Behind:

protected void fileUpload_Click(object sender, EventArgs e)
{
    if (fileBrowse.PostedFile != null)
    {
        string siteUrl = @"http://myportal/teamsite";
        string docLibraryName = "Shared Documents";
        string folderPath = "2008/01";
        string fileName = Path.GetFileName(fileBrowse.PostedFile.FileName);
        byte[] fileBytes = GetFileBytes();
        Dictionary<string, string> columnValues = new Dictionary<string, string>();
        columnValues.Add("Document Type", "Financial");
        columnValues.Add("Author", "Mr. Burns");
        UploadFile(siteUrl, docLibraryName, folderPath, fileName, fileBytes, columnValues, true);
    }
}

public byte[] GetFileBytes()
{
    Stream stream = fileBrowse.PostedFile.InputStream;
    byte[] bytes = new byte[stream.Length];
    stream.Read(bytes, 0, (int)stream.Length);
    stream.Close();
    return bytes;
}

public void UploadFile(string siteUrl, string docLibraryName, string folderPath,
    string fileName, byte[] fileByteArray, Dictionary<string, string> columnValues, bool overWrite)
{
    try
    {
        string fileUrl = siteUrl.EnsureEndSlash() + docLibraryName.EnsureEndSlash() +
            folderPath.EnsureEndSlash() + fileName;
        string currentFolder = siteUrl.EnsureEndSlash() + docLibraryName.EnsureEndSlash();
        using (SPWeb web = new SPSite(siteUrl).OpenWeb())
        {
            web.AllowUnsafeUpdates = true;
            SPFolder folder = web.Folders[docLibraryName];
            if (folderPath != string.Empty)
            {
                foreach (string subFolder in folderPath.Split('/'))
                {
                    currentFolder = currentFolder.EnsureEndSlash() + subFolder;
                    if (!web.GetFolder(currentFolder).Exists)
                    {
                        folder.SubFolders.Add(subFolder);
                    }
                    folder = folder.SubFolders[subFolder];
                }
            }
            if (overWrite || !web.GetFile(fileUrl).Exists)
            {
                folder.Files.Add(fileName, fileByteArray, true);
            }
            else
            {
                folder.Files[fileName].CheckOut();
                folder.Files.Add(fileName, fileByteArray, true);
                folder.Files[fileName].CheckIn(String.Empty);
            }
            SPFile file = folder.Files[fileUrl];
            foreach (KeyValuePair<string, string> columnValue in columnValues)
            {
                file.Item.Properties[columnValue.Key] = columnValue.Value;
            }
            file.Item.Update();
        }
    }
    catch (Exception ex)
    {
        //TODO: Add Exception Handling
    }
}

Upload Multiple Files With ASP.NET

Out of the box, you can easily upload files using the ASP.NET FileUpload control. However there are times when the design calls for the ability to upload multiple files. In my example, I use a single FileUpload control along with a GridView for layout and some Session state to store the posted files. You can copy and paste the code below to accomplish this task.

Design

MultipleUpload

ASP.NET

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script language="javascript" type="text/javascript">
        function SetFileGridIndex(index)
        {
            var myControl = document.getElementById('<%= fileGridIndex.ClientID %>');
            myControl.value = index;
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div style="font-family: Verdana; font-size: small; text-align:center; width:100%;">
        <asp:GridView ID="fileGrid" AutoGenerateColumns="False" ShowFooter="true" BorderColor="Black"
            BorderWidth="1px" runat="server" OnRowCommand="fileGrid_RowCommand" GridLines="None"
            OnRowDeleting="fileGrid_RowDeleting">
            <Columns>
                <asp:BoundField DataField="Key" HeaderText="Files To Upload" />
                <asp:TemplateField ShowHeader="False">
                    <ItemTemplate>
                        <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False" CommandName="Delete"
                            Text="Delete" OnClientClick='<%# Eval("Key", "SetFileGridIndex(\"{0}\");") %>'></asp:LinkButton>
                    </ItemTemplate>
                    <FooterTemplate>
                        <asp:Button ID="Upload" runat="server" Text="Upload" CommandName="Upload" />
                    </FooterTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
        <asp:HiddenField ID="fileGridIndex" runat="server" />
        <div id="fileList" runat="server">
        </div>
        <asp:FileUpload ID="fileUpload" runat="server" />
        <asp:Button ID="AddFile" runat="server" Text="Add" OnClick="AddFile_Click" Height="20px" />
        <br />
    </div>
    </form>
</body>
</html>

Code Behind:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

public partial class _Default : System.Web.UI.Page
{
    private Dictionary<string, HttpPostedFile> postedFiles;
    public Dictionary<string, HttpPostedFile> PostedFiles
    {
        get
        {
            return ((Dictionary<string, HttpPostedFile>)Session["PostedFiles"]);
        }
        set
        {
            Session["PostedFiles"] = value;
            postedFiles = value;
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            Session["PostedFiles"] = new Dictionary<string, HttpPostedFile>();
        }
    }

    public void LoadData()
    {
        fileGrid.DataSource = PostedFiles;
        fileGrid.DataBind();
    }

    protected void AddFile_Click(object sender, EventArgs e)
    {
        if (!PostedFiles.ContainsKey(Path.GetFileName(fileUpload.PostedFile.FileName)))
        {
            PostedFiles.Add(Path.GetFileName(fileUpload.PostedFile.FileName), fileUpload.PostedFile);
            LoadData();
        }
    }

    protected void fileGrid_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        switch (e.CommandName)
        {
            case "Delete":
                string fileName = fileGridIndex.Value;
                PostedFiles.Remove(fileName);
                LoadData();
                break;
            case "Upload":
                foreach (HttpPostedFile postedFile in PostedFiles.Values)
                {
                    postedFile.SaveAs(@"c:\\temp\" + Path.GetFileName(postedFile.FileName));    //CHANGE ME
                }
                PostedFiles = new Dictionary<string, HttpPostedFile>();
                LoadData();
                break;
        }
    }
    protected void fileGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
    {

    }
}