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
<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
<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,
<telerik:RadScriptManager ID="RadScriptManager1" runat="server">
</telerik:RadScriptManager>
right after form
tag, together with:
<%@ 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.
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.
<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:
public abstract class PresenterBase
{
#region Methods
public abstract object GetFilteredData(int? selectedValue);
#endregion // Methods
}
Also the ScopePresenter
class, the concrete presenter, is implemented there:
public class ScopePresenter : PresenterBase, IDisposable
{
#region Fields
private IObjectScope scope = NorthwindProvider.GetNewObjectScope();
#endregion // Fields
#region Constructors
public void Dispose()
{
scope.Dispose();
}
#endregion // Constructors
#region Methods
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:
public interface IScopeView
{
#region Properties
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:
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)
{
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
:
<%@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
...
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
,
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:
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