mirror of https://github.com/abpframework/abp.git
30 changed files with 371 additions and 90 deletions
@ -0,0 +1,9 @@ |
|||||
|
namespace ProductManagement |
||||
|
{ |
||||
|
public static class ProductConsts |
||||
|
{ |
||||
|
public const int MaxCodeLength = 32; |
||||
|
|
||||
|
public const int MaxNameLength = 256; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
using System; |
||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
|
||||
|
namespace ProductManagement |
||||
|
{ |
||||
|
public class Product : AuditedAggregateRoot<Guid> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A unique value for this product.
|
||||
|
/// ProductManager ensures the uniqueness of it.
|
||||
|
/// It can not be changed after creation of the product.
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
public string Code { get; private set; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public string Name { get; private set; } |
||||
|
|
||||
|
public float Price { get; private set; } |
||||
|
|
||||
|
public int StockCount { get; private set; } |
||||
|
|
||||
|
private Product() |
||||
|
{ |
||||
|
//Default constructor is needed for ORMs.
|
||||
|
} |
||||
|
|
||||
|
internal Product( |
||||
|
Guid id, |
||||
|
[NotNull] string code, |
||||
|
[NotNull] string name, |
||||
|
float price = 0.0f, |
||||
|
int stockCount = 0) |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(code, nameof(code)); |
||||
|
|
||||
|
if (code.Length >= ProductConsts.MaxCodeLength) |
||||
|
{ |
||||
|
throw new ArgumentException($"Product code can not be longer than {ProductConsts.MaxCodeLength}"); |
||||
|
} |
||||
|
|
||||
|
Id = id; |
||||
|
Code = code; |
||||
|
SetName(Check.NotNullOrWhiteSpace(name, nameof(name))); |
||||
|
SetPrice(price); |
||||
|
SetStockCountInternal(stockCount, triggerEvent: false); |
||||
|
} |
||||
|
|
||||
|
public Product SetName([NotNull] string name) |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(name, nameof(name)); |
||||
|
|
||||
|
if (name.Length >= ProductConsts.MaxNameLength) |
||||
|
{ |
||||
|
throw new ArgumentException($"Product name can not be longer than {ProductConsts.MaxNameLength}"); |
||||
|
} |
||||
|
|
||||
|
Name = name; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public Product SetPrice(float price) |
||||
|
{ |
||||
|
if (price < 0.0f) |
||||
|
{ |
||||
|
throw new ArgumentException($"{nameof(price)} can not be less than 0.0!"); |
||||
|
} |
||||
|
|
||||
|
Price = price; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public Product SetStockCount(int stockCount) |
||||
|
{ |
||||
|
return SetStockCountInternal(stockCount); |
||||
|
} |
||||
|
|
||||
|
private Product SetStockCountInternal(int stockCount, bool triggerEvent = true) |
||||
|
{ |
||||
|
if (StockCount < 0.0f) |
||||
|
{ |
||||
|
throw new ArgumentException($"{nameof(stockCount)} can not be less than 0!"); |
||||
|
} |
||||
|
|
||||
|
if (StockCount == stockCount) |
||||
|
{ |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
if (triggerEvent) |
||||
|
{ |
||||
|
AddDistributedEvent(new ProductStockCountChangedEto(StockCount, stockCount)); |
||||
|
} |
||||
|
|
||||
|
StockCount = stockCount; |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace ProductManagement |
||||
|
{ |
||||
|
public class ProductCodeAlreadyExistsException : BusinessException |
||||
|
{ |
||||
|
public ProductCodeAlreadyExistsException(string productCode) |
||||
|
: base("PM:000001", $"A product with code {productCode} has already exists!") |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
using Volo.Abp.Domain.Services; |
||||
|
|
||||
|
namespace ProductManagement |
||||
|
{ |
||||
|
public class ProductManager : DomainService |
||||
|
{ |
||||
|
private readonly IRepository<Product, Guid> _productRepository; |
||||
|
|
||||
|
public ProductManager(IRepository<Product, Guid> productRepository) |
||||
|
{ |
||||
|
_productRepository = productRepository; |
||||
|
} |
||||
|
|
||||
|
public async Task<Product> CreateAsync( |
||||
|
[NotNull] string code, |
||||
|
[NotNull] string name, |
||||
|
float price = 0.0f, |
||||
|
int stockCount = 0) |
||||
|
{ |
||||
|
var existingProduct = await _productRepository.FirstOrDefaultAsync(p => p.Code == code); |
||||
|
if (existingProduct != null) |
||||
|
{ |
||||
|
throw new ProductCodeAlreadyExistsException(code); |
||||
|
} |
||||
|
|
||||
|
return await _productRepository.InsertAsync( |
||||
|
new Product( |
||||
|
GuidGenerator.Create(), |
||||
|
code, |
||||
|
name, |
||||
|
price, |
||||
|
stockCount |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Domain.Entities.Events.Distributed; |
||||
|
|
||||
|
namespace ProductManagement |
||||
|
{ |
||||
|
[Serializable] |
||||
|
public class ProductStockCountChangedEto : EtoBase |
||||
|
{ |
||||
|
public int OldCount { get; set; } |
||||
|
|
||||
|
public int CurrentCount { get; set; } |
||||
|
|
||||
|
private ProductStockCountChangedEto() |
||||
|
{ |
||||
|
//Default constructor is needed for deserialization.
|
||||
|
} |
||||
|
|
||||
|
public ProductStockCountChangedEto(int oldCount, int currentCount) |
||||
|
{ |
||||
|
OldCount = oldCount; |
||||
|
CurrentCount = currentCount; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,38 +0,0 @@ |
|||||
using System; |
|
||||
using Microsoft.EntityFrameworkCore; |
|
||||
using Volo.Abp; |
|
||||
|
|
||||
namespace ProductManagement.EntityFrameworkCore |
|
||||
{ |
|
||||
public static class ProductManagementDbContextModelCreatingExtensions |
|
||||
{ |
|
||||
public static void ConfigureProductManagement( |
|
||||
this ModelBuilder builder, |
|
||||
Action<ProductManagementModelBuilderConfigurationOptions> optionsAction = null) |
|
||||
{ |
|
||||
Check.NotNull(builder, nameof(builder)); |
|
||||
|
|
||||
var options = new ProductManagementModelBuilderConfigurationOptions(); |
|
||||
|
|
||||
optionsAction?.Invoke(options); |
|
||||
|
|
||||
/* Configure all entities here. Example: |
|
||||
|
|
||||
builder.Entity<Question>(b => |
|
||||
{ |
|
||||
//Configure table & schema name
|
|
||||
//b.ToTable(options.TablePrefix + "Questions", options.Schema);
|
|
||||
|
|
||||
//Properties
|
|
||||
//b.Property(q => q.Title).IsRequired().HasMaxLength(QuestionConsts.MaxTitleLength);
|
|
||||
|
|
||||
//Configure relations
|
|
||||
//b.HasMany(question => question.Tags).WithOne().HasForeignKey(qt => qt.QuestionId);
|
|
||||
|
|
||||
//Configure indexes
|
|
||||
//b.HasIndex(q => q.CreationTime);
|
|
||||
}); |
|
||||
*/ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
7
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/IProductManagementDbContext.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/IProductManagementDbContext.cs
7
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/IProductManagementDbContext.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/IProductManagementDbContext.cs
5
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementDbContext.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementDbContext.cs
5
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementDbContext.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementDbContext.cs
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.EntityFrameworkCore.Modeling; |
||||
|
|
||||
|
namespace ProductManagement.EntityFrameworkCore |
||||
|
{ |
||||
|
public static class ProductManagementDbContextModelCreatingExtensions |
||||
|
{ |
||||
|
public static void ConfigureProductManagement( |
||||
|
this ModelBuilder builder, |
||||
|
Action<ProductManagementModelBuilderConfigurationOptions> optionsAction = null) |
||||
|
{ |
||||
|
Check.NotNull(builder, nameof(builder)); |
||||
|
|
||||
|
var options = new ProductManagementModelBuilderConfigurationOptions(); |
||||
|
|
||||
|
optionsAction?.Invoke(options); |
||||
|
|
||||
|
builder.Entity<Product>(b => |
||||
|
{ |
||||
|
b.ToTable(options.TablePrefix + "Products", options.Schema); |
||||
|
|
||||
|
b.ConfigureAudited(); |
||||
|
|
||||
|
b.Property(x => x.Code).IsRequired().HasMaxLength(ProductConsts.MaxCodeLength); |
||||
|
b.Property(x => x.Name).IsRequired().HasMaxLength(ProductConsts.MaxNameLength); |
||||
|
|
||||
|
b.HasIndex(q => q.Code); |
||||
|
b.HasIndex(q => q.Name); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
4
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreModule.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreModule.cs
4
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreModule.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreModule.cs
0
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementModelBuilderConfigurationOptions.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementModelBuilderConfigurationOptions.cs
0
samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementModelBuilderConfigurationOptions.cs → samples/MicroserviceDemo/modules/product/src/ProductManagement.EntityFrameworkCore/ProductManagement/EntityFrameworkCore/ProductManagementModelBuilderConfigurationOptions.cs
0
samples/MicroserviceDemo/modules/product/test/ProductManagement.Application.Tests/MyCompanyName/ProductManagement/ProductManagementApplicationTestModule.cs → samples/MicroserviceDemo/modules/product/test/ProductManagement.Application.Tests/ProductManagement/ProductManagementApplicationTestModule.cs
0
samples/MicroserviceDemo/modules/product/test/ProductManagement.Application.Tests/MyCompanyName/ProductManagement/ProductManagementApplicationTestModule.cs → samples/MicroserviceDemo/modules/product/test/ProductManagement.Application.Tests/ProductManagement/ProductManagementApplicationTestModule.cs
@ -1,7 +0,0 @@ |
|||||
namespace ProductManagement |
|
||||
{ |
|
||||
public abstract class ProductManagementDomainTestBase : ProductManagementTestBase<ProductManagementDomainTestModule> |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,94 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Uow; |
||||
|
|
||||
|
namespace ProductManagement |
||||
|
{ |
||||
|
public abstract class ProductManagementDomainTestBase : ProductManagementTestBase<ProductManagementDomainTestModule> |
||||
|
{ |
||||
|
#region WithUnitOfWork
|
||||
|
|
||||
|
protected virtual void WithUnitOfWork(Action action) |
||||
|
{ |
||||
|
WithUnitOfWork(new UnitOfWorkOptions(), action); |
||||
|
} |
||||
|
|
||||
|
protected virtual void WithUnitOfWork(UnitOfWorkOptions options, Action action) |
||||
|
{ |
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
||||
|
|
||||
|
using (var uow = uowManager.Begin(options)) |
||||
|
{ |
||||
|
action(); |
||||
|
|
||||
|
uow.Complete(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual Task WithUnitOfWorkAsync(Func<Task> func) |
||||
|
{ |
||||
|
return WithUnitOfWorkAsync(new UnitOfWorkOptions(), func); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task WithUnitOfWorkAsync(UnitOfWorkOptions options, Func<Task> action) |
||||
|
{ |
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
||||
|
|
||||
|
using (var uow = uowManager.Begin(options)) |
||||
|
{ |
||||
|
await action(); |
||||
|
|
||||
|
await uow.CompleteAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual TResult WithUnitOfWork<TResult>(Func<TResult> func) |
||||
|
{ |
||||
|
return WithUnitOfWork(new UnitOfWorkOptions(), func); |
||||
|
} |
||||
|
|
||||
|
protected virtual TResult WithUnitOfWork<TResult>(UnitOfWorkOptions options, Func<TResult> func) |
||||
|
{ |
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
||||
|
|
||||
|
using (var uow = uowManager.Begin(options)) |
||||
|
{ |
||||
|
var result = func(); |
||||
|
uow.Complete(); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual Task<TResult> WithUnitOfWorkAsync<TResult>(Func<Task<TResult>> func) |
||||
|
{ |
||||
|
return WithUnitOfWorkAsync(new UnitOfWorkOptions(), func); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(UnitOfWorkOptions options, Func<Task<TResult>> func) |
||||
|
{ |
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
||||
|
|
||||
|
using (var uow = uowManager.Begin(options)) |
||||
|
{ |
||||
|
var result = await func(); |
||||
|
await uow.CompleteAsync(); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace ProductManagement |
||||
|
{ |
||||
|
public class ProductManager_Tests : ProductManagementDomainTestBase |
||||
|
{ |
||||
|
private readonly ProductManager _productManager; |
||||
|
|
||||
|
public ProductManager_Tests() |
||||
|
{ |
||||
|
_productManager = GetRequiredService<ProductManager>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Create_A_Valid_Product() |
||||
|
{ |
||||
|
//Act
|
||||
|
var product = await WithUnitOfWorkAsync( |
||||
|
async () => |
||||
|
{ |
||||
|
return await _productManager.CreateAsync("P000837212", "My Product 837212", stockCount: 42); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
//Assert
|
||||
|
product.Code.ShouldBe("P000837212"); |
||||
|
product.Name.ShouldBe("My Product 837212"); |
||||
|
product.Price.ShouldBe(0.0f); |
||||
|
product.StockCount.ShouldBe(42); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,7 +0,0 @@ |
|||||
namespace ProductManagement.EntityFrameworkCore |
|
||||
{ |
|
||||
public class MyEntityRepository_Tests : MyEntityRepository_Tests<ProductManagementEntityFrameworkCoreTestModule> |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
1
samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreTestModule.cs → samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreTestModule.cs
1
samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/MyCompanyName/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreTestModule.cs → samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/ProductManagement/EntityFrameworkCore/ProductManagementEntityFrameworkCoreTestModule.cs
@ -1,16 +0,0 @@ |
|||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.Modularity; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace ProductManagement |
|
||||
{ |
|
||||
public abstract class MyEntityRepository_Tests<TStartupModule> : ProductManagementTestBase<TStartupModule> |
|
||||
where TStartupModule : IAbpModule |
|
||||
{ |
|
||||
[Fact] |
|
||||
public async Task Test1() |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue