Skip to main content

How to resolve "InvalidOperationException: Cannot resolve 'ProjectName.SendEmailCustomUmbracoWorkflow' from root provider because it requires scoped service 'ProjectName.ICustomServiceName'"

When you access a custom service from within your Custom Umbraco Workflow or a similar environment, you may encounter an "Invalid Operation Exception." This error typically occurs because you are attempting to use a scoped service within a singleton context.

Solution

If you need to use a scoped service within a singleton, inject IServiceScopeFactory into the singleton. Then, create a scope to pull out your context when needed.

using ProjectName.Microsites.Cms.DataAccess.Constants;
using ProjectName.Web.Services.Interfaces;
using Newtonsoft.Json.Linq;
using Umbraco.Forms.Core;
using Umbraco.Forms.Core.Attributes;
using Umbraco.Forms.Core.Enums;
using Umbraco.Forms.Core.Persistence.Dtos;

namespace ProjectName.Web.UI.Workflows
{
    public class CrmFieldMapper : WorkflowType
    {
        private readonly ILogger<CrmFieldMapper> _logger;
        private readonly IServiceScopeFactory _serviceScopeFactory;

        public CrmFieldMapper(ILogger<CrmFieldMapper> logger,
        IServiceScopeFactory serviceScopeFactory) //IFormDataService formDataService)
        {
            Id = new Guid("b8167f85-0ca4-4d63-80e1-069eef772dca");
            Name = "Crm Field Mapper";
            Description = "Custom workflow to map form fields to CRM fields";
            Icon = "icon-trafic";

            _logger = logger;
// Inject IServiceScopeFactory into the singleton
            _serviceScopeFactory = serviceScopeFactory;
            //_formDataService = formDataService;
        }

        [Setting("Field Mappings",
        Description = "Map fields from the form to CRM",
        View = $"~/App_Plugins/CrmFieldMapper/crmfieldmapper.html",
        PreValues = "")]
        public string MappingsField { get; set; } = string.Empty;

        public override async Task<WorkflowExecutionStatus> ExecuteAsync(
            WorkflowExecutionContext context)
        {
            try
            {
// Create a scope for your service and then use it
                using var scope = _serviceScopeFactory.CreateScope();
                var formDataService = scope.ServiceProvider
                .GetRequiredService<IFormDataService>();

                var mappableCRMFields = formDataService
                .GetAllActiveMappableCRMFieldsCached();

                if (mappableCRMFields != null)
                {
                    var record = context.Record;

                    var formGuid = record.Form;

                    var fieldMappings = new Dictionary<string, int>();

                    foreach (var mappableCRMField in mappableCRMFields)
                    {
                        var formAlias = GetFormAlias(record,
                        mappableCRMField.CRMFieldFriendlyName);
                        if (!string.IsNullOrEmpty(formAlias))
                        {
                            // Mapping is done for this form value,
// add it to the fieldMappings dictionary
                            fieldMappings.Add(formAlias,
                            mappableCRMField.Id);
                        }
                    }

                    if (fieldMappings.Any())
                    {
                        formDataService.SaveFormFieldMappings(formGuid,
                        fieldMappings,
                        FormMappingConstants.FieldMappingNotSetOptionId);
                    }
                }

                return WorkflowExecutionStatus.Completed;
            }
            catch (Exception ex)
            {
                _logger.LogError($"CrmFieldMapper Execute exception,
                ex.Message: {ex.Message},
                ex.StackTrace: {ex.StackTrace}");
            }
            return WorkflowExecutionStatus.Failed;
        }

        public override List<Exception> ValidateSettings()
        {
            List<Exception> exceptions = new List<Exception>();

            // Validation errors can be added
// to make sure certain mappings have been set
            //var settings = JArray.Parse(MappingsField)
            // as IEnumerable<dynamic>;
            //if (!settings.Any(x => x.alias.ToString().ToLower() == "name") ||
            // !settings.Any(x => x.alias.ToString().ToLower() == "email") ||
            // !settings.Any(x => x.alias.ToString().ToLower() == "message"))
            //{
            //    exceptions.Add(new Exception("Mappings for name,
            // email and message must be set."));
            //}

            return exceptions;
        }

        private string GetFormAlias(Record record, string alias)
        {
            // parse the mappings JSON string
            var settings = JArray.Parse(MappingsField) as IEnumerable<dynamic>;

            if (settings != null)
            {
                var mappedValue = settings
                .FirstOrDefault(x => x.alias.ToString().ToLower() ==
                alias.ToLower())?.value;

                if (mappedValue != null)
                {
                    // dynamic mappedValue is always a guid if it is not null
                    // get the field guid for the given alias and
// return the corresponding alias from the record
                    return record.GetRecordField((Guid)mappedValue).Alias;
                }
            }

            return string.Empty;
        }
    }
}

Error details



Comments

Popular posts from this blog

How to fix Git push error: "RPC failed; curl 56 HTTP/2 stream 7 was reset send-pack: unexpected disconnect while reading sideband packet fatal: the remote end hung up unexpectedly"

Problem Today I saw the following problem when I tried to push my changes to a Git server after doing some work for upgrading an Umbraco v7 project to v8.18.8.  Possible reasons After some investigations, it seems like this could be because of the following reasons; Git is not happy with the amount of changes that are being pushed into the server.  There are possible limitations on the server about the size/amount of files that you can push. Your internet connection is not good and stable enough. Your Git client's version is old. Solution options For me, the easiest option was connecting to another Wifi and trying again. Apparently, this option helped quite a few people, so it is worth giving it a try. Unfortunately, it didn't work for me. A bad internet connection wasn't an option for me either, as my internet is pretty fast (500 Mbps). Similarly, my Git client version was the latest version (git version 2.41.0.windows.3).  On StackOverflow, there were a lot of recommend...

How to fix "Microsoft SQL Error SQL71564: Error validating element [USERNAME]: The element [USERNAME] has been orphaned from its login and cannot be deployed."

I needed to export a database in BACPAC format today in order to restore it somewhere else, and I encountered the following error. To resolve this issue, I deleted all of the users mentioned in the error log. After successfully creating the BACPAC file, I used it to create a new database with no problems. Error: TITLE: Microsoft SQL Server Management Studio ------------------------------ One or more unsupported elements were found in the schema used as part of a data package. Error SQL71564: Error validating element [USER1]: The element [USER1] has been orphaned from its login and cannot be deployed. Error SQL71564: Error validating element [USER2]: The element [USER2] has been orphaned from its login and cannot be deployed. Error SQL71564: Error validating element [USER3]: The element [USER3] has been orphaned from its login and cannot be deployed. Error SQL71564: Error validating element [USER4]: The element [USER4] has been orphaned from its login and cannot be deployed. Error SQL71...

How to use JQuery Ajax Methods for Async ASP.NET MVC Action Methods

Making repeatedly calls to async methods can be a nightmare. In this case, it makes sense to use 2 ajax methods, instead of one. Here is a simple solution to overcome this problem. See that  ajaxcalls   is emptied after the success response for the first ajax call and then the second ajax method is used to make one single call to the async action method. Hope it helps. View: @section Scripts{     < script type ="text/javascript">         var smartDebitObject = new Object();         smartDebitObject.MembershipNumber = $( "#MembershipNumber" ).val();         smartDebitObject.ProfileId = $( "#ProfileId" ).val();         smartDebitObject.FirstName = $( "#FirstName" ).val();         smartDebitObject.LastName = $( "#LastName" ).val();     ...