SharePoint 4 Developers

Additional reference guide in .NET / SharePoint Development

Creating Document Sets Programmatically

In this post I will show how to create document sets programmatically, by exploring the assembly Microsoft.Office.DocumentManagement.dll, available at the directory \14\ISAPI.

Hi folks,

I have been requested to create lots of document sets in replacement of folders recently. It is easy to create document sets manually, but when you need to create lots of them, the best solution is to do it programmatically.

Document sets are more than simple folders. They hold metadata, which is changing the way users are working and they are taking advantage of it.

As a quick note, in this post I will show how to create document sets programmatically, by exploring the assembly Microsoft.Office.DocumentManagement.dll, available at the directory \14\ISAPI\.

The Solution

Basically this solution shows how to create a document set and add it to a Document Library.

Note: 2 content types were created manually for demonstration purposes: Word Template and Excel Spreadsheet Template.

Check this out:

Code Snippet
  1. static void Main(string[] args)
  2. {
  3.     var url = ConfigurationManager.AppSettings["Url"].ToString();
  4.     var sub = ConfigurationManager.AppSettings["Sub"].ToString();
  5.     var library = ConfigurationManager.AppSettings["Library"].ToString();
  6.  
  7.     using (SPSite site = new SPSite(url))
  8.     {
  9.         using (SPWeb web = (sub == "*") ? site.RootWeb : site.OpenWeb(sub))
  10.         {
  11.             string docsetName = "Document Set Test";
  12.             SPContentType ctype = null;
  13.  
  14.             if (web.ContentTypes[docsetName] == null)
  15.             {
  16.                 // Creating the document set (content type)
  17.                 ctype = new SPContentType(web.ContentTypes["Document Set"], web.ContentTypes, "Document Set Test");
  18.                 ctype.FieldLinks.Add(new SPFieldLink(web.Fields["Author"]));
  19.                 ctype.Group = "Test Content Types";
  20.                 web.ContentTypes.Add(ctype);
  21.  
  22.                 // Getting the document set (content type)
  23.                 DocumentSetTemplate docsetTemplate = DocumentSetTemplate.GetDocumentSetTemplate(ctype);
  24.  
  25.                 // Setting the content types
  26.                 docsetTemplate.AllowedContentTypes.Remove(web.ContentTypes["Document"].Id);
  27.                 docsetTemplate.AllowedContentTypes.Add(web.ContentTypes["Word Template"].Id);
  28.                 docsetTemplate.AllowedContentTypes.Add(web.ContentTypes["Excel Spreadsheet Template"].Id);
  29.  
  30.                 // Sharing fields
  31.                 docsetTemplate.SharedFields.Add(web.Fields["Author"]);
  32.  
  33.                 // Displaying fields
  34.                 docsetTemplate.WelcomePageFields.Add(web.Fields["Author"]);
  35.  
  36.                 // Adding default document
  37.                 FileStream wordFile = File.OpenRead(Path.GetFullPath(@"DocumentSet\Default.dotx"));
  38.                 byte[] binWordFile = new byte[wordFile.Length];
  39.                 wordFile.Read(binWordFile, 0, binWordFile.Length);
  40.  
  41.                 docsetTemplate.DefaultDocuments.Add("Default.dotx", web.ContentTypes["Word Template"].Id, binWordFile);
  42.  
  43.                 // Updating the document set (content type),    
  44.                 docsetTemplate.Update(true);
  45.                 ctype.Update();
  46.             }
  47.  
  48.             ctype = ctype jQuery15207137137458194047_1344344305332 web.ContentTypes[docsetName];
  49.  
  50.             if (web.Lists.TryGetList(library) == null)
  51.             {
  52.                 // Creating document library
  53.                 Guid libraryGuid = web.Lists.Add(library, "", SPListTemplateType.DocumentLibrary);
  54.                 SPDocumentLibrary list = (SPDocumentLibrary)web.Lists[libraryGuid];
  55.  
  56.                 // Setting properties
  57.                 list.OnQuickLaunch = true;
  58.                 list.ContentTypesEnabled = true;
  59.                 list.EnableFolderCreation = false;
  60.  
  61.                 // Defining content types
  62.                 list.ContentTypes.Delete(list.ContentTypes["Document"].Id);
  63.                 list.ContentTypes.Add(ctype);
  64.                 list.Update();
  65.  
  66.                 System.Collections.Hashtable properties = new System.Collections.Hashtable();
  67.                 properties.Add("DocumentSetDescription", "Just an example"); //InternalName
  68.                 properties.Add("_Author", "MM"); //InternalName
  69.  
  70.                 // Creating the document set
  71.                 DocumentSet.Create(list.RootFolder, "DocSet1", list.ContentTypes.BestMatch(ctype.Id), properties, true);
  72.             }
  73.         }
  74.     }
  75. }

Note: A Document Library is created dynamically for demonstration purposes.

Download the solution here.

I hope it helps. Smile

References:
http://technet.microsoft.com/en-us/library/ff603637.aspx
http://office.microsoft.com/en-us/sharepoint-server-help/CH010372625.aspx
http://msdn.microsoft.com/en-us/library/ee574540.aspx

Cheers,

Marcel Medina

Click here to read the same content in Portuguese.

Programmatically Setting Conditional Formatting

Learn how to how to apply conditional formatting to views manually and programmatically by using SharePoint Designer 2010 and Visual Studio 2010 respectively.

Hi folks,

Today I want to talk about conditional formatting, which allows you to apply HTML styles to views depending on the criteria specified. This is not something new in SharePoint, in MOSS 2007 this already existed.

In this post I am going to demonstrate how to apply conditional formatting manually and programmatically, so stay tuned!

Conditional formatting in SharePoint Designer 2010

Let’s take the Grades list below as an example. Basically the idea is to implement a range of colours against the column Grade that differentiates grades. Based on the grade these colours will vary.

1_680
Figure 1 – Grades List

By using the SharePoint Designer 2010, this customisation is easy. Go to the Grades list in SharePoint Designer 2010 and open up the view called “All Items”, according to the Figure 2:

2_680
Figure 2 – All Items view

Interestingly the view can be visualised in both ways (Design and Code), which allows an easy customisation. By following the steps according to the Figure 3 the conditional formatting will be applied against the column Grade.

3_680
Figure 3 – Conditional Formatting in SharePoint 2010

Immediately after the step 3 above, you need to create a condition criteria that will do the task we need by applying a style in the Grades List.

4
Figure 4 – Condition Criteria

According to the condition criteria above, set the background colour to be Green.

Note: Different colours need to be applied against different condition criteria.

5
Figure 5 – Background colour

After configuring all the condition criteria, the conditional formatting can be visualised in different colours, according to the Figure 6:

6_680
Figure 6 – Conditional Formatting colours

The conditional formatting bar is very clear, and summarises the criteria you have specified. It allows you to configure the styles at any time. Pretty handy!

In the end you will get your the Grades List set according to the Figure 7:

7_680
Figure 7 – Conditional Formatting applied to the Grades List

Conditional formatting in Visual Studio 2010

So far you have seen how to set conditional formatting manually. Now you will see how to set this up programmatically!

First of all you need to identify the Xsl section (from Figure 6), copy all the content from this section to a xsl file, according to the code below:

Code Snippet
  1. <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" xmlns:o="urn:schemas-microsoft-com:office:office">
  2.   <xsl:include href="/_layouts/xsl/main.xsl"/>
  3.   <xsl:include href="/_layouts/xsl/internal.xsl"/>
  4.   <xsl:param name="AllRows" select="/dsQueryResponse/Rows/Row[$EntityName = '' or (position() &gt;= $FirstRow and position() &lt;= $LastRow)]"/>
  5.   <xsl:param name="dvt_apos">&apos;</xsl:param>
  6.   <xsl:template name="FieldRef_printTableCell_EcbAllowed.Grade" match="FieldRef[@Name='Grade']" mode="printTableCellEcbAllowed" ddwrt:dvt_mode="body" ddwrt:ghost="" xmlns:ddwrt2="urn:frontpage:internal">
  7.     <xsl:param name="thisNode" select="."/>
  8.     <xsl:param name="class" />
  9.     <td>
  10.       <xsl:attribute name="style">
  11.         <xsl:if test="normalize-space($thisNode/@Grade) = 'A'" ddwrt:cf_explicit="1" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">background-color: #009900;</xsl:if>
  12.         <xsl:if test="normalize-space($thisNode/@Grade) = 'B'" ddwrt:cf_explicit="1" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">background-color: #66FF33;</xsl:if>
  13.         <xsl:if test="normalize-space($thisNode/@Grade) = 'C'" ddwrt:cf_explicit="1" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">background-color: #FFFF00;</xsl:if>
  14.         <xsl:if test="normalize-space($thisNode/@Grade) = 'D'" ddwrt:cf_explicit="1" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">background-color: #FF9900;</xsl:if>
  15.         <xsl:if test="normalize-space($thisNode/@Grade) = 'E'" ddwrt:cf_explicit="1" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">background-color: #FF0000;</xsl:if>
  16.       </xsl:attribute>
  17.  
  18.       <xsl:if test="@ClassInfo='Menu' or @ListItemMenu='TRUE'">
  19.         <xsl:attribute name="height">100%</xsl:attribute>
  20.         <xsl:attribute name="onmouseover">OnChildItem(this)</xsl:attribute>
  21.       </xsl:if>
  22.       <xsl:attribute name="class">
  23.         <xsl:call-template name="getTDClassValue">
  24.           <xsl:with-param name="class" select="$class" />
  25.           <xsl:with-param name="Type" select="@Type"/>
  26.           <xsl:with-param name="ClassInfo" select="@ClassInfo"/>
  27.         </xsl:call-template>
  28.       </xsl:attribute>
  29.       <xsl:apply-templates select="." mode="PrintFieldWithECB">
  30.         <xsl:with-param name="thisNode" select="$thisNode"/>
  31.       </xsl:apply-templates>
  32.     </td>
  33.   </xsl:template>
  34. </xsl:stylesheet>

Note: The SharePoint Designer 2010 created a subsection called xsl:attribute to store the conditional formatting you have specified (Lines 10 to 16).

In this example I am creating a feature that is going to deploy the Grades list. So check the structure of the solution for you to have an idea:

9
Figure 8 – Conditional Formatting solution

Note: The xsl files in SharePoint 2010 must be deployed under the directory Layouts\Xsl.

To deploy the Grades list I have coded the FeatureActivated method (available in the Event Receiver of the feature):

Code Snippet
  1. public override void FeatureActivated(SPFeatureReceiverProperties properties)
  2. {
  3.     SPWeb web = (SPWeb)properties.Feature.Parent;
  4.  
  5.     string grades = "Grades";
  6.  
  7.     if (web.Lists.TryGetList(grades) == null)
  8.     {
  9.         // Creating list
  10.         Guid listGuid = web.Lists.Add(grades, "", SPListTemplateType.GenericList);
  11.         SPList list = web.Lists[listGuid];
  12.         list.OnQuickLaunch = true;
  13.  
  14.         // Configuring fields
  15.         SPField title = list.Fields["Title"];
  16.         title.Title = "Name";
  17.         title.Update(true);
  18.  
  19.         list.Fields.Add("Grade", SPFieldType.Text, true);
  20.  
  21.         // Updating view
  22.         SPView mainView = list.Views[0];
  23.         mainView.ViewFields.DeleteAll();
  24.         mainView.ViewFields.Add("Attachments");
  25.         mainView.ViewFields.Add("LinkTitle");
  26.         mainView.ViewFields.Add("Grade");
  27.         mainView.XslLink = "ConditionalFormattingXsl/grades.xsl";
  28.         mainView.Update();
  29.  
  30.         // Saving changes
  31.         list.Update();
  32.     }
  33. }

Note: Pay attention to the line 27. That’s what I am talking about! Beautiful.

Once you have deployed the solution, activate it and you will get the Grades list created with the Conditional Formatting applied automatically.

8_680
Figure 9 – Feature created

Download the solution here.

To make things easier, always use SharePoint Designer to set up conditional formatting on your views. Then copy the Xsl section and paste to a xsl file. It does the trick.

I hope it helps.

References:
http://stefan-stanev-sharepoint-blog.blogspot.com/2010/08/xsltlistviewwebpart-several-xslt-tips.html
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spview.xsllink.aspx
http://office.microsoft.com/en-us/sharepoint-designer-help/apply-conditional-formatting-to-a-data-view-HA010099624.aspx

Cheers,

Marcel Medina

Click here to read the same content in Portuguese.

Business Connectivity Services – Part V

Take advantage of the interface IContextProperty to get the BDC Connection details. Learn how to create ECTs via Assembly .NET in SharePoint 2010 through Business Connectivity Services.

Hi folks,

This is the one more article of the BCS series. Learn how to create ECTs via Assembly .NET in SharePoint 2010 through Business Connectivity Services!

This approach will take advantage of the interface IContextProperty to get the BDC Connection details set against the BDC Model.

Enjoy it!

Creating ECTs via Assembly .NET

Creating ECTs via Assembly .NET just requires VS2010. The SPD2010 will not be used.

This approach can be applied in environments that:

  • Need to create business rules, which can be developed against the BDC model;
  • Use databases other than SQL Server, which can be implemented in the data access tier;
  • Require the deployment of WSP packages to activate features, which is automatically generated by VS2010;

If you have this scenario, this implementation has a higher level of difficulty by the level of details in creating the assembly .NET, which is displayed in the next sections of this article.

Working with Visual Studio 2010

The template called Business Data Connectivity Model will be used in this article. Start by opening up VS2010 and create a new project according to the Figure 1:

Solution

Figure 1 - Creating a new project and solution

Immediately after that a Wizard helps you in setting up the project. The URL needs to be validated for debugging purposes, and the solution will be created as a Farm Solution, according to the Figure 2:

1part1

Figure 2 – Wizard

Note: Once the project is created, delete the classes called Entity1 and Entity1Service. Don’t forget to delete the entity created which is available in the BDC Designer, because new classes will be generated in a new model.

Data Access Layer

Due to the fact the feature will be created as a package WSP, I preferred to provide data access in the same project. Some objects need to be added to manipulate the database, and for demonstration purposes I am using LINQ to SQL because it is simpler to implement.
Add the LINQ to SQL object to the project and name it Dev.dbml, then create a new connection through the Server Explorer that uses Windows Authentication, open the database and drag the table Contact (Part II) according to the Figure 3:

LINQtoSQL

Figure 3 - Adding table Contact

Before manipulating data of the Contact object, a class that contains CRUD methods needs to be created. So create a class called ContactManager and add the code below, whose comments are well documented:

Code Snippet
  1. public class ContactManager
  2. {
  3.     /// <summary>
  4.     /// Gets all the Contacts
  5.     /// </summary>
  6.     /// <returns>Contacts Array</returns>
  7.     public Contact[] GetContacts()
  8.     {
  9.         var contacts = new List<Contact>();
  10.  
  11.         using (DevDataContext dev = new DevDataContext())
  12.         {
  13.             contacts = (from cont in dev.Contacts
  14.                         select cont).ToList();
  15.         }
  16.         return contacts.ToArray();
  17.     }
  18.  
  19.     /// <summary>
  20.     /// Gets a specific Contact
  21.     /// </summary>
  22.     /// <param name="contactId">Contact Id</param>
  23.     /// <returns>Returns the Contact</returns>
  24.     public Contact GetContactById(int contactId)
  25.     {
  26.         var contact = new Contact();
  27.  
  28.         using (DevDataContext dev = new DevDataContext())
  29.         {
  30.             contact = (from cont in dev.Contacts
  31.                         where cont.ContactID == contactId
  32.                         select cont).First();
  33.         }
  34.         return contact;
  35.     }
  36.  
  37.     /// <summary>
  38.     /// Updates a specific Contact
  39.     /// </summary>
  40.     /// <param name="contact">Contact to be updated</param>
  41.     public void UpdateContact(Contact contact)
  42.     {
  43.         var contactDB = new Contact();
  44.  
  45.         using (DevDataContext dev = new DevDataContext())
  46.         {
  47.             contactDB = (from cont in dev.Contacts
  48.                             where cont.ContactID == contact.ContactID
  49.                             select cont).First();
  50.  
  51.             // Alters the object
  52.             contactDB.Address = contact.Address;
  53.             contactDB.City = contact.City;
  54.             contactDB.CompanyName = contact.CompanyName;
  55.             contactDB.ContactName = contact.ContactName;
  56.             contactDB.ContactTitle = contact.ContactTitle;
  57.             contactDB.Country = contact.Country;
  58.             contactDB.Email = contact.Email;
  59.             contactDB.Fax = contact.Fax;
  60.             contactDB.Phone = contact.Phone;
  61.             contactDB.PostalCode = contact.PostalCode;
  62.             contactDB.Region = contact.Region;
  63.  
  64.             // Submitting the changes
  65.             dev.Refresh(System.Data.Linq.RefreshMode.KeepChanges, contactDB);
  66.             dev.SubmitChanges();
  67.         }
  68.     }
  69.  
  70.     /// <summary>
  71.     /// Adds a Contact
  72.     /// </summary>
  73.     /// <param name="contact">New Contact</param>
  74.     public void AddContact(Contact contact)
  75.     {
  76.         using (DevDataContext dev = new DevDataContext())
  77.         {
  78.             dev.Contacts.InsertOnSubmit(contact);
  79.             dev.SubmitChanges();
  80.         }
  81.     }
  82.  
  83.     /// <summary>
  84.     /// Deletes a Contacts
  85.     /// </summary>
  86.     /// <param name="contactId">Contact Id</param>
  87.     public void DeleteContact(int contactId)
  88.     {
  89.         using (DevDataContext dev = new DevDataContext())
  90.         {
  91.             var contact = (from cont in dev.Contacts
  92.                             where cont.ContactID == contactId
  93.                             select cont).First();
  94.             dev.Contacts.DeleteOnSubmit(contact);
  95.             dev.SubmitChanges();
  96.         }
  97.     }
  98. }

Note: This was already explained when creating ECTs via Web Services in part II, however as this is important in this demo, the same code was reused.

Create a new partial class called DevDataContext, as we already have this class automatically generated by Visual Studio. Create a new constructor, according to the code below:

Code Snippet
  1. public partial class DevDataContext
  2.     {
  3.         public DevDataContext() :
  4.             base(BdcModel1.ContactService.GetConnection(), mappingSource)
  5.         {
  6.             OnCreated();
  7.         }
  8.     }

Note: This new constructor contains a call to the method GetConnection(). It belongs to the ContactService class and returns the connection string, which is obtained directly from the BDC Metadata Model (we’ll check it soon).

BDC Model

By using the new BDC Panels in Visual Studio the Metadata Model will be created. It needs to be created based on the Data Access layer, as we will work with the Contact entity. Do this by using the BDC Designer Panel, according to the Figure 4:

1and2Figure 4 - Contact entity added

As the Contact entity was created, now it must be configured. In order to do that add an Identifier called ContactID and the following Methods: GetConnection, ReadItem, ReadList, Delete, Update and Create. The context menu makes things easier, according to the Figure 5:

2

Figure 5 - Identifier and Methods added

To add and configure methods, the most convenient way is the utilisation of the BDC Method Details Panel (as this tool is going to generate XML automatically) according to the Figure 6:

3

Figure 6 - BDC Method Details Panel in action

 

The Table 1 below shows a summary of the configuration started in the Figure 6. It contains all the methods and signatures:

Option Method Parameters Direction Type Descriptor
Create Creator Method Create returnContact Return ReturnContact
    newContact In NewContact
Create Deleter Method Delete contactID In ContactID
Create Finder Method ReadList contactList Return ContactList
Create Specific Finder Method ReadItem contact Return Contact
    contactID In ContactID
Create Updater Method Update contact In Contact
Create Blank Method GetConnection parameter Return connection
Table 1 - Automatic method generation of the Contact entity

Note: A Blank Method will create a method called Method, which needs to be renamed. In this example call it GetConnection.

Before configuring the methods, make sure that the ContactID identifier type is System.Int32. Take the ReadItem method as the first one to be configured and click over the parameter (aka type descriptor) Contact, so we can select its type according to the Figure 7:

bdcmethoddetails

Figure 7 - TypeDescriptor type

The next step is to include new Type Descriptors to the Type Descriptor called Contact through BDC Explorer Panel, which basically represent its attributes. The Figure 9 displays how to add Type Descriptors and the Table 2 reveals the Type Descriptors to be added.

6part1Figure 8 - Adding TypeDescriptors to Contact

 

Type Descriptor Type
Address System.String
City System.String
CompanyName System.String
ContactID System.Int32
ContactName System.String
ContactTitle System.String
Country System.String
Email System.String
Fax System.String
Phone System.String
PostalCode System.String
Region System.String
Table 2 - Types of Type Descriptors

Once created, these Type Descriptors will be automatically reused when creating other methods. The code below shows the XML automatically generated of the Contact entity after configuring of all methods. This was extracted from the file called BdcModel1.bdcm:

Code Snippet
  1. <Entity Name="Contact" Namespace="ContactServices.BdcModel.BdcModel1" Version="1.0.0.64">
  2.   <Properties>
  3.     <Property Name="Class" Type="System.String">ContactServices.BdcModel.BdcModel1.ContactService, BdcModel1</Property>
  4.   </Properties>
  5.   <Identifiers>
  6.     <Identifier Name="ContactID" TypeName="System.Int32" />
  7.   </Identifiers>
  8.   <Methods>
  9.     <Method Name="ReadItem">
  10.       <Parameters>
  11.         <Parameter Name="contact" Direction="Return">
  12.           <TypeDescriptor Name="Contact" TypeName="ContactServices.BdcModel.Contact, BdcModel1" IsCollection="false">
  13.             <TypeDescriptors>
  14.               <TypeDescriptor Name="Address" TypeName="System.String" />
  15.               <TypeDescriptor Name="City" TypeName="System.String" />
  16.               <TypeDescriptor Name="CompanyName" TypeName="System.String" />
  17.               <TypeDescriptor Name="ContactID" TypeName="System.Int32" IsCollection="false" IdentifierEntityName="Contact" IdentifierEntityNamespace="ContactServices.BdcModel.BdcModel1" IdentifierName="ContactID" />
  18.               <TypeDescriptor Name="ContactName" TypeName="System.String" />
  19.               <TypeDescriptor Name="ContactTitle" TypeName="System.String" />
  20.               <TypeDescriptor Name="Country" TypeName="System.String" />
  21.               <TypeDescriptor Name="Email" TypeName="System.String" />
  22.               <TypeDescriptor Name="Fax" TypeName="System.String" />
  23.               <TypeDescriptor Name="Phone" TypeName="System.String" />
  24.               <TypeDescriptor Name="PostalCode" TypeName="System.String" />
  25.               <TypeDescriptor Name="Region" TypeName="System.String" />
  26.             </TypeDescriptors>
  27.           </TypeDescriptor></Parameter>
  28.         <Parameter Name="contactID" Direction="In">
  29.           <TypeDescriptor Name="ContactID" TypeName="System.Int32" IdentifierEntityName="Contact" IdentifierEntityNamespace="ContactServices.BdcModel.BdcModel1" IdentifierName="ContactID" /></Parameter>
  30.       </Parameters>
  31.       <MethodInstances>
  32.         <MethodInstance Name="ReadItem" Type="SpecificFinder" ReturnParameterName="contact" ReturnTypeDescriptorPath="Contact" />
  33.       </MethodInstances></Method>
  34.     <Method Name="ReadList">
  35.       <Parameters>
  36.         <Parameter Name="contactList" Direction="Return">
  37.           <TypeDescriptor Name="ContactList" TypeName="System.Collections.Generic.IEnumerable`1[[ContactServices.BdcModel.Contact, BdcModel1]]" IsCollection="true">
  38.             <TypeDescriptors>
  39.               <TypeDescriptor Name="Contact" IsCollection="false" TypeName="ContactServices.BdcModel.Contact, BdcModel1">
  40.                 <TypeDescriptors>
  41.                   <TypeDescriptor Name="Address" TypeName="System.String" />
  42.                   <TypeDescriptor Name="City" TypeName="System.String" />
  43.                   <TypeDescriptor Name="CompanyName" TypeName="System.String" />
  44.                   <TypeDescriptor Name="ContactID" IdentifierEntityNamespace="ContactServices.BdcModel.BdcModel1" IdentifierEntityName="Contact" IdentifierName="ContactID" IsCollection="false" TypeName="System.Int32" />
  45.                   <TypeDescriptor Name="ContactName" TypeName="System.String" />
  46.                   <TypeDescriptor Name="ContactTitle" TypeName="System.String" />
  47.                   <TypeDescriptor Name="Country" TypeName="System.String" />
  48.                   <TypeDescriptor Name="Email" TypeName="System.String" />
  49.                   <TypeDescriptor Name="Fax" TypeName="System.String" />
  50.                   <TypeDescriptor Name="Phone" TypeName="System.String" />
  51.                   <TypeDescriptor Name="PostalCode" TypeName="System.String" />
  52.                   <TypeDescriptor Name="Region" TypeName="System.String" /></TypeDescriptors></TypeDescriptor></TypeDescriptors></TypeDescriptor></Parameter>
  53.       </Parameters>
  54.       <MethodInstances>
  55.         <MethodInstance Name="ReadList" Type="Finder" ReturnParameterName="contactList" ReturnTypeDescriptorPath="ContactList" />
  56.       </MethodInstances></Method>
  57.     <Method Name="Create">
  58.       <Parameters>
  59.         <Parameter Name="returnContact" Direction="Return">
  60.           <TypeDescriptor Name="ReturnContact" IsCollection="false" TypeName="ContactServices.BdcModel.Contact, BdcModel1">
  61.             <TypeDescriptors>
  62.               <TypeDescriptor Name="Address" TypeName="System.String" />
  63.               <TypeDescriptor Name="City" TypeName="System.String" />
  64.               <TypeDescriptor Name="CompanyName" TypeName="System.String" />
  65.               <TypeDescriptor Name="ContactID" IdentifierEntityNamespace="ContactServices.BdcModel.BdcModel1" IdentifierEntityName="Contact" IdentifierName="ContactID" IsCollection="false" TypeName="System.Int32" />
  66.               <TypeDescriptor Name="ContactName" TypeName="System.String" />
  67.               <TypeDescriptor Name="ContactTitle" TypeName="System.String" />
  68.               <TypeDescriptor Name="Country" TypeName="System.String" />
  69.               <TypeDescriptor Name="Email" TypeName="System.String" />
  70.               <TypeDescriptor Name="Fax" TypeName="System.String" />
  71.               <TypeDescriptor Name="Phone" TypeName="System.String" />
  72.               <TypeDescriptor Name="PostalCode" TypeName="System.String" />
  73.               <TypeDescriptor Name="Region" TypeName="System.String" /></TypeDescriptors></TypeDescriptor></Parameter>
  74.         <Parameter Name="newContact" Direction="In">
  75.           <TypeDescriptor Name="NewContact" IsCollection="false" TypeName="ContactServices.BdcModel.Contact, BdcModel1">
  76.             <TypeDescriptors>
  77.               <TypeDescriptor Name="Address" TypeName="System.String" CreatorField="true" />
  78.               <TypeDescriptor Name="City" TypeName="System.String" CreatorField="true" />
  79.               <TypeDescriptor Name="CompanyName" TypeName="System.String" CreatorField="true" />
  80.               <TypeDescriptor Name="ContactID" IdentifierEntityNamespace="ContactServices.BdcModel.BdcModel1" IdentifierEntityName="Contact" IdentifierName="ContactID" IsCollection="false" TypeName="System.Int32" CreatorField="true" />
  81.               <TypeDescriptor Name="ContactName" TypeName="System.String" CreatorField="true" />
  82.               <TypeDescriptor Name="ContactTitle" TypeName="System.String" CreatorField="true" />
  83.               <TypeDescriptor Name="Country" TypeName="System.String" CreatorField="true" />
  84.               <TypeDescriptor Name="Email" TypeName="System.String" CreatorField="true" />
  85.               <TypeDescriptor Name="Fax" TypeName="System.String" CreatorField="true" />
  86.               <TypeDescriptor Name="Phone" TypeName="System.String" CreatorField="true" />
  87.               <TypeDescriptor Name="PostalCode" TypeName="System.String" CreatorField="true" />
  88.               <TypeDescriptor Name="Region" TypeName="System.String" CreatorField="true" /></TypeDescriptors></TypeDescriptor></Parameter>
  89.       </Parameters>
  90.       <MethodInstances>
  91.         <MethodInstance Name="Create" Type="Creator" ReturnParameterName="returnContact" ReturnTypeDescriptorPath="ReturnContact" />
  92.       </MethodInstances></Method>
  93.     <Method Name="Update">
  94.       <Parameters>
  95.         <Parameter Name="contact" Direction="In">
  96.           <TypeDescriptor Name="Contact" IsCollection="false" TypeName="ContactServices.BdcModel.Contact, BdcModel1">
  97.             <TypeDescriptors>
  98.               <TypeDescriptor Name="Address" TypeName="System.String" UpdaterField="true" />
  99.               <TypeDescriptor Name="City" TypeName="System.String" UpdaterField="true" />
  100.               <TypeDescriptor Name="CompanyName" TypeName="System.String" UpdaterField="true" />
  101.               <TypeDescriptor Name="ContactID" IdentifierEntityNamespace="ContactServices.BdcModel.BdcModel1" IdentifierEntityName="Contact" IdentifierName="ContactID" IsCollection="false" TypeName="System.Int32" UpdaterField="true" />
  102.               <TypeDescriptor Name="ContactName" TypeName="System.String" UpdaterField="true" />
  103.               <TypeDescriptor Name="ContactTitle" TypeName="System.String" UpdaterField="true" />
  104.               <TypeDescriptor Name="Country" TypeName="System.String" UpdaterField="true" />
  105.               <TypeDescriptor Name="Email" TypeName="System.String" UpdaterField="true" />
  106.               <TypeDescriptor Name="Fax" TypeName="System.String" UpdaterField="true" />
  107.               <TypeDescriptor Name="Phone" TypeName="System.String" UpdaterField="true" />
  108.               <TypeDescriptor Name="PostalCode" TypeName="System.String" UpdaterField="true" />
  109.               <TypeDescriptor Name="Region" TypeName="System.String" UpdaterField="true" /></TypeDescriptors></TypeDescriptor></Parameter>
  110.       </Parameters>
  111.       <MethodInstances>
  112.         <MethodInstance Name="Update" Type="Updater" />
  113.       </MethodInstances></Method>
  114.     <Method Name="Delete">
  115.       <Parameters>
  116.         <Parameter Name="contactID" Direction="In">
  117.           <TypeDescriptor Name="ContactID" TypeName="System.Int32" IdentifierEntityName="Contact" IdentifierEntityNamespace="ContactServices.BdcModel.BdcModel1" IdentifierName="ContactID" /></Parameter>
  118.       </Parameters>
  119.       <MethodInstances>
  120.         <MethodInstance Name="Delete" Type="Deleter" />
  121.       </MethodInstances></Method>
  122.     <Method Name="GetConnection">
  123.       <Parameters>
  124.         <Parameter Name="parameter" Direction="Return">
  125.           <TypeDescriptor Name="connection" TypeName="System.String" /></Parameter>
  126.       </Parameters></Method>
  127.   </Methods>
  128.   </Entity>

