Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

SharePoint MVP with Telerik ORM

4.67/5 (2 votes)
17 Sep 2009CPOL5 min read 27.5K   169  
This article represents an MVP solution, where the View is exposed to the SharePoint and Model generated by Telerik OpenAccess ORM

This article appears in the Third Party Products and Tools section. Articles in this section are for the members only and must not be used to promote or advertise products in any way, shape or form. Please report any spam or advertising.

Image 1

Introduction

This article represents the example of implementation MVP (Model-View-Presenter) pattern for SharePoint. The View layer is exposed to SharePoint 2007 MOSS (it can be exposed to WSS as well). The Model layer is implemented using the Telerik OpenAccess ORM.

Moreover the Telerik Rad ASP.NET AJAX Controls (RadComboBox and RadGrid) were used for data representation.

Prerequisites

In order to be able to use Telerik Rad controls under SharePoint, massive changes should be made to web application's web.config file. The article "SharePoint feature to extend SharePoint site with AJAX 3.5 and Telerik Rad Controls" explains how to automate the process of applying those changes.

The source code project was created using SPVisualDev, which uses WSPBuilder for deployment. So you should download and install them from CodePlex, or deploy and activate SharePoint feature manually.

Telerik OpenAccess ORM web.config Entries

Telerik OpenAccess ORM requires some web.config changes in addition to those mentioned above:

  • into 'configuration/configSections' section
    XML
    <section name="openaccess" type="Telerik.OpenAccess.Config.ConfigSectionHandler,
            Telerik.OpenAccess.Config, Version=1.0.0.0, Culture=neutral,
            PublicKeyToken=7ce17eeaf1d59342" requirePermission="false" />
  • into 'configuration' section
    XML
    <openaccess xmlns="http://www.telerik.com/OpenAccess">
      <references>
        <reference assemblyname="Dapasoft.Internal.MVP.Model" configrequired="True" />
      </references>
      <connections />
    </openaccess>

Also Telerik.OpenAccess.Query.dll should be added to the GAC.

Those changes can be applied manually or using the tool from the above mentioned article.

Script Manager

Telerik Rad ASP.NET AJAX Controls used in this sample require Script Manager.

My suggestion for now is to add it manually to the master page,

XML
<telerik:RadScriptManager ID="RadScriptManager1" runat="server">
  </telerik:RadScriptManager>

right after form tag, together with:

XML
<%@ Register Assembly="Telerik.Web.UI, Version=2009.2.826.35, Culture=neutral,
PublicKeyToken=121FAE78165BA3D4" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>

at the beginning of the page.

See, how it looks in SharePoint Designer.

Image 2

I didn't succeed in adding the Script Manager dynamically from web part code, but I am still going to endeavor.

Data Source

As a data source, Northwind database, installed on SQL Server 2008, was used. The App.Config section explains how to change those settings.

Model-View-Presenter

The application's solution is composed of three projects: Dapasoft.Internal.MVP.Model, Dapasoft.Internal.MVP.Presenter and Dapasoft.Internal.MVP.View, which represent layers of the MVP pattern.

Consider them one by one.

Model

The Model component is created using Telerik OpenAccess ORM. Most of the code in Dapasoft.Internal.MVP.Model project is generated by that tool. The tool provides the set of wizards, which make the customization process easy and intuitive. The Telerik provides Step-by-step Tutorial, which navigates you through the creation and customization of the project. The '4.1 Building the "Model" Assembly' chapter of this tutorial explains how to create the model. I'm not going to reiterate through it, but speak about different problems I was confronted with.

App.config

One of the generated files is App.config. It stores information about references, connections and mapping. Most likely, you are going to modify the name of you SQL server, inside the connections section. The servername tag allows you to specify it.

XML
<connections>
  <connection id="NorthwindConnection">
    <databasename>Northwind</databasename>
    <servername>[ServerName]</servername>
    <integratedSecurity>True</integratedSecurity>
    <backendconfigurationname>mssqlConfiguration</backendconfigurationname>
  </connection>
</connections>

The App.config file is compiled as an embedded resource. The namespace tag in the mapping section, records the default namespace during the use of Mapping wizard. Everything is fine, while you aren't going to modify the default namespace. I renamed it, and spent couple of hours trying to find why the application doesn't work. For some reason, the initial name is recorded somewhere and I wasn't able to clean it. Finally, I recreated the project from scratch.

Enhancing

The Model project has to pass the enhancing process "for adding persistence capability", as it is mentioned in the online help. The project property contains the Enhancing flag. Setting it to true should automatically start the enhancing process, i.e. run the venhance application, which is part of the package.

