Base solution for your next web application

Activities of "Siyeza"

Hi,

Yes it does, thank you.

Hi,

Apologies, I think I've found the issue. The method I'm calling to enqueue the job is an async method, and I was calling it in n non-async method. I thought Visual Studio would produce a compiler warning for this but it doesn't.

Calling BackgroundJobManager.EnqueueAsync in a non-async method can produce unexpected results, because BackgroundJobManager is a singleton, which means the IBackgroundJobStore can end up participating in multiple, unrelated UnitOfWorks. This can result in multiple in UnitOfWorks trying to insert the same job, depending on thread timing.

Apologies, again.

Hi,

Yes, we're hosting the application in a single docker container. I also get this error when running the application locally on my development machine.

We're also using the database per tenant architecure, but I don't think this is the issue because as far as I can tell the background jobs are always created in the host DB.

Thanks

Thank you!

Answer

ASP.NET CORE MVC & jQuery, .NET CORE 3.1, v8.4

Hi,

I tested my assumption about TransactionScope.Suppress and I was correct. The inner scope doesn't wait for the outer scope to complete before the changes show in the database. Please see the code below.

 using (var outerScope = new TransactionScope(TransactionScopeOption.Suppress))
            {
                var outerConnection = new SqlConnection("Server=localhost;Database=Test;Trusted_Connection=true;Integrated Security=True;");
                outerConnection.Open();

                var command = outerConnection.CreateCommand();
                command.CommandText = "SELECT * FROM dbo.ResearchProjects";
                command.ExecuteNonQuery();

                using (var innerScope = new TransactionScope(TransactionScopeOption.Required))
                {
                    var innerCommand = outerConnection.CreateCommand();
                    command.CommandText = $"INSERT INTO dbo.ResearchProjects(ID) VALUES('{Guid.NewGuid()}');";
                    command.ExecuteNonQuery();

                    innerScope.Complete();
                }

                outerScope.Complete();
            }

I still think UnitOfWork in ABP should work the same way.

I appreciate that it currently does not work this way, so my question is how can I disable UnitOfWork interceptor for a specific ApplicationService. I can't use [UnitOfWork(IsDisabled=true)] because this ApplicationService gets called by other ApplicationServices.

Thanks,

Hi,

I tried CurrentUnitOfWork.SaveChangesAsync() but it doens't solve the problem. It saves the changes in the context of the transaction (IE. genrate entity IDs etc) but those changes don't show in the DB until the outer unit of work has completed.

This is bad because it means the outer unit of work is keeping alive the transaction created in the inner unit of work. That goes against how TransactionScope.Suppress is supposed to work. The outer unit or work (created with TransactionScope.Suppress) is behaving like it was created with TransactionScope.Required.

For example, consider the code below (in a non-ABP scenario). As far as I know, changes made in innerScope will complete and reflect as soon as the innerScope has completed. It won't wait for outerScope to complete first.

using(var outerScope = new TransactionScope(TransactionScopeOption.Suppress)){

    using(var innerScope = new TransactionScope(TransactionScopeOption.Required)){
            // Make changes
            ...
            
            innerScope.Complete();
    }

    outerScope.Complete();
}

Hi,

ABP version is 5.6 Base framework is .NET Core

I have an ApplicationService with the following method:

/// <summary>
        /// Submits the specified item to the workflow
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>                      
        public async Task SubmitAsync(ItemActionRequestDto request)
        {
            Guard.ArgumentNotNull(request, nameof(request));      

            using (var unitOfWork = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress))
            {
                // Map the request
                var workflowRequest = ObjectMapper.Map<ItemActionRequest>(request);
                workflowRequest.UserId = AbpSession.UserId.Value;

                // Submit the item to the workflow
                var workflow = await ItemWorkflowFactory.CreateAsync(workflowRequest);
                await workflow.SubmitAsync(workflowRequest);

                unitOfWork.Complete();
            }
        }

The workflow.SubmitAsync call will eventually call the following DomainService method:

/// <summary>
        /// Creates the workflow item for the specified Item
        /// </summary>
        /// <param name="item"></param>                
        public virtual void CreateWorkflowItem(TItem item)
        {
            Guard.ArgumentNotNull(item, nameof(item));

            using(var unitOfWork = UnitOfWorkManager.Begin(TransactionScopeOption.Required))
            {
                // Check if workflow item already exists
                var workflowItem = WorkflowItemRepository.FirstOrDefault(w => w.ItemRef == item.Id);
                if (workflowItem != null)
                {
                    throw new InvalidOperationException(string.Format(InternalMessages.Workflow_ItemAlreadyExists, item.Id));
                }

                // Create workflow item
                workflowItem = new WorkflowItem
                {
                    ItemRef = item.Id,
                    ItemType = item.ItemType,
                    ItemSubTypeCode = item.ItemSubTypeCode,
                    ItemSubTypeName = GetItemSubTypeName(item),
                    ItemAction = item.ItemAction,
                    TenantId = item.TenantId,
                    Trigger = WorkflowTrigger.None,
                    State = WorkflowState.NotStarted
                };

                // Get the route for the item
                var routingManager = GetRoutingManager(item);
                workflowItem.RouteItemTypeCode = routingManager.Route.ItemTypeCode;

                // Save the item
                item.WorkflowItem = workflowItem;
                ItemRepository.Update(item);


                unitOfWork.Complete();
            }

           
        }

The changes made in this inner UnitOfWork will not reflect in the DB until the outer UnitOfWork (created with TransactionScopeOption.Suppress) has completed.

Thanks

Thank you

Thank you

Showing 1 to 10 of 82 entries