Hurry Up, Ends March 14!

Activities of "s.beckers"

Hi @EngincanV,

Hi, since in your entity configuration the UserProfile has a not nullable UserId field you can't set the user.Profile as null without deleting the userProfile.

Added a new DeleteProfile method in user repository:

public class EfCoreUserRepository : EfCoreRepository<AbpEFCorePlaygroundDbContext, User, Guid>, IUserRepository
{
    public EfCoreUserRepository(
        IDbContextProvider<AbpEFCorePlaygroundDbContext> dbContextProvider)
        : base(dbContextProvider)
    {
    }

    public async Task DeleteProfileAsync(User user)
    {
        if (user.Profile == null)
        {
            return;
        }

        AbpEFCorePlaygroundDbContext dbContext = await GetDbContextAsync();
        dbContext.Remove(user.Profile);

        user.Profile = null;
    }
}

Update in app service:

public async Task DeleteUserProfileAsync()
{
    var user = await _userRepository.GetAsync(x => x.Id == Guid.Parse("c64b873e-c067-43e0-ae00-07992a880837"));

    if (user.Profile == null)
    {
        return;
    }

    await _userRepository.DeleteProfileAsync(user);

    await _userRepository.UpdateAsync(user);
}

The user updated event is correct: But when I continue after the breakpoint, the request still keeps executing, because of the infinite loop in the AbpDbContext: And the user profile is not deleted in the database.

Best regards, Steff Beckers

Hi @EngincanV,

I've reverted the changes in the app service.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbpEFCorePlayground.Users;

public class UsersAppService : AbpEFCorePlaygroundAppService, IUsersAppService
{
    private readonly IUserRepository _userRepository;

    public UsersAppService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public async Task DeleteUserProfileAsync()
    {
        var user = await _userRepository.GetAsync(x => x.Id == Guid.Parse("c64b873e-c067-43e0-ae00-07992a880837"));

        if (user.Profile == null)
        {
            return;
        }

        user.Profile = null;
        
        await _userRepository.UpdateAsync(user);
    }
}

I've been debugging the AbpDbContext, after I execute the delete user profile request, I can toggle between these 2 breakpoints as an infinite loop: AbpDbContext ApplyAbpConceptsForModifiedEntity(EntityEntry entry, bool forceApply = false) AbpDbContext ApplyAbpConceptsForDeletedEntity(EntityEntry entry) The entry.Reload(); is responsible for the non-stop querying in this case.

Best regards, Steff Beckers

Hi @EngincanV,

I've updated the AbpEFCorePlayground example app: https://cdn.fuzed.app/share/abp/AbpEFCorePlayground.zip

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;

namespace AbpEFCorePlayground.Users;

public class UsersAppService : AbpEFCorePlaygroundAppService, IUsersAppService
{
    private readonly IRepository<UserProfile, Guid> _userProfileRepository;
    private readonly IUserRepository _userRepository;

    public UsersAppService(
        IRepository<UserProfile, Guid> userProfileRepository,
        IUserRepository userRepository)
    {
        _userProfileRepository = userProfileRepository;
        _userRepository = userRepository;
    }

    public async Task DeleteUserProfileAsync()
    {
        var user = await _userRepository.GetAsync(x => x.Id == Guid.Parse("c64b873e-c067-43e0-ae00-07992a880837"));

        if (user.Profile == null)
        {
            return;
        }

        await _userProfileRepository.DeleteAsync(user.Profile);
    }
}

I have added an event handler for User aggregate updates:

using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;

namespace AbpEFCorePlayground.Users;

public class UserEventHandlers : IDistributedEventHandler<EntityUpdatedEto<UserEto>>, ITransientDependency
{
    public Task HandleEventAsync(EntityUpdatedEto<UserEto> eventData)
    {
        return Task.CompletedTask;
    }
}

The user profile is deleted in the database now, but not in the events. Also, in our main project we disabled adding repositories for all entities, we only have repositories for aggregate roots:

