Archive | May 2013

Sharepoint 2013 App Custom Action

This post is about how to create a Custom Action using a SharePoint 2013 app project.

In this sample I’ll use a Menu action, that will be available to list items in Page Library based lists.

This menu action will update a field in the target list item.

Start creating a SharePoint 2013 app Visual Studio 2013 project, SharePoint Hosted.

Then add a new item of type “Menu Item Custom Action”:

AppRA1

Select “List Template” and “Page Library”:

AppRA2

Select your action name and target page:

AppRA3

Your Menu Action elements.xml file should be like:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="56b2571e-1ae7-48e8-be14-2652fc86bc79.Action1"
                RegistrationType="List"
                RegistrationId="850"
                Location="EditControlBlock"
                Sequence="10001"
                Title="Action1">
    <!--
    Update the Url below to the page you want the custom action to use.
    Start the URL with the token ~remoteAppUrl if the page is in the
    associated web project, use ~appWebUrl if page is in the app project.
    -->
    <UrlAction Url="~appWebUrl/Pages/Default.aspx?{StandardTokens}&amp;SPListItemId={ItemId}&amp;SPListId={ListId}" />
  </CustomAction>
</Elements>

In app Menu Action or Ribbon Action you can’t add javascript code to your Command Action (like in this post), so you need to define the  behaviour of your action in the navigation page.
My Default.aspx file includes the App.js file, I edited this file and add the following code:

 'use strict';

var list;
 var parentWeb;
 var currentItem;

$(document).ready(function () {

var hostweburl = GetSiteUrl();

var clientContext = new SP.ClientContext.get_current();
 var parentCtx = new SP.AppContextSite(clientContext, hostweburl)
 var parentWeb = parentCtx.get_web();
 clientContext.load(parentWeb);
 list = parentWeb.get_lists().getById(GetListId());
 clientContext.load(list);
 currentItem = list.getItemById(GetItemId());
 clientContext.load(currentItem);
 if (currentItem != null) {
 currentItem.set_item('Category', 'Cat1');
 currentItem.update();
 }
 clientContext.executeQueryAsync(onListLoadSucceeded,
 onRequestFailed);
 });

function onListLoadSucceeded() {
 $('#message').text('Done');
 }

function onRequestFailed(sender, args) {
 alert('Error:' + args.get_message());
 }

function getParameterByName(name) {
 var name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
 var regexS = "[\\?&]" + name + "=([^&#]*)";
 var regex = new RegExp(regexS);
 var results = regex.exec(window.location.href);
 if (results == null)
 {
 return "";
 }
 else
 {
 return decodeURIComponent(results[1].replace(/\ /g, " "));
 }
 }

function GetListId() {
 return getParameterByName("SPListId");
 }

function GetSiteUrl() {
 return getParameterByName("SPHostUrl");
 }

function GetItemId() {
 return getParameterByName("SPListItemId");
 }
 

After deployed my solution, I can see and execute my Custom Action:

AppRA4

Advertisements

Sharepoint 2013 Open a Modal Dialog from a Custom Ribbon Action

This post is a sequence from this one (https://raquelalineblog.wordpress.com/2013/05/28/sharepoint-2013-custom-ribbon-action-to-update-a-list-item-field/).

In this post I’ll show how to call a modal dialog and get a value from that to update a list item field value.

The first thing was to create a mapped folder in my SharePoint 2013 project, to Layouts folder.

Then I add a new application page, where I have a text area to get data from user, an Ok button and a Cancel button.
My application page is this:

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetCategory.aspx.cs" Inherits="SharePointCustomMenuAction.Layouts.SharePointCustomMenuAction.MoveDocs" DynamicMasterPageFile="~masterurl/default.master" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">

</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">

     <Sharepoint:ScriptLink ID="ScriptLink1" Name="sp.ui.dialog.js" LoadAfterUI="true" Localizable="false" runat="server"></Sharepoint:ScriptLink>
     <script type="text/javascript" language="javascript">

            function onCancel(sender, args) {
                SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, '');
            }

            function onOk(sender, args) {
                SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, document.getElementById('txtCategory').value);
            }
    </script>
    <form id="aspnetForm" action="">

        <p>
            <label id="label1">Enter Category Value</label>
        </p>
        <p>
            <textarea id="txtCategory" rows="1" cols="10"></textarea>

        </p>
        <p>
            <button  id="Ok" value="Ok" onclick="onOk();">Ok</button>&nbsp;&nbsp;<button id="Cancel" value="Cancel" onclick="onCancel();">Cancel</button>
        </p>
    </form>
</asp:Content>

<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
Get Category
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
Get Category
</asp:Content>

I changed my Custom Action’s elements.xml file to call the modal dialog and get the result and value.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
    <CustomAction
    Id="Ribbon.Library.Actions.AddAButton"
    Location="CommandUI.Ribbon"
    RegistrationId="101"
    RegistrationType="List"
    Title="MoveDocs Ribbon Button">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition
          Location="Ribbon.Library.Share.Controls._children">
          <Button Id="Ribbon.Library.Share.NewRibbonButton"
            Command="MoveDocsButtonCommand"
            LabelText="UpdateCategory"
            TemplateAlias="o2" />
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler
          Command="MoveDocsButtonCommand"
          CommandAction="javascript:
            var context;
            var notifyId;
            var currentItem;
        
          function onQuerySucceededUpdate (sender, args) {
              alert('item updated');
          }    

          function onQueryFailedUpdate(sender, args) {
            alert('Error occured' + args.get_message());
          }  
          
          function UpdateCategory(val)
          {
             context = SP.ClientContext.get_current();
             var web = context.get_web();
             context.load(web, 'Title');  
             var currentlibid = SP.ListOperation.Selection.getSelectedList();
             var currentLib = web.get_lists().getById(currentlibid);
             var selectedItems = SP.ListOperation.Selection.getSelectedItems(context);
             for(var i in selectedItems)
              {
                currentItem = currentLib.getItemById(selectedItems[i].id);
                context.load(currentItem);
                if(currentItem != null) {
                  currentItem.set_item('Category', val);
                  currentItem.update();
                }
                context.executeQueryAsync(Function.createDelegate(this, onQuerySucceededUpdate), Function.createDelegate(this, onQueryFailedUpdate));
              }//End for
          }//End UpdateCategory


          var dlg=SP.UI.ModalDialog.showModalDialog({url: '{SiteUrl}/_Layouts/SharePointCustomMenuAction/GetCategory.aspx', dialogReturnValueCallback:function(res, val) {       
                if(res=='1')
                  {
                    UpdateCategory(val);
                  }
                else
                {
                  alert('Operação Cancelada');
                }
               }
              });
          
          
           
"/>
      </CommandUIHandlers>
    </CommandUIExtension>
  </CustomAction>
  
  <Module Name="CustomAction">
  </Module>
</Elements>

Sharepoint 2013 Custom Ribbon Action To Update a List Item Field

In this post I’ll show how to develop a Custom Ribbon Action, that iterates through selected document library (with a custom column named Category) items and updates  a field value (category).

I want to do another post to  get the value from a modal dialog, but for now, the updated value will be hard coded.

I started creating a SharePoint 2013 empty project in Visual Studio 2012 (selected the deployment as a farm solution, but could also be sandbox).

Add a new module to my project called “CustomAction”, and add the following code, to elements.xml file:

 

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
    <CustomAction
    Id="Ribbon.Library.Actions.AddAButton"
    Location="CommandUI.Ribbon"
    RegistrationId="101"
    RegistrationType="List"
    Title="MoveDocs Ribbon Button">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition
          Location="Ribbon.Library.Share.Controls._children">
          <Button Id="Ribbon.Library.Share.NewRibbonButton"
            Command="MoveDocsButtonCommand"
            LabelText="UpdateCategory"
            TemplateAlias="o2" />
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler
          Command="MoveDocsButtonCommand"
          CommandAction="javascript:
            var context;
            var currentItem ;
          
        
          function onQuerySucceededUpdate (sender, args) {
              alert('item updated');
          }    

          function onQueryFailedUpdate(sender, args) {
            alert('Error occured' + args.get_message());
          }  
          
          function UpdateCategory(val)
          {
            context = SP.ClientContext.get_current();
            var web = context.get_web();
             context.load(web);
             var currentlibid = SP.ListOperation.Selection.getSelectedList();
             var currentLib = web.get_lists().getById(currentlibid);
             var selectedItems = SP.ListOperation.Selection.getSelectedItems(context);
             for(var i in selectedItems)
              {
                currentItem = currentLib.getItemById(selectedItems[i].id);
                context.load(currentItem);
                if(currentItem != null) {
                  currentItem.set_item('Category', val);
                  currentItem.update();
                }
                context.executeQueryAsync(Function.createDelegate(this, onQuerySucceededUpdate), Function.createDelegate(this, onQueryFailedUpdate));
              }// End for
          
          }//End UpdateCategory

          
          UpdateCategory('Cat1');
           
