Advanced Code Tasks with the Package Deployer

Running simple, one-hit code tasks in the Package Deployer is pretty easy and works when you have one or two projects that you are working on.

However, I’m pretty lazy and not much for running the same patterns over and over again.

To this end, I came up with a Framework that requires you to only code your Package once while letting you maintain your tasks in another project that you don’t need to reference directly.

The implementation is accomplished via an Interface library that gets shared between projects.  In this case, I called it “IDeploymentTask” and gave it some property and events to be used while it does it work so it can update the UI accordingly.  This interface is located in a common project that both my Package Deployer users and now this library.

The “brains” of the operation has been added to the Package Deployer itself where the following code looks for dlls in the currently executing directory and pulls out whatever dlls support that interface (using Reflection).  It then executes those classes, updating the deployer as it goes.

private void LoadAssemblies()
{
if (string.IsNullOrEmpty(_configFileDir))
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
_configFileDir = Path.GetDirectoryName(path);
}
}


public void ExecuteTasks()
{
try
{
Assembly assembly = null;

if (string.IsNullOrEmpty(_configFileDir))
{
LoadAssemblies();
}

Dictionary<int,IDeploymentTask> taskPriority = new Dictionary<int, IDeploymentTask>();

foreach (string f in Directory.GetFiles(_configFileDir, "*.dll"))
{
assembly = Assembly.Load(AssemblyName.GetAssemblyName(f));
System.Diagnostics.Debug.WriteLine("ASSEMBLY: " + assembly.FullName);

foreach (Type t in assembly.GetTypes())
{
if (typeof(IDeploymentTask).IsAssignableFrom(t) && !t.IsInterface)
{
IDeploymentTask task = (IDeploymentTask)(Activator.CreateInstance(assembly.FullName, t.FullName).Unwrap());
task.Service = this.CrmSvc.OrganizationServiceProxy;

if (!taskPriority.ContainsKey(task.ExecutionPriority)) { 
taskPriority.Add(task.ExecutionPriority,task);
}
}
}
}

var priorities = taskPriority.Keys.ToList();
priorities.Sort();

//Order the Tasks by Priority and Run them.
foreach (int key in priorities)
{
taskPriority[key].OnTaskStarted += Task_OnTaskStarted;
taskPriority[key].OnTaskError += Task_OnTaskError;
taskPriority[key].OnTaskCompleted += Task_OnTaskCompleted;

taskPriority[key].Init();
taskPriority[key].Execute();
}

}
catch (Exception ex)
{
PackageLog.Log("ExecuteTaskError: " + ex);
}
}

private void Task_OnTaskStarted(object sender, DeploymentEventArgs e)
{
CreateProgressItem(String.Format("{0}, Priority: {1}.",e.Details,e.Priority.ToString()));
}

private void Task_OnTaskCompleted(object sender, DeploymentEventArgs e)
{
RaiseUpdateEvent(e.Details, ProgressPanelItemStatus.Complete);
}

private void Task_OnTaskError(object sender, DeploymentEventArgs e)
{
RaiseUpdateEvent(e.Details, ProgressPanelItemStatus.Failed);
}

You might be thinking that this is a little overkill, however, we have 15 Dynamics365 solutions we need to validate across 7 environments that I’ve become too tired to manually check and validate and make sure that each has the right set of components.  In addition, we have in the realm of 15 – 17 tasks that must get executed post-deployment which are managed by multiple individuals and have become prone to error over the last year.

In addition, it’s important to note that each project can determine it’s own priority so tasks are executed in the proper order (in case people don’t talk to each other).

What this solution does is enable the following to occur;

  1. Any user can build their own Custom Code Task and not mess up my Package Deployment solution.
  2. The release manager only needs to drop updated solution files into our project and can then sit back and deploy code to a variety of environments, safe in the knowledge that everything is being done consistently.
  3. Deployment times and post validation will decrease because we’ll be easily alerted to what is different between environments.

In a large deployment, this will save time but more importantly, our consistency and stability will increase dramatically (which is the bigger win).

 

Post A Reply

%d bloggers like this: