/// <summary> /// Starts an existing custom C# activity locally and enables local debugging. You need to set a breakpoint in your custom component's code. /// </summary> /// <param name="pipelineName">The name of the pipeline which contains the custom C# activity</param> /// <param name="activityName">The name of the activity which you want to debug</param> /// <param name="sliceStart">SliceStart which is used when the activity is executed</param> /// <param name="sliceEnd">SliceStart which is used when the activity is executed</param> /// <param name="activityLogger">Allows you to specify a custom Activity Logger to do your logging. Default is a Console Logger.</param> /// <returns></returns> public IDictionary <string, string> ExecuteActivity(string pipelineName, string activityName, DateTime sliceStart, DateTime sliceEnd, IActivityLogger activityLogger) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Debugging Custom Activity '{0}' from Pipeline '{1}' ...", activityName, pipelineName); Console.WriteLine("The Code from the last build of the ADF project will be used ({0}). Make sure to rebuild the ADF project if it does not reflect your latest changes!", _buildPath); Dictionary <string, string> ret = null; string dependencyPath = Path.Combine(Environment.CurrentDirectory, "CustomActivityDependencies_TEMP"); if (Directory.Exists(dependencyPath)) { try { // it might happen that two activities are executed in the same run and the directory is blocked // so we need to catch the exception and continue with our execution // the folder might not be cleaned up properly in this case but during the execution of the first activity it will Directory.Delete(dependencyPath, true); } catch (UnauthorizedAccessException e) { } } if (!Pipelines.ContainsKey(pipelineName)) { throw new KeyNotFoundException(string.Format("A pipeline with the name \"{0}\" was not found. Please check the spelling and make sure it was loaded correctly in the ADF Local Environment and see the console output", pipelineName)); } // don not apply Configuration again for GetADFObjectFromJson as this would overwrite changes done by MapSlices!!! Pipeline pipeline = (Pipeline)GetADFObjectFromJson(MapSlices(_armFiles.Single(x => x.Value["name"].ToString() == Pipelines[pipelineName].Name).Value, sliceStart, sliceEnd), "Pipeline", false); Activity activityMeta = pipeline.GetActivityByName(activityName); // create a list of all Input- and Output-Datasets defined for the Activity List <Dataset> activityInputDatasets = _adfDataSets.Values.Where(adfDS => activityMeta.Inputs.Any(ds => adfDS.Name == ds.Name)).ToList(); List <Dataset> activityOutputDatasets = _adfDataSets.Values.Where(adfDS => activityMeta.Outputs.Any(ds => adfDS.Name == ds.Name)).ToList(); List <Dataset> activityAllDatasets = activityInputDatasets.Concat(activityOutputDatasets).ToList(); List <LinkedService> activityLinkedServices = new List <LinkedService>(); // apply the Slice-Settings to all relevant objects (Datasets and Activity) for (int i = 0; i < activityAllDatasets.Count; i++) { // MapSlices for the used Datasets activityAllDatasets[i] = (Dataset)GetADFObjectFromJson(MapSlices(_armFiles[activityAllDatasets[i].Name + ".json"], sliceStart, sliceEnd), "Dataset", false); // currently, as of 2017-01-25, the same LinkedService might get added multiple times if it is referenced by multiple datasets // this is the same behavior as if the activity was executed with ADF Service!!! activityLinkedServices.Add(_adfLinkedServices.Values.Single(x => x.Name == activityAllDatasets[i].Properties.LinkedServiceName)); } DotNetActivity dotNetActivityMeta = (DotNetActivity)activityMeta.TypeProperties; Console.WriteLine("The Custom Activity refers to the following ZIP-file: '{0}'", dotNetActivityMeta.PackageFile); FileInfo zipFile = _adfDependencies.Single(x => dotNetActivityMeta.PackageFile.EndsWith(x.Value.Name)).Value; Console.WriteLine("Using '{0}' from ZIP-file '{1}'!", dotNetActivityMeta.AssemblyName, zipFile.FullName); UnzipFile(zipFile, dependencyPath); Assembly assembly = Assembly.LoadFrom(dependencyPath + "\\" + dotNetActivityMeta.AssemblyName); Type type = assembly.GetType(dotNetActivityMeta.EntryPoint); IDotNetActivity dotNetActivityExecute = Activator.CreateInstance(type) as IDotNetActivity; Console.WriteLine("Executing Function '{0}'...{1}--------------------------------------------------------------------------", dotNetActivityMeta.EntryPoint, Environment.NewLine); Console.ForegroundColor = ConsoleColor.Gray; ret = (Dictionary <string, string>)dotNetActivityExecute.Execute(activityLinkedServices, activityAllDatasets, activityMeta, activityLogger); if (Directory.Exists(dependencyPath)) { try { // This might fail as the DLL is still loaded in the current Application Domain Directory.Delete(dependencyPath, true); } catch (UnauthorizedAccessException e) { } } return(ret); }