I've been pulling my hair out for weeks now and I need some help. This is a SharePoint utility that copies RDL files to project sites. Why on earth are my IAsyncResult.EndInvokes called after processing the entire list? On average, I can copy all RDL files to 1 project site in 10 seconds. With this in mind, I would expect to hit my breakpoint inside the IAsyncResult method within a minute. Instead, the breakpoint doesn't hit until after all reports have been copied to all projects, which then the breakpoint hits in a rapid fire succession. What am I doing wrong here?
I'm constrained to using .NET 3.5 so TAP processing is not an option :(
Delegate:
public delegate string CopyReportsParallel(string targetSiteUrl, List<SourceReportInfo> reports, EditorSettings settings);
This method compiles the list of RDL's and target project sites. For each target site, a delegate it created to handle the copy logic:
private static void bw_DoWork(Object sender, DoWorkEventArgs e)
{
int workerIndex = bwObject.targetProjectSiteUrls.Count;
_totalCount = bwObject.targetProjectSiteUrls.Count;
foreach (string s in bwObject.targetProjectSiteUrls)
{
if (cancelled)
break;
//Get a delegate for the operation
CopyReportsParallel d = new CopyReportsParallel(ParallelCopyLogic);
IAsyncResult ar = d.BeginInvoke(s, bwObject.reports, bwObject.settings, BackgroundWorkerIsDone, d);
}
waitHandle.WaitOne();
}
Here is my call back method. Ideally I would want as many threads to process asynchronously and report back immediately after finishing so that I could update the webpage with a progress bar.
private static void BackgroundWorkerIsDone(IAsyncResult ar)
{
CopyReportsParallel d = (CopyReportsParallel)ar.AsyncState;
string result = d.EndInvoke(ar);
lock ((_lockObject))
{
_completedCount += 1;
if (logTheHistory)
{
int completedPercent = _completedCount * 100 / _totalCount;
string report = result + Environment.NewLine + "Update - " + completedPercent.ToString() + "% Complete " + " # of Projects Completed: " + _completedCount.ToString();
bw.ReportProgress(completedPercent, report);
}
if (_completedCount == _totalCount)
{
waitHandle.Set();
}
}
}
The parallel copy logic (per request):
private static string ParallelCopyLogic(string targetSiteUrl, List<SourceReportInfo> reports, EditorSettings settings)
{
string _runDetail = "";
_runDetail += (System.DateTime.Now.ToString() + " - Working on Project: " + targetSiteUrl + Environment.NewLine);
using (_reportSvc = new ReportingService2010())
{
_reportSvc.Url = reportSvcURl;
_reportSvc.Credentials = System.Net.CredentialCache.DefaultCredentials;
_reportSvc.PreAuthenticate = true;
_reportSvc.Timeout = Timeout.Infinite;
using (SPSite targetSite = new SPSite(targetSiteUrl))
using (SPWeb targetWeb = targetSite.OpenWeb())
{
foreach (SourceReportInfo reportInfo in reports)
{
try
{
SPDocumentLibrary targetReportLibrary = targetWeb.Lists[reportInfo.SourceReport.ParentList.RootFolder.Name] as SPDocumentLibrary;
SPFile targetReportFile = targetReportLibrary.RootFolder.Files.Add(reportInfo.SoureReportUrl, reportInfo.SourceReportFileBytes, reportInfo.SourceReport.Properties, true);
SPListItem targetReport = targetReportFile.GetListItem();
string sourceURL = reportInfo.SourceReport.Web.Url + "/" + reportInfo.SourceReport.Url;
string destinationURL = targetReport.Web.Url + "/" + targetReport.Url;
string targetListUrl = targetReport.Web.Url + "/" + targetReport.ParentList.RootFolder.Name;
bool dsUpdated = false;
bool paramUpdated = false;
bool ddUpdated = false;
if (!targetReport.HasUniqueRoleAssignments)
targetReport.BreakRoleInheritance(true);
foreach (SPRoleAssignment role in reportInfo.RoleAssignments)
targetReport.RoleAssignments.Add(role);
targetReport.Update();
// Update copied Items
if (reportInfo.SoureReportUrl.EndsWith(".rdl"))
{
SPSecurity.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(delegate()
{
ReportService.DataSource[] sourceDataSources = _reportSvc.GetItemDataSources(sourceURL);
ReportService.DataSource[] destDataSources = _reportSvc.GetItemDataSources(destinationURL);
for (int i = 0; i < sourceDataSources.Length; i++)
{
DataSourceReference sourceDs = sourceDataSources[i].Item as DataSourceReference;
DataSourceDefinition sourceDef = sourceDataSources[i].Item as DataSourceDefinition;
DataSourceDefinition destDef = destDataSources[i].Item as DataSourceDefinition;
// Updates the target data source references
if (sourceDs != null && sourceDs.Reference != null && sourceDataSources[i].Name == destDataSources[i].Name)
{
string sourceDsName = System.IO.Path.GetFileName(sourceDs.Reference);
destDataSources[i].Item = new DataSourceReference { Reference = targetListUrl + "/" + sourceDsName };
dsUpdated = true;
}
//Updates the target data source definitions
else if (sourceDef != null && destDef != null && sourceDataSources[i].Name == destDataSources[i].Name)
{
//Set data source credentials
destDef.Enabled = true;
destDef.CredentialRetrieval = sourceDef.CredentialRetrieval;
destDef.ImpersonateUser = sourceDef.ImpersonateUser;
destDef.WindowsCredentials = sourceDef.WindowsCredentials;
if (settings != null && destDef.CredentialRetrieval == CredentialRetrievalEnum.Store)
{
destDef.UserName = settings.DataAccessUserName;
settings.GetDataAccessPassword(destDef);
}
destDataSources[i].Item = destDef;
ddUpdated = true;
}
}
if (dsUpdated | ddUpdated)
{
_reportSvc.SetItemDataSources(destinationURL, destDataSources);
dsUpdated = false;
ddUpdated = false;
}
//Updates the target report's project code default parameter
ItemParameter[] parameters = _reportSvc.GetItemParameters(destinationURL, null, false, null, null);
foreach (ItemParameter item in parameters)
{
//Have to check if prompt text is empty as this API doesnt expose hidden property
if (item.Name.ToLower() == "projectcode" && string.IsNullOrEmpty(item.Prompt) && item.DefaultValues.Length == 1)
{
item.DefaultValues[0] = targetWeb.Name;
paramUpdated = true;
break;
}
}
if (paramUpdated)
{
_reportSvc.SetItemParameters(destinationURL, parameters);
paramUpdated = false;
}
}));
}
else if (reportInfo.SoureReportUrl.EndsWith(".rsds"))
{
//Update Shared Datasets
DataSourceDefinition sourceDef = _reportSvc.GetDataSourceContents(sourceURL);
DataSourceDefinition destinationDef = _reportSvc.GetDataSourceContents(destinationURL);
destinationDef.Enabled = true;
destinationDef.CredentialRetrieval = sourceDef.CredentialRetrieval;
destinationDef.ImpersonateUser = sourceDef.ImpersonateUser;
destinationDef.WindowsCredentials = sourceDef.WindowsCredentials;
if (settings != null && destinationDef.CredentialRetrieval != CredentialRetrievalEnum.Integrated)
{
destinationDef.UserName = settings.DataAccessUserName;
settings.GetDataAccessPassword(destinationDef);
}
_reportSvc.SetDataSourceContents(destinationURL, destinationDef);
}
_runDetail += (System.DateTime.Now.ToString() + " - Finished updating report: " + targetReport.Name.ToString() + Environment.NewLine);
}
catch (Exception ex)
{
_runDetail += (System.DateTime.Now.ToString() + " - There was an error on project: " + targetSiteUrl + Environment.NewLine);
throw new InvalidOperationException(string.Format("Error while updating report {0} on Project Site {1}. Exception:{2}", reportInfo.SoureReportUrl, targetSiteUrl, ex.Message), ex);
}
}
}
}
return _runDetail;
}
Please let me know what you think or if you need any more info. I've looked and looked and looked so I'm not trying to have anyone "do my homework here". I would love to learn a thing or two from some more seasoned programmers.
Thanks!