mirror of https://github.com/abpframework/abp.git
committed by
GitHub
52 changed files with 2234 additions and 1541 deletions
@ -0,0 +1,203 @@ |
|||||
|
# Dynamic Events in ABP |
||||
|
|
||||
|
ABP's Event Bus is a core infrastructure piece. The **Local Event Bus** handles in-process communication between services. The **Distributed Event Bus** handles cross-service communication over message brokers like RabbitMQ, Kafka, Azure Service Bus, and Rebus. |
||||
|
|
||||
|
Both are fully type-safe — you define event types at compile time, register handlers via DI, and everything is wired up automatically. This works great, but it has one assumption: **you know all your event types at compile time**. |
||||
|
|
||||
|
In practice, that assumption breaks down in several scenarios: |
||||
|
|
||||
|
- You're building a **plugin system** where third-party modules register their own event types at runtime — you can't pre-define an `IDistributedEventHandler<TEvent>` for every possible plugin event |
||||
|
- Your system receives events from **external systems** (webhooks, IoT devices, partner APIs) where the event schema is defined by the external party, not by your codebase |
||||
|
- You're building a **low-code platform** where end users define event-driven workflows through a visual designer — the event names and payloads are entirely determined at runtime |
||||
|
|
||||
|
ABP's **Dynamic Events** extend the existing `IEventBus` and `IDistributedEventBus` interfaces with string-based publishing and subscription. You can publish events by name, subscribe to events by name, and handle payloads without any compile-time type binding — all while coexisting seamlessly with the existing typed event system. |
||||
|
|
||||
|
## Publishing Events by Name |
||||
|
|
||||
|
The most straightforward use case: publish an event using a string name and an arbitrary payload. |
||||
|
|
||||
|
```csharp |
||||
|
public class OrderAppService : ApplicationService |
||||
|
{ |
||||
|
private readonly IDistributedEventBus _eventBus; |
||||
|
|
||||
|
public OrderAppService(IDistributedEventBus eventBus) |
||||
|
{ |
||||
|
_eventBus = eventBus; |
||||
|
} |
||||
|
|
||||
|
public async Task PlaceOrderAsync(PlaceOrderInput input) |
||||
|
{ |
||||
|
// Business logic... |
||||
|
|
||||
|
// Publish a dynamic event — no event class needed |
||||
|
await _eventBus.PublishAsync( |
||||
|
"OrderPlaced", |
||||
|
new { OrderId = input.Id, CustomerEmail = input.Email } |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
The payload can be any serializable object — an anonymous type, a `Dictionary<string, object>`, or even an existing typed class. The event bus serializes the payload and sends it to the broker with the string name as the routing key. |
||||
|
|
||||
|
### What If a Typed Event Already Exists? |
||||
|
|
||||
|
If the string name matches an existing typed event (via `EventNameAttribute`), the framework automatically converts the payload to the typed class and routes it through the **typed pipeline**. Both typed handlers and dynamic handlers are triggered. |
||||
|
|
||||
|
```csharp |
||||
|
[EventName("OrderPlaced")] |
||||
|
public class OrderPlacedEto |
||||
|
{ |
||||
|
public Guid OrderId { get; set; } |
||||
|
public string CustomerEmail { get; set; } |
||||
|
} |
||||
|
|
||||
|
// This handler will still receive the event, with auto-converted data |
||||
|
public class OrderEmailHandler : IDistributedEventHandler<OrderPlacedEto> |
||||
|
{ |
||||
|
public Task HandleEventAsync(OrderPlacedEto eventData) |
||||
|
{ |
||||
|
// eventData.OrderId and eventData.CustomerEmail are populated |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Publishing by name with `new { OrderId = ..., CustomerEmail = ... }` triggers this typed handler — the framework handles the serialization round-trip. This is especially useful for scenarios where a service needs to emit events without taking a dependency on the project that defines the event type. |
||||
|
|
||||
|
## Subscribing to Dynamic Events |
||||
|
|
||||
|
Dynamic subscription lets you register event handlers at runtime, using a string event name. |
||||
|
|
||||
|
```csharp |
||||
|
public override async Task OnApplicationInitializationAsync( |
||||
|
ApplicationInitializationContext context) |
||||
|
{ |
||||
|
var eventBus = context.ServiceProvider |
||||
|
.GetRequiredService<IDistributedEventBus>(); |
||||
|
|
||||
|
// Subscribe to a dynamic event — no event class needed |
||||
|
eventBus.Subscribe("PartnerOrderReceived", |
||||
|
new PartnerOrderHandler(context.ServiceProvider)); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
The handler implements `IDistributedEventHandler<DynamicEventData>`: |
||||
|
|
||||
|
```csharp |
||||
|
public class PartnerOrderHandler : IDistributedEventHandler<DynamicEventData> |
||||
|
{ |
||||
|
private readonly IServiceProvider _serviceProvider; |
||||
|
|
||||
|
public PartnerOrderHandler(IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
_serviceProvider = serviceProvider; |
||||
|
} |
||||
|
|
||||
|
public async Task HandleEventAsync(DynamicEventData eventData) |
||||
|
{ |
||||
|
// eventData.EventName = "PartnerOrderReceived" |
||||
|
// eventData.Data = the raw payload from the broker |
||||
|
|
||||
|
var orderProcessor = _serviceProvider |
||||
|
.GetRequiredService<IPartnerOrderProcessor>(); |
||||
|
|
||||
|
await orderProcessor.ProcessAsync(eventData.EventName, eventData.Data); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
`DynamicEventData` is a simple POCO with two properties: |
||||
|
|
||||
|
- **`EventName`** — the string name that identifies the event |
||||
|
- **`Data`** — the raw event data payload (the deserialized `object` from the broker) |
||||
|
|
||||
|
> `Subscribe` returns an `IDisposable`. Call `Dispose()` to unsubscribe the handler at runtime. |
||||
|
|
||||
|
## Mixed Typed and Dynamic Handlers |
||||
|
|
||||
|
Typed and dynamic handlers coexist naturally. When both are registered for the same event name, **both are triggered** — the framework automatically converts the data to the appropriate format for each handler. |
||||
|
|
||||
|
```csharp |
||||
|
// Typed handler — receives OrderPlacedEto |
||||
|
eventBus.Subscribe<OrderPlacedEto, OrderEmailHandler>(); |
||||
|
|
||||
|
// Dynamic handler — receives DynamicEventData for the same event |
||||
|
eventBus.Subscribe("OrderPlaced", new AuditLogHandler()); |
||||
|
``` |
||||
|
|
||||
|
When `OrderPlacedEto` is published (by type or by name), both handlers fire. The typed handler receives a fully deserialized `OrderPlacedEto` object. The dynamic handler receives a `DynamicEventData` wrapping the raw payload. |
||||
|
|
||||
|
This enables a powerful pattern: the core business logic uses typed handlers for safety, while infrastructure concerns (auditing, logging, plugin hooks) use dynamic handlers for flexibility. |
||||
|
|
||||
|
## Outbox Support |
||||
|
|
||||
|
Dynamic events go through the same **outbox/inbox pipeline** as typed events. If you have outbox configured, dynamic events benefit from the same reliability guarantees — they are stored in the outbox table within the same database transaction as your business data, then reliably delivered to the broker by the background worker. |
||||
|
|
||||
|
No additional configuration is needed. The outbox works transparently for both typed and dynamic events: |
||||
|
|
||||
|
```csharp |
||||
|
// This dynamic event goes through the outbox if configured |
||||
|
using var uow = _unitOfWorkManager.Begin(); |
||||
|
await _eventBus.PublishAsync( |
||||
|
"OrderPlaced", |
||||
|
new { OrderId = orderId }, |
||||
|
onUnitOfWorkComplete: true, |
||||
|
useOutbox: true |
||||
|
); |
||||
|
await uow.CompleteAsync(); |
||||
|
``` |
||||
|
|
||||
|
## Local Event Bus |
||||
|
|
||||
|
Dynamic events work on the local event bus too, not just the distributed bus. The API is the same: |
||||
|
|
||||
|
```csharp |
||||
|
var localEventBus = context.ServiceProvider |
||||
|
.GetRequiredService<ILocalEventBus>(); |
||||
|
|
||||
|
// Subscribe dynamically |
||||
|
localEventBus.Subscribe("UserActivityTracked", |
||||
|
new SingleInstanceHandlerFactory( |
||||
|
new ActionEventHandler<DynamicEventData>(eventData => |
||||
|
{ |
||||
|
// Handle the event |
||||
|
return Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
// Publish dynamically |
||||
|
await localEventBus.PublishAsync("UserActivityTracked", new |
||||
|
{ |
||||
|
UserId = currentUser.Id, |
||||
|
Action = "PageView", |
||||
|
Url = "/products/42" |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Provider Support |
||||
|
|
||||
|
Dynamic events work with all distributed event bus providers: |
||||
|
|
||||
|
| Provider | Dynamic Subscribe | Dynamic Publish | |
||||
|
|---|---|---| |
||||
|
| LocalDistributedEventBus (default) | ✅ | ✅ | |
||||
|
| RabbitMQ | ✅ | ✅ | |
||||
|
| Kafka | ✅ | ✅ | |
||||
|
| Rebus | ✅ | ✅ | |
||||
|
| Azure Service Bus | ✅ | ✅ | |
||||
|
| Dapr | ❌ | ❌ | |
||||
|
|
||||
|
Dapr requires topic subscriptions to be declared at application startup and cannot add subscriptions at runtime. Calling `Subscribe(string, ...)` on the Dapr provider throws an `AbpException`. |
||||
|
|
||||
|
## Summary |
||||
|
|
||||
|
`IEventBus.PublishAsync(string, object)` and `IEventBus.Subscribe(string, handler)` let you publish and subscribe to events by name at runtime — no compile-time types required. If the event name matches a typed event, the framework auto-converts the payload and triggers both typed and dynamic handlers. Dynamic events go through the same outbox/inbox pipeline as typed events, so reliability guarantees are preserved. This works across all providers except Dapr, and coexists seamlessly with the existing typed event system. |
||||
|
|
||||
|
## References |
||||
|
|
||||
|
- [Local Event Bus](https://abp.io/docs/latest/framework/infrastructure/event-bus/local) |
||||
|
- [Distributed Event Bus](https://abp.io/docs/latest/framework/infrastructure/event-bus/distributed) |
||||
|
- [RabbitMQ Integration](https://abp.io/docs/latest/framework/infrastructure/event-bus/distributed/rabbitmq) |
||||
|
- [Kafka Integration](https://abp.io/docs/latest/framework/infrastructure/event-bus/distributed/kafka) |
||||
|
- [Dynamic Distributed Events Sample](https://github.com/abpframework/abp-samples/tree/master/DynamicDistributedEvents) |
||||
|
After Width: | Height: | Size: 307 KiB |
@ -0,0 +1,17 @@ |
|||||
|
namespace Volo.Abp.EventBus; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Wraps arbitrary event data with a string-based event name for dynamic (type-less) event handling.
|
||||
|
/// </summary>
|
||||
|
public class DynamicEventData |
||||
|
{ |
||||
|
public string EventName { get; } |
||||
|
|
||||
|
public object Data { get; } |
||||
|
|
||||
|
public DynamicEventData(string eventName, object data) |
||||
|
{ |
||||
|
EventName = Check.NotNullOrWhiteSpace(eventName, nameof(eventName)); |
||||
|
Data = Check.NotNull(data, nameof(data)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,258 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.EventBus.Local; |
||||
|
|
||||
|
public class LocalEventBus_Dynamic_Test : EventBusTestBase |
||||
|
{ |
||||
|
[Fact] |
||||
|
public async Task Should_Handle_Dynamic_Handler_With_EventName() |
||||
|
{ |
||||
|
var handleCount = 0; |
||||
|
var eventName = "TestEvent-" + Guid.NewGuid().ToString("N"); |
||||
|
|
||||
|
using var subscription = LocalEventBus.Subscribe(eventName, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
handleCount++; |
||||
|
d.EventName.ShouldBe(eventName); |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 1 }); |
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 2 }); |
||||
|
|
||||
|
handleCount.ShouldBe(2); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Handle_Typed_Handler_When_Published_With_EventName() |
||||
|
{ |
||||
|
var handleCount = 0; |
||||
|
|
||||
|
using var subscription = LocalEventBus.Subscribe<MySimpleEventData>(async (data) => |
||||
|
{ |
||||
|
handleCount++; |
||||
|
await Task.CompletedTask; |
||||
|
}); |
||||
|
|
||||
|
var eventName = EventNameAttribute.GetNameOrDefault<MySimpleEventData>(); |
||||
|
await LocalEventBus.PublishAsync(eventName, new MySimpleEventData(42)); |
||||
|
|
||||
|
handleCount.ShouldBe(1); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Convert_Dictionary_To_Typed_Handler() |
||||
|
{ |
||||
|
MySimpleEventData? receivedData = null; |
||||
|
|
||||
|
using var subscription = LocalEventBus.Subscribe<MySimpleEventData>(async (data) => |
||||
|
{ |
||||
|
receivedData = data; |
||||
|
await Task.CompletedTask; |
||||
|
}); |
||||
|
|
||||
|
var eventName = EventNameAttribute.GetNameOrDefault<MySimpleEventData>(); |
||||
|
await LocalEventBus.PublishAsync(eventName, new Dictionary<string, object> |
||||
|
{ |
||||
|
{ "Value", 42 } |
||||
|
}); |
||||
|
|
||||
|
receivedData.ShouldNotBeNull(); |
||||
|
receivedData.Value.ShouldBe(42); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Trigger_Both_Typed_And_Dynamic_Handlers() |
||||
|
{ |
||||
|
var typedHandleCount = 0; |
||||
|
var dynamicHandleCount = 0; |
||||
|
|
||||
|
using var typedSubscription = LocalEventBus.Subscribe<MySimpleEventData>(async (data) => |
||||
|
{ |
||||
|
typedHandleCount++; |
||||
|
await Task.CompletedTask; |
||||
|
}); |
||||
|
|
||||
|
var eventName = EventNameAttribute.GetNameOrDefault<MySimpleEventData>(); |
||||
|
|
||||
|
using var dynamicSubscription = LocalEventBus.Subscribe(eventName, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
dynamicHandleCount++; |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(new MySimpleEventData(1)); |
||||
|
|
||||
|
typedHandleCount.ShouldBe(1); |
||||
|
dynamicHandleCount.ShouldBe(1); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Unsubscribe_Dynamic_Handler() |
||||
|
{ |
||||
|
var handleCount = 0; |
||||
|
var eventName = "TestEvent-" + Guid.NewGuid().ToString("N"); |
||||
|
|
||||
|
var handler = new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
handleCount++; |
||||
|
await Task.CompletedTask; |
||||
|
}); |
||||
|
var factory = new SingleInstanceHandlerFactory(handler); |
||||
|
|
||||
|
var disposable = LocalEventBus.Subscribe(eventName, factory); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 1 }); |
||||
|
handleCount.ShouldBe(1); |
||||
|
|
||||
|
disposable.Dispose(); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 2 }); |
||||
|
handleCount.ShouldBe(1); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Not_Throw_For_Unknown_Event_Name() |
||||
|
{ |
||||
|
// Publishing to an unknown event name should not throw (consistent with typed PublishAsync behavior)
|
||||
|
await LocalEventBus.PublishAsync("NonExistentEvent", new { Value = 1 }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Access_Data_In_Dynamic_Handler() |
||||
|
{ |
||||
|
object? receivedData = null; |
||||
|
var eventName = "TestEvent-" + Guid.NewGuid().ToString("N"); |
||||
|
|
||||
|
using var subscription = LocalEventBus.Subscribe(eventName, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
receivedData = d.Data; |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Name = "Hello", Count = 42 }); |
||||
|
|
||||
|
receivedData.ShouldNotBeNull(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Receive_Typed_Data_Via_Typed_Handler_From_Dynamic_Publish() |
||||
|
{ |
||||
|
MySimpleEventData? receivedData = null; |
||||
|
var eventName = EventNameAttribute.GetNameOrDefault<MySimpleEventData>(); |
||||
|
|
||||
|
using var subscription = LocalEventBus.Subscribe<MySimpleEventData>(async (d) => |
||||
|
{ |
||||
|
receivedData = d; |
||||
|
await Task.CompletedTask; |
||||
|
}); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new MySimpleEventData(99)); |
||||
|
|
||||
|
receivedData.ShouldNotBeNull(); |
||||
|
receivedData.Value.ShouldBe(99); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Unsubscribe_All_Dynamic_Handlers() |
||||
|
{ |
||||
|
var handleCount = 0; |
||||
|
var eventName = "TestEvent-" + Guid.NewGuid().ToString("N"); |
||||
|
|
||||
|
LocalEventBus.Subscribe(eventName, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
handleCount++; |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
LocalEventBus.Subscribe(eventName, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
handleCount++; |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 1 }); |
||||
|
handleCount.ShouldBe(2); |
||||
|
|
||||
|
LocalEventBus.UnsubscribeAll(eventName); |
||||
|
|
||||
|
// After UnsubscribeAll, publishing still works (key exists) but no handlers are invoked
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 2 }); |
||||
|
handleCount.ShouldBe(2); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Handle_Multiple_Dynamic_Events_Independently() |
||||
|
{ |
||||
|
var countA = 0; |
||||
|
var countB = 0; |
||||
|
var eventNameA = "EventA-" + Guid.NewGuid().ToString("N"); |
||||
|
var eventNameB = "EventB-" + Guid.NewGuid().ToString("N"); |
||||
|
|
||||
|
using var subA = LocalEventBus.Subscribe(eventNameA, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
countA++; |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
using var subB = LocalEventBus.Subscribe(eventNameB, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
countB++; |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventNameA, new { Value = 1 }); |
||||
|
await LocalEventBus.PublishAsync(eventNameB, new { Value = 2 }); |
||||
|
await LocalEventBus.PublishAsync(eventNameA, new { Value = 3 }); |
||||
|
|
||||
|
countA.ShouldBe(2); |
||||
|
countB.ShouldBe(1); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Receive_EventName_In_DynamicEventData() |
||||
|
{ |
||||
|
string? receivedEventName = null; |
||||
|
var eventName = "TestEvent-" + Guid.NewGuid().ToString("N"); |
||||
|
|
||||
|
using var subscription = LocalEventBus.Subscribe(eventName, |
||||
|
new SingleInstanceHandlerFactory(new ActionEventHandler<DynamicEventData>(async (d) => |
||||
|
{ |
||||
|
receivedEventName = d.EventName; |
||||
|
await Task.CompletedTask; |
||||
|
}))); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 1 }); |
||||
|
|
||||
|
receivedEventName.ShouldBe(eventName); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Convert_Anonymous_Object_To_Typed_Handler() |
||||
|
{ |
||||
|
MySimpleEventData? receivedData = null; |
||||
|
var eventName = EventNameAttribute.GetNameOrDefault<MySimpleEventData>(); |
||||
|
|
||||
|
using var subscription = LocalEventBus.Subscribe<MySimpleEventData>(async (d) => |
||||
|
{ |
||||
|
receivedData = d; |
||||
|
await Task.CompletedTask; |
||||
|
}); |
||||
|
|
||||
|
await LocalEventBus.PublishAsync(eventName, new { Value = 77 }); |
||||
|
|
||||
|
receivedData.ShouldNotBeNull(); |
||||
|
receivedData.Value.ShouldBe(77); |
||||
|
} |
||||
|
} |
||||
@ -1,28 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<OutputType>Exe</OutputType> |
|
||||
<TargetFramework>net10.0</TargetFramework> |
|
||||
<RootNamespace>DistDemoApp</RootNamespace> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus.RabbitMQ\Volo.Abp.EventBus.RabbitMQ.csproj" /> |
|
||||
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools"> |
|
||||
<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> |
|
||||
@ -1,44 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Volo.Abp.EntityFrameworkCore; |
|
||||
using Volo.Abp.EntityFrameworkCore.DistributedEvents; |
|
||||
using Volo.Abp.EntityFrameworkCore.SqlServer; |
|
||||
using Volo.Abp.EventBus.Distributed; |
|
||||
using Volo.Abp.EventBus.RabbitMq; |
|
||||
using Volo.Abp.Modularity; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
[DependsOn( |
|
||||
typeof(AbpEntityFrameworkCoreSqlServerModule), |
|
||||
typeof(AbpEventBusRabbitMqModule), |
|
||||
typeof(DistDemoAppSharedModule) |
|
||||
)] |
|
||||
public class DistDemoAppEfCoreRabbitMqModule : AbpModule |
|
||||
{ |
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
|
||||
{ |
|
||||
context.Services.AddAbpDbContext<TodoDbContext>(options => |
|
||||
{ |
|
||||
options.AddDefaultRepositories(); |
|
||||
}); |
|
||||
|
|
||||
Configure<AbpDbContextOptions>(options => |
|
||||
{ |
|
||||
options.UseSqlServer(); |
|
||||
}); |
|
||||
|
|
||||
Configure<AbpDistributedEventBusOptions>(options => |
|
||||
{ |
|
||||
options.Outboxes.Configure(config => |
|
||||
{ |
|
||||
config.UseDbContext<TodoDbContext>(); |
|
||||
}); |
|
||||
|
|
||||
options.Inboxes.Configure(config => |
|
||||
{ |
|
||||
config.UseDbContext<TodoDbContext>(); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,162 +0,0 @@ |
|||||
// <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("20210910152547_Added_Boxes_Initial")] |
|
||||
partial class Added_Boxes_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"); |
|
||||
}); |
|
||||
|
|
||||
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"); |
|
||||
}); |
|
||||
|
|
||||
modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.IncomingEventRecord", b => |
|
||||
{ |
|
||||
b.Property<Guid>("Id") |
|
||||
.HasColumnType("uniqueidentifier"); |
|
||||
|
|
||||
b.Property<DateTime>("CreationTime") |
|
||||
.HasColumnType("datetime2") |
|
||||
.HasColumnName("CreationTime"); |
|
||||
|
|
||||
b.Property<byte[]>("EventData") |
|
||||
.IsRequired() |
|
||||
.HasColumnType("varbinary(max)"); |
|
||||
|
|
||||
b.Property<string>("EventName") |
|
||||
.IsRequired() |
|
||||
.HasMaxLength(256) |
|
||||
.HasColumnType("nvarchar(256)"); |
|
||||
|
|
||||
b.Property<string>("ExtraProperties") |
|
||||
.HasColumnType("nvarchar(max)") |
|
||||
.HasColumnName("ExtraProperties"); |
|
||||
|
|
||||
b.Property<string>("MessageId") |
|
||||
.HasColumnType("nvarchar(450)"); |
|
||||
|
|
||||
b.Property<bool>("Processed") |
|
||||
.HasColumnType("bit"); |
|
||||
|
|
||||
b.Property<DateTime?>("ProcessedTime") |
|
||||
.HasColumnType("datetime2"); |
|
||||
|
|
||||
b.HasKey("Id"); |
|
||||
|
|
||||
b.HasIndex("MessageId"); |
|
||||
|
|
||||
b.HasIndex("Processed", "CreationTime"); |
|
||||
|
|
||||
b.ToTable("AbpEventInbox"); |
|
||||
}); |
|
||||
|
|
||||
modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.OutgoingEventRecord", b => |
|
||||
{ |
|
||||
b.Property<Guid>("Id") |
|
||||
.HasColumnType("uniqueidentifier"); |
|
||||
|
|
||||
b.Property<DateTime>("CreationTime") |
|
||||
.HasColumnType("datetime2") |
|
||||
.HasColumnName("CreationTime"); |
|
||||
|
|
||||
b.Property<byte[]>("EventData") |
|
||||
.IsRequired() |
|
||||
.HasColumnType("varbinary(max)"); |
|
||||
|
|
||||
b.Property<string>("EventName") |
|
||||
.IsRequired() |
|
||||
.HasMaxLength(256) |
|
||||
.HasColumnType("nvarchar(256)"); |
|
||||
|
|
||||
b.Property<string>("ExtraProperties") |
|
||||
.HasColumnType("nvarchar(max)") |
|
||||
.HasColumnName("ExtraProperties"); |
|
||||
|
|
||||
b.HasKey("Id"); |
|
||||
|
|
||||
b.ToTable("AbpEventOutbox"); |
|
||||
}); |
|
||||
#pragma warning restore 612, 618
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,103 +0,0 @@ |
|||||
using System; |
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
|
||||
|
|
||||
namespace DistDemoApp.Migrations |
|
||||
{ |
|
||||
public partial class Added_Boxes_Initial : Migration |
|
||||
{ |
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
|
||||
{ |
|
||||
migrationBuilder.CreateTable( |
|
||||
name: "AbpEventInbox", |
|
||||
columns: table => new |
|
||||
{ |
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
|
||||
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
|
||||
MessageId = table.Column<string>(type: "nvarchar(450)", nullable: true), |
|
||||
EventName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), |
|
||||
EventData = table.Column<byte[]>(type: "varbinary(max)", nullable: false), |
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), |
|
||||
Processed = table.Column<bool>(type: "bit", nullable: false), |
|
||||
ProcessedTime = table.Column<DateTime>(type: "datetime2", nullable: true) |
|
||||
}, |
|
||||
constraints: table => |
|
||||
{ |
|
||||
table.PrimaryKey("PK_AbpEventInbox", x => x.Id); |
|
||||
}); |
|
||||
|
|
||||
migrationBuilder.CreateTable( |
|
||||
name: "AbpEventOutbox", |
|
||||
columns: table => new |
|
||||
{ |
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), |
|
||||
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true), |
|
||||
EventName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), |
|
||||
EventData = table.Column<byte[]>(type: "varbinary(max)", nullable: false), |
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false) |
|
||||
}, |
|
||||
constraints: table => |
|
||||
{ |
|
||||
table.PrimaryKey("PK_AbpEventOutbox", x => x.Id); |
|
||||
}); |
|
||||
|
|
||||
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); |
|
||||
}); |
|
||||
|
|
||||
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); |
|
||||
}); |
|
||||
|
|
||||
migrationBuilder.CreateIndex( |
|
||||
name: "IX_AbpEventInbox_MessageId", |
|
||||
table: "AbpEventInbox", |
|
||||
column: "MessageId"); |
|
||||
|
|
||||
migrationBuilder.CreateIndex( |
|
||||
name: "IX_AbpEventInbox_Processed_CreationTime", |
|
||||
table: "AbpEventInbox", |
|
||||
columns: new[] { "Processed", "CreationTime" }); |
|
||||
} |
|
||||
|
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
|
||||
{ |
|
||||
migrationBuilder.DropTable( |
|
||||
name: "AbpEventInbox"); |
|
||||
|
|
||||
migrationBuilder.DropTable( |
|
||||
name: "AbpEventOutbox"); |
|
||||
|
|
||||
migrationBuilder.DropTable( |
|
||||
name: "TodoItems"); |
|
||||
|
|
||||
migrationBuilder.DropTable( |
|
||||
name: "TodoSummaries"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,160 +0,0 @@ |
|||||
// <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"); |
|
||||
}); |
|
||||
|
|
||||
modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.IncomingEventRecord", b => |
|
||||
{ |
|
||||
b.Property<Guid>("Id") |
|
||||
.HasColumnType("uniqueidentifier"); |
|
||||
|
|
||||
b.Property<DateTime>("CreationTime") |
|
||||
.HasColumnType("datetime2") |
|
||||
.HasColumnName("CreationTime"); |
|
||||
|
|
||||
b.Property<byte[]>("EventData") |
|
||||
.IsRequired() |
|
||||
.HasColumnType("varbinary(max)"); |
|
||||
|
|
||||
b.Property<string>("EventName") |
|
||||
.IsRequired() |
|
||||
.HasMaxLength(256) |
|
||||
.HasColumnType("nvarchar(256)"); |
|
||||
|
|
||||
b.Property<string>("ExtraProperties") |
|
||||
.HasColumnType("nvarchar(max)") |
|
||||
.HasColumnName("ExtraProperties"); |
|
||||
|
|
||||
b.Property<string>("MessageId") |
|
||||
.HasColumnType("nvarchar(450)"); |
|
||||
|
|
||||
b.Property<bool>("Processed") |
|
||||
.HasColumnType("bit"); |
|
||||
|
|
||||
b.Property<DateTime?>("ProcessedTime") |
|
||||
.HasColumnType("datetime2"); |
|
||||
|
|
||||
b.HasKey("Id"); |
|
||||
|
|
||||
b.HasIndex("MessageId"); |
|
||||
|
|
||||
b.HasIndex("Processed", "CreationTime"); |
|
||||
|
|
||||
b.ToTable("AbpEventInbox"); |
|
||||
}); |
|
||||
|
|
||||
modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.DistributedEvents.OutgoingEventRecord", b => |
|
||||
{ |
|
||||
b.Property<Guid>("Id") |
|
||||
.HasColumnType("uniqueidentifier"); |
|
||||
|
|
||||
b.Property<DateTime>("CreationTime") |
|
||||
.HasColumnType("datetime2") |
|
||||
.HasColumnName("CreationTime"); |
|
||||
|
|
||||
b.Property<byte[]>("EventData") |
|
||||
.IsRequired() |
|
||||
.HasColumnType("varbinary(max)"); |
|
||||
|
|
||||
b.Property<string>("EventName") |
|
||||
.IsRequired() |
|
||||
.HasMaxLength(256) |
|
||||
.HasColumnType("nvarchar(256)"); |
|
||||
|
|
||||
b.Property<string>("ExtraProperties") |
|
||||
.HasColumnType("nvarchar(max)") |
|
||||
.HasColumnName("ExtraProperties"); |
|
||||
|
|
||||
b.HasKey("Id"); |
|
||||
|
|
||||
b.ToTable("AbpEventOutbox"); |
|
||||
}); |
|
||||
#pragma warning restore 612, 618
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,57 +0,0 @@ |
|||||
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<DistDemoAppEfCoreRabbitMqModule>(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
@ -1,34 +0,0 @@ |
|||||
using Microsoft.EntityFrameworkCore; |
|
||||
using Volo.Abp.Domain.Entities; |
|
||||
using Volo.Abp.EntityFrameworkCore; |
|
||||
using Volo.Abp.EntityFrameworkCore.DistributedEvents; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
public class TodoDbContext : AbpDbContext<TodoDbContext>, IHasEventOutbox, IHasEventInbox |
|
||||
{ |
|
||||
public DbSet<TodoItem> TodoItems { get; set; } |
|
||||
public DbSet<TodoSummary> TodoSummaries { get; set; } |
|
||||
public DbSet<OutgoingEventRecord> OutgoingEvents { get; set; } |
|
||||
public DbSet<IncomingEventRecord> IncomingEvents { get; set; } |
|
||||
|
|
||||
public TodoDbContext(DbContextOptions<TodoDbContext> options) |
|
||||
: base(options) |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
|
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder) |
|
||||
{ |
|
||||
base.OnModelCreating(modelBuilder); |
|
||||
|
|
||||
modelBuilder.ConfigureEventOutbox(); |
|
||||
modelBuilder.ConfigureEventInbox(); |
|
||||
|
|
||||
modelBuilder.Entity<TodoItem>(b => |
|
||||
{ |
|
||||
b.Property(x => x.Text).IsRequired().HasMaxLength(128); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,29 +0,0 @@ |
|||||
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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,19 +0,0 @@ |
|||||
{ |
|
||||
"ConnectionStrings": { |
|
||||
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=DistEventsDemo;Trusted_Connection=True;TrustServerCertificate=True" |
|
||||
}, |
|
||||
"RabbitMQ": { |
|
||||
"Connections": { |
|
||||
"Default": { |
|
||||
"HostName": "localhost" |
|
||||
} |
|
||||
}, |
|
||||
"EventBus": { |
|
||||
"ClientName": "DistDemoApp", |
|
||||
"ExchangeName": "DistDemo" |
|
||||
} |
|
||||
}, |
|
||||
"Redis": { |
|
||||
"Configuration": "127.0.0.1" |
|
||||
} |
|
||||
} |
|
||||
@ -1,21 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<OutputType>Exe</OutputType> |
|
||||
<TargetFramework>net10.0</TargetFramework> |
|
||||
<RootNamespace>DistDemoApp</RootNamespace> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus.Kafka\Volo.Abp.EventBus.Kafka.csproj" /> |
|
||||
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<None Update="appsettings.json"> |
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
|
||||
</None> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,38 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Volo.Abp.EventBus.Distributed; |
|
||||
using Volo.Abp.EventBus.Kafka; |
|
||||
using Volo.Abp.Modularity; |
|
||||
using Volo.Abp.MongoDB; |
|
||||
using Volo.Abp.MongoDB.DistributedEvents; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
[DependsOn( |
|
||||
typeof(AbpMongoDbModule), |
|
||||
typeof(AbpEventBusKafkaModule), |
|
||||
typeof(DistDemoAppSharedModule) |
|
||||
)] |
|
||||
public class DistDemoAppMongoDbKafkaModule : AbpModule |
|
||||
{ |
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
|
||||
{ |
|
||||
context.Services.AddMongoDbContext<TodoMongoDbContext>(options => |
|
||||
{ |
|
||||
options.AddDefaultRepositories(); |
|
||||
}); |
|
||||
|
|
||||
Configure<AbpDistributedEventBusOptions>(options => |
|
||||
{ |
|
||||
options.Outboxes.Configure(config => |
|
||||
{ |
|
||||
config.UseMongoDbContext<TodoMongoDbContext>(); |
|
||||
}); |
|
||||
|
|
||||
options.Inboxes.Configure(config => |
|
||||
{ |
|
||||
config.UseMongoDbContext<TodoMongoDbContext>(); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,57 +0,0 @@ |
|||||
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<DistDemoAppMongoDbKafkaModule>(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
@ -1,26 +0,0 @@ |
|||||
using MongoDB.Driver; |
|
||||
using Volo.Abp.Data; |
|
||||
using Volo.Abp.MongoDB; |
|
||||
using Volo.Abp.MongoDB.DistributedEvents; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
[ConnectionStringName("Default")] |
|
||||
public class TodoMongoDbContext : AbpMongoDbContext, IHasEventOutbox, IHasEventInbox |
|
||||
{ |
|
||||
public IMongoCollection<TodoItem> TodoItems => Collection<TodoItem>(); |
|
||||
public IMongoCollection<TodoSummary> TodoSummaries => Collection<TodoSummary>(); |
|
||||
|
|
||||
public IMongoCollection<OutgoingEventRecord> OutgoingEvents |
|
||||
{ |
|
||||
get => Collection<OutgoingEventRecord>(); |
|
||||
set {} |
|
||||
} |
|
||||
public IMongoCollection<IncomingEventRecord> IncomingEvents |
|
||||
{ |
|
||||
get => Collection<IncomingEventRecord>(); |
|
||||
set {} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1,19 +0,0 @@ |
|||||
{ |
|
||||
"ConnectionStrings": { |
|
||||
"Default": "mongodb://localhost:27018,localhost:27019,localhost:27020/DistEventsDemo" |
|
||||
}, |
|
||||
"Kafka": { |
|
||||
"Connections": { |
|
||||
"Default": { |
|
||||
"BootstrapServers": "localhost:9092" |
|
||||
} |
|
||||
}, |
|
||||
"EventBus": { |
|
||||
"GroupId": "DistDemoApp", |
|
||||
"TopicName": "DistDemoTopic" |
|
||||
} |
|
||||
}, |
|
||||
"Redis": { |
|
||||
"Configuration": "127.0.0.1" |
|
||||
} |
|
||||
} |
|
||||
@ -1,21 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<OutputType>Exe</OutputType> |
|
||||
<TargetFramework>net10.0</TargetFramework> |
|
||||
<RootNamespace>DistDemoApp</RootNamespace> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus.Rebus\Volo.Abp.EventBus.Rebus.csproj" /> |
|
||||
<ProjectReference Include="..\DistDemoApp.Shared\DistDemoApp.Shared.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<None Update="appsettings.json"> |
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
|
||||
</None> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,53 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Rebus.Persistence.InMem; |
|
||||
using Rebus.Transport.InMem; |
|
||||
using Volo.Abp.EventBus.Distributed; |
|
||||
using Volo.Abp.EventBus.Rebus; |
|
||||
using Volo.Abp.Modularity; |
|
||||
using Volo.Abp.MongoDB; |
|
||||
using Volo.Abp.MongoDB.DistributedEvents; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
[DependsOn( |
|
||||
typeof(AbpMongoDbModule), |
|
||||
typeof(AbpEventBusRebusModule), |
|
||||
typeof(DistDemoAppSharedModule) |
|
||||
)] |
|
||||
public class DistDemoAppMongoDbRebusModule : AbpModule |
|
||||
{ |
|
||||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|
||||
{ |
|
||||
PreConfigure<AbpRebusEventBusOptions>(options => |
|
||||
{ |
|
||||
options.InputQueueName = "eventbus"; |
|
||||
options.Configurer = rebusConfigurer => |
|
||||
{ |
|
||||
rebusConfigurer.Transport(t => t.UseInMemoryTransport(new InMemNetwork(), "eventbus")); |
|
||||
rebusConfigurer.Subscriptions(s => s.StoreInMemory()); |
|
||||
}; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
|
||||
{ |
|
||||
context.Services.AddMongoDbContext<TodoMongoDbContext>(options => |
|
||||
{ |
|
||||
options.AddDefaultRepositories(); |
|
||||
}); |
|
||||
|
|
||||
Configure<AbpDistributedEventBusOptions>(options => |
|
||||
{ |
|
||||
options.Outboxes.Configure(config => |
|
||||
{ |
|
||||
config.UseMongoDbContext<TodoMongoDbContext>(); |
|
||||
}); |
|
||||
|
|
||||
options.Inboxes.Configure(config => |
|
||||
{ |
|
||||
config.UseMongoDbContext<TodoMongoDbContext>(); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,57 +0,0 @@ |
|||||
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<DistDemoAppMongoDbRebusModule>(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
@ -1,19 +0,0 @@ |
|||||
using MongoDB.Driver; |
|
||||
using Volo.Abp.Data; |
|
||||
using Volo.Abp.MongoDB; |
|
||||
using Volo.Abp.MongoDB.DistributedEvents; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
[ConnectionStringName("Default")] |
|
||||
public class TodoMongoDbContext : AbpMongoDbContext, IHasEventOutbox, IHasEventInbox |
|
||||
{ |
|
||||
public IMongoCollection<TodoItem> TodoItems => Collection<TodoItem>(); |
|
||||
public IMongoCollection<TodoSummary> TodoSummaries => Collection<TodoSummary>(); |
|
||||
|
|
||||
public IMongoCollection<OutgoingEventRecord> OutgoingEvents => Collection<OutgoingEventRecord>(); |
|
||||
|
|
||||
public IMongoCollection<IncomingEventRecord> IncomingEvents => Collection<IncomingEventRecord>(); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1,19 +0,0 @@ |
|||||
{ |
|
||||
"ConnectionStrings": { |
|
||||
"Default": "mongodb://localhost:27018,localhost:27019,localhost:27020/DistEventsDemo" |
|
||||
}, |
|
||||
"Kafka": { |
|
||||
"Connections": { |
|
||||
"Default": { |
|
||||
"BootstrapServers": "localhost:9092" |
|
||||
} |
|
||||
}, |
|
||||
"EventBus": { |
|
||||
"GroupId": "DistDemoApp", |
|
||||
"TopicName": "DistDemoTopic" |
|
||||
} |
|
||||
}, |
|
||||
"Redis": { |
|
||||
"Configuration": "127.0.0.1" |
|
||||
} |
|
||||
} |
|
||||
@ -1,29 +0,0 @@ |
|||||
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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,23 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>net10.0</TargetFramework> |
|
||||
<RootNamespace>DistDemoApp</RootNamespace> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="DistributedLock.Redis" /> |
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" /> |
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" /> |
|
||||
<PackageReference Include="Serilog.Sinks.Async" /> |
|
||||
<PackageReference Include="Serilog.Sinks.Console" /> |
|
||||
<PackageReference Include="Serilog.Sinks.File" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.EventBus\Volo.Abp.EventBus.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,39 +0,0 @@ |
|||||
using System; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.Extensions.Hosting; |
|
||||
using Volo.Abp; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
public class DistDemoAppHostedService : IHostedService |
|
||||
{ |
|
||||
private readonly IAbpApplicationWithExternalServiceProvider _application; |
|
||||
private readonly IServiceProvider _serviceProvider; |
|
||||
private readonly DemoService _demoService; |
|
||||
|
|
||||
public DistDemoAppHostedService( |
|
||||
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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,39 +0,0 @@ |
|||||
using Medallion.Threading; |
|
||||
using Medallion.Threading.Redis; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using StackExchange.Redis; |
|
||||
using Volo.Abp.Autofac; |
|
||||
using Volo.Abp.Domain; |
|
||||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|
||||
using Volo.Abp.EventBus; |
|
||||
using Volo.Abp.Modularity; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
[DependsOn( |
|
||||
typeof(AbpAutofacModule), |
|
||||
typeof(AbpDddDomainModule), |
|
||||
typeof(AbpEventBusModule) |
|
||||
)] |
|
||||
public class DistDemoAppSharedModule : AbpModule |
|
||||
{ |
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
|
||||
{ |
|
||||
var configuration = context.Services.GetConfiguration(); |
|
||||
|
|
||||
context.Services.AddHostedService<DistDemoAppHostedService>(); |
|
||||
|
|
||||
Configure<AbpDistributedEntityEventOptions>(options => |
|
||||
{ |
|
||||
options.EtoMappings.Add<TodoItem, TodoItemEto>(); |
|
||||
options.AutoEventSelectors.Add<TodoItem>(); |
|
||||
}); |
|
||||
|
|
||||
context.Services.AddSingleton<IDistributedLockProvider>(sp => |
|
||||
{ |
|
||||
var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); |
|
||||
return new RedisDistributedSynchronizationProvider(connection.GetDatabase()); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,63 +0,0 @@ |
|||||
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); |
|
||||
} |
|
||||
|
|
||||
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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,15 +0,0 @@ |
|||||
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}"; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,12 +0,0 @@ |
|||||
using System; |
|
||||
using Volo.Abp.EventBus; |
|
||||
|
|
||||
namespace DistDemoApp |
|
||||
{ |
|
||||
[EventName("todo-item")] |
|
||||
public class TodoItemEto |
|
||||
{ |
|
||||
public DateTime CreationTime { get; set; } |
|
||||
public string Text { get; set; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,41 +0,0 @@ |
|||||
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}"; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
<Solution> |
|
||||
<Project Path="DistDemoApp.EfCoreRabbitMq/DistDemoApp.EfCoreRabbitMq.csproj" /> |
|
||||
<Project Path="DistDemoApp.MongoDbKafka/DistDemoApp.MongoDbKafka.csproj" /> |
|
||||
<Project Path="DistDemoApp.MongoDbRebus/DistDemoApp.MongoDbRebus.csproj" /> |
|
||||
<Project Path="DistDemoApp.Shared/DistDemoApp.Shared.csproj" /> |
|
||||
</Solution> |
|
||||
Loading…
Reference in new issue