Base solution for your next web application
Open Closed

Tests Module Must be Multi Tenant? #1137


User avatar
0
marcosli created

Hi,

In the new release of AspNetZero (1.10), the test module now must be multi tenant?

Thanks


24 Answer(s)
  • User Avatar
    0
    hikalkan created
    Support Team

    No, not needed. But there was a bug in Abp.TestBase nuget package. We fixed it and published v0.9.0.1 (<a class="postlink" href="https://www.nuget.org/packages/Abp.TestBase">https://www.nuget.org/packages/Abp.TestBase</a>). Please upgrade your Abp.* nuget packages to v0.9.0.1 and your unit tests will also work for single tenant mode.

  • User Avatar
    0
    marcosli created

    Hi Halil,

    I've upgraded the abp.* packages to version 0.9.1.0. And change the entity configuration class just like in this post [https://github.com/aspnetboilerplate/module-zero/issues/198]). But now in my tests i receive an exception when trying to delete some records.

    Here is the exception message: A relationship from the 'Agencia_Banco' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'Agencia_Banco_Source' must also in the 'Deleted' state.

    Here are the entities definitions and map settings:

    public class Agencia : FullAuditedEntity, IPassivable
        {
            public Agencia()
            {
                IsActive = true;
            }
    
            public bool IsActive { get; set; }
            public virtual Banco Banco { get; set; }
            public string Nome { get; set; }
    	}
    
        public class Banco : FullAuditedEntity, IPassivable
        {
            public Banco()
            {
                IsActive = true;
            }
    
            public bool IsActive { get; set; }
            public string Nome { get; set; }
            public virtual ICollection<Agencia> Agencias { get; protected set; }
        }
    
        public class AgenciaMap : EntityTypeConfiguration<Agencia>
        {
            public AgenciaMap()
            {
                HasKey(x => x.Id);
    
                Property(x => x.IsActive)
                    .IsRequired();
    
                HasRequired(x => x.Banco)
                    .WithMany(x => x.Agencias)
                    .Map(x => x.MapKey("BancoId"))
                    .WillCascadeOnDelete(false);
    
                Property(x => x.Nome)
                    .HasMaxLength(50)
                    .IsRequired();
            }
        }
    	
        public class BancoMap : EntityTypeConfiguration<Banco>
        {
            public BancoMap()
            {
                HasKey(x => x.Id);
    
                Property(x => x.IsActive)
                    .IsRequired();
    
                Property(x => x.Nome)
                    .HasMaxLength(Banco.NomeMaxLength)
                    .IsRequired();
    
                HasMany(x => x.Agencias);
            }
        }
    

    Here are the unit tests

    [Fact]
            public async Task DeletingUsingApplicationService()
            {
                CreateFakeData();
                IAgenciaAppService agenciaAppService = Resolve<IAgenciaAppService>();
                await agenciaAppService.Delete(new IdInput(1));
                await agenciaAppService.GetById(new IdInput(1)).ShouldThrowAsync<UserFriendlyException>();
            }
    
            [Fact]
            public async Task DeletingUsingDomainService()
            {
                CreateFakeData();
                IAgenciaService agenciaService = Resolve<IAgenciaService>();
                await agenciaService.Delete(1);
                await agenciaService.GetById(1).ShouldThrowAsync<UserFriendlyException>();
            }
    

    The exception only occurs when using ApplicationService. But the most curious is that internally, the application service delete method calls the domain service delete method. So my guess, is there any interceptor besides the AuditingInterceptor? I've disabled the auditing in the module pre initialize method. Any idea of what can be that? In previous version of AspNetZero, it was working perfectly.

    Thanks

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    Let me ask some question to make it clearer for me.

    • Is that only occurs in unit tests or do you get same error when you run the application and use the app service?
    • Which version of ABP correctly worked before?
    • Can you share your code of entities and code to delete the entities.
  • User Avatar
    0
    marcosli created

    Hi Halil,

    I've downloaded the version of AspNetZero template v1.10.1.0 and have merged with my project. But the error to delete an entity still occurs.

    Here are the answers:

    1. I got the same error when running the application
    2. The version that i was using before: 0.8.4.0
    3. Below is the code that delete the entities. To keep it simple, i remove a lot of code and let only that is important to understand what i'm doing.
    // Generic Domain Service
        public abstract class EZControlDomainServiceBase<TEntity, TPrimaryKey> : EZControlDomainServiceBase, IService<TEntity, TPrimaryKey>
            where TEntity : class, IEntity<TPrimaryKey>
            where TPrimaryKey : IEquatable<TPrimaryKey>
        {
            public IRepository<TEntity, TPrimaryKey> Repository { get; set; }
    
            protected EZControlDomainServiceBase(IRepository<TEntity, TPrimaryKey> repository)
            {
                this.Repository = repository;
            }
    
            public virtual async Task Delete(TPrimaryKey id)
            {
                TEntity record = null;
    
                try
                {
                    record = await Repository.GetAsync(id);
                }
                catch (Exception ex)
                {
                    throw new UserFriendlyException("Record not found.", ex.Message);
                }
    
                if (record != null)
                {
                    await Repository.DeleteAsync(record);
                }
            }
        }
    
        // Generic Application Service
        public abstract class EZControlAppServiceBase<TEntity, TPrimaryKey> : EZControlAppServiceBase
            where TEntity : class, IEntity<TPrimaryKey>
            where TPrimaryKey : IEquatable<TPrimaryKey>
        {
            public virtual async Task Delete<TIdInput>(IService<TEntity, TPrimaryKey> service, TIdInput input)
                where TIdInput : IdInput<TPrimaryKey>
            {
                await service.Delete(input.Id);
            }
        }
    
        // The Application Service
        public class AgenciaAppService : EZControlAppServiceBase<Agencia>, IAgenciaAppService
        {
            private readonly IAgenciaService _service;
    
            public AgenciaAppService(IAgenciaService service)            
            {
                _service = service;
            }
    
            public async Task Delete(IdInput input)
            {
                await Delete(_service, input);
            }
        }
    
  • User Avatar
    0
    marcosli created

    Hi Halil,

    I've found one more thing that could help to understand where is the problem. If i let the method HasRequired() commented everything works fine. But this was working in the version 0.8.4.0. I've another entities mapping configuration in the same situation.

    public class AgenciaMap : EntityTypeConfiguration<Agencia>
        {
            public AgenciaMap()
            {
                HasKey(x => x.Id);
    
                Property(x => x.IsActive)
                    .IsRequired();
    
                //HasRequired(x => x.Banco)
                //    .WithMany(x => x.Agencias)
                //    .Map(x => x.MapKey("BancoId"))
                //    .WillCascadeOnDelete(false);
    
                Property(x => x.Nome)
                    .HasMaxLength(50)
                    .IsRequired();
            }
        }
    

    Here is the DbContext code, to show how i'm loading the entities type configuration

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
    
                modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
    
                modelBuilder.Conventions.Add<TableNameConvention>();
                modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
                modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
                modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
            }
    
  • User Avatar
    0
    hikalkan created
    Support Team

    Are both of Agencia and Banco entities are soft delete entities?

  • User Avatar
    0
    marcosli created

    Yes, they are soft deleted.

    These entities are inherited from FullAuditedEntity.

    On post [http://forum.aspnetboilerplate.com/viewtopic.php?p=5826#p5826]) i've added the entities code.

  • User Avatar
    0
    marcosli created

    Halil,

    I've changed the entity to no be soft delete anymore and now is working perfectly.

    What have changed from version version 0.8.4.0 to the 0.9.x ?

    In this new release of abp i can't keep my entities as soft delete?

    Thanks

  • User Avatar
    0
    hikalkan created
    Support Team

    Surely, you can have soft deletes. But I thought that both of your entities should be softdelete together. Can you try it?

  • User Avatar
    0
    marcosli created

    I've made some tests and to make it run 100% without exceptions both entities can't have soft deletes. if one entity of the relation have soft deleted the exception is raised.

  • User Avatar
    0
    marcosli created

    Halil,

    Do you have any idea why i can't use soft deletes in that situation? Or maybe is that a bug in version 0.9.x?

  • User Avatar
    0
    hikalkan created
    Support Team

    This may be related to that: EF marks entities as deleted then ABP cancels it. When EF deletes the entity, it also deletes the relation which may not be cancelled by ABP. To test it;

    Please share all entities and mapping code related to this situation. We will create same in our environment to test it and understand the problem.

  • User Avatar
    0
    marcosli created

    Instead of posting the code here may i reproduce the error in a demo application and post here the link to download the application?

    I would like to show you another issue that i'm having.

    Thanks Halil

  • User Avatar
    0
    hikalkan created
    Support Team

    Sure, please.

  • User Avatar
    0
    marcosli created

    Hi Halil,

    Here is the url to download the project that I've created to show you the errors after migrating from abp version 0.8.x to 0.9.x.

    To simulate the errors, please execute these tests:

    1. Delete_Agencia
    2. ShouldCreateFornecedorAndGetFornecedoresPaginado

    A work around to fix that errors temporally, here are the changes:

    1. Change the inheritance in the following entities "Agencia" and "Banco" from FullAuditedEntity to Entity
    2. Change the inheritance in the following entities "Pessoa" and "SubtipoPessoaEntityBase" from Entity to FullAuditedEntity

    Best Regards

    Marcos

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    You shouldn't share the ASP.NET ZERO code publicly! Please immediately delete the link you provided (I downloaded it). We will check your code.

  • User Avatar
    0
    marcosli created

    I'm so sorry Halil, I didn't mean any harm!

    I've already delete that file.

  • User Avatar
    0
    marcosli created

    Hi Halil,

    Have you checked what can be that issue?

    Thanks

  • User Avatar
    0
    marcosli created

    Hi Halil,

    Any news?

    Thanks

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    Sorry for the late reply. Please send related parts of your project to <a href="mailto:[email protected]">[email protected]</a>, so we try to repeat it.

    Thanks.

  • User Avatar
    0
    marcosli created

    Hi Halil,

    I've sent the e-mail... Did you received?

    Thanks

  • User Avatar
    0
    marcosli created

    Halil,

    Did you take a look at this issue?

    Thanks

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    The first issue is related to ISoftDelete interface. You can see the related issue in ABP repository here <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/274">https://github.com/aspnetboilerplate/as ... issues/274</a>.

    For the second issue with ShouldCreateFornecedorAndGetFornecedoresPaginado, we are still working on it :)

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    For the second issue;

    This is not related to FullAudited or another ABP related stuff. To prove it, add a public DateTime MyTime { get; set; } property to Pessoa class and set MyTime = DateTime.Now; in it's constructor. It will work. But I don't know why. It should be something effects EF's behaviour.

    Anyway, the problem is the LINQ statement you have written, which cross joins and retrives multiple instance of the same entity (thus, getting more than one entity with same id). When I changed it like that, it worked:

    public async Task<PagedResultOutput<FornecedorListDto>> GetFornecedoresPaginado(GetFornecedorInput input)
            {
                try
                {
                    var queryFornecedor = _service.GetAll();
                    var queryPessoa = _pessoaService.GetAll();
                    //var queryPessoaFisica = _pessoaFisicaService.GetAll();
                    //var queryPessoaJuridica = _pessoaJuridicaService.GetAll();
    
                    var query = from fornecedor in queryFornecedor
                                join pessoa in queryPessoa on fornecedor.PessoaId equals pessoa.Id
                                //join pessoaFisica in queryPessoaFisica on pessoa.Id equals pessoaFisica.Id into pessoaFisicaGroup
                                //from pf in pessoaFisicaGroup.DefaultIfEmpty()
                                //join pessoaJuridica in queryPessoaJuridica on pessoa.Id equals pessoaJuridica.Id into pessoaJuridicaGroup
                                //from pj in pessoaJuridicaGroup.DefaultIfEmpty()
                                select new
                                {
                                    fornecedor = fornecedor,
                                    pessoa = pessoa,
                                    //pessoaFisica = pf,
                                    //pessoaJuridica = pj
                                };
    
                    if (input != null)
                    {
                        if (!string.IsNullOrEmpty(input.Nome))
                        {
                            query = query.Where(x =>
                                    ((x.pessoa is PessoaFisica) && (((PessoaFisica) x.pessoa).Nome.Contains(input.Nome))) ||
                                    ((x.pessoa is PessoaJuridica) && (((PessoaJuridica) x.pessoa).NomeFantasia.Contains(input.Nome))) ||
                                    ((x.pessoa is PessoaJuridica) && (((PessoaJuridica) x.pessoa).RazaoSocial.Contains(input.Nome)))
                            );
                        }
                            //query = query.Where(x =>
                            //x.pessoaFisica.Nome.Contains(input.Nome) ||
                            //x.pessoaJuridica.NomeFantasia.Contains(input.Nome) ||
                            //x.pessoaJuridica.RazaoSocial.Contains(input.Nome));
                    }
    
                    var count = await query.CountAsync();
                    var dados = await query
                        .OrderBy("fornecedor.Id")
                        .PageBy(input)
                        .ToListAsync();
    
                    List<FornecedorListDto> listDtos = new List<FornecedorListDto>();
    
                    foreach (var item in dados)
                    {
                        FornecedorListDto fornecedortListDto = item.fornecedor.MapTo<FornecedorListDto>();
    
                        listDtos.Add(fornecedortListDto);
                    }
    
                    return new PagedResultOutput<FornecedorListDto>(count, listDtos);
                }
                catch (Exception ex)
                {
                    throw new UserFriendlyException(ex.Message);
                }
            }
    

    I think you should not join to the same table (PessoaJuridica, PessoaFisica and Pessoa are mapped to same table as you know).