Pages

Thursday 8 September 2011

Yield keyword in C Sharp

One of the cool and yet most unknown feature of the C# is the yield keyword. The yield keyword is used in an iterator block to provide a value to the enumerator object or to signal the end of the iteration. When used the expression is evaluated and returned as a value to the enumerator object. Note the expression has to be implicitebly convertible to yield type of the iterator. Here is an example

public static IEnumerable<int> GetIntCollectionFromString(string SomeString)
{
string[] val = SomeString.Split(' ');
int intVal;
foreach (string token in val)
{
if (int.TryParse(token, out intVal))
{
yield return intVal;
}
else
{
yield break;
}
}
}

Here since we are using the yield keyword the function will return the collection of intVal instead of the value of intVal. The statement will only return when the iteration of the loop is complete.

There are some limitation with the yield keywords.
Unsafe blocks are not allowed
Parameters to the method, operator, or accessor cannot be ref or out.

Wednesday 7 September 2011

How To Create an ASP.NET HTTP Handler by Using Visual C# .NET

Implement the Handler
Open Microsoft Visual Studio .NET. In Visual C# .NET, create a new Class Library project named MyHandler.
Set a reference to the System.Web.dll assembly.
Add the following directive to the class:
using System.Web;

Rename the class SyncHandler.cs, and then change the class definition to reflect this.
Implement the IHttpHandler interface. Your class definition should appear as follows:
public class SyncHandler : IHttpHandler

Implement the IsReusable property and the ProcessRequest method of the IHttpHandler interface. Because this is a synchronous handler, return False for the IsReusable property so that the handler is not pooled.
public bool IsReusable
{
get {return false;}
}

public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello from custom handler.");
}
Compile the project.


Deploy the Handler
Create a new directory named Handler under the C:\Inetpub\Wwwroot directory.
Create a subdirectory named Bin in the newly created Handler directory. The resultant path is C:\Inetpub\Wwwroot\Handler\Bin.
Copy MyHandler.dll from your project's Bin\Debug directory to the C:\Inetpub\Wwwroot\Handler\Bin directory.
Follow these steps to mark the new Handler directory as a Web application:
Open Internet Services Manager.
Right-click the Handler directory, and then click Properties.
On the Directory tab, click Create.
Follow these steps to create an application mapping for the handler. For this handler, create a mapping to the Aspnet_isapi.dll file for the *.sync extension. Whenever a .sync file is requested, the request is routed to ASP.NET, and ASP.NET executes the code in the handler.
Right-click on the Handler Web application, and then click Properties.
On the Directory tab, click Configuration.
Click Add to add a new mapping.
In the Executable text box, type the following path: Microsoft Windows 2000:
C:\WINNT\Microsoft.NET\Framework\<version#>\Aspnet_isapi.dll
Microsoft Windows XP:
C:\WINDOWS\Microsoft.NET\Framework\<version#>\Aspnet_isapi.dll
In the Extension text box, type .sync.
Make sure that the Check that file exists check box is cleared, and then click OK to close the Add/Edit Application Extension Mapping dialog box.
Click OK to close the Application Configuration and the Handler Properties dialog boxes.
Close Internet Services Manager.


Configure the System
In the C:\Inetpub\Wwwroot\Handler directory, create a new file named Web.config.
Add the following code to Web.config:
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.sync" type="MyHandler.SyncHandler, MyHandler" />
</httpHandlers>
</system.web>
</configuration>

In the verb="*" attribute, we instruct the handler to process a request that uses any verb (for example, POST, HEAD, GET, and so on). If you want this handler to process only the POST request, change this to verb="POST".

In the path="*.sync" attribute, we instruct the handler to process any incoming requests for files with the .sync extension.

In the type="MyHandler.SyncHandler, MyHandler" attribute, we instruct the handler that processes the request to implement in the MyHandler.SyncHandler namespace, and this class resides in the MyHandler assembly.


Test the Module
To test a handler, a page does not need to exist in the file system. For example, request the Default.sync file in the Handler Web application (http:///Handler/Default.sync). You should receive the following results:
Hello from custom handler.




Monday 5 September 2011

Organizing Data in Lists

Every application needs a flexible data model as a foundation. The user needs somewhere to store, deal with, and retrieve data. The data represents the natural and virtual objects managed in the application. SharePoint organizes data in lists and their derivatives, the libraries. Several objects assist with defining the list’s structure, such as field types and content types.

Lists and Their Elements

Metadata is a semantically enhanced description of the data to express the meaning of data. For example, adding additional information (such as author, date, and keywords) to a document stored in a document library enhances the ability of a search engine to find it and order the results. SharePoint’s list model is amazingly flexible. Moreover, it empowers ordinary users to create and manage their own data store—normally a function “for experts only.” After years of computer technology advancement, the administration of a data model is no longer rocket science. However, this does not mean that you—as a developer—cannot programmatically interact with the data model. The whole power of the list model and its associated parts is available through the object model. Whatever the end user may do (and most of what they can’t do) through the UI, you can accomplish with the object model.

Early on in your contact with SharePoint lists, you should have encountered the term content type. Though they are ignored by many, they are, as a matter of fact, a core concept.

CONTENT TYPE DEFINITION

A content type is a reusable collection of settings you want to apply to a certain category of content. Content types enable you to manage the metadata and behaviors of a document or item type in a centralized, reusable way.

Creating a List Programmatically

You can easily create a new list using the Add method of the SPListCollection type. The code shown next demonstrates this in a console application.

using (SPSite site = new SPSite("http://sharepointserve"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = null;
string listName = "Books";
// Check whether the list already exists
try
{
list = web.Lists[listName];
}
catch (ArgumentException)
{
}
if (list == null)
{
Guid listId = web.Lists.Add(listName, "All our books",
SPListTemplateType.GenericList);
list = web.Lists[listId];
list.OnQuickLaunch = true;
list.Update();
}
Console.WriteLine("Created list {0} with id {1}", list.Title, list.ID);
}
}

This code runs in a console application and creates a new generic (custom) list without any additional columns. Before creating a new list, you should check whether it already exists. You can either loop through all lists or try to access the new list and catch the exception thrown if it doesn’t yet exist. An alternative approach is to use a LINQ query:

bool exists = (from l in web.Lists.OfType()
where l.Title.Equals(listName)
select l).Count() > 0;

The list itself is not queryable and must be copied into a List element using the OfType operator. That means the list is copied completely and uses a significant amount of memory if you have many lists. However, from the perspective of clean code, the pattern seems smarter than a try/catch block.

Adding Fields to the List

Once the list exists, you can add fields. Fields represent the columns. Internally, a field is a type derived from SPField. SharePoint includes a number of predefined fields for specific types. The following example demonstrates adding several fields for scalar types:

list.Fields.Add("ISBN", SPFieldType.Text, true);
list.Fields.Add("LeadAuthor", SPFieldType.Text, true);
list.Fields.Add("Price", SPFieldType.Currency, false);
list.Update();

The SPFieldType enumeration contains all possible types. You can also define custom types and extend the list of field types. A field type is not only a scalar or complex value; it can also have a relationship to a custom field editor.

Changing Field Properties

Certain properties are not available through the UI. Even SharePoint Designer, with its advanced capabilities, eventually reaches its limits. The example in the next Listing shows how to enable an invisible field to appear in the dialogs again and remove the read-only flag.

using (SPSite site = new SPSite("http://sharepointserve"))
{
using (SPWeb web = site.OpenWeb())
{
SPList questions = web.Lists["Questions"];
SPField field = questions.Fields["Voters"];
field.Hidden = false;
field.ReadOnlyField = false;
field.Update();
}
}

The change affects only the SPField object—not the list. The code snippet assumes you have a Questions list with a Voters field that is hidden and read-only. The changes are assigned to the underlying content type if the column is defined there. There is no need to change the particular content type directly.

Adding Items to the List

Adding items is as straightforward as adding fields. However, the procedure is not yet type-safe because the column’s ID must be provided as a string or Guid. The field name you need to use here is the internal Name, not the DisplayName.

Using a string that clearly describes the field is recommended:

SPListItem item = list.Items.Add();
item["Title"] = "ASP.NET Extensibility";
item["ISBN"] = "978-1-4305-1983-5";
item["LeadAuthor"] = "Joerg Krause";
item["Price"] = 59.99;
item.Update();

An ArgumentException is thrown if a field name does not exist. Alternatively, you can use the field’s zero-based index or its Guid to identify the field. This is recommended if the index or Guid is returned from any previous operation, to avoid errors due to mistyping.

Working with Attachments

Each item in a SharePoint list has an associated SPAttachmentCollection array. The collection contains strings, which may seem odd, as you would expect SPFile objects instead. What you see is a direct representation of the underlying storage model. Attachments are part of the containing web. This means that all lists share all their attachments in one library. The names collection must be combined with the web’s URL to give the complete address to the file in the repository.

Fortunately, the SPAttachmentCollection has several useful direct methods, such as Add, Delete, and Recycle.

SPList list = web.Lists["MyList"];
foreach (string fileName in item.Attachments)
{
SPFile file = item.ParentList.ParentWeb.GetFile(
item.Attachments.UrlPrefix + fileName);
// Work with SPFile
}

Another usage scenario could look like this:

using (SPSite site = new SPSite("http://sharepointserve"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["WordDocuments"];
SPListItem item = list.Items[id];
if (item.Attachments.Count > 0)
{
SPFile attachment = item.ParentList.ParentWeb.GetFile(
item.Attachments.UrlPrefix + item.Attachments[0]);
Stream content = attachment.OpenBinaryStream();
using (Package p = Package.Open(content))
{
// handle as a package (just a suggestion)
}
}
}
}

This code assumes that you have a list, called WordDocuments. The current item is selected by using the variable id. The code checks whether one or more attachments are present, and if so, it reads the first attachment (index zero). The attachment is loaded into an SPFile object and read as a binary stream. We suggest you use the Package class to access files in the package format (such as XPS, DOCX, XSLX, and so forth). The Package class is defined in the namespace System.IO.Packaging—the containing assembly is WindowsBase.dll.

Item- and List-Level Event Receivers

To interact with lists and items, you can attach event receivers. With these your code is called when a specified condition is met in the attached list. The implementation uses the SPListItemEventReceiver base class—you simply override the methods you want to activate.

Synchronous vs. Asynchronous Events

SharePoint is rich with the events it exposes. We can categorize SharePoint events in two ways: by the “level” that fires the event (site, list, item) and by the type of the event (synchronous or asynchronous).

WHAT SYNCHRONOUS/ASYNCHRONOUS MEANS FOR EVENTS?

Synchronous events happen “before” the actual event. You have the HttpContext; you can display an error message in the browser, and if needed, you can cancel the event before it occurs.
Asynchronous events happen “after” the actual event. There is no HttpContext; you cannot directly show an error message, and you cannot cancel the event. On the other hand, you can handle what happens after the event is fired, including manipulating the object.
As examples of synchronous and asynchronous events, consider item-level events such as ItemAdding and ItemAdded. With ItemAdding, you can inspect the item, and, if necessary, cancel the addition and display an error message in the SharePoint page to the user. Inside the ItemAdded event, however, you know that the item is already in the SharePoint list, but you can start some post-add actions concerning that item.

Synchronous events are also called “before” events. They appear before the internal operation occurs. The event handler is also blocking the current flow. For this reason, we recommend you avoid time-consuming operations here.

Event Description

ItemAdding Occurs when a new item is added to its containing object
ItemAttachmentAdding Occurs when a user adds an attachment to an item
ItemAttachmentDeleting Occurs when a user removes an attachment from an item
ItemCheckingIn Occurs as a file is being checked in
ItemCheckingOut Occurs before an item is checked out
ItemDeleting Occurs before an existing item is completely deleted
ItemFileMoving Occurs when a file is being moved
ItemUncheckedOut Occurs before an item is being unchecked out
ItemUpdating Occurs when an existing item is changed, for example, when the user changes data in one or more fields
Asynchronous events are classified as “after” events. They are called asynchronously. Lengthy operations do not block the current thread. Because the internal operation is completed, you can safely manipulate the freshly added or changed data.
Event Description
ItemAdded Occurs after a new item has been added to its containing object
ItemAttachmentAdded Occurs after a user adds an attachment to an item
ItemAttachmentDeleted Occurs after a user removes an attachment from an item
ItemCheckedIn Occurs after an item is checked in
ItemCheckedOut Occurs after an item is checked out
ItemDeleted Occurs after an existing item is completely deleted
ItemFileMoved Occurs after a file is moved
ItemUncheckingOut Occurs after an item is unchecked out
ItemFileConverted Occurs after a file has been converted
ItemUpdated Occurs after an existing item is changed, for example, when the user changes data in one or more fields
List Events

List events (see below) occur for list operations that affect the whole list, such as schema changes. As for the list item events, you can choose synchronous and asynchronous events. The implementation uses the SPListEventReceiver base class—you simply override the appropriate methods.

Synchronous List-Level Events are shown next:

Event Description
FieldAdding Occurs when a field link is being added to a content type
FieldDeleting Occurs when a field is being removed from the list
FieldUpdating Occurs when a field link is being updated

Asynchronous List-Level Events are similar:

Event Description
FieldAdded Occurs after a field link is added
FieldDeleted Occurs after a field has been removed from the list
FieldUpdated Occurs after a field link has been updated

Developing and Deploying an Event Receiver

This section contains a step-by-step guide to developing event receivers and attaching them to the appropriate objects. In this example, we will create a simple receiver, which will handle two asynchronous events (ItemAdded and ItemUpdated) in all Document and Picture libraries at a SharePoint web site, and deploy it as a feature at the site level. The templates Visual Studio 2010 provides for this adds the required configuration files. The template comes with a wizard that asks for the particular event you want to create. Therefore, there is just one template for all possible events.



The wizard asks for the SharePoint site and whether the solution is a sandboxed or farm solution. Next you can select the type of event (List, List Item, and so on) and what specific event handler the wizard should create.

The wizard creates the package (solution), the feature that activates and deactivates the event handlers, and the event handler definition. An event handler definition consists of an Element.xml file that defines events, where they attach and the entry point of the assembly that contains the code:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Receivers ListTemplateId="104">
<Receiver>
<Name>EventReceiver1ItemAdded</Name>
<Type>ItemAdded</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>ItemEventReceiverProject.EventReceiver1.EventReceiver1</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
<Receiver>
<Name>EventReceiver1ItemUpdated</Name>
<Type>ItemUpdated</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>ItemEventReceiverProject.EventReceiver1.EventReceiver1</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
</Receivers>
</Elements>

The Event Handler Code

The code snippet created by the wizard (see Listing 4-14) attaches the two events to their event handlers. This is shown “as is.” No changes were made to fix namespaces or anything else.

using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;

namespace Apress.SP2010.EventReceiver1
{
/// <summary>
/// List Item Events
/// </summary>
public class EventReceiver1 : SPItemEventReceiver
{
/// <summary>
/// An item is being added.
/// </summary>
public override void ItemAdded(SPItemEventProperties properties)
{
base.ItemAdded(properties);
}

/// <summary>
/// An item is being updated.
/// </summary>
public override void ItemUpdated(SPItemEventProperties properties)
{
base.ItemUpdated(properties);
}

}
}

The receiver class must inherit from SPItemEventReceiver. To attach other events, you can override the corresponding methods in this class and extend the Elements.xml to connect the handler with the event source.

Document Libraries

Document libraries are lists that handle documents. Other than the attachments, which are stored in the containing web’s central attachment library, a document library stores its files directly. The programmatic access to libraries is similar to lists. In addition to list-related functions, you use streams, such as a MemoryStream, when saving or loading files.

Reading a File from the Shared Documents Library

Each site contains a library called Shared Documents. Code shown next explains how to retrieve a file as an SPFile object.

using (SPSite site = new SPSite("http://sharepointserve"))
{
using (SPWeb web = site.OpenWeb())
{
if (web.GetFolder("Shared Documents").Exists)
{
SPFolder folder = web.GetFolder("Shared Documents");
SPFile file = folder.Files["WordTemplate.docx"];
}
}
}

Adding a Folder to a Library

If you work with folders, your users might not be satisfied with the UI support. For greater capability, you can create a function that adds folders programmatically.

using (SPSite site = new SPSite("http://sharepointserve"))
{
using (SPWeb web = site.OpenWeb())
{
SPList lib = web.Lists["Shared Documents"];
SPFolder parent = lib.RootFolder;
SPFolder child = parent.SubFolders.Add("SubFolderName");
parent.Update();
}
}

This code adds a SubFolderName folder to the library’s root.

Saving Data to Libraries

To upload a file to a library, you use a similar approach (see Listing 4-21). The file must be available as a Stream or a byte array.

using (SPSite site = new SPSite("http://sharepointserve"))
{
using (SPWeb web = site.OpenWeb())
{
spweb.AllowUnsafeUpdates = true;

SPFolder folder = web.Folders[site.URL + "/Shared Documents/"];
byte[] content = null;
using (FileStream filestream = new FileStream(@"C:\Sample.docx",
System.IO.FileMode.Open))
{
content = new byte[(int) filestream.Length];
filestream.Read(content, 0, (int) filestream.Length);
filestream.Close();
}

SPFile file = folder.Files.Add("Sample.docx", content, true);
// do something usefil with file
}
}

You can add the file to a specific folder:

SPFile file = folder.SubFolders["SubFolder"].Files.Add("Sample.docx",
content,
true);

The content variable is the same as shown previously.

Document Sets

Documents sets are new in SharePoint Server 2010. Technically, these sets are another content type, with particular functionality. A document set is related to a document library and is similar to a folder. Like a folder, you can add multiple documents to one document set. While a folder can be opened, a document set is much more. Each document set can have a different set of content types. Creating a new document set means creating a set of different documents. Imagine you want to produce offers for customers. The offer can contain various Office documents, such as the quote, terms and conditions, a calculation sheet, a presentation with some company information, and more. All these can be assembled by a user with a single click.

Document sets have several advantages:
Each set has its own version history. It’s not necessary to track changes at the document level.

You can assign access rights on a per-set level.

You can assign and start workflows related to a set.

Users can download the whole document set as a ZIP file.

The home page related to a document set is a wiki page that can be edited easily.

Documents that are part of a document set can share their metadata.

Before you can access and use document sets programmatically, some preparatory steps are required. The document sets feature is available at the site collection level. To activate it, open the site settings and click “Site collection features.” On the next page, click the Activate button near the Document Sets feature entry.



A document set can now be created by adding a new content type, which inherits from the built-in content type Document Set.

Query Data Using CAML

You can use CAML to do the following:

Modify parameters to transport complex data
Define the body of SOAP messages to transport data using web services
Configure SharePoint for usage or deployment
Add specific behavior to features
SharePoint utilizes many XML files to define behavior and content. Most of these files use CAML as the dialect to express SharePoint-specific settings. In this section, we examine more closely the usage of CAML to query the data store. CAML is defined in several schemas. When you first attempt to understand CAML, it looks like an amalgam of many different dialects. However, it’s simply a collection of schemas that make CAML powerful enough for both data schema definition and data retrieval.

Using CAML to Retrieve Data

The Query schema is well documented, and numerous examples are available. However, the first time you try it, you’ll discover it’s not self-explanatory. All queries must be executed against a list. You need a reference to either an SPList or SPView object, which accepts a CAML query and can run it. The following examples demonstrate the bare bones of the technique.
The basic way to query data from SharePoint lists is to use queries constructed with CAML. The next code snippet shows a simple CAML query to fetch all items from a SharePoint list called Books whose Publisher is Apress .

public IEnumerable<SPListItem> GetBooksFromAPress()
{
SPList list = SPContext.Current.Web.Lists["Books"];
SPQuery query = new SPQuery();
query.Query = @"<Where>
<Eq>
<FieldRef Name='Publisher' />
<Value Type='Text'>Apress</Value>
</Eq>
</Where>";
IEnumerable<SPListItem> books = list.GetItems(query).OfType<SPListItem>();
return books;
}

In this example, the list is obtained from the current SPWeb instance. A new SPQuery object is instantiated to contain the query. The query is written as an XML string fragment.
With CAML, you can easily build your own custom queries, including conditions (Where) and logical operators (AND, OR) in XML format. Internally, SharePoint translates the CAML query into a SQL query to retrieve the data from the SharePoint content database.

How to Create a CAML Query

You can use LINQ to XML and the XElement type to create a query:

SPQuery queryl = new SPQuery();
queryl.Query = new XElement("Where",
new XElement("Eq",
new XElement("FieldRef", new XAttribute("Name", "Company")),
new XElement("Value", new XAttribute("Type", "Text")))) .ToString();

While this approach takes care of closing tags correctly and will always create valid XML, it does not seem easier. The CAML keywords are strings, and hence they can suffer from typing errors. You might consider using constants to predefine such keywords:

const string WHERE = "Where";
const string EQ = "Eq";
const string FIELDREF = "FieldRef";
const string VALUE = "Value";
const string NAME = "Name";
const string TYPE = "Type";
SPQuery queryc = new SPQuery();
queryc.Query = new XElement(WHERE,
new XElement(EQ,
new XElement(FIELDREF, new XAttribute(NAME, "Company")),
new XElement(VALUE, new XAttribute(TYPE, "Text"))))
.ToString();

That’s indeed safe and compact, but it’s still a manually built query. To overcome the manual construction of CAML queries, there are some useful implementations of CAML query builders that support creating a CAML query in a safe manner. To demonstrate this, check out an example using CAML.NET from John Holliday, which is a free project hosted on CodePlex (www.codeplex.com/camldotnet). Here is how to use it:

public IEnumerable<SPListItem> GetBooksFromAPress2()
{
SPList list = SPContext.Current.Web.Lists["Books"];
SPQuery query = new SPQuery();
query.Query = CAML.Where(
CAML.Eq(
CAML.FieldRef("Publisher"),
CAML.Value("APress")
)
);
IEnumerable<SPListItem> books = list.GetItems(query).OfType<SPListItem>();
return books;
}

If you need to use many manually constructed CAML queries, we strongly recommend using a CAML query builder such as CAML.NET. This is the least error-prone and most efficient method.

Joins and Projections

Joins between tables are well known in relational databases. The complexity and power of SharePoint lists overcome the need for frequent usage of joins. However, when working with views, you will find that it’s occasionally desirable to add the odd field from another list. Lookups are a good way to do this, but they require navigating from the parent list to the related list in order to view fields. If you want to create a view that spans several lists and show the results in just one list, you need to use joins. Direct joins are a new feature in SharePoint 2010. Projections define the fields that become part of the result set’s field list. This is comparable with the mapping of fields in the SELECT clause in SQL.

NOTE

Though this may look like a replacement for lookups, it does not eliminate the need for them. A join uses a field reference previously defined by a lookup. Thus, you have to create a lookup field first.
Regular lookups can be multilookups—meaning that you can form a many-to-many relationship. Using joins, this is not possible; you must use a single-value lookup.

Joins and projections have a direct representation in views and queries. This includes the corresponding properties—Joins and ProjectedField—of the SPQuery and SPView objects. While both objects use CAML, the XML snippets do not become part of a regular query, and they are not child elements of the element. Instead, the root elements are and , respectively. The XML is assigned to the properties of the SPQuery and SPView objects without their respective root elements

Joins

From SQL you may know that there are different kinds of joins. In CAML you can define two kinds of joins, INNER and LEFT, using the Type attribute:

<Joins>
<Join Type="INNER" ListAlias="Authors">
<Eq>
<FieldRef Name="FullName" RefType="Id" />
<FieldRef List="Authors" RefType="Id" />
</Eq>
</Join>
</Joins>

An inner join links two lists using an existing lookup. The result is a combination of both result sets. A left join (known in SQL as a LEFT OUTER JOIN) will return the same combination but includes the result of the parent list even if the related list does not have a matching entry. The common right join available in SQL is not supported by CAML.

Projected Fields in Views

The ProjectedFields element enables fields from the foreign lists to be viewed in the list view. The fields must also be identified in the ViewFields child element of the View element. The foreign lists are identified by their aliases, as defined in the Joins element. Again, if there is only one join, then there is no alias that is different from the list’s internal name. In this case, the reference in ViewFields uses the actual list name.

The ShowField attribute identifies which field from the foreign list is used in the view. This must be the internal name. The Type attribute always has the value Lookup—it does not indicate the data type of the field. The source type of a projected field is limited to a number of simple types, such as Counter, Currency, Integer, and Text.

Using Joins and Projected Fields

The following example shows how to use joins and projections. It is a console application that uses two lists: Books and Authors. The Books list relates to Authors through a field called LeadAuthor. The Authors list derives from Contacts and has an additional field called FullName.
The application (Listing 4-31) retrieves data from these two lists.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Xml;

namespace Apress.SP2010.CAMLJoins
{
class Program
{
static void Main(string[] args)
{
using (SPSite site = new SPSite("http://sharepointserve"))
{
using (SPWeb web = site.OpenWeb())
{
SPList bookList = web.Lists["Books"];
SPList authList = web.Lists["Authors"];
SPField la = bookList.Fields["LeadAuthor"];
SPField fa = authList.Fields["Full Name"];
string join = @"<join Type='LEFT' ListAlias='Authors'>
<eq>
<fieldref Name='" + la.InternalName
+ @"' RefType='ID' />
<fieldref List='Authors' Name='ID' />
</Eq>
</Join>";
string projField = @"<field Name='Fullname' Type='Lookup'
List='Authors'
ShowField='" + fa.InternalName
+ "' />";
SPQuery query = new SPQuery();
query.Query = "";
query.Joins = join;
query.ProjectedFields = projField;
query.ViewFields = @"<fieldref Name='Fullname' />
<fieldref Name='Title' />";
SPListItemCollection items = bookList.GetItems(query);
foreach (SPListItem item in items)
{
Console.WriteLine("{0} has these lead authors: ",
item.Title);
if (item["Fullname"] == null)
{
Console.WriteLine(" no authors assigned");
continue;
}
SPFieldLookupValue sc =
new SPFieldLookupValue(item["Fullname"].ToString());
Console.WriteLine(" -{0}", sc.LookupValue);
}
}
}
Console.ReadLine();
}
}
}

The join is from the parent list Books to the child list Authors. This is defined by List='Authors' in the join’s second FieldRef element. The ListAlias attribute must echo this, as it is the only join. The first FieldRef refers to the parent list’s lookup field, using the internal name. It’s read from the SPField object that has a property InternalName.

The variable pfld stores the projected fields. The declaration contains one field, FullName, from the Authors list. ShowField is also defined using the internal name. The name defined in the Name attribute is used in the ViewField definition. The Title field is also added to the result set. The XML snippets are assigned to the appropriate properties of SPQuery.

The GetItems method executes the query. Because item["Fullname"] returns a lookup, the value it returns is in the form of “#1;Joerg Krause.” To process it, we create an SPFieldLookupValue object, passing the data as a string into the constructor to re-create the lookup value. From this object the LookupValue returns the full name.

Popular Posts