"/>
      </CommandUIHandlers>
    </CommandUIExtension>
  </CustomAction>
  
  <Module Name="CustomAction">
  </Module>
</Elements>

Sharepoint 2013 Custom Workflow Activity SingleTask with Custom Content Type

In this post I’ll talk about how to develop a custom Workflow Activity that creates a SingleTask, with a custom Content Type,  collect data from user and update the target list item with that data.

This post is a sequence from (https://raquelalineblog.wordpress.com/2013/05/11/sharepoint-2013-workflow-custom-declarative-activity/), where I explain in more detail the steps to create a custom Workflow Activity.

I started creating a SharePoint 2013 Empty Project, deployed as a sandbox solution.

I added a new item of type “Content Type”:

CT1

Then I followed the wizard to configure my new Content Type, selected to inherit from item:

CT2

Defined my new content type columns:

CT3

And the basic settings:

CT4

Then I added a new item to my project, of type “Workflow Custom Activity”, and added the actions to have the logic I needed:

CT5

The first actions in my Activity are to get the username of the current’s item author. I used that username in my SingleTask action, in the “Assigned to” field.

CT51

After that I started configuring my Single Task action.

Since I wanted to use a custom Content Type, I started to get my content’s type id. I got that from my content’s type elements.xml file:

CT6

Then I configured my Single Task as in the next picture:

CT7

In the next actions I did the logic to get the value of the Category field from my Workfflow Single Task, and add that field’s  value to the field Category in my target workflow item.

The fisrt step was to get the TaskListId:

CT8

Next I got the Task Item from the Task List, using the taskId, returned in my Single Task action:

CT81

Then I got the value of Category field from the TaskItem:

CT82

And finally I updated my Workflow target list item with that value:

CT83

I created a SharePoint Designer Workflow that calls my new Activity:

CT9

Run my Workflow, and saw my task with my new Content Type (I had to configure my Task List to support this Content Type, in List Settings. This is possible to be done automatically by code):

CT10

Completed my Workflow:

CT11

And saw my Item with the value collected from my task:

CT12

Sharepoint 2013 Silverlight Webpart

This post is about how to integrate a Silverlight Webpart in SharePoint 2013 and use the Sharepoint Client Object Model, to show SharePoint Data.

The goal is to create a Silverlight Webpart that queries data to QuickLaunch Navigation and shows the result in a two levels TreeView Control.

I started creating a SharePoint 2013 Silverlight Webpart Project in Visual Studio 2012 (named SilverlightSample).

It automatically created a SilverlightWebpart (SilverlightSampleWebPart).

Then I starded with MainPage.xaml, where I inserted a TreeView Control, and created the templates to render data.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:Client="clr-namespace:Microsoft.SharePoint.Client;assembly=Microsoft.SharePoint.Client.Silverlight"
    x:Class="SilverlightSampleWebPart.MainPage"
    mc:Ignorable="d"
    d:DesignHeight="200" d:DesignWidth="120">
    <UserControl.Resources>

        <sdk:HierarchicalDataTemplate x:Key="ChildrenTemplate"
                                         ItemsSource="{Binding Path=Children}">
            <TextBlock Text="{Binding Path=Title}" />
        </sdk:HierarchicalDataTemplate>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White" Height="200" Width="120">
        <sdk:TreeView Grid.Row="2"  Name="treeView"
                      ItemTemplate="{StaticResource ChildrenTemplate}"
                      >
        </sdk:TreeView>
    </Grid>
</UserControl>

 

Next I edited MainPage.xaml.cs file, to put the logic to get Data.

I created a class SiteNodes, where the first node is set to Web Title, and the Children nodes are the first level navigation nodes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.SharePoint.Client;

namespace SilverlightSampleWebPart
{
    public partial class MainPage : UserControl
    {
        private ClientContext context;
        private NavigationNodeCollection nav;
        SiteNodes site;

        public class SiteNodes
        {
            public string Title { get; set; }
            public List<SiteChildren> Children { get; set; }

        }

        public class SiteChildren
        {
            public string Title { get; set; }
        }

        public MainPage()
        {
            InitializeComponent();
            context = new ClientContext(ApplicationContext.Current.Url);
            context.Load(context.Web);
            nav = context.Web.Navigation.QuickLaunch;
            context.Load(nav);

            context.ExecuteQueryAsync(new ClientRequestSucceededEventHandler(OnRequestSucceeded), null);
        }

        private void OnRequestSucceeded(Object sender, ClientRequestSucceededEventArgs args)
        {
            // This is not called on the UI thread.
            Dispatcher.BeginInvoke(BindData);
        }

        private void BindData()
        {
             List<SiteNodes> siteColl = new List<SiteNodes>();
             site = new SiteNodes { Title = context.Web.Title };
             site.Children = new List<SiteChildren>();
             foreach (NavigationNode nn in nav)
             {
                 BindChilds(nn);
             }
             siteColl.Add(site);
             treeView.ItemsSource = siteColl;
        }

        private void BindChilds(NavigationNode nn)
        {
            SiteChildren sn = new SiteChildren{ Title = nn.Title };
            site.Children.Add(sn);

        }

        private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {

        }

    }
}
 

After that I deployed my solution and add my new SilverlightWebpart to my page:

Silverlight

Sharepoint 2013 LookupSPGroupMembers Workflow Activity

I created  a custom workflow activity that writes to history list all users from a specific group.

In this post I will not explain step by step the details to create a custom activity, you can see that in  (https://raquelalineblog.wordpress.com/2013/05/11/sharepoint-2013-workflow-custom-declarative-activity/).

I’ll be focused on the detail to iterate through group members Dynamic Value result.

My Custom Avctivity is:

WFGM2

I’ll explain now the details of the principal activities.

We need a LookupSPGroupMembers activity that will put the result in a local variable GroupMembers, of type DynamicValue:

WFGM3

This DynamicValue is an  array of DynamicValues, so I”ll get the number of returned elements , I need 2 activities for that (GetDynamicValueProperty, and CountDynamicValueItems from the variable properties).

WFGM4

WFGM5

Next, I need to iterate through the variable GroupMembers to get the loginNames:

WFGM6

I log to HistoryList each LoginName:

WFGM7

The result is:

WFGM1

Sharepoint 2013 BCS in Visual Studio 2012

Sharepoint 2013 BCS in Visual Studio 2012

When we need to integrate external data sources in SharePoint, one recommended way is using BCS services (BDC).

BCS can be very useful when we need to create a BackOffice in SharePoint 2013 to manage data from an external relational database.

In this post I’ll talk about how to create a BCS Model to manage an entity (Product) in an external SQL database, using Visual Studio 2012).

BCS0

I started creating an Empty SharePoint 2013 project and selected the farm deployment.

Then I add to my project a new Item (Business Data Connectivity Model), named it BdcProduct:

BCS1

By default, the Business Data Connectivity Model adds Entity1.cs and Entity1Service.cs classes. At this point, if you deploy your solution, you can test this BCS Entity1 (I think this is a good test to start).

After that I renamed the classes Entity1.cs and Entity1Service.sc to Product.cs and Product1.cs and started changing the BdcProduct.bdcm to have the properties and methods that I needed:

BDC5

After that I edited my BdcProduct.bdcm with XML editor to check that I didn’t miss anything. My BdcProduct.bdcm is like:


<?xml version="1.0" encoding="utf-8"?>
<Model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog" Name="BdcProduct">
  <LobSystems>
    <LobSystem Name="BdcProduct" Type="DotNetAssembly">
      <LobSystemInstances>
        <LobSystemInstance Name="BdcProduct" />
      </LobSystemInstances>
      <Entities>
        <Entity Name="Product" Namespace="BCSSample.BdcProduct" EstimatedInstanceCount="1000" Version="1.0.0.53">
          <Properties>
            <Property Name="Class" Type="System.String">BCSSample.BdcProduct.ProductService, BdcProduct</Property>
          </Properties>
          <Identifiers>
            <Identifier Name="ID" TypeName="System.Int32" />
            <!-- TODO: Change the name of the ID and if needed the TypeName of your identifier. -->
          </Identifiers>
          <Methods>
            <Method Name="ReadList">
              <Parameters>
                <Parameter Name="productList" Direction="Return">
                  <TypeDescriptor Name="ProductList" TypeName="System.Collections.Generic.IEnumerable`1[[BCSSample.BdcProduct.Product, BdcProduct]]" IsCollection="true">
                    <TypeDescriptors>
                      <TypeDescriptor TypeName="BCSSample.BdcProduct.Product, BdcProduct" Name="Product">
                        <TypeDescriptors>
                          <TypeDescriptor Name="ID" TypeName="System.Int32" IsCollection="false" IdentifierName="ID" LobName="ID" />
                          <TypeDescriptor Name="Name" TypeName="System.String" LobName="Name" />
                          <TypeDescriptor Name="Description" TypeName="System.String" LobName="Description" /></TypeDescriptors></TypeDescriptor></TypeDescriptors></TypeDescriptor></Parameter>
              </Parameters>
              <MethodInstances>
                <MethodInstance Name="ReadList" Type="Finder" ReturnParameterName="productList" />
              </MethodInstances></Method>
            <Method Name="ReadItem">
              <Parameters>
                <Parameter Name="product" Direction="Return">
                  <TypeDescriptor TypeName="BCSSample.BdcProduct.Product, BdcProduct" Name="Product">
                    <TypeDescriptors>
                      <TypeDescriptor Name="ID" TypeName="System.Int32" IsCollection="false" IdentifierName="ID" LobName="ID" />
                      <TypeDescriptor Name="Name" TypeName="System.String" LobName="Name" />
                      <TypeDescriptor Name="Description" TypeName="System.String" LobName="Description" /></TypeDescriptors></TypeDescriptor></Parameter>
                <Parameter Name="ID" Direction="In">
                  <TypeDescriptor Name="ID" TypeName="System.Int32" IdentifierName="ID" /></Parameter>
              </Parameters>
              <MethodInstances>
                <MethodInstance Name="ReadItem" Type="SpecificFinder" ReturnParameterName="product" />
              </MethodInstances></Method>
            <Method Name="GetConnection" />
            <Method Name="Create">
              <Parameters>
                <Parameter Name="returnProduct" Direction="Return">
                  <TypeDescriptor Name="ReturnProduct" TypeName="BCSSample.BdcProduct.Product, BdcProduct">
                    <TypeDescriptors>
                      <TypeDescriptor Name="ID" LobName="ID" IdentifierName="ID" IsCollection="false" TypeName="System.Int32" />
                      <TypeDescriptor Name="Name" LobName="Name" TypeName="System.String" />
                      <TypeDescriptor Name="Description" LobName="Description" TypeName="System.String" /></TypeDescriptors></TypeDescriptor></Parameter>
                <Parameter Name="newProduct" Direction="In">
                  <TypeDescriptor Name="NewProduct" TypeName="BCSSample.BdcProduct.Product, BdcProduct">
                    <TypeDescriptors>
                      <TypeDescriptor Name="Name" LobName="Name" TypeName="System.String" CreatorField="true" />
                      <TypeDescriptor Name="Description" LobName="Description" TypeName="System.String" CreatorField="true" /></TypeDescriptors></TypeDescriptor></Parameter>
              </Parameters>
              <MethodInstances>
                <MethodInstance Name="Create" Type="Creator" ReturnParameterName="returnProduct" ReturnTypeDescriptorPath="ReturnProduct" />
              </MethodInstances></Method>
            <Method Name="Delete">
              <Parameters>
                <Parameter Name="iD" Direction="InOut">
                  <TypeDescriptor Name="ID" TypeName="System.Int32" IdentifierEntityName="Product" IdentifierEntityNamespace="BCSSample.BdcProduct" IdentifierName="ID" /></Parameter>
              </Parameters>
              <MethodInstances>
                <MethodInstance Name="Delete" Type="Deleter" />
              </MethodInstances></Method>
            <Method Name="Update">
              <Parameters>
                <Parameter Name="product" Direction="In">
                  <TypeDescriptor Name="Product" TypeName="BCSSample.BdcProduct.Product, BdcProduct">
                    <TypeDescriptors>
                      <TypeDescriptor Name="Name" LobName="Name" TypeName="System.String" UpdaterField="true" />
                      <TypeDescriptor Name="Description" LobName="Description" TypeName="System.String" UpdaterField="true" />
                      <TypeDescriptor Name="ID" TypeName="System.Int32" IsCollection="false" ReadOnly="false" UpdaterField="true" PreUpdaterField="true" /></TypeDescriptors></TypeDescriptor></Parameter>
              </Parameters>
              <MethodInstances>
                <MethodInstance Name="Update" Type="Updater" />
              </MethodInstances></Method>
          </Methods>
        </Entity>
      </Entities>
    </LobSystem>
  </LobSystems>
</Model>

After define the Properties and Methods we  need to code to define how and where we’ll get our data.

In this sample I used LINQ to SQL, so I add a new item of this type to my project:

BCS6

To simplify my job, I copied the code from partial classe Product (added automatically by LINQ to SQL), to my BDC class Product.cs and deleteted the LINQ to SQL Product partial class.

My BDC Product.cs is:

using System;
using System.Collections.Generic;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Text;

namespace BCSSample.BdcProduct
{
    ///
/// This class contains the properties for Entity1. The properties keep the data for Entity1.
    /// If you want to rename the class, don't forget to rename the entity in the model xml as well.
    ///
        //TODO: Implement additional properties here. The property Message is just a sample how a property could look like.

    [global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Product")]
	public partial class Product
	{

		private int _ID;

		private string _Name;

		private string _Description;

		public Product()
		{
		}

        [global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_ID",  DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
   	    public int ID
		{
			get
			{
				return this._ID;
			}
			set
			{
				if ((this._ID != value))
				{
					this._ID = value;
				}
			}
		}

		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", DbType="VarChar(250) NOT NULL", CanBeNull=false)]
		public string Name
		{
			get
			{
				return this._Name;
			}
			set
			{
				if ((this._Name != value))
				{
					this._Name = value;
				}
			}
		}

		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Description", DbType="VarChar(250)")]
		public string Description
		{
			get
			{
				return this._Description;
			}
			set
			{
				if ((this._Description != value))
				{
					this._Description = value;
				}
			}
		}
	}
}
 

After that I created a ProductManager class to implement my methods:

 using BCSSample.BdcProduct;
using System.Collections.Generic;
using System.Linq;
using BCSSample;

namespace BCSSample
{
    public  class ProductManager
    {
        public string strCon = BCSSample.BdcProduct.ProductService.GetConnection();

        public Product[] GetProducts()
        {
            var products = new List();

            using (ProductDataDataContext ctx = new ProductDataDataContext(strCon))
            {
                products = (from prd in ctx.Products
                            select prd).ToList();
            }
            return products.ToArray();
        }

        public Product GetProductById(int productId)
        {
            var product = new Product();

            using (ProductDataDataContext ctx = new ProductDataDataContext(strCon))
            {
                product = (from prod in ctx.Products
                           where prod.ID == productId
                           select prod).First();
            }

            return product;
        }

        public void UpdateProduct(Product product)
        {
            var productDB = new Product();

            using (ProductDataDataContext ctx = new ProductDataDataContext(strCon))
            {
                productDB = (from prd in ctx.Products
                             where prd.ID == product.ID
                             select prd).First();

                // Alters the object
                productDB.Name = product.Name;
                productDB.Description = product.Description;

                ctx.Refresh(System.Data.Linq.RefreshMode.KeepChanges, productDB);
                ctx.SubmitChanges();
            }
        }

        public void AddProduct(Product product)
        {
            using (ProductDataDataContext ctx = new ProductDataDataContext(strCon))
            {
                ctx.Products.InsertOnSubmit(product);
                 ctx.SubmitChanges();
            }
        }

        public void DeleteProduct(int productId)
        {
            using (ProductDataDataContext ctx = new ProductDataDataContext(strCon))
            {
                var product = (from prd in ctx.Products
                               where prd.ID == productId
                               select prd).First();
                ctx.Products.DeleteOnSubmit(product);
                ctx.SubmitChanges();
            }
        }

    }
}
 

And then changed my ProductService class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.BusinessData.SystemSpecific;
using Microsoft.BusinessData.Runtime;
using Microsoft.BusinessData.MetadataModel.Collections;

namespace BCSSample.BdcProduct
{
    ///
/// All the methods for retrieving, updating and deleting data are implemented in this class file.
    /// The samples below show the finder and specific finder method for Entity1.
    ///
    public class ProductService : IContextProperty
    {

        private static IMethodInstance methodInstance;
        private static ILobSystemInstance lobSystem;
        private static IExecutionContext executionContext;
        public IMethodInstance MethodInstance
        {
            get { return methodInstance; }
            set { methodInstance = value; }
        }

        public ILobSystemInstance LobSystemInstance
        {
            get { return lobSystem; }
            set { lobSystem = value; }
        }

        public IExecutionContext ExecutionContext
        {
            get { return executionContext; }
            set { executionContext = value; }
        }

        public static string GetConnection()
        {

            INamedPropertyDictionary properties = lobSystem.GetLobSystem().GetProperties();

            string template = "Data Source={0};Initial Catalog={1};Integrated Security={2};";

            string dataSource = "RdbConnection Data Source";
            string initialCatalog = "RdbConnection Initial Catalog";
            string integratedSecurity = "RdbConnection Integrated Security";

            if (!properties.ContainsKey(dataSource) || !properties.ContainsKey(initialCatalog) || !properties.ContainsKey(integratedSecurity))
                throw new Exception("LobSystemInstance does not contain a connection string");

            return string.Format(template, properties[dataSource].ToString(), properties[initialCatalog].ToString(), properties[integratedSecurity].ToString());

        }

        ///
/// This is a sample specific finder method for Entity1.
        /// If you want to delete or rename the method think about changing the xml in the BDC model file as well.
        ///
        ///
        /// Entity1
        public static Product ReadItem(int id)
        {

            //TODO: This is just a sample. Replace this simple sample with valid code.
            ProductManager prdmgr = new ProductManager();
            Product product = prdmgr.GetProductById(id);
            return product;

        }
        ///
/// This is a sample finder method for Entity1.
        /// If you want to delete or rename the method think about changing the xml in the BDC model file as well.
        ///
        /// IEnumerable of Entities
        public static IEnumerable ReadList()
        {

            //TODO: This is just a sample. Replace this simple sample with valid code.
            ProductManager prdmgr = new ProductManager();
            Product[] productList = prdmgr.GetProducts();
            return productList;

        }

        public static Product Create(Product newProduct)
        {
            if (string.IsNullOrEmpty(newProduct.Name))
                throw new ObjectNotFoundException("Product Name is mandatory.");
            ProductManager prdmgr = new ProductManager();
            prdmgr.AddProduct(newProduct);
            return newProduct;

        }

        public static void Delete(ref int iD)
        {
            ProductManager prdmgr = new ProductManager();
            prdmgr.DeleteProduct(iD);

        }

        public static void Update(Product product)
        {
            if (string.IsNullOrEmpty(product.Name))
                throw new ObjectNotFoundException("Product Name is mandatory.");
            ProductManager prdmgr = new ProductManager();

            prdmgr.UpdateProduct(product);

        }

    }
}
 

I add a new item (BdcResource1.bdcr) to my project to define connection properties:

<?xml version="1.0" encoding="utf-8"?>
<!-- TODO: Replace the $ModelName$ and $LobSystemName$ with your model name and <?xml version="1.0" encoding="utf-8"?>
<!-- TODO: Replace the $ModelName$ and $LobSystemName$ with your model name and LobSystem name respectively. -->
<Model xmlns="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog/Resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="BdcProduct">
    <LobSystems>
        <LobSystem Name="BdcProduct">
          <Properties>
            <Property Name="AuthenticationMode" Type="System.String">PassThrough</Property>
            <!-- AuthenticationMode can be set to PassThrough, RevertToSelf, RdbCredentials, or WindowsCredentials. -->
            <Property Name="DatabaseAccessProvider" Type="System.String">SqlServer</Property>
            <!-- Options:  SQL Server, OleDB, Oracle, or ODBC. -->
            <Property Name="RdbConnection Data Source" Type="System.String">WIN-F9PITQQTMF3</Property>
            <!-- Type the database server name or the SQL Server instance name in the format SQLServer\Instance. -->
            <Property Name="RdbConnection Initial Catalog" Type="System.String">SampleDatabse</Property>
            <!-- Type the database name. -->
            <Property Name="RdbConnection Integrated Security" Type="System.String">SSPI</Property>
            <!-- Type SSPI for Integrated Security. -->
            <Property Name="RdbConnection Pooling" Type="System.String">false</Property>
            <!-- Type true or false for Pooling -->
          </Properties>
        </LobSystem>
    </LobSystems>
</Model> 

 

After that I deployed my solution and test it.

Created  an External List:

BDC2

Selected my new External Content type:

BDC3

Started using my new list:

BDC4

References: http://msdn.microsoft.com/en-us/library/jj163782.aspx