Note: The file BdcModel1.bdcm can be visualised in XML directly through Visual Studio, just using the context menu Open With... and then XML (Text) Editor.

BDC Model Connection

Connection details can be set inside of the LobSystemInstance properties if you are using a database or web service. As we have this scenario, the code below shows the connection details:

Code Snippet
  1. <LobSystemInstance Name="BdcModel1">
  2.   <Properties>
  3.     <Property Name="AuthenticationMode" Type="System.String">PassThrough</Property>
  4.     <!-- AuthenticationMode can be set to PassThrough, RevertToSelf, RdbCredentials, or WindowsCredentials. -->
  5.     <Property Name="DatabaseAccessProvider" Type="System.String">SqlServer</Property>
  6.     <!-- Options:  SQL Server, OleDB, Oracle, or ODBC. -->
  7.     <Property Name="RdbConnection Data Source" Type="System.String">W2008R2Sub1</Property>
  8.     <!-- Type the database server name or the SQL Server instance name in the format SQLServer\Instance. -->
  9.     <Property Name="RdbConnection Initial Catalog" Type="System.String">Dev</Property>
  10.     <!-- Type the database name. -->
  11.     <Property Name="RdbConnection Integrated Security" Type="System.String">SSPI</Property>
  12.     <!-- Type SSPI for Integrated Security. -->
  13.     <Property Name="RdbConnection Pooling" Type="System.String">false</Property>
  14.     <!-- Type true or false for Pooling -->
  15.   </Properties>
  16. </LobSystemInstance>

Note: If you want to know more about the authentication types available through BCS, check the section References. Basically the XML above contains properties commonly used when creating connection strings.

BDC Model Entity Methods

Along the creation of methods through BDC Panels, Visual Studio took charge of the generation of the ContactService class and its respective methods. They are not implemented yet and are throwing an exception type of NotImplementedException.

Before coding all methods, do you remember the code that contains the method called GetConnection? It needs to return a database connection string, but how? Well, we are going to take advantage of the LobSystemInstance properties set previously.

But how to read the XML that contains those properties? Easy peasy! Just implement the interface IContextProperty in the ContactService class and the BDC does the trick for you.

Note: When an assembly contains a class that implements IContextProperty, its properties are automatically initialized at the time the assembly is executed in BDC. It helps a lot!

Do that by adding a reference to the assembly Microsoft.Business, which is available at the 14: \ISAPI\Microsoft.BusinessData.dll.

Check out the code below, which shows the ContactService class:

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ContactServices.BdcModel;
  6. using Microsoft.BusinessData.SystemSpecific;
  7. using Microsoft.BusinessData.MetadataModel;
  8. using Microsoft.BusinessData.Runtime;
  9. using Microsoft.BusinessData.MetadataModel.Collections;
  10.  
  11. namespace ContactServices.BdcModel.BdcModel1
  12. {
  13.     public partial class ContactService : IContextProperty
  14.     {
  15.         private static IMethodInstance methodInstance;
  16.         private static ILobSystemInstance lobSystemInstance;
  17.         private static IExecutionContext executionContext;
  18.  
  19.         #region IContextProperty implementation
  20.  
  21.         public IMethodInstance MethodInstance
  22.         {
  23.             get { return methodInstance; }
  24.             set { methodInstance = value; }
  25.         }
  26.  
  27.         public ILobSystemInstance LobSystemInstance
  28.         {
  29.             get { return lobSystemInstance; }
  30.             set { lobSystemInstance = value; }
  31.         }
  32.  
  33.         public IExecutionContext ExecutionContext
  34.         {
  35.             get { return executionContext; }
  36.             set { executionContext = value; }
  37.         }
  38.  
  39.         #endregion
  40.  
  41.         public static Contact ReadItem(int contactID)
  42.         {
  43.             // Implement your own business rules
  44.             return new ContactManager().GetContactById(contactID);
  45.         }
  46.  
  47.         public static IEnumerable<Contact> ReadList()
  48.         {
  49.             // Implement your own business rules
  50.             return new ContactManager().GetContacts();
  51.         }
  52.  
  53.         public static Contact Create(Contact newContact)
  54.         {
  55.             if (string.IsNullOrEmpty(newContact.ContactName) || string.IsNullOrEmpty(newContact.Email) || string.IsNullOrEmpty(newContact.Phone))
  56.                 throw new ObjectNotFoundException("ContactName, Email and Phone are mandatories.");
  57.  
  58.             var manager = new ContactManager();
  59.             manager.AddContact(newContact);
  60.             return manager.GetContactById(newContact.ContactID);
  61.         }
  62.  
  63.         public static void Update(Contact contact)
  64.         {
  65.             if (string.IsNullOrEmpty(contact.ContactName) || string.IsNullOrEmpty(contact.Email) || string.IsNullOrEmpty(contact.Phone))
  66.                 throw new ObjectNotFoundException("ContactName, Email and Phone are mandatories.");
  67.  
  68.             new ContactManager().UpdateContact(contact);
  69.         }
  70.  
  71.         public static void Delete(int contactID)
  72.         {
  73.             // Implement your own business rules
  74.             new ContactManager().DeleteContact(contactID);
  75.         }
  76.  
  77.         public static string GetConnection()
  78.         {
  79.             INamedPropertyDictionary properties = lobSystemInstance.GetProperties();
  80.  
  81.             string template = "Data Source={0};Initial Catalog={1};Integrated Security={2};";
  82.  
  83.             string dataSource = "RdbConnection Data Source";
  84.             string initialCatalog = "RdbConnection Initial Catalog";
  85.             string integratedSecurity = "RdbConnection Integrated Security";
  86.  
  87.             if (!properties.ContainsKey(dataSource) || !properties.ContainsKey(initialCatalog) || !properties.ContainsKey(integratedSecurity))
  88.                 throw new Exception("LobSystemInstance does not contain a connection string");
  89.  
  90.             return string.Format(template, properties[dataSource].ToString(), properties[initialCatalog].ToString(), properties[integratedSecurity].ToString());
  91.  
  92.         }
  93.     }
  94. }

