Showing results after a SharePoint Longrunning operation on the same page

To be honest, this is quite a hack, however it can still be used on SHP2010 and SHP2013.

The basic idea is to do the following :

  • In page_load create an SPLongOperation
  • Let the operation Begin
  • Execute the Database call in the operation
  • Do not provide an end on the operation, so it won’t redirect
  • Add javascript to the bottom of the page
//This code goes into the code-behind of the application page
 public string WindowTitle
        {
            get
            {
                return SPEncode.HtmlEncode(SPContext.Current.ListItem.Title);
            }
        }

 protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                this.EnsureChildControls();
                using (SPLongOperation lng = new SPLongOperation(this.Page))
                {
                    //Custom Messages on the Spinning Wheel Screen
                    lng.LeadingHTML = "Loading items from DB...";
                    lng.TrailingHTML = "Please wait while items are loaded...";
                    //Start the long operation
                    lng.Begin();

                    LoadItems();
                }
}

asp.net code add to bottom of the application page:

<script type="text/javascript">
        var gearsItem = document.getElementById('GearPage');
        if (gearsItem) {
            gearsItem.style.display = 'none';
            document.title = '<%=WindowTitle %>'; //set the title of the page to the title of the Current SPListItem
        }
    </script>

I’ve done this in a SharePoint modal window, the result is that the database resultset is shown in the same modal window without the need to redirect the user to a results page!

This is shown while the data is loading :
data2

Then this is shown once the data has loaded :
data1

IQueryable gotcha!

Regarding to the title, this could have also been written as : “Why isn’t my IQueryable updated with values from another object” or “IQueryable properties not updating”.

In short, lets say you’re working with data coming from two separate databases and you need to join the data into one Data Transfer Object for displaying purposes.

First off: you should create a view on SQL-level that joins these two tables and expose it in your EF model.

However there are some requirements for this :

  • The databases must be within reach of eachother ( on the same machine, for instance)
  • You need to have permissions to create the view

In my case, the latter could not be done, neither do I have sufficient permissions, nor am I allowed to update the DB-schema.

So I created two EF models (Model-First) :
=> MyApplicationDatabase.edmx
==> MyUser Table
==> MyTicket Table
=> MyCompanyDatabase.edmx
==> Employee Table
==> Department Table
==> Etc

In My Application I needed to display all the business information for the current logged in user.
So this meant going through the MyCompanyDatabase model and loading this additional information.

Eventually. my code looked something like this :

public DTO.MyTicket[] GetLatestTickets(int top = 10)
{
      var topTickets = (from ticket in myApplicationDbContext.MyTickets
                        select new DTO.MyTicket{
                           TicketOwner = ticket.OwnerId,
                           Created = ticket.Created,
                           Modified = ticket.Modified,
                           Price = ticket.Price
                        }).Take(top);
      //at this point in time, the topTickets array is not yet been executed
      
      foreach(var topTicket in topTickets)
      {
           topTicket.PriceInEuro = topTicket.Price * 1.386;
      }
      
      return topTickets.ToArray();//Now the query gets executed and returns the ticket, without filling in the PriceInEuro
}

This is due to “deferred execution”, you can find numerous examples of this on the web.

In order to make sure the missing references and/or properties are filled in, you need to execute the IQueryable so that you have a reference to an actual collection of items.

This is easy, you just move the ToArray() to the first LINQ statement;
This will execute the db query and put the results in the topTickets object, you can then alter any item in the array and return the modified array from the method.

It’s a typical ‘gotcha’ when dealing with EF, I’ve thought my inner objects where blank due to JSON serialization on WCF, instead I made this classic mistake =)

Here’s the solution :

public DTO.MyTicket[] GetLatestTickets(int top = 10)
{
      var topTickets = (from ticket in myApplicationDbContext.MyTickets
                        select new DTO.MyTicket{
                           TicketOwner = ticket.OwnerId,
                           Created = ticket.Created,
                           Modified = ticket.Modified,
                           Price = ticket.Price
                        }).Take(top).ToArray();
      
      foreach(var topTicket in topTickets)
      {
           topTicket.PriceInEuro = topTicket.Price * 1.386;
      }
      
      return topTickets;
}

Cheers,

Maarten