| File: MSBuildTaskBase.cs | Web Access |
| Project: src\arcade\src\Common\Microsoft.Arcade.Common\Microsoft.Arcade.Common.csproj (Microsoft.Arcade.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using MSBuild = Microsoft.Build.Utilities; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using System; using System.Reflection; using System.Linq; namespace Microsoft.Arcade.Common { public abstract partial class MSBuildTaskBase : MSBuild.Task { #region Common Variables protected const string AssetsVirtualDir = "assets/"; #endregion /// <summary> /// This is the name of the method on the implementation of this class that behaves as "Execute" would in a basic /// impelmentation of an MSBuild Task. /// </summary> private const string ExecuteMethodName = "ExecuteTask"; /// <summary> /// An MSBuild Task uses the Execute method as main method as an entry point into the task's functionality. /// It is extended in this abstract class, behind the scenes, to configure dependency injection. /// </summary> /// <returns></returns> public override sealed bool Execute() { ServiceCollection collection = new ServiceCollection(); ConfigureServices(collection); using (var provider = collection.BuildServiceProvider()) { return InvokeExecute(provider); } } /// <summary> /// A helper method that takes the service provider contains and extrapolates the dependencies referenced in the /// ExecuteTask method and then subsequently invokes the ExecuteTask method. /// /// This method can also be called from tests so that the service provider can contain any mocks that will be used /// in the test. /// </summary> /// <param name="provider"></param> /// <returns></returns> public bool InvokeExecute(ServiceProvider provider) { return (bool)GetExecuteMethod().Invoke(this, GetExecuteArguments(provider)); } /// <summary> /// Default service configuration. This method can be overridden in the impelmented class so that /// it can configure the dependencies that the implemented task requires. /// /// This method can also be called from the tests to complete the service collection with any dependencies /// that aren't configured for the tests. /// </summary> /// <param name="collection"></param> public abstract void ConfigureServices(IServiceCollection collection); /// <summary> /// Uses some reflection magic to look up the types of depenencies that to be injected into the implemented task. /// </summary> /// <returns></returns> public Type[] GetExecuteParameterTypes() { return GetType().GetMethod(ExecuteMethodName).GetParameters().Select(p => p.ParameterType).ToArray(); } /// <summary> /// Looks up all the dependencies from the service provider that are defined as parameters in ExecuteTask /// so they can be injected into the ExecuteTask method when it's called. /// </summary> /// <param name="serviceProvider"></param> /// <returns></returns> private object[] GetExecuteArguments(ServiceProvider serviceProvider) { return GetExecuteParameterTypes().Select(t => serviceProvider.GetRequiredService(t)).ToArray(); } /// <summary> /// Gets the ExecuteTask method that is in the implementation of the MSBuild Task. /// </summary> /// <returns></returns> private MethodInfo GetExecuteMethod() { return GetType().GetMethod(ExecuteMethodName); } } }