/// <summary> /// Adds the missing members to a target class, that support the common delivery framework implementation.. /// </summary> /// <param name="source">The source code that contains the target class to be updated.</param> /// <param name="targetClass">The target class to have the members added to.</param> /// <param name="missingMembers">The missing members to be added to the target class.</param> /// <param name="boundsCheckLogic">Optional parameter that determines if bounds checking logic should be added to methods. This will default to true.</param> /// <param name="loggingLogic">Optional parameter that determines if enter, exit and exception management logic should be added to methods.</param> /// <param name="supportAsyncMethods">Optional parameter that determines if methods will use the async keyword when support multi-threaded operations.</param> /// <param name="supportsCDFAspNet">Optional parameters that determines if add members support aspnet error handling with CommonDeliveryFramework.</param> /// <returns>The updated SourceCode Model once the missing members have been added.</returns> /// <exception cref="ArgumentNullException">Thrown if data needed to process a member is not passed.</exception> /// <exception cref="CodeFactoryException">Thrown is model data is not valid or a processing error occurs.</exception> public static async Task <CsSource> AddMembersToClassWithCDFSupportAsync(CsSource source, CsClass targetClass, IReadOnlyList <CsMember> missingMembers, bool boundsCheckLogic = true, bool loggingLogic = true, bool supportAsyncMethods = true, bool supportsCDFAspNet = false) { //Bounds checking to confirm all needed data was passed. if (source == null) { throw new ArgumentNullException(nameof(source)); } if (targetClass == null) { throw new ArgumentNullException(nameof(targetClass)); } if (missingMembers == null) { throw new ArgumentNullException(nameof(missingMembers)); } //Confirming the target models are loaded and are source code if (!source.IsLoaded) { throw new CodeFactoryException("Source code model was not loaded."); } if (!targetClass.IsLoaded) { throw new CodeFactoryException("The target class model data is not loaded."); } if (!targetClass.LoadedFromSource) { throw new CodeFactoryException("Target class is not loaded from source code cannot update."); } //If an empty list of missing members was provided then return the original source code if (!missingMembers.Any()) { return(source); } //Variables used to hold the most current version of the code factory models that are being updated. CsClass currentClassModel = targetClass; CsSource currentSourceModel = source; currentSourceModel = await currentSourceModel.AddMissingNamespaces(missingMembers, currentClassModel.Namespace); currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass; if (currentClassModel == null) { throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members"); } //Checking to make sure there is a logger field implemented if not add it to the class. currentSourceModel = await currentClassModel.AddMicrosoftExtensionsLoggerFieldAsync(AspNetCoreConstants.FieldNameLogger, currentSourceModel); currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass; if (currentClassModel == null) { throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members"); } currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.CommonDeliveryFrameworkLibraryName); currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass; if (currentClassModel == null) { throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members"); } if (supportsCDFAspNet) { //Confirming CDF ASPnet namespace is added to the code file. currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.CommonDeliveryFrameworkAspNetLibraryName); currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass; if (currentClassModel == null) { throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members"); } //Confirming the mvc namespace is added to the code file. currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.MvcNamespace); currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass; if (currentClassModel == null) { throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members"); } } if (supportAsyncMethods) { //Confirming the mvc namespace is added to the code file. currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.SystemThreadingTasksNamespace); currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass; if (currentClassModel == null) { throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members"); } } //Loading a namespace manager that will update types definitions to use the correct namespace format. This reads in all the using statements assigned to the class. var namespaceManager = source.LoadNamespaceManager(currentClassModel.Namespace); bool isControllerClass = currentClassModel.IsController(); //Processing the missing members foreach (var member in missingMembers) { //Clearing the formatted source code for the member before processing each member string sourceCode = null; switch (member.MemberType) { case CsMemberType.Event: //Validating the member event model loaded correctly if not return to the top. No exception will be thrown. if (!(member is CsEvent eventModel)) { continue; } //Formatting a default event definition. sourceCode = FormatMemberEvent(eventModel, namespaceManager); break; case CsMemberType.Method: //Validating the member event model loaded correctly if not return to the top. No exception will be thrown. if (!(member is CsMethod methodModel)) { continue; } bool isControllerAction = false; if (supportsCDFAspNet & isControllerClass) { isControllerAction = IsControllerAction(methodModel); } //Formatting the method implementation based on the optional parameters provided. sourceCode = FormatMemberMethod(methodModel, namespaceManager, loggingLogic, boundsCheckLogic, supportAsyncMethods, isControllerAction); break; case CsMemberType.Property: //Validating the member property model loaded correctly if not return to the top. No exception will be thrown. if (!(member is CsProperty propertyModel)) { continue; } //Formatting a default property definition. sourceCode = FormatMemberProperty(propertyModel, namespaceManager); break; default: continue; } if (string.IsNullOrEmpty(sourceCode)) { continue; } //Creating a source formatter and appending the final output with two indent levels to within the body over a class. var sourceFormatter = new CodeFactory.SourceFormatter(); sourceFormatter.AppendCodeBlock(2, sourceCode); currentSourceModel = await currentClassModel.AddToEndAsync(sourceFormatter.ReturnSource()); if (currentSourceModel == null) { throw new CodeFactoryException("Cannot load the source code file, cannot complete adding interface members"); } currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass; if (currentClassModel == null) { throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members"); } } return(currentSourceModel); }