diff --git a/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityDomainService.cs b/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityDomainService.cs index 07559412..c64f7ae1 100644 --- a/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityDomainService.cs +++ b/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityDomainService.cs @@ -41,21 +41,21 @@ public virtual async Task> List([FromService] IEntityContex queryable = entityQueryEventArgs.Queryable; bool isOrdered = entityQueryEventArgs.IsOrdered; OnListQuery(ref queryable, ref isOrdered); - var dtoQueryable = queryable.ProjectTo(mapper.ConfigurationProvider); - OnListQuery(ref dtoQueryable, ref isOrdered); if (!isOrdered) { - var sortProperty = EntityDescriptor.GetMetadata().SortProperty; + var sortProperty = EntityDescriptor.GetMetadata().SortProperty; if (sortProperty != null) { - var parameter = Expression.Parameter(typeof(TListDTO)); - dynamic express = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(TListDTO), sortProperty.ClrType), Expression.Property(parameter, sortProperty.ClrName), parameter); - if (EntityDescriptor.GetMetadata().IsSortDescending) - dtoQueryable = Queryable.OrderByDescending(dtoQueryable, express); + var parameter = Expression.Parameter(typeof(TEntity)); + dynamic express = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(TEntity), sortProperty.ClrType), Expression.Property(parameter, sortProperty.ClrName), parameter); + if (EntityDescriptor.GetMetadata().IsSortDescending) + queryable = Queryable.OrderByDescending(queryable, express); else - dtoQueryable = Queryable.OrderBy(dtoQueryable, express); + queryable = Queryable.OrderBy(queryable, express); } } + var dtoQueryable = queryable.ProjectTo(mapper.ConfigurationProvider); + //OnListQuery(ref dtoQueryable, ref isOrdered); ViewModel model = new ViewModel(dtoQueryable); await RaiseEvent(new EntityQueryModelCreatedEventArgs(model)); return model; @@ -66,10 +66,10 @@ protected virtual void OnListQuery(ref IQueryable queryable, ref bool i } - protected virtual void OnListQuery(ref IQueryable queryable, ref bool isOrdered) - { + //protected virtual void OnListQuery(ref IQueryable queryable, ref bool isOrdered) + //{ - } + //} #endregion @@ -92,10 +92,19 @@ public virtual async Task> Create([FromService] IEntityCo mapper.Map(dto, entity); await RaiseEvent(new EntityMappedEventArgs(entity, dto)); entityContext.Add(entity); - await RaiseEvent(new EntityPreCreateEventArgs(entity)); - await entityContext.Database.SaveAsync(); - await RaiseEvent(new EntityCreatedEventArgs(entity)); - model.IsSuccess = true; + var preCreateEventArgs = new EntityPreCreateEventArgs(entity); + await RaiseEvent(preCreateEventArgs); + if (preCreateEventArgs.IsCanceled) + { + model.IsSuccess = true; + entityContext.Detach(entity); + } + else + { + await entityContext.Database.SaveAsync(); + await RaiseEvent(new EntityCreatedEventArgs(entity)); + model.IsSuccess = true; + } model.Item = mapper.Map(entity); return model; } @@ -114,13 +123,22 @@ public virtual async Task> CreateRange([FromService] mapper.Map(dto, entity); await RaiseEvent(new EntityMappedEventArgs(entity, dto)); entityContext.Add(entity); - await RaiseEvent(new EntityPreCreateEventArgs(entity)); - var listDto = mapper.Map(entity); - entities.Add(listDto, entity); - model.AddItem(listDto); + var preCreateEventArgs = new EntityPreCreateEventArgs(entity); + await RaiseEvent(preCreateEventArgs); + if (preCreateEventArgs.IsCanceled) + { + entityContext.Detach(entity); + } + else + { + var listDto = mapper.Map(entity); + entities.Add(listDto, entity); + model.AddItem(listDto); + } } else { + model.IsSuccess = false; model.AddItem(null, results.Where(t => t.ErrorMessage != null).Select(t => new KeyValuePair(t.MemberNames.FirstOrDefault() ?? string.Empty, t.ErrorMessage!)).ToList()); } } diff --git a/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityPreCreateEventArgs.cs b/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityPreCreateEventArgs.cs index 4e16780f..bd372202 100644 --- a/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityPreCreateEventArgs.cs +++ b/src/Wodsoft.ComBoost.Data/Wodsoft/ComBoost/Data/EntityPreCreateEventArgs.cs @@ -12,5 +12,7 @@ public EntityPreCreateEventArgs(T entity) } public T Entity { get; } + + public bool IsCanceled { get; set; } } } diff --git a/test/Wodsoft.ComBoost.Data.Test/EntityDomainServiceTest.cs b/test/Wodsoft.ComBoost.Data.Test/EntityDomainServiceTest.cs index 4a9f20d5..a88b16e6 100644 --- a/test/Wodsoft.ComBoost.Data.Test/EntityDomainServiceTest.cs +++ b/test/Wodsoft.ComBoost.Data.Test/EntityDomainServiceTest.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; @@ -18,13 +19,22 @@ namespace Wodsoft.ComBoost.Data.Test { public class EntityDomainServiceTest { + private SqliteConnection _connection; + + private void CreateConnection() + { + _connection = new SqliteConnection("Filename=:memory:"); + _connection.Open(); + } + [Fact] public async Task CURDTest() { + CreateConnection(); var mock = Host.CreateDefaultBuilder() .ConfigureServices(services => { - services.AddDbContext(); + services.AddDbContext(options => options.UseSqlite(_connection)); services.AddEFCoreContext(); services.AddComBoost() .AddLocalService(builder => @@ -80,7 +90,7 @@ await mock.RunAsync(async sp => Assert.Equal(model.Item.CreationDate, model.Item.ModificationDate); user = model.Item; }); - + await mock.RunAsync(async sp => { var template = sp.GetRequiredService>(); @@ -116,10 +126,11 @@ await mock.RunAsync(async sp => [Fact] public async Task ValidationTest() { + CreateConnection(); var mock = Host.CreateDefaultBuilder() .ConfigureServices(services => { - services.AddDbContext(options => options.UseSqlite("Filename=:memory:")); + services.AddDbContext(options => options.UseSqlite(_connection)); services.AddEFCoreContext(); services.AddComBoost() .AddLocalService(builder => @@ -156,5 +167,73 @@ await mock.RunAsync(async sp => Assert.Contains(model.ErrorMessage, t => t.Key == nameof(UserDto.Email)); }); } + + [Fact] + public async Task CancelCreateTest() + { + CreateConnection(); + var mock = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddDbContext(options => options.UseSqlite(_connection)); + services.AddEFCoreContext(); + services.AddComBoost() + .AddLocalService(builder => + { + builder.AddEntityService(); + }) + .AddMock(); + services.AddAutoMapper(config => + { + config.CreateMap() + .ForMember(t => t.Password, options => options.Ignore()); + config.CreateMap() + .ForMember(t => t.Password, options => options.Ignore()) + .AfterMap((dto, entity) => + { + if (!string.IsNullOrEmpty(dto.Password)) + entity.SetPassword(dto.Password); + }); + }); + }) + .Build(); + + mock.Run(sp => + { + sp.GetRequiredService().Database.EnsureCreated(); + }); + + await mock.RunAsync(async sp => + { + var template = sp.GetRequiredService>(); + var viewModel = await template.List(); + Assert.Empty(viewModel.Items); + }); + + await mock.RunAsync(async sp => + { + var template = sp.GetRequiredService>(); + template.Context.EventManager.AddEventHandler>((context, e) => + { + e.IsCanceled = true; + return Task.CompletedTask; + }); + var model = await template.Create(new UserDto + { + UserName = "test1", + Email = "test1@test.com", + DisplayName = "Test Account 1", + Password = "123456" + }); + Assert.True(model.IsSuccess); + }); + + await mock.RunAsync(async sp => + { + var template = sp.GetRequiredService>(); + var viewModel = await template.List(); + Assert.Empty(viewModel.Items); + }); + } } } diff --git a/test/Wodsoft.ComBoost.Test.Common/Entities/UserEntity.cs b/test/Wodsoft.ComBoost.Test.Common/Entities/UserEntity.cs index 570b0c14..575223df 100644 --- a/test/Wodsoft.ComBoost.Test.Common/Entities/UserEntity.cs +++ b/test/Wodsoft.ComBoost.Test.Common/Entities/UserEntity.cs @@ -8,6 +8,7 @@ namespace Wodsoft.ComBoost.Test.Entities { public class UserEntity: UserBase { + [Key] [Required] [MaxLength(16)] public string UserName { get; set; }