May, 2010

  • Visual Studio SharePoint Development Blog

    Deploying documents with content types and associating content types with libraries (Part I)

    • 3 Comments

    Content type, document and a library

    In this series of posts I will show you how to create a content type project, deploy a Word document template together with the content type, and then associate the content type with document library on the SharePoint site. When developing content types, one common scenario is to deploy a document template along with the content type. The deployed document template appears on the New menu for the SharePoint library content type it is associated with. So, let’s start by creating a new C# SharePoint 2010 Content Type project.

    1. Start Visual Studio 2010 with administrator privileges.
    2. Click File à New Project and select the Content Type project under the SharePoint à 2010 node.
    3. Name the project MyContentType and click OK. 
    4. Select the Deploy as a farm solution option in the wizard and click Next. We are creating a farm solution because we want to deploy the document using mapped folders and mapped folder can be used only in farm solutions. 
    5. Select Document for the base content type.
    6. Click Finish to create the project.

    Once the content type project is created, we can add a document to the project. This document will be deployed as a part of our content type. As mentioned earlier we are going to use a mapped folder to deploy the document. Right-click the project name and select Add -> SharePoint "Layouts" Mapped folder. This command creates the Layouts and MyContentType subfolder in the project as shown below.

    Assuming your document is prepared and ready you can right-click the MyContentType folder (under the Layouts mapped folder in the project) and select Add à Existing Item. Browse to the document you want to use, select it and click Add to add it to the mapped folder. When you add a document to the mapped folder the deployment type and location properties are automatically to the required values.

    The next step is to associate this document with the content type. To do this, open the Elements.xml file under the ContentType1 folder and insert the DocumentTemplate element right after the FieldRefs closing tag:

    <DocumentTemplate TargetName="/_layouts/MyContentType/MyDocument.docx"/>

    Using the DocumentTemplate tag we are pointing to the document that is going to be deployed to the layouts folder. You can press F5 now to deploy the project and start debugging. By default the Site Content Types page opens. Click MyContentType - ContentType1 in the list to open the content type settings page which should look similar to the one below.

    Click the Advanced settings link. Notice that the URL for the Document Template is set to the document we deployed as shown below.

    Let's try to associate this content type with the new document library. Click the Libraries link on the SharePoint site and then click the Create link to create a new library. Select the Document Library item from the list and name it My Document Library as shown below.

    Click the Create button to create the document library. Now that we have the document library we need to associate the content type with it. Click the Library Settings button to open Document Library Settings page. The default content type for document libraries is Document content type - we need to change that in order to use the content type we deployed. But before we do that we have to allow the management of content types in this library. To do this, click the Advanced settings link and on the Advanced Settings page, click Yes to allow management of content types as shown below and click OK to save changes.

    We are redirected back to the Document Library Settings page and you will notice the Content Types section on the page is now visible. Click the Add from existing site content types link to add our content type to the library. Select the MyContentType - ContentType from the list and click Add to associate the content type with the library. Click OK to save the changes - the content types section on the settings page now shows our content type.

    However, the Document content type is still set to be the default. We can easily change that by clicking the Change new button order and default content type link. On the page that opens uncheck the Document content type and click OK to save changes. This hides the Document content type and makes our content type the default.

    Finally, navigate back to the My Document Library and click the Documents tab on the ribbon. If you click the New Document button you will notice the document we deployed shows in the list.

    Deploying and associating documents with content types is fairly easy. Associating the content type to a library however requires a bit more (manual) work. In the next post I will show you how to automatically allow management of content types in the library and make the content type a default one.

    Peter Jausovec

  • Visual Studio SharePoint Development Blog

    Surfacing Business Data in a Web Part that you Create by Using SharePoint Tools in Visual Studio

    • 2 Comments

    So you add a Visual Web Part to your SharePoint solution and you want the Web Part to show some data. No problem. I’ll show you three ways to do it. This post shows you how to surface data in your Visual Web Part by performing the following tasks:

    • Binding data to a GridView at design time.
    • Binding data to a GridView by using LINQ to SQL code.
    • Binding data to a GridView by querying the Business Data Catalog.

    I have also published a sample that shows these tasks here. For now, let’s make some data appear!

    Binding Data to a GridView at Design Time

    1. In Visual Studio, create a Visual Web Part project.

    2. From the Toolbox, drag a GridView to the designer.

    3. Attempt to configure a data source for the GridView. Wait! Where can I select Database? I only see XML File. Could this be a bug?

    original[1]

    Nope. This is not a bug. The ability to configure other data sources by using the Data Source Configuration Wizard is a feature that has not been implemented in the Visual Web Designer for SharePoint projects in Visual Studio. You have two options.

    · Write XML that defines the data source in the Source view of the designer (Not fun).

    · Configure your data source in an ASP.NET project and then copy over the data source object (fun).

    For this post, I’ll configure a SQLDataSource object in an ASP.NET project. Then, just copy control over to my Visual Web Part project. Then, I will bind the GridView to that control.

    1. In Visual Studio, create a new ASP.NET Web Application project.

    2. From the Toolbox, drag a SQLDataSource control to the designer of any page.

    original[1]

    3. Configure the control by using the Data Source Configuration Wizard (Shown earlier). Use the wizard to generate create, update, delete, and select commands.

    4. In the designer of the ASP.NET project, copy the SQLDataSource control.

    original[1]

    5. In the designer of the Visual Web Part project, paste the SQLDataSource control.

    6. Bind the GridView to that data source.

    original[1]

    7. In the GridView Tasks dialog box, select Enable Editing, and Enable Deleting. Selecting these options will add Edit and Delete links to the GridView.

    original[1]

    8. Finally, add the name of the primary key column of your database table to the DataKeyNames property of the GridView.

    original[1]

    9. Design the rest of the GridView (look and feel etc.).

    If you open the designer in Source view, your GridView should look something like this:

    GridView

    <asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="CustomerID" PageSize="3" EnableModelValidation="True">
    
      <Columns>
    
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
    
      </Columns>
    
    </asp:GridView>
    

    SQLDataSource

    <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:AdventureWorksConnectionString%>" ProviderName="System.Data.SqlClient"
    
      SelectCommand="SELECT TOP 3 [CustomerID], [FirstName], [MiddleName], [LastName] FROM [SalesLT].[Customer]"
    
      DeleteCommand="DELETE FROM [SalesLT].[Customer] WHERE [CustomerID] = @CustomerID"
    
      InsertCommand="INSERT INTO [SalesLT].[Customer] ([FirstName], [MiddleName], [LastName]) VALUES (@FirstName, @MiddleName, @LastName)"
    
      UpdateCommand="UPDATE [SalesLT].[Customer] SET [FirstName] = @FirstName, [MiddleName] = @MiddleName, [LastName] = @LastName WHERE [CustomerID] = @CustomerID">
    
      <DeleteParameters>
    
        <asp:Parameter Name="CustomerID" Type="Int32" />
    
      </DeleteParameters>
    
      <InsertParameters>
    
        <asp:Parameter Name="FirstName" Type="String" />
    
        <asp:Parameter Name="MiddleName" Type="String" />
    
        <asp:Parameter Name="LastName" Type="String" />
    
      </InsertParameters>
    
      <UpdateParameters>
    
        <asp:Parameter Name="FirstName" Type="String" />
    
        <asp:Parameter Name="MiddleName" Type="String" />
    
        <asp:Parameter Name="LastName" Type="String" />
    
        <asp:Parameter Name="CustomerID" Type="Int32" />
    
      </UpdateParameters>
    
    </asp:SqlDataSource>
    

    A Quick note about Connection Strings

    Notice the following line in the above source:

    ConnectionString="<%$ ConnectionStrings:AdventureWorksConnectionString%>"

    This line grabs the connection string from the web.config file of the root site. This is one way to persist a connection string. There are other ways to do this as well. You can read about them in the MSDN topic Managing Application Configuration.

    The sample contains code that adds the connection string to web.config by using code in the feature receiver of the Visual Web Part project.

    Bind Data to a GridView by using LINQ to SQL Code

    There are probably a dozen ways to structure this. Here is one way to do it.

    · Create a class library.

    · Generate objects to access the data.

    · Add methods that retrieve, edit, and delete the data.

    · Bind the GridView to those methods.

    Create a Class Library

    1. Add a Class Library project to your solution.

    2. Compile the project.

    3. Strong name sign the assembly of the project. You can read more about how to do that here.

    4. Add the assembly to the package manifest. You can read more about how to do that here.

    Generate objects to access the data

    1. Add a new data source to the Class Library project. You can read more about how to do that here.

    2. Add a LINQ to SQL Classes item to the Class Library project.

    original[1]

    3. In Server Explorer, drag a table onto the O/R Designer.

    This creates an Entity that represents the table. Your code will read, edit, and delete data by using the entity not the actual table.

    original[2]

    Add methods that retrieve, edit, and delete the data

    1. In the class file of the Class Library project, add code to retrieve, update and delete data.

    Example:

    public class DAL
    
    {
    
      public static AdventureWorksDataContext GetDataContext()
    
      {
    
        string strConnString = ConfigurationManager.ConnectionStrings["AdventureWorksConnectionString"].ConnectionString;
    
        return new AdventureWorksDataContext(strConnString);
    
      }
    
      public static IEnumerable<Customer> GetCustomers()
    
      {
    
        AdventureWorksDataContext dataContext = GetDataContext();
    
        IEnumerable<Customer> myCustomers = from customers in dataContext.Customers.Take(3)
    
                                            select customers;
    
        return myCustomers;
    
      }
    
      public static void UpdateCustomer(int customerID, string firstName, string middleName, string lastName)
    
      {
    
        AdventureWorksDataContext dataContext = GetDataContext();
    
        var CustomerToUpdate = (from Customers in dataContext.Customers
    
                                where Customers.CustomerID == customerID
    
                                select Customers).Single();
    
        
    
        CustomerToUpdate.CustomerID = customerID;
    
        CustomerToUpdate.FirstName = firstName;
    
        CustomerToUpdate.MiddleName = middleName;
    
        CustomerToUpdate.LastName = lastName;
    
        dataContext.SubmitChanges();
    
      }
    
      public static void DeleteCustomer(int CustomerID)
    
      {
    
        AdventureWorksDataContext dataContext = GetDataContext();
    
        Customer Customer =(from Customers in dataContext.Customers.AsEnumerable().Take(5)
    
                            where Customers.CustomerID == CustomerID
    
                            select Customers).Single();
    
        dataContext.Customers.DeleteOnSubmit(Customer);
    
        dataContext.SubmitChanges();
    
      }
    
    }

    Binding the GridView to your methods

    1. In the designer of your Visual Web part, create event handlers for the RowCancelingEdit, RowDeleting, RowEditing, and RowUpdating events of the GridView.

    original[2]

    2. Right-click the designer and then click View Code.

    3. Add code for each of the event handlers.

    Example:

    public partial class VisualWebPart1UserControl : UserControl
    
    {
    
      protected void Page_Load(object sender, EventArgs e)
    
      {
    
        if (!IsPostBack)
    
        {
    
          BindGridView(GridView2);
    
        }
    
      }
    
      private void BindGridView(GridView GridView)
    
      {
    
        GridView.DataSource = DAL.DAL.GetCustomers();
    
        GridView.DataBind();
    
      }
    
      protected void GridView_RowDeleting(object sender, GridViewDeleteEventArgs e)
    
      {
    
        GridView TempGridView = (GridView)sender;
    
        TableCell cell = TempGridView.Rows[e.RowIndex].Cells[1];
    
        DAL.DAL.DeleteCustomer(Convert.ToInt32(cell.Text));
    
        BindGridView(TempGridView);
    
      }
    
      protected void GridView_RowEditing(object sender, GridViewEditEventArgs e)
    
      {
    
        GridView TempGridView = (GridView)sender;
    
        TempGridView.EditIndex = e.NewEditIndex;
    
        BindGridView(TempGridView);
    
      }
    
      protected void GridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
    
      {
    
        int CustomerID = 0;
    
        string FirstName ="";
    
        string MiddleName="";
    
        string LastName="";
    
      
    
        GridView TempGridView = (GridView)sender;
    
        GridViewRow gvr = TempGridView.Rows[e.RowIndex];
    
        for (int i = 0; i < gvr.Cells.Count; i++)
    
        {
    
          switch (TempGridView.HeaderRow.Cells[i].Text)
    
          {
    
            case "CustomerID":
    
              CustomerID = Convert.ToInt32(((TextBox)(gvr.Cells[i].Controls[0])).Text);
    
              break;
    
            case "FirstName":
    
              FirstName = ((TextBox)(gvr.Cells[i].Controls[0])).Text;
    
              break;
    
            case "MiddleName":
    
              MiddleName = ((TextBox)(gvr.Cells[i].Controls[0])).Text;
    
              break;
    
            case "LastName":
    
              LastName = ((TextBox)(gvr.Cells[i].Controls[0])).Text;
    
              break;
    
            default:
    
              break;
    
           }
    
        }
    
        DAL.DAL.UpdateCustomer(CustomerID, FirstName, MiddleName, LastName);
    
        TempGridView.EditIndex = -1;
    
        BindGridView(TempGridView);
    
      }
    
      protected void GridView_RowUpdated(object sender, GridViewUpdatedEventArgs e)
    
      {
    
        GridView TempGridView = (GridView)sender;
    
        e.KeepInEditMode = false;
    
        TempGridView.EditIndex = -1;
    
      }
    
      protected void GridView_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
    
      {
    
        GridView TempGridView = (GridView)sender;
    
        TempGridView.EditIndex = -1;
    
        BindGridView(TempGridView);
    
      }
    
    }

    Binding Data to a GridView by Querying the Business Data Connectivity Service

    Perhaps your organization has a bunch of data in the Business Data Connectivity Service in SharePoint. No problem. You can bind your GridView to that too.

    <Quick Shameless Plug>

    If you are not familiar with the Business Data Connectivity Services in SharePoint, it is the coolest thing since ice cubes. You can read more about it here. Visual Studio has a project template to help deploy data models to the service. You can read more about that here.

    </Quick Shameless Plug>

    1. Add a class to your Class Library project.

    2. Add a reference to the Microsoft.BusinessData assembly. Unfortunately, you won’t find that assembly in the .NET tab of the Add Reference dialog box. I found it by browsing to this location - C:\program files\common files\microsoft shared\web server extensions\14\ISAPI\Microsoft.BusinessData.dll.

    3. In your class, add the following using (or for VB Imports) statements:

    using Microsoft.SharePoint;

    using Microsoft.SharePoint.BusinessData.SharedService;

    using Microsoft.BusinessData.MetadataModel;

    using Microsoft.SharePoint.Administration;

    4. Add code.

    Example:

    class BDC_DAL
    
    {
    
      const string nameSpace = "AdventureWorksCustomers.BdcModel1";
    
      const string entityName = "Customer";
    
      
    
      private static IMetadataCatalog GetCatalog()
    
      {
    
        BdcService service = SPFarm.Local.Services.GetValue<BdcService>(String.Empty);
    
        IMetadataCatalog catalog = service.GetDatabaseBackedMetadataCatalog(
    
        SPServiceContext.Current);
    
        return catalog;
    
      }
    
      public static object GetCustomers()
    
      {
    
        IMetadataCatalog catalog = GetCatalog();
    
        IEntity entity = catalog.GetEntity(nameSpace, entityName);
    
        ILobSystemInstance LobSysteminstance = entity.GetLobSystem().
    
        GetLobSystemInstances()[0].Value;
    
        IMethodInstance methodInstance = entity.GetMethodInstance("ReadList", MethodInstanceType.Finder);
    
        object result = entity.Execute(methodInstance, LobSysteminstance);
    
        return result;
    
      }
    
      public static void UpdateCustomer(int CustomerID, string FirstName, string MiddleName, string LastName)
    
      {
    
        IMetadataCatalog catalog = GetCatalog();
    
        IEntity entity = catalog.GetEntity(nameSpace, entityName);
    
        ILobSystemInstance LobSysteminstance = entity.GetLobSystem().
    
        GetLobSystemInstances()[0].Value;
    
        IMethodInstance methodInstance = entity.GetMethodInstance ("Update", MethodInstanceType.Updater);
    
        object[] args = { CustomerID, FirstName, MiddleName, LastName };
    
        entity.Execute(methodInstance, LobSysteminstance, ref args);
    
      }
    
      public static void DeleteCustomer(int CustomerID)
    
      {
    
        IMetadataCatalog catalog = GetCatalog();
    
        IEntity entity = catalog.GetEntity(nameSpace, entityName);
    
        ILobSystemInstance LobSysteminstance = entity.GetLobSystem().
    
        GetLobSystemInstances()[0].Value;
    
        IMethodInstance methodInstance = entity.GetMethodInstance("Delete", MethodInstanceType.Deleter);
    
        object[] args = { CustomerID };
    
        entity.Execute(methodInstance, LobSysteminstance, ref args);
    
      }
    
    }

    5. Now, just call these methods from the event handlers of your GridView.

    Surfacing data in a Visual Web Part, application page, or a user control, is not a whole lot different than the way that you do this in a plain vanilla ASP.NET Web application. Here a few take-away points:

    · You can configure data sources visually by using an ASP.NET project and port over your data source object. – or – you can just write XML that describes the data source yourself.

    · If you use a separate assembly for your data access code, make sure to sign the assembly and add that assembly to the package manager.

    You can get to data described in the Business Data Connectivity service by using the Business Data Connectivity services object model.

    Norm Estabrook

  • Visual Studio SharePoint Development Blog

    Visual Studio 2010 SharePoint Developer Tools ‘Import’ Templates

    • 2 Comments

    With Visual Studio 2010, Microsoft has introduced in-the-box tools for SharePoint development. Included in those tools are various templates for you guys to start off with e.g. Visual Webparts, Content Type, List Instances, Workflows etc. There are two templates provided which support what our team has come to call the continuum between SharePoint Designer and Visual Studio. The two templates are the ‘Import SharePoint Solution Package’ template and the ‘Import Reusable Workflow’ template.

    There seems to be some confusion around when to use the Import SharePoint Solution Package vs. Import Reusable Workflow templates. This blog post will get into the details of the differences and when we would want to use each of the templates. The key difference between the Import Reusable Workflow and the Import Solution Package is that the Import Reusable Workflow project template actually converts your declarative SharePoint Designer workflow into a VS code workflow that we can then open in the Visual Studio workflow designer, make edits, add code activities, debug and even add association or initiation forms to. The Import SharePoint Solution Package template on the other hand allows us to import reusable workflows (like the Import Reusable Workflow template), but the key difference is that it does not convert the workflow to a code workflow. This means we won’t be make edits, code additions or debug the workflow.

    The primary scenarios for wanting to use the Import Solution Package template are:

    • Importing custom SharePoint items (lists, content types, fields etc.) from an existing site
    • Importing entire existing site into Visual Studio and customizing it

    The primary scenarios for wanting to use the import Reusable Worklfow templates are:

    • Importing and converting Reusable Workflows (made in SharePoint Designer) into code workflows for the purposes of customizing the Workflow with custom activities and code.
    • Adding initiation and association forms to your converted Workflow

    The rest of the blog shows the step by step of how the VS ‘import’ templates differ.

    We will be using a solution package which was exported from a SharePoint Foundation site. This solution package contains a very simple workflow and a custom list instance both created in SharePoint Designer (SPD).

    The workflow looks like the following in SharePoint Designer:

    image

    This is a very simple Workflow which checks if the title field contains the words ‘New task’ and if it does the task is then assigned to the person who created it.

    The list instance looks like the following in SharePoint Designer:

    image

    Once we have created the Workflow and the Custom List, we can export the entire site via SharePoint (Site Actions -> Site Settings -> Save Site as Template). SharePoint will save this site as a WSP (SharePoint Solution Package) file and we will be using this solution package with the aforementioned templates and pointing out how they differ and when to use them.

    Import SharePoint Solution Package

    We start off by creating a new project in Visual Studio 2010 and use the ‘Import SharePoint Solution Package’ template from new project dialog:

    image

    For this blog, we’ll be using Visual Basic, but all SharePoint development templates are available in both C# and VB. I just like VB better :).

    After naming the project (‘wspimportblog’ in this case) we press “OK” and we see the SharePoint Customization Wizard page:

    image

    This wizard, which is the same for every SharePoint project template in VS, gives us the option of selecting which local site we want to choose for debugging and the trust level of this particular SharePoint solution. After pressing “Next” we get to a wizard which is specific to the Import Solution Package template. This particular page allows us to select the solution package we want to import from the file system:

    image

    After selecting the solution package that we exported above, we are presented with a list of items that are available within.

    image

    The wizard parses through the entire solution package and lists out all available items in the solution package along with their types .

    We can select the items we want to import using the checkbox next to each item. The checked items will be imported into your Visual Studio 2010 project by default, which means that if we went ahead with the wizard without changing the default selection we would end up importing the entire exported site into your VS project. For the purposes of this blog we just selected custom list instance (Shopping List), and the workflow created in SharePoint Designer (the workflow appears as a module instead of a workflow, I’ll explain why this happens later on):

    image

    If we scroll through the list of items to be imported in the wizard we will notice that there is a Workflow list instance in the list as well. This list instance is created when SharePoint designer is installed on the SharePoint server and SharePoint designer is used to deploy a Workflow. This list is where all the workflows (declarative or code) are deployed to. If we did not have SharePoint designer deployed Workflows on the machine we were deploying to, we would have to import the Workflow list instance as well in order to successfully deploy the imported workflow module on the target site.

    Hint: We can press Ctrl+A (select all) when one of the items in the list is selected. This will select all the available entries in this list and allow us to select or unselect all of the items in the list with a single click; something which is very useful when we just want to select a few items from the list unless you want to go and select or unselect every item individually :).

    Once we press Finish we might be presented with a ‘Selection Dependency Warning’ dialog:

    image

    When we make the selection of items we want to import (previous image) the wizard detects the dependencies of the selected items and prompts the user to ask if they want the dependencies imported along with the actual items. If we are importing a solution package which was generated on a site different to where we will deploy it back (target site), then it will be probable that all the required dependencies are not available on the target site and importing them will enable successful deployment of the selected items. In this case, we want all the dependencies to come into the project, so we should press “Yes”. The wizard parses out the items that are to be imported and builds a Visual Studio 2010 project containing all those items. This is what the Solution explorer should look like once the solution package has been successfully imported:

    image

    Notice how Visual Studio 2010 recognizes what is being imported. List Instances, Fields and Modules are placed under the List Instances, Field and Module folders etc. Furthermore the required features are created (3 in this case). One feature would contain the List, the other the workflow and finally there will be one feature for the imported property bags. Property bags, which can be required while deploying, will always be brought in and handled by VS i.e. they will not show up in the dependency warning dialog or in the item selection dialog, but they will be imported.

    Previously I mentioned that the workflow we were importing was showing up as a Module, because of which, we can now see that the Workflow is under the “Modules” folder in the Solution Explorer. This is because of the difference between how we import workflows in the ‘Import SharePoint Solution Package’ template and the ‘Import Reusable Workflow’ template. For this template (Import SharePoint Solution Package) we bring the workflow in as a module to be able to deploy it back to SharePoint. The workflow cannot be customized or debugged (If you want to customize or extend your declarative Workflow, move on to the next section and we’ll discuss how that can be done). However other items (non-workflow) can be used. E.g. if we imported a content type, or imported a field, we could use those with other SharePoint Items that we create within the project.

    When importing an entire site into Visual Studio, there may be items in the solution package that we don’t recognize. These unrecognized items will be placed in an “Other elements” folder within your solution; however this “Other elements” folder is not deployed.

    Pressing F5 deploys both the List and the Workflow up to SharePoint. Since Visual Studio 2010 treats the imported workflow as a module with this template, there isn’t an option to associate this workflow to a list from within Visual Studio 2010. To use the recently deployed workflow we will have to go inside SharePoint and associate the workflow with an appropriate list.

    Import Reusable Workflow

    Onto using the ‘Import Reusable Workflow’ template! We use the same solution package that we used with the ‘Import SharePoint Solution Package’ template in the previous section. In the ‘New Project’ dialog we select the ‘Import Reusable Workflow’ template and give it a name (‘workflowimport’ in this case):

    image

    Upon pressing “OK” we get to the SharePoint Customization Wizard. For the ‘Import Reusable Workflow’ template the wizard always defaults to ‘Deploy as Farm Solution’. This is because code workflows can only be deployed as a farm solution and therefore ‘Deploy as sandboxed solution’ is greyed out. The second wizard page lets us choose the solution package file from the file system, similar to the ones in ‘Import SharePoint Solution Package’ template. Once we select the solution package we want and press “Next” the wizard parses through the solution package and finds all Workflows contained within:

    image

    In the previous template, the workflow was recognized as a module which keeps the workflow as a declarative workflow that can’t be edited or extended with code in VS, but in the case of the ‘Import Re-usable Workflow’ template, the wizard not only recognizes the custom Workflow (Workflow1) it also converts the declarative workflow to a code workflow, as evidenced by the screen shot above. Once we press finish the VS project containing the workflow is created and the solution explorer should look something like:

    image

    The workflow is now recognized and placed under the “Workflows” folder. For further customization we can also associate the workflow from within VS using the ‘Workflow debug settings’ which is available when we right-click the workflow in the solution explorer or we could add an initiation or association form for this Workflow (Right-click on the Workflow -> Add new item -> Initiation or Association form). Once the workflow has been converted into a code workflow, it can be opened up in the workflow designer. The workflow designer has a whole list of controls that can be used to modify the workflow and write code to customize it, put in breakpoints to debug and step through the code etc. (that is a blog for another day :))

    image image

    With the ‘Import Reusable Workflow’ template we don’t need to import the Workflow list instance regardless of whether there is a SharePoint Designer deployed Workflow on the SharePoint server we are deploying to. Upon conversion and association Visual Studio 2010 makes sure that all the requirements for successful deployment of the workflow are covered.

    Once the workflow has been customized and associated, we can press F5 and the converted workflow is then deployed out to the Workflow List in the target SharePoint site.

    Wrap up

    Hopefully this blog helps answer some questions regarding the difference between the two import templates.

    The ‘Import Reusable Workflow’ template parses reusable WFs out of a solution package and converts them into code workflows so that the developer can customize and debug the workflow.

    The ‘Import SharePoint Solution Package’ template gives the developer the opportunity to select some or all of the items available inside the solution package and then work with them.

    For more information around the importing workflows and entire solution packages please refer to the following MSDN documentation:

    Please let us know if there are any questions around this topic and we can hopefully help you out. Happy Importing!

     

    Saaid Khan

  • Visual Studio SharePoint Development Blog

    Walkthrough of enabling CRUD for SharePoint 2010 external lists using Visual Studio 2010

    • 0 Comments

    In our previous blog post of this series Walkthrough of creating a SharePoint 2010 external list using Visual Studio 2010 Beta, we introduced how to create a simple “Hello world” external list in SharePoint 2010 Beta using Business Data Connectivity Designer in Visual Studio 2010 Beta.

    In this blog, we will show you how to pull data from an external database into an external list and enable Create, Read, Update and Delete (CRUD) functions to the external list.

    First of all, you need to have SharePoint 2010 Public Beta and Visual Studio 2010 installed on your machine in order to complete this walkthrough. We’ll use “Northwind” database as external data source, so if you do not have an existing “Northwind” database available, we’ll walk you through to create a local database using SQL Server Express first (SQL Server Express comes with Visual Studio installation by default, in case you don’t yet have it, download it here).

    At the end of this post, we will complete with a BDC model project which has a “Customer” entity connects to “Customer” table in “Northwind” database, and have CRUD operations enabled. The finished project can also be downloaded from here.

    Prepare the data source

    If you already have a “Northwind” database, you can skip this section. Otherwise, please download SharePoint2010_BDCSamples.zip from here and extract the SQL script file CreateSampleNorthwindDB.sql.

    Open Visual Studio. Go to View->Server Explorer. Right click Data Connections in Server Explorer, and select Create New SQL Server Database.

    1. In the prompt dialog, type “localhost\sqlexpress” in Server Name text box, and give the new database name “SampleNorthwind”.

    * If you're using the SQL Express that comes with SharePoint Server, please replace “localhost\sqlexpress" with "localhost\sharepoint”.

    2. Start a Command Prompt. Go to Start->Run, type “Cmd” in the text box and click OK.

    3. In the Command Prompt, type in following command and press enter:

    sqlcmd -S localhost\sqlexpress -d samplenorthwind -i <Path of CreateSampleNorthwindDB.sql file>

    Create BDC Project

    Create a new C# BDC Model project and rename it “BdcSampleCSharp”. VB code snippets will also be provided, so you can create VB BDC Model project if you want. In this walkthrough, we will use C# project as an example. (Check this blog post on how to create a BDC project)

    Connect to external data source

    To use the SampleNorthWind database, we add a LINQ to SQL model to the project:

    1. On the Project menu, click Add New Item, in the prompt Add New Item dialog select Data in the Installed Templates pane, in the Templates pane select LINQ to SQL Classes, in the Name box, type “Customer”, and then click Add.

    2. In the Server Explorer, go to Data Connections->[hostname]\sqlexpress.SampleNorthWind.dbo->Tables->Customers, drag the Customers table and drop it on the Customer.dbml design surface.

    3. Add a new class file and rename it “CustomerDataContext.cs”. Replace the code of the class with the following code snippet.

    Note: We made the connection string a constant in the code only for demo purpose, if you’re using your own database, modify the connection string as needed. In our future post we will introduce how to set the connection string in a custom property on LobSystemInstance inside the BDC model and read the value through IContextProperty interface at runtime.

    C#:

    public partial class CustomerDataContext
    {
      private const string ConnectionString = @"Data Source=localhost\SQLEXPRESS;Initial Catalog=SampleNorthwind;Integrated Security=True;Pooling=False";
      public CustomerDataContext() : 
                base(ConnectionString, mappingSource)
      {
        OnCreated();
      }
    }

    VB:

    Partial Public Class CustomerDataContext
      Private Const ConnectionString As String = "Data Source=localhost\SQLEXPRESS;Initial Catalog=SampleNorthwind;Integrated Security=True;Pooling=False"
      Public Sub New() MyBase.New(ConnectionString, mappingSource)
        OnCreated()
      End Sub
    End Class

    Design BDC Model

    1. On the design surface, delete entity Entity1 which is created by default. On the View menu, click on Toolbox if it is not shown. Create a new entity by drag and drop the Entity icon from Toolbox onto design surface (see the screenshot below). In the Properties Browser, change the value of Entity’s Name property to “Customer”.

    clip_image001

    2. Create a new Identifier CustomerID on entity Customer. To do so, on the design surface, right click the entity, click Add->Identifier. A new identifier appears on the entity, and then rename it to “CustomerID”.

    3. Create a Specific Finder method for the entity. To do so, on the design surface, select entity Customer, you could find a <Add a Method> command in the Method Details Window. If the Method Details Window is not opened, you can find it in menu View->Other Windows->BDC Method Details. From the <Add a Method> drop-down list, select Create Specific Finder Method:

    clip_image002

    A method named ReadItem appears on entity Customer. In the Method Details Window, you will find that the method takes a In parameter and a Return parameter. In the next step we will define TypeDescriptors associated with these parameters.

    4. Add TypeDescriptors for the return parameter Customer. The edit need to be done in BDC Explorer. You can find it by going to View->Other Windows->BDC Explorer.

    a) In the Method Details Window, click <Edit> command in the drop down control from TypeDescriptor Customer as depicted below. After the click the BDC Explorer will get focused on the TypeDescriptor Customer.

    clip_image003

    b) In BDC Explorer right click the focused TypeDescriptor Customer and select Add Type Descriptor:

    clip_image004

    A new TypeDescriptor is created under TypeDescriptor Customer. In the Properties Browser, rename it to “CustomerID”. Next you need to set the Identifier property to “CustomerID” to tell the BCS runtime which identifier this TypeDescriptor represents. Here is a screenshot of Properties Browser after this step:

    clip_image005

    c) Continue to add following TypeDescriptors under Customer by repeating the operation described above: Address, City, CompanyName, ContactName, Country, Fax, Phone, PostalCode and Region, for each TypeDescriptor, you need to change its Type Name to make sure they match the type defined in the LINQ to SQL model. In this example, all the TypeDesriptors have a type of System.String which is the default one so we do not need to change them. After this step, we get the following TypeDescriptors in BDC Explorer:

    clip_image006

    d) Next we need to define the actual type of the TypeDescriptor Customer. Click TypeDescriptor Customer in BDC Explorer. You will find in the Properties Browser the value of Type Name property is System.String by default, we need to change it to “BdcSampleCSharp.Customer, BdcModel1” which is a LobSystem qualified type name. This specifies the actual the data type of the data structure that is represented by this TypeDescriptor Customer.

    5. Create the other types of methods by the same way described in step 6. After this step we will have five methods in the entity Customer: ReadItem, ReadList, Create, Update and Delete. If you check the ReadList method in Method Details Window, the TypeDescriptors of the return parameter have already been defined with the same structure as we just built above. This is because when creating a new method, BDC designer will search the possible TypeDescriptors defined in the other methods of this entity and copy them to the newly created methods. This saves the developers so much time to define them repetitively.

    Add code behind to access external data source

    Now it’s time to add code to implement the CRUD functions. In Solution Explorer, find and open CustomerService.cs, and then replace the implementation with the following code snippet:

    C#:

    public static Customer ReadItem(string customerID)
    {
      CustomerDataContext context = new CustomerDataContext();
      Customer cust = context.Customers.Single(c => c.CustomerID == customerID);
      return cust;
    }
    
    public static IEnumerable<Customer> ReadList()
    {
      CustomerDataContext context = new CustomerDataContext();
      IEnumerable<Customer> custList = context.Customers;
      return custList;
    }
    
    public static Customer Create(Customer newCustomer)
    {
      CustomerDataContext context = new CustomerDataContext();
      context.Customers.InsertOnSubmit(newCustomer); context.SubmitChanges();
      Customer cust = context.Customers.Single(c => c.CustomerID == newCustomer.CustomerID);
      return cust;
    }
    
    public static void Delete(string customerID)
    {
      CustomerDataContext context = new CustomerDataContext();
      Customer cust = context.Customers.Single(c => c.CustomerID == customerID);
      context.Customers.DeleteOnSubmit(cust);
      context.SubmitChanges();
    }
    
    public static void Update(Customer customer, string customerID)
    {
      CustomerDataContext context = new CustomerDataContext();
      Customer cust = context.Customers.Single(c => c.CustomerID == customer.CustomerID);
      cust.CustomerID = customer.CustomerID;
      cust.Address = customer.Address;
      cust.City = customer.City;
      cust.CompanyName = customer.CompanyName;
      cust.ContactName = customer.ContactName;
      cust.ContactTitle = customer.ContactTitle;
      cust.Country = customer.Country;
      cust.Fax = customer.Fax;
      cust.Phone = customer.Phone;
      cust.PostalCode =customer.PostalCode;
      cust.Region = customer.Region;
      context.SubmitChanges();
    }

    VB:

    Public Shared Function ReadItem(ByVal customerID As String) As Customer
      Dim context As New CustomerDataContext
      Dim cust = (From c In context.Customers _
      Where c.CustomerID = customerID _
      Select c).Single()
      Return cust
    End Function
    
    Public Shared Function ReadList() As IEnumerable(Of Customer)
      Dim context As New CustomerDataContext
      Return context.Customers
    End Function
    
    Public Shared Function Create(ByVal newCustomer As Customer) As Customer
      Dim context As New CustomerDataContext
      context.Customers.InsertOnSubmit(newCustomer)
      context.SubmitChanges()
      
      Dim cust = (From c In context.Customers _
      Where c.CustomerID = newCustomer.CustomerID _
      Select c).Single()
      Return cust
    End Function
    
    Public Shared Sub Delete(ByVal customerID As String)
      Dim context As New CustomerDataContext
      Dim cust = (From c In context.Customers _
      Where c.CustomerID = customerID _
      Select c).Single()
      
      context.Customers.DeleteOnSubmit(cust)
      context.SubmitChanges()
    End Sub
    
    Public Shared Sub Update(ByVal customer As Customer, ByVal customerID As String)
      Dim context As New CustomerDataContext
      Dim cust = (From c In context.Customers _
      Where c.CustomerID = customer.CustomerID _
      Select c).Single()
    
      cust.Address = customer.Address
      cust.City = customer.City
      cust.CompanyName = customer.CompanyName
      cust.ContactName = customer.ContactName
      cust.ContactTitle = customer.ContactTitle
      cust.Country = customer.Country
      cust.Fax = customer.Fax
      cust.Phone = customer.Phone
      cust.PostalCode = customer.PostalCode
      cust.Region = customer.Region
      context.SubmitChanges()
    End Sub

    Create an external list to test out the BDC Model!

    Now we are done! Let’s deploy the solution and create an external list to test it out (Check this blog to see how to create an external list). After the list is created, you will see the following page appears when you click on the list name:

    clip_image007

    All the data are pulled out to SharePoint successfully, now you can create, delete or edit customer records.

    To create a new Customer, find the List Tools->Items->New Item button on the ribbon shown in the following screenshot:

    clip_image008

    Click on the button, New Item dialog will pop out as below:

    clip_image009

    Fill in the dialog and click Save, you will find the item created in the list right away.

    To edit or delete the item, just click the down arrow at the right side of CustomerID, all the operations you need are there.

    clip_image010

    Yanchen Wu

Page 1 of 1 (4 items)