I had a requirement to create an Aggregation Web Part to combine information from different sites in the same site collection. Moreover, it need to be represented as a table with sortable, filterable and exportable features enabled. Basically similar behavior we have in SharePoint OOTB lists.
We have here three main challenges:
Data Access Layer: get all the information thru Site Collection efficiently.
Presentation Layer: display all the information in a table with sortable and filterable columns.
Export to excel: Add this feature to table.
Data Access Layer
The best object to get information thru all Site Collection in terms of performance is SPSiteDataQuery.
public DataTable PopulateDatasetSPSiteDataQuery(string sortExpression, string contentType, string fieldsNames) { if (!string.IsNullOrEmpty(contentType)) { if (HttpRuntime.Cache[CACHE_KEY] == null) { string status = "The following items are NOT fetched from the cache"; //In order to know all field Internal Names: $dependenciesList.Fields | Select InternalName SPSiteDataQuery q = new SPSiteDataQuery(); string[] fields = getFieldsNames(fieldsNames);
using (SPWeb w = new SPSite(String.Concat(HttpContext.Current.Request.Url.Scheme, "://", HttpContext.Current.Request.Url.Authority, "/PWA/")).OpenWeb()) { DataTable oDataTable = new DataTable(); using (SPMonitoredScope populateDataSet = new SPMonitoredScope("GetSiteData_PopulateData")) { try { oDataTable = w.GetSiteData(q); Logger.LogInfo(Logger.Constants.LogSource.LOGSRC_COMMON, "Number of items from GetSiteData_PopulateData: " + oDataTable.Rows.Count); } catch (Exception) { oDataTable = null;
}
}
if (oDataTable != null) { newTable.Columns.Add("ProjectName"); foreach (DataColumn col in oDataTable.Columns) // Loop over the rows. { newTable.Columns.Add(col.ColumnName); }
foreach (DataRow row in oDataTable.Rows) // Loop over the items. { DataRow newRow = newTable.Rows.Add();
newRow["ProjectName"] = row["FileDirRef"].ToString().Split('/')[1]; foreach (DataColumn col in oDataTable.Columns) // Loop over the rows. { string initialValue = row[col.ColumnName].ToString(); string finalValue = initialValue; if (!string.IsNullOrEmpty(initialValue)) { if (col.ColumnName.Equals("Team") || col.ColumnName.Equals("Depends_x0020_On")) { try { SPFieldLookupValue lookupGroup = new SPFieldLookupValue(initialValue); finalValue = lookupGroup.LookupValue; } catch (Exception) { } } if (col.ColumnName.Equals("Due_x0020_Date") || col.ColumnName.Equals("DueDate")) { finalValue = ""; DateTime date; if (DateTime.TryParse(row[col.ColumnName].ToString(), out date)) { finalValue = date.ToString("dd/MM/yyyy"); }
HttpRuntime.Cache.Add(CACHE_KEY, newTable, null, DateTime.MaxValue, TimeSpan.FromMinutes(CACHE_MINUTES), System.Web.Caching.CacheItemPriority.Default, null); } else { string status = "The following items ARE fetched from the cache!"; newTable = (DataTable)HttpRuntime.Cache[CACHE_KEY]; } }
//clean up the sort expression if needed - the sort descending //menu item causes the double in some cases if (sortExpression.ToLowerInvariant().EndsWith("desc desc")) sortExpression = sortExpression.Substring(0, sortExpression.Length - 5);
//need to handle the actual sorting of the data if (!string.IsNullOrEmpty(sortExpression)) { try { DataView view = new DataView(newTable); view.Sort = sortExpression; DataTable newTable2 = view.ToTable(); newTable.Clear(); newTable.Merge(newTable2); } catch (Exception ex) { LiteralControl literal = new LiteralControl(string.Concat("SortExpression: ", ex.ToString())); this.Controls.Add(literal); } }
return newTable; }
Presentation Layer
After evaluating different alternatives the easiest approach is to use SPGridView because it provides OOTB features like filtering and sorting.
System.IO.MemoryStream stream = new System.IO.MemoryStream(); using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook, true)) { DataSet ds = new DataSet(); var dv = (DataView)gridDS.Select(); if (dv != null && dv.Count > 0) { DataTable dt = dv.ToTable(); //Check to hide columns List<String> columnsInGrid = new List<string>(); if (oGrid.HeaderRow != null) { for (int i = 0; i < oGrid.HeaderRow.Cells.Count; i++) { DataControlFieldHeaderCell headerfield = oGrid.HeaderRow.Cells[i] as DataControlFieldHeaderCell; DataControlField spbf = headerfield.ContainingField as DataControlField; //Notice that SortException allways must be the same that DataField columnsInGrid.Add(spbf.SortExpression); //["DataField"]); } } for (int i = dt.Columns.Count - 1; i >= 0; i--) { if (!columnsInGrid.Contains(dt.Columns[i].ToString())) { dt.Columns.RemoveAt(i); } } ds.Tables.Add(dt); } ExportExcel.WriteExcelFile(ds, document);
// NOTE: If you get an "HttpCacheability does not exist" error on the following line, make sure you have // manually added System.Web to this project's References. string filename = "Report.xlsx";