Open Closed

Abp - UnitOfWork #7482


User avatar
0
mgurer created
  • ABP Framework version: v8.2.0
  • UI Type: Blazor WASM
  • Database System: EF Core (PostgreSQL)
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes
  • Exception message and full stack trace:
  • Steps to reproduce the issue:

Hello,

I have just upgraded my v8.02 solution to v8.2.0

I exprerience a strange problem on all versions after v8.0.2. The versions below also does not produce such problem.

When I try to complete a unitofwork by call completeasync() method, the methods falls into a loop and never returns. In each loop LocalEvents collection gets filled with one item although collection is cleared in previous loop.

When I checked the unitofwork source code and commented out the second call to SaveChangesAsync method, everything seems to work fine. I know this change has serious drawbacks, but I could not figure out another solution.

Below is the code (abp's unitofwork.cs) that I altered;

public virtual async Task CompleteAsync(CancellationToken cancellationToken = default)
{
    if (_isRolledback)
    {
        return;
    }

    PreventMultipleComplete();

    try
    {
        _isCompleting = true;
        await SaveChangesAsync(cancellationToken);

        while (LocalEvents.Count != 0 || DistributedEvents.Count != 0)
        {
            if (LocalEvents.Count != 0)
            {
                var localEventsToBePublished = LocalEvents.OrderBy(e => e.EventOrder).ToArray();
                LocalEvents.Clear();
                await UnitOfWorkEventPublisher.PublishLocalEventsAsync(
                    localEventsToBePublished
                );
            }

            if (DistributedEvents.Count != 0)
            {
                var distributedEventsToBePublished = DistributedEvents.OrderBy(e => e.EventOrder).ToArray();
                DistributedEvents.Clear();
                await UnitOfWorkEventPublisher.PublishDistributedEventsAsync(
                    distributedEventsToBePublished
                );
            }

            //This is the place where I commented out.
            //await SaveChangesAsync(cancellationToken);
        }

        await CommitTransactionsAsync(cancellationToken);
        IsCompleted = true;
        await OnCompletedAsync();
    }
    catch (Exception ex)
    {
        _exception = ex;
        throw;
    }
}

Here is the test service method that I used to trigger the problem during my debug session;

[AllowAnonymous]
    [UnitOfWork(IsDisabled = true)]
public async Task<string> TestAsync(Guid id)
{
    try
    {
        var message = await _messageRepository.FindAsync(id);

        if (message == null)
            return "null";

        message.SetTitle(message.Title + "!");

        await _messageRepository.UpdateAsync(message, true);

        return message.Title;
    }
    catch (Exception e)
    {
        return e.Message + " -- " + e.InnerException?.Message;
    }
}

During my test above, data gets updated in db but the method never returns back to caller. I even tested by creating a new unitofwork instance using unitofworkmanager and manually triggering the completeasync method as described in your unitofwork documentation.

Can you advice me a solution for my problem?

Thanks Murat


3 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share a simple project to reproduce your problem? https://wetransfer.com/

    Thanks.

    liming.ma@volosoft.com

  • User Avatar
    0
    mgurer created

    Hi,

    I think problem was due to a property on Message entity. The cli typed property was mapped to a db column containing json string. The json mapping convension somehow didnt worked in new version. Everytime context is saved, the context assumed that the property is altered so savechanges triggered once more which lead the loop in unitofwork.

    I simplified the mapping convension of entity property to json column, the problem gone away. I wont dig into detail and have no clue what changed between abp versions.

    Here is the mentioned simplified convension function which works fine.

    public static PropertyBuilder<Message> HasMessageJsonConversion(this PropertyBuilder<Message> propertyBuilder) { var settings = GetSettings();

    var converter = new ValueConverter&lt;Message, string&gt;
    (
        v => JsonConvert.SerializeObject(v, settings),
        v => JsonConvert.DeserializeObject&lt;Message&gt;(v, settings)
    );
    
    var comparer = new ValueComparer&lt;Message&gt;
    (
        (l, r) => l.Equals(r, settings),
        v => v == null ? 0 : JsonConvert.SerializeObject(v, settings).GetHashCode(),
        v => JsonConvert.DeserializeObject&lt;Message&gt;(JsonConvert.SerializeObject(v, settings), settings)
    );
    
    propertyBuilder.HasConversion(converter);
    propertyBuilder.Metadata.SetValueConverter(converter);
    propertyBuilder.Metadata.SetValueComparer(comparer);
    
    return propertyBuilder;
    

    }

    Thanks Murat

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Good news. 👍

Made with ❤️ on ABP v9.0.0-preview Updated on September 19, 2024, 10:13