context.Services.AddAbpDbContext<AbpEFCorePlaygroundDbContext>(options =>
{
        /* Remove "includeAllEntities: true" to create
         * default repositories only for aggregate roots */
    options.AddDefaultRepositories(includeAllEntities: true);
});

We removed "includeAllEntities: true". In this case we can't use IRepository<UserProfile, Guid>. A workaround for this could be adding a Task DeleteProfileAsync(User user) method to the IUserRepository (accessing the DbContext), but also doesn't fix the issue related to the event, since user.Profile is still filled.

Best regards, Steff Beckers

Hi @EngincanV,

and make the UserId as nullable

A UserProfile record can't exist without a reference to a User, so the UserId on UserProfile should not be nullable.

Best regards, Steff Beckers

Hi @EngincanV,

Hi, based on your EfCorePlayground project, you can call _context.UserProfiles.Remove(user.Profile!) method before setting user.Profile to null:

The EfCorePlayground project doesn't have the issue, the AbpEfCorePlayground project has.

Best regards, Steff Beckers

Hi @EngincanV,

If you want to do this with repository pattern, then you should inject the profile repository and use its DeleteAsync method:

I tried this approach, but it still results in the API non-stop spamming the database with queries.

I've executed the API endpoint for 1 minute, then stopped the API.

SQL Server Profiler output, with 15k of the same queries as result.

Hi @EngincanV,

I have the following User aggregate root:

public class User : FullAuditedAggregateRoot<Guid>
{
    public User(
        Guid id,
        string name)
    {
        Id = id;
        Name = name;
    }

    private User()
    {
    }

    public string Name { get; set; }

    public UserProfile? Profile { get; set; }
}

I also have an User record in my database (via IDataSeedContributor):

User user = new User(
    id: Guid.Parse("c64b873e-c067-43e0-ae00-07992a880837"),
    name: "Steff Beckers")
{
    Profile = new UserProfile()
    {
        Bio = "Software Developer"
    }
};

await _userRepository.InsertAsync(user);

Now I want to delete the user's profile. What I'm thinking is:

var user = await _userRepository.GetAsync(userId);

user.Profile = null;

await _userRepository.UpdateAsync(user);

But this results in the error. How else should I delete the user's profile?

Best regards, Steff Beckers

Hi @EngincanV,

Any progress on this issue?

Best regards, Steff Beckers

Hi @EngincanV,

Sure. Here are two documentation that you can refer:

I think these docs are not related as the issue is not related to cascade/restrict delete. I want to delete the dependent entity, instead of the principal entity.

Did you test my working ASP.NET Core Web API example app? https://cdn.fuzed.app/share/abp/EFCorePlayground.zip

The following ABP framework example app, with the same EF Core configuration, doesn't work: https://cdn.fuzed.app/share/abp/AbpEFCorePlayground.zip

Best regards, Steff Beckers

Hi @EngincanV,

The problem is that you are setting the UserId as not nullable in the UserProfile, and this restricts you to set the user.Profile as null. This is default behaviour of EF Core.

Can you provide me the docs related to this default behaviour of EF Core?

You can try the same in a plain asp.net core application.

I have already created an ASP.NET Core Web API example app (EFCorePlayground), where this behaviour works as expected. In this example app I can just set user.Profile to null, update the user and the dependent user profile record is deleted.

Also, the cascade-delete does not apply in the update scenarios. For example, if you delete the related user, the relevant profile will also be deleted, because of the cascade-delete option. However, setting a user's profile as null is not related to that. To be able to set it null, you should make it as optional. And if you want to ensure, setting it null also should delete the dependent 1-1 entity you should update your code according to that.

You are correct, this support ticket is not related to cascade delete functionality.

Best regards, Steff Beckers

Showing 1 to 10 of 47 entries
Learn More, Pay Less
33% OFF
All Trainings!
Get Your Deal
Mastering ABP Framework Book
Do you need assistance from an ABP expert?
Schedule a Meeting
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.2.0-preview. Updated on March 13, 2025, 04:08