With the exception of the GetConnection method, all the others call CRUD methods from the ContactManager class. Still, in the Create and Update methods there is a validation of the required fields ContactName, Email and Phone, which cannot be null or empty as they throw an exception type of ObjectNotFoundException.

As expected the GetConnection method gets the properties of LobSystemInstance, which contains the BDC connection details, by returning the connection string used in the constructor of the DevDataContext Partial Class.

Creating the Deployment Package

Deployment of WSP packages in the real world is performed either manually or using scripts, not directly with the intervention of Visual Studio. I am explaining this for purposes of demonstration and understanding of STSADM.

Before generating the WSP package, some more settings are needed. Rename the feature to BdcModelFeature and add a property SiteUrl in the XML file BdcModelFeature.Template.xml that contains the URL for deployment, according to the Figure 9:

9_680 Figure 9 – Feature Property

Finally generate the WSP package by selecting Package in the context menu of Visual Studio. Get the WSP package from the Bin directory and by using STSADM deploy it with the following commands:

stsadm -o addsolution -filename "ContactServices.BdcModel.wsp"

stsadm -o deploysolution -name "ContactServices.BdcModel.wsp" -allowGacDeployment –immediate

stsadm -o execadmsvcjobs

stsadm -o installfeature -filename "ContactServices.BdcModel_BdcModelFeature\feature.xml" –force

stsadm -o activatefeature -name "ContactServices.BdcModel_BdcModelFeature"

Note: Download the solution here.

Configuring the External List

At this stage it is already possible to create an External List that will provide a visual interface to the external data in SharePoint 2010. Both SharePoint Designer and the SharePoint UI provides a way for us to create an External List, in the Part II I have showed how to use SharePoint Designer for this and now I am going to show how to create it by SharePoint UI.

Go to the web site and access Site Actions > More Options. The Figure 10 displays the modal dialog to select the External List:

10
Figure 10 – Selecting the External List

Then pick up the External Content Type ContactServices.BdcModel.BdcModel1.Contact and create the Contacts List, as displayed in the Figure 11:

11_680
Figure 11 - Creating the Contacts External List

After doing this you will get your Contacts List created. Is that all? No, you need to set permissions in the BDC Service Application for this Assembly, otherwise you will get the following screen:

BDC1
Figure 12 – Access denied by Business Data Connectivity

Settings BDC Permissions

After the deployment of the .NET Assembly you need to set proper permissions in the BDC Service Application for users to see the external data. Go to the BDC Service Application in the Central Administration and find the .NET Assembly:

BDC2_680
Figure 13 – Find the .NET Assembly

Then specify the users that are going to be allowed to see the external data, according to the Figure 14:

BDC3_680
Figure 14 – Set Object Permissions

By doing this, refresh the External List (Figure 12) and you will get the external data. Smile

This was advanced guys, from now on you are able to create your own ECTs via .NET Assemblies.

The BDC series is not yet finished, next time I will show how to interface ECTs with Client Applications. See you next time!

Reference:
http://msdn.microsoft.com/en-us/library/ee556826(v=office.14).aspx
http://msdn.microsoft.com/en-us/library/microsoft.businessdata.systemspecific.icontextproperty(office.14).aspx

Cheers,

Marcel Medina

Click here to read the same content in Portuguese.