mirror of https://github.com/abpframework/abp.git
Browse Source
Publish events while unit of work is being completed, in the same unit of workpull/9943/head
committed by
GitHub
61 changed files with 1541 additions and 441 deletions
@ -0,0 +1,15 @@ |
|||||
|
namespace Volo.Abp.Domain.Entities |
||||
|
{ |
||||
|
public class DomainEventRecord |
||||
|
{ |
||||
|
public object EventData { get; } |
||||
|
|
||||
|
public long EventOrder { get; } |
||||
|
|
||||
|
public DomainEventRecord(object eventData, long eventOrder) |
||||
|
{ |
||||
|
EventData = eventData; |
||||
|
EventOrder = eventOrder; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,32 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
|
|
||||
namespace Volo.Abp.Domain.Entities.Events |
|
||||
{ |
|
||||
public class EntityChangeReport |
|
||||
{ |
|
||||
public List<EntityChangeEntry> ChangedEntities { get; } |
|
||||
|
|
||||
public List<DomainEventEntry> DomainEvents { get; } |
|
||||
|
|
||||
public List<DomainEventEntry> DistributedEvents { get; } |
|
||||
|
|
||||
public EntityChangeReport() |
|
||||
{ |
|
||||
ChangedEntities = new List<EntityChangeEntry>(); |
|
||||
DomainEvents = new List<DomainEventEntry>(); |
|
||||
DistributedEvents = new List<DomainEventEntry>(); |
|
||||
} |
|
||||
|
|
||||
public bool IsEmpty() |
|
||||
{ |
|
||||
return ChangedEntities.Count <= 0 && |
|
||||
DomainEvents.Count <= 0 && |
|
||||
DistributedEvents.Count <= 0; |
|
||||
} |
|
||||
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
return $"[EntityChangeReport] ChangedEntities: {ChangedEntities.Count}, DomainEvents: {DomainEvents.Count}, DistributedEvents: {DistributedEvents.Count}"; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,22 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Domain.Entities.Events |
||||
|
{ |
||||
|
public class EntityEventReport |
||||
|
{ |
||||
|
public List<DomainEventEntry> DomainEvents { get; } |
||||
|
|
||||
|
public List<DomainEventEntry> DistributedEvents { get; } |
||||
|
|
||||
|
public EntityEventReport() |
||||
|
{ |
||||
|
DomainEvents = new List<DomainEventEntry>(); |
||||
|
DistributedEvents = new List<DomainEventEntry>(); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"[{nameof(EntityEventReport)}] DomainEvents: {DomainEvents.Count}, DistributedEvents: {DistributedEvents.Count}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
using Volo.Abp.EventBus.Local; |
||||
|
using Volo.Abp.Uow; |
||||
|
|
||||
|
namespace Volo.Abp.EventBus |
||||
|
{ |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
public class UnitOfWorkEventPublisher : IUnitOfWorkEventPublisher, ITransientDependency |
||||
|
{ |
||||
|
private readonly ILocalEventBus _localEventBus; |
||||
|
private readonly IDistributedEventBus _distributedEventBus; |
||||
|
|
||||
|
public UnitOfWorkEventPublisher( |
||||
|
ILocalEventBus localEventBus, |
||||
|
IDistributedEventBus distributedEventBus) |
||||
|
{ |
||||
|
_localEventBus = localEventBus; |
||||
|
_distributedEventBus = distributedEventBus; |
||||
|
} |
||||
|
|
||||
|
public async Task PublishLocalEventsAsync(IEnumerable<UnitOfWorkEventRecord> localEvents) |
||||
|
{ |
||||
|
foreach (var localEvent in localEvents) |
||||
|
{ |
||||
|
await _localEventBus.PublishAsync(localEvent.EventType, localEvent.EventData, onUnitOfWorkComplete: false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public async Task PublishDistributedEventsAsync(IEnumerable<UnitOfWorkEventRecord> distributedEvents) |
||||
|
{ |
||||
|
foreach (var distributedEvent in distributedEvents) |
||||
|
{ |
||||
|
await _distributedEventBus.PublishAsync(distributedEvent.EventType, distributedEvent.EventData, onUnitOfWorkComplete: false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Threading; |
||||
|
|
||||
|
namespace Volo.Abp.Uow |
||||
|
{ |
||||
|
public static class EventOrderGenerator |
||||
|
{ |
||||
|
private static long _lastOrder; |
||||
|
|
||||
|
public static long GetNext() |
||||
|
{ |
||||
|
return Interlocked.Increment(ref _lastOrder); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Uow |
||||
|
{ |
||||
|
public interface IUnitOfWorkEventPublisher |
||||
|
{ |
||||
|
Task PublishLocalEventsAsync(IEnumerable<UnitOfWorkEventRecord> localEvents); |
||||
|
|
||||
|
Task PublishDistributedEventsAsync(IEnumerable<UnitOfWorkEventRecord> distributedEvents); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Uow |
||||
|
{ |
||||
|
public class NullUnitOfWorkEventPublisher : IUnitOfWorkEventPublisher, ISingletonDependency |
||||
|
{ |
||||
|
public Task PublishLocalEventsAsync(IEnumerable<UnitOfWorkEventRecord> localEvents) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public Task PublishDistributedEventsAsync(IEnumerable<UnitOfWorkEventRecord> distributedEvents) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Uow |
||||
|
{ |
||||
|
public class UnitOfWorkEventRecord |
||||
|
{ |
||||
|
public object EventData { get; } |
||||
|
|
||||
|
public Type EventType { get; } |
||||
|
|
||||
|
public long EventOrder { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extra properties can be used if needed.
|
||||
|
/// </summary>
|
||||
|
public Dictionary<string, object> Properties { get; } = new Dictionary<string, object>(); |
||||
|
|
||||
|
public UnitOfWorkEventRecord( |
||||
|
Type eventType, |
||||
|
object eventData, |
||||
|
long eventOrder) |
||||
|
{ |
||||
|
EventType = eventType; |
||||
|
EventData = eventData; |
||||
|
EventOrder = eventOrder; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,9 +1,15 @@ |
|||||
using Volo.Abp.TestApp.Testing; |
using System.Threading.Tasks; |
||||
|
using Volo.Abp.TestApp.Testing; |
||||
|
using Xunit; |
||||
|
|
||||
namespace Volo.Abp.MemoryDb.DomainEvents |
namespace Volo.Abp.MemoryDb.DomainEvents |
||||
{ |
{ |
||||
public class DomainEvents_Tests : DomainEvents_Tests<AbpMemoryDbTestModule> |
public class DomainEvents_Tests : DomainEvents_Tests<AbpMemoryDbTestModule> |
||||
{ |
{ |
||||
|
[Fact(Skip = "MemoryDB doesn't support transactions.")] |
||||
|
public override Task Should_Rollback_Uow_If_Event_Handler_Throws_Exception() |
||||
|
{ |
||||
|
return base.Should_Rollback_Uow_If_Event_Handler_Throws_Exception(); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class DemoService : ITransientDependency |
||||
|
{ |
||||
|
private readonly IRepository<TodoItem, Guid> _todoItemRepository; |
||||
|
|
||||
|
public DemoService(IRepository<TodoItem, Guid> todoItemRepository) |
||||
|
{ |
||||
|
_todoItemRepository = todoItemRepository; |
||||
|
} |
||||
|
|
||||
|
public async Task CreateTodoItemAsync() |
||||
|
{ |
||||
|
var todoItem = await _todoItemRepository.InsertAsync( |
||||
|
new TodoItem |
||||
|
{ |
||||
|
Text = "todo item " + DateTime.Now.Ticks |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
Console.WriteLine("Created a new todo item: " + todoItem); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<OutputType>Exe</OutputType> |
||||
|
<TargetFramework>net5.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.*" /> |
||||
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0" /> |
||||
|
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" /> |
||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> |
||||
|
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" /> |
||||
|
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.*"> |
||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |
||||
|
<PrivateAssets>compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native</PrivateAssets> |
||||
|
</PackageReference> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Update="appsettings.json"> |
||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
||||
|
</None> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,37 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Autofac; |
||||
|
using Volo.Abp.Domain.Entities.Events.Distributed; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore.SqlServer; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpEntityFrameworkCoreSqlServerModule), |
||||
|
typeof(AbpAutofacModule) |
||||
|
)] |
||||
|
public class DistDemoAppModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddHostedService<MyProjectNameHostedService>(); |
||||
|
|
||||
|
context.Services.AddAbpDbContext<TodoDbContext>(options => |
||||
|
{ |
||||
|
options.AddDefaultRepositories(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpDbContextOptions>(options => |
||||
|
{ |
||||
|
options.UseSqlServer(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpDistributedEntityEventOptions>(options => |
||||
|
{ |
||||
|
options.EtoMappings.Add<TodoItem, TodoItemEto>(); |
||||
|
options.AutoEventSelectors.Add<TodoItem>(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
// <auto-generated />
|
||||
|
using System; |
||||
|
using DistDemoApp; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Microsoft.EntityFrameworkCore.Infrastructure; |
||||
|
using Microsoft.EntityFrameworkCore.Metadata; |
||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace DistDemoApp.Migrations |
||||
|
{ |
||||
|
[DbContext(typeof(TodoDbContext))] |
||||
|
[Migration("20210825110134_Initial")] |
||||
|
partial class Initial |
||||
|
{ |
||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
||||
|
{ |
||||
|
#pragma warning disable 612, 618
|
||||
|
modelBuilder |
||||
|
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) |
||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128) |
||||
|
.HasAnnotation("ProductVersion", "5.0.9") |
||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); |
||||
|
|
||||
|
modelBuilder.Entity("DistDemoApp.TodoItem", b => |
||||
|
{ |
||||
|
b.Property<Guid>("Id") |
||||
|
.HasColumnType("uniqueidentifier"); |
||||
|
|
||||
|
b.Property<string>("ConcurrencyStamp") |
||||
|
.IsConcurrencyToken() |
||||
|
.HasMaxLength(40) |
||||
|
.HasColumnType("nvarchar(40)") |
||||
|
.HasColumnName("ConcurrencyStamp"); |
||||
|
|
||||
|
b.Property<DateTime>("CreationTime") |
||||
|
.HasColumnType("datetime2") |
||||
|
.HasColumnName("CreationTime"); |
||||
|
|
||||
|
b.Property<Guid?>("CreatorId") |
||||
|
.HasColumnType("uniqueidentifier") |
||||
|
.HasColumnName("CreatorId"); |
||||
|
|
||||
|
b.Property<string>("ExtraProperties") |
||||
|
.HasColumnType("nvarchar(max)") |
||||
|
.HasColumnName("ExtraProperties"); |
||||
|
|
||||
|
b.Property<string>("Text") |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(128) |
||||
|
.HasColumnType("nvarchar(128)"); |
||||
|
|
||||
|
b.HasKey("Id"); |
||||
|
|
||||
|
b.ToTable("TodoItems"); |
||||
|
}); |
||||
|
#pragma warning restore 612, 618
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
|
||||
|
namespace DistDemoApp.Migrations |
||||
|
{ |
||||
|
public partial class Initial : Migration |
||||
|
{ |
||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "TodoItems", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
||||
|
Text = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_TodoItems", x => x.Id); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.DropTable( |
||||
|
name: "TodoItems"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// <auto-generated />
|
||||
|
using System; |
||||
|
using DistDemoApp; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Microsoft.EntityFrameworkCore.Infrastructure; |
||||
|
using Microsoft.EntityFrameworkCore.Metadata; |
||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace DistDemoApp.Migrations |
||||
|
{ |
||||
|
[DbContext(typeof(TodoDbContext))] |
||||
|
[Migration("20210825112717_Added_Summary_Table")] |
||||
|
partial class Added_Summary_Table |
||||
|
{ |
||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
||||
|
{ |
||||
|
#pragma warning disable 612, 618
|
||||
|
modelBuilder |
||||
|
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) |
||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128) |
||||
|
.HasAnnotation("ProductVersion", "5.0.9") |
||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); |
||||
|
|
||||
|
modelBuilder.Entity("DistDemoApp.TodoItem", b => |
||||
|
{ |
||||
|
b.Property<Guid>("Id") |
||||
|
.HasColumnType("uniqueidentifier"); |
||||
|
|
||||
|
b.Property<string>("ConcurrencyStamp") |
||||
|
.IsConcurrencyToken() |
||||
|
.HasMaxLength(40) |
||||
|
.HasColumnType("nvarchar(40)") |
||||
|
.HasColumnName("ConcurrencyStamp"); |
||||
|
|
||||
|
b.Property<DateTime>("CreationTime") |
||||
|
.HasColumnType("datetime2") |
||||
|
.HasColumnName("CreationTime"); |
||||
|
|
||||
|
b.Property<Guid?>("CreatorId") |
||||
|
.HasColumnType("uniqueidentifier") |
||||
|
.HasColumnName("CreatorId"); |
||||
|
|
||||
|
b.Property<string>("ExtraProperties") |
||||
|
.HasColumnType("nvarchar(max)") |
||||
|
.HasColumnName("ExtraProperties"); |
||||
|
|
||||
|
b.Property<string>("Text") |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(128) |
||||
|
.HasColumnType("nvarchar(128)"); |
||||
|
|
||||
|
b.HasKey("Id"); |
||||
|
|
||||
|
b.ToTable("TodoItems"); |
||||
|
}); |
||||
|
|
||||
|
modelBuilder.Entity("DistDemoApp.TodoSummary", b => |
||||
|
{ |
||||
|
b.Property<int>("Id") |
||||
|
.ValueGeneratedOnAdd() |
||||
|
.HasColumnType("int") |
||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); |
||||
|
|
||||
|
b.Property<string>("ConcurrencyStamp") |
||||
|
.IsConcurrencyToken() |
||||
|
.HasMaxLength(40) |
||||
|
.HasColumnType("nvarchar(40)") |
||||
|
.HasColumnName("ConcurrencyStamp"); |
||||
|
|
||||
|
b.Property<byte>("Day") |
||||
|
.HasColumnType("tinyint"); |
||||
|
|
||||
|
b.Property<string>("ExtraProperties") |
||||
|
.HasColumnType("nvarchar(max)") |
||||
|
.HasColumnName("ExtraProperties"); |
||||
|
|
||||
|
b.Property<byte>("Month") |
||||
|
.HasColumnType("tinyint"); |
||||
|
|
||||
|
b.Property<int>("TotalCount") |
||||
|
.HasColumnType("int"); |
||||
|
|
||||
|
b.Property<int>("Year") |
||||
|
.HasColumnType("int"); |
||||
|
|
||||
|
b.HasKey("Id"); |
||||
|
|
||||
|
b.ToTable("TodoSummaries"); |
||||
|
}); |
||||
|
#pragma warning restore 612, 618
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
|
||||
|
namespace DistDemoApp.Migrations |
||||
|
{ |
||||
|
public partial class Added_Summary_Table : Migration |
||||
|
{ |
||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "TodoSummaries", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<int>(type: "int", nullable: false) |
||||
|
.Annotation("SqlServer:Identity", "1, 1"), |
||||
|
Year = table.Column<int>(type: "int", nullable: false), |
||||
|
Month = table.Column<byte>(type: "tinyint", nullable: false), |
||||
|
Day = table.Column<byte>(type: "tinyint", nullable: false), |
||||
|
TotalCount = table.Column<int>(type: "int", nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_TodoSummaries", x => x.Id); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.DropTable( |
||||
|
name: "TodoSummaries"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
// <auto-generated />
|
||||
|
using System; |
||||
|
using DistDemoApp; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Microsoft.EntityFrameworkCore.Infrastructure; |
||||
|
using Microsoft.EntityFrameworkCore.Metadata; |
||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace DistDemoApp.Migrations |
||||
|
{ |
||||
|
[DbContext(typeof(TodoDbContext))] |
||||
|
partial class TodoDbContextModelSnapshot : ModelSnapshot |
||||
|
{ |
||||
|
protected override void BuildModel(ModelBuilder modelBuilder) |
||||
|
{ |
||||
|
#pragma warning disable 612, 618
|
||||
|
modelBuilder |
||||
|
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) |
||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128) |
||||
|
.HasAnnotation("ProductVersion", "5.0.9") |
||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); |
||||
|
|
||||
|
modelBuilder.Entity("DistDemoApp.TodoItem", b => |
||||
|
{ |
||||
|
b.Property<Guid>("Id") |
||||
|
.HasColumnType("uniqueidentifier"); |
||||
|
|
||||
|
b.Property<string>("ConcurrencyStamp") |
||||
|
.IsConcurrencyToken() |
||||
|
.HasMaxLength(40) |
||||
|
.HasColumnType("nvarchar(40)") |
||||
|
.HasColumnName("ConcurrencyStamp"); |
||||
|
|
||||
|
b.Property<DateTime>("CreationTime") |
||||
|
.HasColumnType("datetime2") |
||||
|
.HasColumnName("CreationTime"); |
||||
|
|
||||
|
b.Property<Guid?>("CreatorId") |
||||
|
.HasColumnType("uniqueidentifier") |
||||
|
.HasColumnName("CreatorId"); |
||||
|
|
||||
|
b.Property<string>("ExtraProperties") |
||||
|
.HasColumnType("nvarchar(max)") |
||||
|
.HasColumnName("ExtraProperties"); |
||||
|
|
||||
|
b.Property<string>("Text") |
||||
|
.IsRequired() |
||||
|
.HasMaxLength(128) |
||||
|
.HasColumnType("nvarchar(128)"); |
||||
|
|
||||
|
b.HasKey("Id"); |
||||
|
|
||||
|
b.ToTable("TodoItems"); |
||||
|
}); |
||||
|
|
||||
|
modelBuilder.Entity("DistDemoApp.TodoSummary", b => |
||||
|
{ |
||||
|
b.Property<int>("Id") |
||||
|
.ValueGeneratedOnAdd() |
||||
|
.HasColumnType("int") |
||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); |
||||
|
|
||||
|
b.Property<string>("ConcurrencyStamp") |
||||
|
.IsConcurrencyToken() |
||||
|
.HasMaxLength(40) |
||||
|
.HasColumnType("nvarchar(40)") |
||||
|
.HasColumnName("ConcurrencyStamp"); |
||||
|
|
||||
|
b.Property<byte>("Day") |
||||
|
.HasColumnType("tinyint"); |
||||
|
|
||||
|
b.Property<string>("ExtraProperties") |
||||
|
.HasColumnType("nvarchar(max)") |
||||
|
.HasColumnName("ExtraProperties"); |
||||
|
|
||||
|
b.Property<byte>("Month") |
||||
|
.HasColumnType("tinyint"); |
||||
|
|
||||
|
b.Property<int>("TotalCount") |
||||
|
.HasColumnType("int"); |
||||
|
|
||||
|
b.Property<int>("Year") |
||||
|
.HasColumnType("int"); |
||||
|
|
||||
|
b.HasKey("Id"); |
||||
|
|
||||
|
b.ToTable("TodoSummaries"); |
||||
|
}); |
||||
|
#pragma warning restore 612, 618
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class MyProjectNameHostedService : IHostedService |
||||
|
{ |
||||
|
private readonly IAbpApplicationWithExternalServiceProvider _application; |
||||
|
private readonly IServiceProvider _serviceProvider; |
||||
|
private readonly DemoService _demoService; |
||||
|
|
||||
|
public MyProjectNameHostedService( |
||||
|
IAbpApplicationWithExternalServiceProvider application, |
||||
|
IServiceProvider serviceProvider, |
||||
|
DemoService demoService) |
||||
|
{ |
||||
|
_application = application; |
||||
|
_serviceProvider = serviceProvider; |
||||
|
_demoService = demoService; |
||||
|
} |
||||
|
|
||||
|
public async Task StartAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
_application.Initialize(_serviceProvider); |
||||
|
|
||||
|
await _demoService.CreateTodoItemAsync(); |
||||
|
} |
||||
|
|
||||
|
public Task StopAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
_application.Shutdown(); |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Serilog; |
||||
|
using Serilog.Events; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class Program |
||||
|
{ |
||||
|
public static async Task<int> Main(string[] args) |
||||
|
{ |
||||
|
Log.Logger = new LoggerConfiguration() |
||||
|
#if DEBUG
|
||||
|
.MinimumLevel.Debug() |
||||
|
#else
|
||||
|
.MinimumLevel.Information() |
||||
|
#endif
|
||||
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) |
||||
|
.Enrich.FromLogContext() |
||||
|
.WriteTo.Async(c => c.File("Logs/logs.txt")) |
||||
|
.WriteTo.Async(c => c.Console()) |
||||
|
.CreateLogger(); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
Log.Information("Starting console host."); |
||||
|
await CreateHostBuilder(args).RunConsoleAsync(); |
||||
|
return 0; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Log.Fatal(ex, "Host terminated unexpectedly!"); |
||||
|
return 1; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
Log.CloseAndFlush(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
internal static IHostBuilder CreateHostBuilder(string[] args) => |
||||
|
Host.CreateDefaultBuilder(args) |
||||
|
.UseAutofac() |
||||
|
.UseSerilog() |
||||
|
.ConfigureAppConfiguration((context, config) => |
||||
|
{ |
||||
|
//setup your additional configuration sources
|
||||
|
}) |
||||
|
.ConfigureServices((hostContext, services) => |
||||
|
{ |
||||
|
services.AddApplication<DistDemoAppModule>(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class TodoDbContext : AbpDbContext<TodoDbContext> |
||||
|
{ |
||||
|
public DbSet<TodoItem> TodoItems { get; set; } |
||||
|
public DbSet<TodoSummary> TodoSummaries { get; set; } |
||||
|
|
||||
|
public TodoDbContext(DbContextOptions<TodoDbContext> options) |
||||
|
: base(options) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder) |
||||
|
{ |
||||
|
base.OnModelCreating(modelBuilder); |
||||
|
|
||||
|
modelBuilder.Entity<TodoItem>(b => |
||||
|
{ |
||||
|
b.Property(x => x.Text).IsRequired().HasMaxLength(128); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System.IO; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Microsoft.EntityFrameworkCore.Design; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class TodoDbContextFactory : IDesignTimeDbContextFactory<TodoDbContext> |
||||
|
{ |
||||
|
public TodoDbContext CreateDbContext(string[] args) |
||||
|
{ |
||||
|
var configuration = BuildConfiguration(); |
||||
|
|
||||
|
var builder = new DbContextOptionsBuilder<TodoDbContext>() |
||||
|
.UseSqlServer(configuration.GetConnectionString("Default")); |
||||
|
|
||||
|
return new TodoDbContext(builder.Options); |
||||
|
} |
||||
|
|
||||
|
private static IConfigurationRoot BuildConfiguration() |
||||
|
{ |
||||
|
var builder = new ConfigurationBuilder() |
||||
|
.SetBasePath(Directory.GetCurrentDirectory()) |
||||
|
.AddJsonFile("appsettings.json", optional: false); |
||||
|
|
||||
|
return builder.Build(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Entities.Events.Distributed; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
using Volo.Abp.Uow; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class TodoEventHandler : |
||||
|
IDistributedEventHandler<EntityCreatedEto<TodoItemEto>>, |
||||
|
IDistributedEventHandler<EntityDeletedEto<TodoItemEto>>, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
private readonly IRepository<TodoSummary, int> _todoSummaryRepository; |
||||
|
|
||||
|
public TodoEventHandler(IRepository<TodoSummary, int> todoSummaryRepository) |
||||
|
{ |
||||
|
_todoSummaryRepository = todoSummaryRepository; |
||||
|
} |
||||
|
|
||||
|
[UnitOfWork] |
||||
|
public virtual async Task HandleEventAsync(EntityCreatedEto<TodoItemEto> eventData) |
||||
|
{ |
||||
|
var dateTime = eventData.Entity.CreationTime; |
||||
|
var todoSummary = await _todoSummaryRepository.FindAsync( |
||||
|
x => x.Year == dateTime.Year && |
||||
|
x.Month == dateTime.Month && |
||||
|
x.Day == dateTime.Day |
||||
|
); |
||||
|
|
||||
|
if (todoSummary == null) |
||||
|
{ |
||||
|
todoSummary = await _todoSummaryRepository.InsertAsync(new TodoSummary(dateTime)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
todoSummary.Increase(); |
||||
|
await _todoSummaryRepository.UpdateAsync(todoSummary); |
||||
|
} |
||||
|
|
||||
|
Console.WriteLine("Increased total count: " + todoSummary); |
||||
|
|
||||
|
throw new ApplicationException("Thrown to rollback the UOW!"); |
||||
|
} |
||||
|
|
||||
|
public async Task HandleEventAsync(EntityDeletedEto<TodoItemEto> eventData) |
||||
|
{ |
||||
|
var dateTime = eventData.Entity.CreationTime; |
||||
|
var todoSummary = await _todoSummaryRepository.FirstOrDefaultAsync( |
||||
|
x => x.Year == dateTime.Year && |
||||
|
x.Month == dateTime.Month && |
||||
|
x.Day == dateTime.Day |
||||
|
); |
||||
|
|
||||
|
if (todoSummary != null) |
||||
|
{ |
||||
|
todoSummary.Decrease(); |
||||
|
await _todoSummaryRepository.UpdateAsync(todoSummary); |
||||
|
Console.WriteLine("Decreased total count: " + todoSummary); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class TodoItem : CreationAuditedAggregateRoot<Guid> |
||||
|
{ |
||||
|
public string Text { get; set; } |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"{base.ToString()}, Text = {Text}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.EventBus; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
[EventName("todo-item")] |
||||
|
public class TodoItemEto |
||||
|
{ |
||||
|
public DateTime CreationTime { get; set; } |
||||
|
public string Text { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.ObjectMapping; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class TodoItemObjectMapper : IObjectMapper<TodoItem, TodoItemEto>, ISingletonDependency |
||||
|
{ |
||||
|
public TodoItemEto Map(TodoItem source) |
||||
|
{ |
||||
|
return new TodoItemEto |
||||
|
{ |
||||
|
Text = source.Text, |
||||
|
CreationTime = source.CreationTime |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public TodoItemEto Map(TodoItem source, TodoItemEto destination) |
||||
|
{ |
||||
|
destination.Text = source.Text; |
||||
|
destination.CreationTime = source.CreationTime; |
||||
|
return destination; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
|
||||
|
namespace DistDemoApp |
||||
|
{ |
||||
|
public class TodoSummary : AggregateRoot<int> |
||||
|
{ |
||||
|
public int Year { get; private set; } |
||||
|
public byte Month { get; private set; } |
||||
|
public byte Day { get; private set; } |
||||
|
public int TotalCount { get; private set; } |
||||
|
|
||||
|
private TodoSummary() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public TodoSummary(DateTime dateTime, int initialCount = 1) |
||||
|
{ |
||||
|
Year = dateTime.Year; |
||||
|
Month = (byte)dateTime.Month; |
||||
|
Day = (byte)dateTime.Day; |
||||
|
TotalCount = initialCount; |
||||
|
} |
||||
|
|
||||
|
public void Increase(int amount = 1) |
||||
|
{ |
||||
|
TotalCount += amount; |
||||
|
} |
||||
|
|
||||
|
public void Decrease(int amount = 1) |
||||
|
{ |
||||
|
TotalCount -= amount; |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"{base.ToString()}, {Year}-{Month:00}-{Day:00}: {TotalCount}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
{ |
||||
|
"ConnectionStrings": { |
||||
|
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=DistEventsDemo;Trusted_Connection=True" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistDemoApp", "DistDemoApp\DistDemoApp.csproj", "{10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|Any CPU = Debug|Any CPU |
||||
|
Release|Any CPU = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{10DBC6BC-1269-4C68-9F6C-12209A3FBF5B}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
||||
Loading…
Reference in new issue