abpframework / abp

Open-source web application framework for ASP.NET Core! Offers an opinionated architecture to build enterprise software solutions with best practices on top of the .NET. Provides the fundamental infrastructure, cross-cutting-concern implementations, startup templates, application modules, UI themes, tooling and documentation.
https://abp.io
GNU Lesser General Public License v3.0
12.92k stars 3.44k forks source link

Saving to database problem in Unit of Work #17604

Open omer-repo opened 1 year ago

omer-repo commented 1 year ago

Is there an existing issue for this?

Description

I found an interesting bug that I'm struggling to detect and explain it here. I hope I can explain it clearly. The main suspect of the bug is Unit Of Work. It works differently according to count of records to be saved to database. Because of this difference, some records are not saved to DB. Let me explain with an example.

I want to save an Invoice to database. Meanwhile, I want to save the Client(customer) and Lines (Invoice lines) of the Invoice (at the same time).

I've been using a method like that to save both Customer (MasterClient), Invoice and InvoiceLines together. (I simplified the method)

using (var uow = _unitOfWorkManager.Begin(true, false))
{
    #region MasterClient
    MasterClient masterClient = new MasterClient(_guidGenerator.Create())
    {
        Name = eInvoice.invoice.Name,
        Surname = eInvoice.invoice.Surname,
        Title = eInvoice.invoice.Title,
    };
    await _masterClientRepository.InsertAsync(masterClient);
    #endregion

    #region Invoice
    await _invoiceRepository.InsertAsync(invoice);
    #endregion

    #region Invoice Lines
    List<InvoiceLine> invoiceLineItems = new List<InvoiceLine>();
    foreach (var item in eInvoice.invoiceLines)
    {
        item.MasterClientId = masterClient.Id;
        item.InvoiceId = invoice.Id;
        var lineItem = new InvoiceLine(_guidGenerator.Create());
        _objectMapper.Map<InvoiceLineDto, InvoiceLine>(item, lineItem);
        var invoiceLine = await _invoiceLineRepository.InsertAsync(lineItem);
    }
    #endregion

    result.value = invoice;
    await uow.CompleteAsync();

    //IMPORTANT => I will use other services here, like background job managers. So, invoice have to be saved to db before these services.
}

This method has saved over 100.000 invoices to DB without a problem until the last week. I discovered that it doesn't save an invoice data to database. I checked the incoming request data and everything seems good, but it can not be saved. I debugged the code but there was no error and it looked saving to database successfully. But when I check the database table, there was no record. I also tracked the SQL Server Profiler but there wasn't any error or rollback. Then I started comparing the request with successful ones previously and modifying the request data.

Finally I found an interesting thing: If the count of invoice lines is 35, invoice is not saved to database while invoice lines are saved. (The request has 35 invoice lines)

I tried from 1 to 34 one by one, all of them are saved successfully. (Customer, Invoice and InvoiceLines). If the count of Invoice Lines is 35 => Customer and InvoiceLines are saved, Invoice is not saved. If the count of Invoice Lines is 36, 37 , 38 => Customer and Invoice are saved, InvoiceLines are not saved. If the count of Invoice Lines is 39 and above => All of them are saved successfully.

I know it sounds very strange, but I couldn't find a logical explanation.

I found a workaround for this problem: If I make autosave of inserting MasterClient to true, then everything works good. But I don't want to use autoSave method as it is not recommended with the UOW in the ABP docs also. (https://docs.abp.io/en/abp/latest/Unit-Of-Work#alternative-to-the-savechanges) await _masterClientRepository.InsertAsync(masterClient,true);

I uploaded the sample project to here with the Postman collection. (It has one request with 35 invoice lines) https://github.com/omer-repo/ACME.POC3

Reproduction Steps

No response

Expected behavior

No response

Actual behavior

No response

Regression?

No response

Known Workarounds

No response

Version

7.3.2

User Interface

Blazor

Database Provider

EF Core (Default)

Tiered or separate authentication server

None (Default)

Operation System

Windows (Default)

Other information

No response

maliming commented 1 year ago

hi

Can you test your code in a unit test method?

gdlcf88 commented 1 year ago

This method has saved over 100.000 invoices to DB without a problem until the last week. I discovered that it doesn't save an invoice data to database. I checked the incoming request data and everything seems good, but it can not be saved. I debugged the code but there was no error and it looked saving to database successfully. But when I check the database table, there was no record. I also tracked the SQL Server Profiler but there wasn't any error or rollback. Then I started comparing the request with successful ones previously and modifying the request data.

I believe you have encountered the same problem as me. See https://github.com/abpframework/abp/issues/15816.

Try removing this line:

app.UseUnitOfWork();

https://github.com/omer-repo/ACME.POC3/blob/master/src/ACME.POC3.HttpApi.Host/POC3HttpApiHostModule.cs#L202

omer-repo commented 1 year ago

@gdlcf88 , I removed but same thing happened. I think it is due to I'm already creating a UOW in the AppService.

stale[bot] commented 10 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

omer-repo commented 10 months ago

I'll add test, but I think it should be an integration test