The problem arises, if the DLL is strongly signed and deployed into the GAC, which is the usual scenario for SharePoint applications.

First of all, the enhancing tool requires the AssemblyKeyFileAttribute to be specified in AssemblyInfo.cs, in spite of the fact that the project is signed using the project's properties. Moreover, it seems to be that the signed assembly requires manual enhancing using the post-build event:

"c:\Program Files\Telerik\OpenAccess ORM\sdk\venhance" -assembly:$(TargetPath)

Otherwise it doesn't work. Also if the assembly is already in the GAC, the enhancing process fails. Therefore I remove DLL from the GAC before each compilation using the pre-build event:

"c:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe" -u $(TargetName)

At last, according to the online help "if you have certain post-build steps, such as copying the assembly files to a particular location, the un-enhanced version of the files will be copied." Therefore placement of the assembly to the GAC is proceeded in the Dapasoft.Internal.MVP.Presenter, which represents the Presenter component.

Presenter

The role of the Presenter component is in communication between Model layer and View layer. Since, in this application, the presenter only passes the filtered data from Model to View, the project contains few classes.

There is the abstract class PresenterBase, which provides the prototype of GetFilteredData method:

C#
/// <summary>
/// PresenterBase class, represents a presenter base for MVP pattern.
/// </summary>
public abstract class PresenterBase
{
    #region Methods

    /// <summary>
    /// Gets the filtered data.
    /// </summary>
    /// <param name="selectedValue">The selected value.</param>
    /// <returns>Object, contains the filtered data.</returns>
    public abstract object GetFilteredData(int? selectedValue);

    #endregion // Methods
}

Also the ScopePresenter class, the concrete presenter, is implemented there:

C#
/// <summary>
/// ScopePresenter class is a presenter of the Telerik ORM model.
/// </summary>
public class ScopePresenter : PresenterBase, IDisposable
{
   #region Fields

    /// <summary>
    /// Database scope.
    /// </summary>
    private IObjectScope scope = NorthwindProvider.GetNewObjectScope();

    #endregion // Fields

    #region Constructors

    /// <summary>
    /// Enables a server control to perform final clean up 
    /// before it is released from memory.
    /// </summary>
    public void Dispose()
    {
        scope.Dispose();
    }

    #endregion // Constructors

    #region Methods

    /// <summary>
    /// Gets the filtered data.
    /// </summary>
    /// <param name="selectedValue">The selected value.</param>
    /// <returns>Object, contains the filtered data.</returns>
    public override object GetFilteredData(int? selectedValue)
    {
        var filteredData = from product in scope.Extent<Product>()
                   where product.Category.CategoryID == selectedValue
                   select new
                   {
                       Product = product.ProductName,
                       QuantityPerUnit = product.QuantityPerUnit,
                       Category = product.Category.CategoryName
                   };

        return filteredData.ToList();
    }

    #endregion // Methods
}

In addition, the mandatory component of MVP pattern, View interface:

C#
/// <summary>
/// IScopeView is an interface for view module of MVP pattern.
/// </summary>
public interface IScopeView
{
    #region Properties

    /// <summary>
    /// Gets or sets the presenter.
    /// </summary>
    /// <value>The presenter.</value>
    ScopePresenter Presenter { get; set; }

    #endregion // Properties
}

Deployment into the GAC

The presenter assembly should be deployed into the GAC. It can be done in the post-build event of the project:

"c:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe" -i
    $(TargetDir)Dapasoft.Internal.MVP.Model.dll
"c:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe" -i $(TargetPath)

As you can see, the Dapasoft.Internal.MVP.Model.dll is also deployed here, because of enhancing tool behavior, described above.

View

The View layer is an entry point of the MVP pattern. In our case, it's a GridUserControl wrapped into the WebPart. The GridUserControl contains inside the RadComboBox and RadGrid, bound to the data.

DapasoftTelerikORM Web Part

The DapasoftTelerikORM web part's content is built in the CreateChildControls method:

C#
/// <summary>
/// Called by the ASP.NET page framework to notify server controls that
/// use composition-based implementation to create any child controls they
/// contain in preparation for posting back or rendering.
/// </summary>
protected override void CreateChildControls()
{
    try
    {
        this.Controls.Clear();

        GridUserControl gridUserControl = (GridUserControl)this.Page.LoadControl
                ("/_controltemplates/DapasoftTelerikORM/GridUserControl.ascx");

        ScopePresenter presenter = new ScopePresenter();

        gridUserControl.Presenter = presenter;

        this.Controls.Add(gridUserControl);
    }
    catch (Exception ex)
    {
        // Put the error message locally on the page
        string errMessage = ex.GetType().ToString() + ": " + ex.Message;
        this.Controls.Add(new LiteralControl(errMessage));
    }
}

As you can see, the presenter is initialized here and passed to the GridUserControl.

GridUserControl User Control

Take a look into the GridUserControl:

ASP.NET
<%@Assembly Name="Dapasoft.Internal.MVP.View, Version=1.0.0.0,
    Culture=neutral, PublicKeyToken=92807702b91201c8" %>
<%@ Control Language="C#" AutoEventWireup="true"
    CodeBehind="~/ControlCode/GridUserControl.ascx.cs"
    Inherits="Dapasoft.Internal.MVP.View._12.
        TEMPLATE.CONTROLTEMPLATES.GridUserControl" %>
<%@ Register Assembly="Telerik.OpenAccess, Version=2009.2.701.5,
    Culture=neutral, PublicKeyToken=7ce17eeaf1d59342"
    Namespace="Telerik.OpenAccess" TagPrefix="telerik" %>
<%@ Register Assembly="Telerik.Web.UI, Version=2009.2.826.35,
    Culture=neutral, PublicKeyToken=121FAE78165BA3D4"
    Namespace="Telerik.Web.UI" TagPrefix="telerik" %>

<telerik:RadAjaxLoadingPanel ID="raLoadingPanel" runat="server" Skin="Default">
</telerik:RadAjaxLoadingPanel>
<telerik:RadAjaxPanel ID="raPanel" runat="server" LoadingPanelID="raLoadingPanel">
    <div>
        <telerik:RadComboBox ID="rcbProduct" runat="server"
            onselectedindexchanged="rcbProduct_SelectedIndexChanged"
            AutoPostBack="True">
        </telerik:RadComboBox>
        <br />
        <telerik:RadGrid ID="rgView" runat="server">
        </telerik:RadGrid>
    </div>
</telerik:RadAjaxPanel>
<br />
<telerik:OpenAccessDataSource ID="oadsNorthwind" runat="server"
    ContextTypeName="" EnableDelete="True" EnableInsert="True" EnableUpdate="True"
    ObjectContextProvider="Dapasoft.Internal.MVP.Model.NorthwindProvider,
    Dapasoft.Internal.MVP.Model, Version=1.0.0.0, Culture=neutral,
    PublicKeyToken=f08c7ad7626f2de2"
    OrderBy="" TypeName="Dapasoft.Internal.MVP.Model.Category" Where="">
</telerik:OpenAccessDataSource>

In addition to RadComboBox and RadGrid, there is OpenAccessDataSource control provided by Telerik OpenAccess ORM. It's used for binding of RadComboBox. Also there are RadAjaxLoadingPanel and RadAjaxPanel, providing AJAX functionality.

The Page_Load method of GridUserControl...

C#
/// <summary>
/// Handles the Load event of the Page control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> 
/// instance containing the event data.</param>
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        this.rcbProduct.DataSource = this.oadsNorthwind;
        this.rcbProduct.DataTextField = "CategoryName";
        this.rcbProduct.DataValueField = "CategoryID";
        this.rcbProduct.DataBind();

        BindGrid(1);
    }
}

... binds the RadComboBox to OpenAccessDataSource.

On index change of the RadComboBox,

C#
/// <summary>
/// Handles the SelectedIndexChanged event of the rcbProduct control.
/// </summary>
/// <param name="sender">The source of the event. </param>
/// <param name="e">The  
/// <see cref="Telerik.Web.UI.RadComboBoxSelectedIndexChangedEventArgs"/> 
/// instance containing the event data. </param>
protected void rcbProduct_SelectedIndexChanged
    (object sender, Telerik.Web.UI.RadComboBoxSelectedIndexChangedEventArgs e)
{
    if ((sender as RadComboBox).SelectedValue != null)
    {
        int? selectValue =
                  (int?)Int32.Parse((sender as RadComboBox).SelectedValue);

        BindGrid(selectValue);
    }
}

the BindGrid method is called, which binds the RadGrid to data, using Presenter:

C#
///  <summary>
/// Binds the grid.
///  </summary>
///  <param name="selectedValue">The selected value. </param>
private void BindGrid(int? selectedValue)
{
    if (this.Presenter != null)
    {
         this.rgView.DataSource = this.Presenter.GetFilteredData(selectedValue);
         this.rgView.DataBind();
    }
}

History

  • 16.09.2009 - Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)