Hi,
In the new release of AspNetZero (1.10), the test module now must be multi tenant?
Thanks
24 Answer(s)
-
0
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.
-
0
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
-
0
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.
-
0
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:
- I got the same error when running the application
- The version that i was using before: 0.8.4.0
- 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); } }
-
0
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>(); }
-
0
Are both of Agencia and Banco entities are soft delete entities?
-
0
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.
-
0
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
-
0
Surely, you can have soft deletes. But I thought that both of your entities should be softdelete together. Can you try it?
-
0
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.
-
0
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?
-
0
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.
-
0
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
-
0
Sure, please.
-
0
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:
- Delete_Agencia
- ShouldCreateFornecedorAndGetFornecedoresPaginado
A work around to fix that errors temporally, here are the changes:
- Change the inheritance in the following entities "Agencia" and "Banco" from FullAuditedEntity to Entity
- Change the inheritance in the following entities "Pessoa" and "SubtipoPessoaEntityBase" from Entity to FullAuditedEntity
Best Regards
Marcos
-
0
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.
-
0
I'm so sorry Halil, I didn't mean any harm!
I've already delete that file.
-
0
Hi Halil,
Have you checked what can be that issue?
Thanks
-
0
Hi Halil,
Any news?
Thanks
-
0
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.
-
0
Hi Halil,
I've sent the e-mail... Did you received?
Thanks
-
0
Halil,
Did you take a look at this issue?
Thanks
-
0
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 :)
-
0
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).