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