mirror of https://github.com/abpframework/abp.git
committed by
GitHub
32 changed files with 380 additions and 50 deletions
@ -0,0 +1,94 @@ |
|||||
|
# ABP.IO Platform 5.1 RC.1 Has Been Released |
||||
|
|
||||
|
Today, we are releasing the [ABP Framework](https://abp.io/) and the [ABP Commercial](https://commercial.abp.io/) version **5.1 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version. |
||||
|
|
||||
|
> **The planned release date for the [5.1 stable](https://github.com/abpframework/abp/milestone/64) version is February 8, 2022**. |
||||
|
|
||||
|
Please try this version and provide feedback for a more stable ABP version 5.1! Thank you all. |
||||
|
|
||||
|
## Get Started with the 5.1 RC |
||||
|
|
||||
|
follow the steps below to try the version 5.1 RC today; |
||||
|
|
||||
|
1) **Upgrade** the ABP CLI to the version `5.1.0-rc.1` using a command line terminal: |
||||
|
|
||||
|
````bash |
||||
|
dotnet tool update Volo.Abp.Cli -g --version 5.1.0-rc.1 |
||||
|
```` |
||||
|
|
||||
|
**or install** if you haven't installed before: |
||||
|
|
||||
|
````bash |
||||
|
dotnet tool install Volo.Abp.Cli -g --version 5.1.0-rc.1 |
||||
|
```` |
||||
|
|
||||
|
2) Create a **new application** with the `--preview` option: |
||||
|
|
||||
|
````bash |
||||
|
abp new BookStore --preview |
||||
|
```` |
||||
|
|
||||
|
See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all the available options. |
||||
|
|
||||
|
> You can also use the *Direct Download* tab on the [Get Started](https://abp.io/get-started) page by selecting the **Preview checkbox**. |
||||
|
|
||||
|
You can use any IDE that supports .NET 6.0 development (e.g. [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/)). |
||||
|
|
||||
|
### Migration Notes & Breaking Changes |
||||
|
|
||||
|
TODO: Startup template changes |
||||
|
|
||||
|
## What's new with 5.1? |
||||
|
|
||||
|
In this section, I will introduce some major features released with this version. |
||||
|
|
||||
|
### DRAFT list of things done |
||||
|
|
||||
|
#### ABP Framework |
||||
|
|
||||
|
* Use new hosting model for the app template. [#10928](https://github.com/abpframework/abp/pull/10928) - what to do for existing solutions? |
||||
|
* Async application initialization methods on the AbpModule class [#6828](https://github.com/abpframework/abp/issues/6828) |
||||
|
* Angular 13? |
||||
|
* [#10552](https://github.com/abpframework/abp/pull/10696) Used file scoped namespaces :) |
||||
|
* Support markdown in CMS-Kit comments [#10792](https://github.com/abpframework/abp/pull/10792) |
||||
|
* eShopOnAbp is getting mature |
||||
|
* Nightly builds are working again |
||||
|
* Coming: New ABP.IO design! |
||||
|
|
||||
|
All issues & PRs in [5.1 milesone](https://github.com/abpframework/abp/milestone/60?closed=1). |
||||
|
|
||||
|
#### ABP Commercial |
||||
|
|
||||
|
* CMS Kit Pro: URL forwarding |
||||
|
* LeptonX is coming for ABP Blazor and MVC UI too (share the sample dashboard) |
||||
|
|
||||
|
### Header 1 |
||||
|
|
||||
|
TODO |
||||
|
|
||||
|
## Community News |
||||
|
|
||||
|
### ABP Community Talks 2021.1 |
||||
|
|
||||
|
 |
||||
|
|
||||
|
This is the second episode of the ABP Community Talks and we are talking about microservice development with the ABP Framework, based on the [eShopOnAbp](https://github.com/abpframework/eShopOnAbp) reference solution. This live meeting will be at **January 20, 2022, 17:00 (UTC)** on YouTube. |
||||
|
|
||||
|
**Join this event on the Kommunity platform: https://kommunity.com/volosoft/events/abp-community-talks-20221-microservice-development-acd0f44b** |
||||
|
|
||||
|
You can also [subscribe to the Volosoft channel](https://www.youtube.com/channel/UCO3XKlpvq8CA5MQNVS6b3dQ) for reminders for further ABP events and videos. |
||||
|
|
||||
|
### New ABP Community posts |
||||
|
|
||||
|
Here, some of the recent posts added to the [ABP community](https://community.abp.io/): |
||||
|
|
||||
|
* [Minimal API development with the ABP Framework](https://community.abp.io/articles/minimal-api-with-abp-hello-world-part-1-sg5i44p8) by [@antosubash](https://github.com/antosubash) (three parts, video tutorial). |
||||
|
* [Integrating the Syncfusion MVC Components to the ABP MVC UI](https://community.abp.io/articles/integrating-the-syncfusion-mvc-components-to-the-abp-mvc-ui-0gpkr1if) by [@EngincanV](https://github.com/EngincanV). |
||||
|
* [Add Tailwind CSS to your ABP Blazor UI](https://community.abp.io/articles/add-tailwindcss-to-your-abp-blazor-ui-vidiwzcy) by [@antosubash](https://github.com/antosubash) (video tutorial). |
||||
|
* [Import external users into the users Table from an ABP Framework application](https://community.abp.io/articles/import-external-users-into-the-users-table-from-an-abp-framework-application-7lnyw415) by [@bartvanhoey](https://github.com/bartvanhoey). |
||||
|
|
||||
|
Thanks to the ABP Community for all the contents they have published. You can also [post your ABP and .NET related (text or video) contents](https://community.abp.io/articles/submit) to the ABP Community. |
||||
|
|
||||
|
## Conclusion |
||||
|
|
||||
|
In this blog post, I summarized the news about that new version and the ABP Community. Please try it and provide feedback by opening issues on [the GitHub repository](https://github.com/abpframework/abp). Thank you all! |
||||
|
After Width: | Height: | Size: 559 KiB |
@ -0,0 +1,52 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
|
||||
|
namespace Volo.Abp.EventBus; |
||||
|
|
||||
|
public class EventHandlerInvoker : IEventHandlerInvoker, ISingletonDependency |
||||
|
{ |
||||
|
private readonly ConcurrentDictionary<string, EventHandlerInvokerCacheItem> _cache; |
||||
|
|
||||
|
public EventHandlerInvoker() |
||||
|
{ |
||||
|
_cache = new ConcurrentDictionary<string, EventHandlerInvokerCacheItem>(); |
||||
|
} |
||||
|
|
||||
|
public async Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType) |
||||
|
{ |
||||
|
var cacheItem = _cache.GetOrAdd($"{eventHandler.GetType().FullName}-{eventType.FullName}", _ => |
||||
|
{ |
||||
|
var item = new EventHandlerInvokerCacheItem(); |
||||
|
|
||||
|
if (typeof(ILocalEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) |
||||
|
{ |
||||
|
item.Local = (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(LocalEventHandlerMethodExecutor<>).MakeGenericType(eventType)); |
||||
|
} |
||||
|
|
||||
|
if (typeof(IDistributedEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) |
||||
|
{ |
||||
|
item.Distributed = (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(DistributedEventHandlerMethodExecutor<>).MakeGenericType(eventType)); |
||||
|
} |
||||
|
|
||||
|
return item; |
||||
|
}); |
||||
|
|
||||
|
if (cacheItem.Local != null) |
||||
|
{ |
||||
|
await cacheItem.Local.ExecutorAsync(eventHandler, eventData); |
||||
|
} |
||||
|
|
||||
|
if (cacheItem.Distributed != null) |
||||
|
{ |
||||
|
await cacheItem.Distributed.ExecutorAsync(eventHandler, eventData); |
||||
|
} |
||||
|
|
||||
|
if (cacheItem.Local == null && cacheItem.Distributed == null) |
||||
|
{ |
||||
|
throw new AbpException("The object instance is not an event handler. Object type: " + eventHandler.GetType().AssemblyQualifiedName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace Volo.Abp.EventBus; |
||||
|
|
||||
|
public class EventHandlerInvokerCacheItem |
||||
|
{ |
||||
|
public IEventHandlerMethodExecutor Local { get; set; } |
||||
|
|
||||
|
public IEventHandlerMethodExecutor Distributed { get; set; } |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
|
||||
|
namespace Volo.Abp.EventBus; |
||||
|
|
||||
|
public delegate Task EventHandlerMethodExecutorAsync(IEventHandler target, object parameter); |
||||
|
|
||||
|
public interface IEventHandlerMethodExecutor |
||||
|
{ |
||||
|
EventHandlerMethodExecutorAsync ExecutorAsync { get; } |
||||
|
} |
||||
|
|
||||
|
public class LocalEventHandlerMethodExecutor<TEvent> : IEventHandlerMethodExecutor |
||||
|
where TEvent : class |
||||
|
{ |
||||
|
public EventHandlerMethodExecutorAsync ExecutorAsync => (target, parameter) => target.As<ILocalEventHandler<TEvent>>().HandleEventAsync(parameter.As<TEvent>()); |
||||
|
|
||||
|
public Task ExecuteAsync(IEventHandler target, TEvent parameters) |
||||
|
{ |
||||
|
return ExecutorAsync(target, parameters); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class DistributedEventHandlerMethodExecutor<TEvent> : IEventHandlerMethodExecutor |
||||
|
where TEvent : class |
||||
|
{ |
||||
|
public EventHandlerMethodExecutorAsync ExecutorAsync => (target, parameter) => target.As<IDistributedEventHandler<TEvent>>().HandleEventAsync(parameter.As<TEvent>()); |
||||
|
|
||||
|
public Task ExecuteAsync(IEventHandler target, TEvent parameters) |
||||
|
{ |
||||
|
return ExecutorAsync(target, parameters); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.EventBus; |
||||
|
|
||||
|
public interface IEventHandlerInvoker |
||||
|
{ |
||||
|
Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType); |
||||
|
} |
||||
@ -0,0 +1,128 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.Abp.Domain.Entities.Events; |
||||
|
using Volo.Abp.Domain.Entities.Events.Distributed; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
using Volo.Abp.EventBus.Local; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.EventBus; |
||||
|
|
||||
|
public class EventHandlerInvoker_Tests : EventBusTestBase |
||||
|
{ |
||||
|
private readonly IEventHandlerInvoker _eventHandlerInvoker; |
||||
|
|
||||
|
public EventHandlerInvoker_Tests() |
||||
|
{ |
||||
|
_eventHandlerInvoker = GetRequiredService<IEventHandlerInvoker>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Invoke_LocalEventHandler_With_MyEventData() |
||||
|
{ |
||||
|
var localHandler = new MyLocalEventHandler(); |
||||
|
var eventData = new MyEventData(); |
||||
|
|
||||
|
await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); |
||||
|
|
||||
|
localHandler.MyEventDataCount.ShouldBe(2); |
||||
|
localHandler.EntityChangedEventDataCount.ShouldBe(0); |
||||
|
localHandler.EntityChangedEventDataCount.ShouldBe(0); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Invoke_LocalEventHandler_Created_And_Changed_Once() |
||||
|
{ |
||||
|
var localHandler = new MyLocalEventHandler(); |
||||
|
var eventData = new EntityCreatedEventData<MyEntity>(new MyEntity()); |
||||
|
|
||||
|
await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); |
||||
|
await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, typeof(EntityChangedEventData<MyEntity>)); |
||||
|
|
||||
|
localHandler.MyEventDataCount.ShouldBe(0); |
||||
|
localHandler.EntityChangedEventDataCount.ShouldBe(1); |
||||
|
localHandler.EntityChangedEventDataCount.ShouldBe(1); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Invoke_DistributedEventHandler_With_MyEventData() |
||||
|
{ |
||||
|
var localHandler = new MyDistributedEventHandler(); |
||||
|
var eventData = new MyEventData(); |
||||
|
|
||||
|
await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); |
||||
|
|
||||
|
localHandler.MyEventDataCount.ShouldBe(1); |
||||
|
localHandler.EntityCreatedCount.ShouldBe(0); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Invoke_DistributedEventHandler_With_EntityCreatedEto() |
||||
|
{ |
||||
|
var localHandler = new MyDistributedEventHandler(); |
||||
|
var eventData = new EntityCreatedEto<MyEntity>(new MyEntity()); |
||||
|
|
||||
|
await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); |
||||
|
|
||||
|
localHandler.MyEventDataCount.ShouldBe(0); |
||||
|
localHandler.EntityCreatedCount.ShouldBe(1); |
||||
|
} |
||||
|
|
||||
|
public class MyEventData |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public class MyEntity : Entity<Guid> |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public class MyDistributedEventHandler : IDistributedEventHandler<MyEventData>, |
||||
|
IDistributedEventHandler<EntityCreatedEto<MyEntity>> |
||||
|
{ |
||||
|
public int MyEventDataCount { get; set; } |
||||
|
public int EntityCreatedCount { get; set; } |
||||
|
|
||||
|
public Task HandleEventAsync(MyEventData eventData) |
||||
|
{ |
||||
|
MyEventDataCount++; |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public Task HandleEventAsync(EntityCreatedEto<MyEntity> eventData) |
||||
|
{ |
||||
|
EntityCreatedCount++; |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyLocalEventHandler : ILocalEventHandler<MyEventData>, |
||||
|
IDistributedEventHandler<MyEventData>, |
||||
|
IDistributedEventHandler<EntityCreatedEventData<MyEntity>>, |
||||
|
IDistributedEventHandler<EntityChangedEventData<MyEntity>> |
||||
|
{ |
||||
|
public int MyEventDataCount { get; set; } |
||||
|
public int EntityCreatedEventDataCount { get; set; } |
||||
|
public int EntityChangedEventDataCount { get; set; } |
||||
|
|
||||
|
public Task HandleEventAsync(MyEventData eventData) |
||||
|
{ |
||||
|
MyEventDataCount++; |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public Task HandleEventAsync(EntityCreatedEventData<MyEntity> eventData) |
||||
|
{ |
||||
|
EntityCreatedEventDataCount++; |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public Task HandleEventAsync(EntityChangedEventData<MyEntity> eventData) |
||||
|
{ |
||||
|
EntityChangedEventDataCount++; |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue