RSS
 

Posts Tagged ‘dynamic data’

Working with DynamicDataRoute in SharePoint WSS.

02 Nov

First of all, this wasn’t an easy job to make it work. SharePoint WSS 3.0 is an older technology as ASP.Net MVC and ASP.Net Dynamic Data. But I found a workaround where you can integrate your own application pages that are build using Dynamic Data Objects. You need to be familiar how to wrap your pages in a SharePoint Feature in order to make everything work (Creating Application Pages in SharePoint).

dynamic data in sharepoint

dynamic data in sharepoint

First Problem

The first problem when you’re trying to convert your current Dynamic Data Website is the Global.asax. In your existing application, you can use CodeBehind files, you can work with syntax of the .NET Framework 3.0. In SharePoint, you have to keep in mind that we’re working with references to assemblies and that we can only use the .NET Framework 2.0. I didn’t find a way to debug my SharePoint Global.asax so writing code in the Global.asax isn’t easy to test.

Our Current DynamicData Website (.NET v3.0 with CodeBehind) Global.asax.cs:

public class Global : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            MetaModel model = new MetaModel();

            //                    IMPORTANT: DATA MODEL REGISTRATION
            // Uncomment this line to register LINQ to SQL classes or an ADO.NET Entity Data
            // model for ASP.NET Dynamic Data. Set ScaffoldAllTables = true only if you are sure
            // that you want all tables in the data model to support a scaffold (i.e. templates)
            // view. To control scaffolding for individual tables, create a partial class for
            // the table and apply the [Scaffold(true)] attribute to the partial class.
            // Note: Make sure that you change "YourDataContextType" to the name of the data context
            // class in your application.
            model.RegisterContext(typeof(YOUREntityFrameWorkModel), new ContextConfiguration() { ScaffoldAllTables = false });

            // The following statement supports separate-page mode, where the List, Detail, Insert, and
            // Update tasks are performed by using separate pages. To enable this mode, uncomment the following
            // route definition, and comment out the route definitions in the combined-page mode section that follows.
            routes.Add(new DynamicDataRoute("{table}/{action}.aspx")
            {
                Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
                Model = model
            });

            // The following statements support combined-page mode, where the List, Detail, Insert, and
            // Update tasks are performed by using the same page. To enable this mode, uncomment the
            // following routes and comment out the route definition in the separate-page mode section above.
            //routes.Add(new DynamicDataRoute("{table}/ListDetails.aspx") {
            //    Action = PageAction.List,
            //    ViewName = "ListDetails",
            //    Model = model
            //});

            //routes.Add(new DynamicDataRoute("{table}/ListDetails.aspx") {
            //    Action = PageAction.Details,
            //    ViewName = "ListDetails",
            //    Model = model
            //});
        }

        void Application_Start(object sender, EventArgs e)
        {
            RegisterRoutes(RouteTable.Routes);
        }

    }

Solution for our first problem:

System.Web.DynamicData.DynamicDataRoute(“MyDocLibData/{table}/{action}.aspx”); and model.DynamicDataFolderVirtualPath = “/MyDocLib/DynamicData”; will be explained later in this post.

Our Converted SharePoint Website (.NET v2.0 without CodeBehind) Global.asax:

<%@ Assembly Name="Microsoft.SharePoint" %>
<%@ Application Language="C#" Inherits="Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication" %>
<%@ Assembly Name="System.Web.Routing" %>
<%@ Assembly Name="System.Web.DynamicData" %>


Second Problem

This problem was the hardest one to solve: The use of URL Routing in SharePoint. Suppose we’re using IIS7, you can see that SharePoint is running in Classic mode. But… URL Routing Module can only run in Integrated Mode. The Managed Pipelines Integrated Mode is more efficient than the Classic Mode. You should consider using the Integrated Mode for all your new applications but sometimes we really have to use the Classic Mode because we want to be backwards compatible! That scenario is the same for SharePoint, so we’re stuck with Classic Mode.

MVC has some workarounds to get things working in Classic Mode. They didn’t work for me at all, maybe because we’re working with DynamicDataRouting instead of MVC URL Routing. But just to let you know I’ve tried this (Using ASP.NET MVC with Different Versions of IIS (C#))

The changes you’ll have to apply to your SharePoint Web.Config to enable Dynamic Data are listed here: How To: Add Dynmic Data to an Existing Web Site. Make sure you add them all to your Web.config.

If you look closely at the modifications you’ll have to adjust to your Web.Config you’ll see the following step:

In the handlers element, add an add element, set its name to “UrlRoutingHandler”, and set its additional attributes as shown in the following example


  
    
  

This is were I discovered the Classic vs. Integrated Mode. Just to prove you that this won’t work in Classic Mode.

What we use SharePoint to browse to an URL that has a DynamicData Route we get an Error 404 Page Not Found. Again, it’s normal because the URL Rewriting Process doesn’t work at all.

Solution for our second problem:

I’ve added all SharePoint Application Pages to a Document Library. When you browse to a page you get the following URL: http://localhost:port/MyDocLib/MyDynamicDefaultPage.aspx. Let’s pay attention that you’re requesting a relative page, it’s not a root page. Because we’re forced to use this structure in SharePoint, we need to set the property in our Code.

model.DynamicDataFolderVirtualPath = “/MyDocLib/DynamicData”;

Don’t add a ~ before your path (/MyDocLib/DynamicData). If you put it there, you will receive the following error:

Exception Details: System.ArgumentException: virtualPath

Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:
[ArgumentException: virtualPath]
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.IsExcludedPath(String virtualPath) +329
Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider.DirectoryExists(String virtualPath) +103

In our Global.asax (the converted SharePoint Global.asax as you copied from above), we’ll check each URL Request. If the Request has the same DynamicData Path that we described in our Global.asax we’ll execute the following code (add this to your Global.asax!):

    const string _tablenames = "YourEFTable1,YourEFTable2";
    const string _extension = ".aspx";
    const string _pages = "List|Details|Edit|Insert";

void Application_BeginRequest(object sender, EventArgs e)
    {
        if (Request.Path.StartsWith("/MyDocLibData/"))
        {
            string[] s = Request.Path.Split('/');

            if (s.Length >= 3)
            {
                string[] tablenames = _tablenames.Split(',');
                foreach (string table in tablenames)
                {
                    if (s[2] == table)
                    {
                        string page = s[s.Length - 1];

                        if (page.EndsWith(_extension) && _pages.ToLower().Contains(page.Split('.')[0].ToLower()))
                        {
                            if (System.IO.File.Exists(@"C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\FEATURES\YourFeatureDirectory\DynamicData\CustomPages\" + table + @"\" + page))
                            {
                                Context.RewritePath("\\MyDocLib\\DynamicData\\CustomPages\\" + table + "\\" + page);
                            }
                            else
                            {
                                Context.RewritePath("\\MyDocLib\\DynamicData\\PageTemplates\\" + page + "?" + Request.QueryString + "&TableName="+table);
                            }
                        }
                        break;
                    }
                }
            }
        }

Basically what it does is the following:
Let’s see if the Request has the Same Path we defined in our Global.asax file as Dynamic Data URL Path. We do this check because each other call will be out of the Method scope immediately, there might be other Requests for each Page that’s loading (the page itself, css, images, …). Pay attention to MyDocLibData URL that will be generated as our link (we defined it before in our Global.asax).

Once we’re sure we are dealing with a Dynamic Path, let’s look if we have an existing Table in the URL. And finally, we rewrite the internal URL of the Website (the routing process). We do a rewrite to the Dynamic Page if the Page exists, otherwise we take the default operation page that exists in PageTemplates (generic CRUD templates).

Okay, our page is Routed, but because we’re rewriting the path to the internal URL representation, we will have to manually set some properties to let our DynamicData work. You need to set your ContextType and EntitySetName for your DataControls. This is an example.

 protected void Page_Init(object sender, EventArgs e)
        {
            string CurrentTableFromURL = Request.Path.Split('/')[4];
            // Lets set the context for the control manually (Routing isn't working)
            GridDataSource.ContextType = typeof(YOUREntityFrameWorkModel);
            // Let's add the table name
            GridDataSource.EntitySetName = CurrentTableFromURL;

            DynamicDataManager1.RegisterControl(GridView1, true /*setSelectionFromUrl*/);

            FilterRepeater.ContextTypeName = typeof(YOUREntityFrameWorkModel).ToString();
            FilterRepeater.TableName = CurrentTableFromURL;
        }

This will give you an idea how we can deal with DynamicData and URL Routing in SharePoint. I wish you luck, you’ll need it for sure :) .