mirror of https://github.com/abpframework/abp.git
89 changed files with 1735 additions and 1103 deletions
@ -0,0 +1,142 @@ |
|||
# ABP Framework v2.3.0 Has Been Released |
|||
|
|||
In the days of **coronavirus**, we have a good news: We've released **ABP Framework v2.3** and this post will explain **what's new** with this release and **what we've done** in the last two weeks. |
|||
|
|||
## About the Coronavirus & Our Team |
|||
|
|||
**We are very sad** about the coronavirus case. As [Volosoft](https://volosoft.com/) team, we have **remote workers** working in their home in different countries. Beginning from the last week, we've **completely started to work remotely** from home including our main office employees. |
|||
|
|||
We believe in and pray for that the World will overcome this issue in a short time. |
|||
|
|||
## About the Release Cycle |
|||
|
|||
Beginning from the ABP v2.1.0, we have started to release feature versions once **in two weeks**, on Thursdays. This is the 3rd release after that decision and we see that it works fine for now and improved our agility. |
|||
|
|||
We will continue to release **feature versions** (like v2.4, v2.5) in every two weeks. In addition, we may release **hotfix versions** (like v2.3.1, v2.3.2) whenever needed. |
|||
|
|||
## What's New in ABP Framework v2.3.0 |
|||
|
|||
We've completed & merged **[104](https://github.com/abpframework/abp/milestone/30?closed=1) issues and pull requests** with **393 commits** in this two weeks development period. |
|||
|
|||
I will introduce some new features and enhancements introduced with this release. |
|||
|
|||
### React Native Mobile Application |
|||
|
|||
We have finally completed the **react native mobile application**. It currently allows you to **login**, manage your **users** and **tenants**. It utilizes the same setting, authorization and localization systems of the ABP Framework. |
|||
|
|||
A few screenshots from the application: |
|||
|
|||
 |
|||
|
|||
It doesn't have much functionality but it is a **perfect starting point** for your own mobile application since it is completely integrated to the backend. |
|||
|
|||
### Angular TypeScript Proxy Generator |
|||
|
|||
It is common to call a REST endpoint in the server from our Angular applications. In this case, we generally create **services** (those have methods for each service method on he server side) and **model objects** (matches to [DTOs](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects) in the server side). |
|||
|
|||
In addition to manually creating such server-interacting services, we could use tools like [NSWAG](https://github.com/RicoSuter/NSwag) to generate service proxies for us. But NSWAG has the following problems we've experienced: |
|||
|
|||
* It generates a **big, single** .ts file which has some problems; |
|||
* It get **too large** when your application grows. |
|||
* It doesn't fit into the **[modular](https://docs.abp.io/en/abp/latest/Module-Development-Basics) approach** of the ABP framework. |
|||
* It creates a bit **ugly code**. We want to have a clean code (just like if we write manually). |
|||
* It can not generate the same **method signature** declared in the server side (because swagger.json doesn't exactly reflect the method signature of the backend service). We've created an endpoint that exposes server side method contacts to allow clients generate a better aligned client proxies. |
|||
|
|||
So, we've decided to create an ABP CLI command to automatically generate the typescript client proxies ([#2222](https://github.com/abpframework/abp/issues/2222)) for your REST API developed with the ABP Framework. |
|||
|
|||
It is easy to use. Just run the following command in the **root folder** of the angular application: |
|||
|
|||
````bash |
|||
abp generate-proxy |
|||
```` |
|||
|
|||
It only creates proxies only for your own application's services. It doesn't create proxies for the services of the application modules you're using (by default). There are several options. See the [CLI documentation](https://docs.abp.io/en/abp/latest/CLI). |
|||
|
|||
### CRUD Application Services for Entities with Composite Keys |
|||
|
|||
` CrudAppService ` is a useful base class to create CRUD application services for your entities. But it doesn't support entities with **composite primary keys**. `AbstractKeyCrudAppService` is the new base class that is developed to support entities with composite primary keys. See [the documentation](https://docs.abp.io/en/abp/latest/Application-Services#abstractkeycrudappservice) for more. |
|||
|
|||
### Add Source Code of the Modules |
|||
|
|||
The application startup template comes with some [application modules](https://docs.abp.io/en/abp/latest/Modules/Index) **pre-installed** as **NuGet & NPM packages**. This have a few important advantages: |
|||
|
|||
* You can **easily [upgrade](https://docs.abp.io/en/abp/latest/CLI#update)** these modules when a new version is available. |
|||
* Your solution becomes **cleaner**, so you can focus on your own code. |
|||
|
|||
However, when you need to make **major customizations** for a depended module, it is not easy as its source code is in your applications. To solve this problem, we've introduces a new command to the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) that **replaces** NuGet packages with their **source code** in your solution. The usage is simple: |
|||
|
|||
````bash |
|||
abp add-module --with-source-code |
|||
```` |
|||
|
|||
This command adds a module with source code or replaces with its source code if it is already added as package references. |
|||
|
|||
> It is suggested to **save your changes** to your source control system before using this command since it makes a lot of changes in your source code. |
|||
|
|||
In addition, we've documented how to customize depended modules without changing their source code (see the section below). It is suggested to use modules as packages to easily upgrade them in the future. |
|||
|
|||
> Source code of the free modules are licensed under **MIT**, so you can freely change them and add into your solution. |
|||
|
|||
### Switch to Preview |
|||
|
|||
ABP Framework is rapidly evolving and we are frequently releasing new versions. However, if you want to follow it closer, you can use the **daily preview packages**. |
|||
|
|||
We've created an ABP CLI command to easily **update to the latest preview packages** for your solution. Run the following command in the root folder of your solution: |
|||
|
|||
````bash |
|||
abp switch-to-preview |
|||
```` |
|||
|
|||
It will change the versions of all ABP related NuGet and NPM packages. You can **switch back to the latest stable** when you want: |
|||
|
|||
````bash |
|||
abp switch-to-stable |
|||
```` |
|||
|
|||
See the [ABP CLI document](https://docs.abp.io/en/abp/latest/CLI#switch-to-preview) fore more. |
|||
|
|||
### Documentation Improvements |
|||
|
|||
#### Extending/Customizing Depended Application Modules |
|||
|
|||
We've created a huge documentation that explains how to customize a depended module without changing its source code. See [the documentation](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Guide). |
|||
|
|||
In addition to the documentation, we've revised all the modules ([#3166](https://github.com/abpframework/abp/issues/3166)) to make their services easily extensible & customizable. |
|||
|
|||
#### EF Core Migration Guide |
|||
|
|||
We've recently created a guide to explain the migration system that is used by the ABP startup templates. [This guide](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Migrations) also explains how to customize the migration structure, split your modules across multiple databases, reusing a module's table and son on. |
|||
|
|||
#### Migration from the ASP.NET Boilerplate |
|||
|
|||
If you have a solution built on the ASP.NET Boilerplate, we've [created a guide](https://docs.abp.io/en/abp/latest/AspNet-Boilerplate-Migration-Guide) that tries to help you if you want to migrate your solution to the new ABP Framework. |
|||
|
|||
### Some Other Features |
|||
|
|||
#### The Framework |
|||
|
|||
* Add `IRepository.GetAsync` and `IRepository.FindAsync` methods ([#3184](https://github.com/abpframework/abp/issues/3148)). |
|||
|
|||
#### Modules |
|||
|
|||
* Get password & email address of the admin while creating a new tenant, for the tenant management module ([#3088](https://github.com/abpframework/abp/issues/3088)). |
|||
* Elastic search integrated full text search for the docs module ([#2901](https://github.com/abpframework/abp/pull/2901)). |
|||
* New Quartz background worker module ([#2762](https://github.com/abpframework/abp/issues/2762)) |
|||
|
|||
#### Samples |
|||
|
|||
* Add multi-tenancy support to the microservice demo ([#3032](https://github.com/abpframework/abp/pull/3032)). |
|||
|
|||
See [the release notes](https://github.com/abpframework/abp/releases/tag/2.3.0) for all feature, enhancement and bugfixes. |
|||
|
|||
## What's Next? |
|||
|
|||
We have the following goals for the next few months: |
|||
|
|||
* Complete the documentation and samples, write more tutorials. |
|||
* Make the framework and existing modules more customizable and extensible. |
|||
* Integrate to gRPC & implement gRPC endpoint for pre-built modules ([#2882](https://github.com/abpframework/abp/issues/2882)). |
|||
* Create a Blazor UI for the ABP Framework & implement it for all the modules and startup templates ([#394](https://github.com/abpframework/abp/issues/394)). |
|||
* Add new features to pre-built modules and create new modules for the [ABP Commercial](https://commercial.abp.io/). |
|||
|
|||
See [the GitHub milestones](https://github.com/abpframework/abp/milestones) for details. |
|||
|
After Width: | Height: | Size: 56 KiB |
@ -0,0 +1,188 @@ |
|||
# Cards |
|||
|
|||
## Introduction |
|||
|
|||
`abp-card` is a content container derived from bootstrap card element. |
|||
|
|||
Basic usage: |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
<img abp-card-image="Top" src="~/imgs/demo/300x200.png"/> |
|||
<abp-card-body> |
|||
<abp-card-title>Card Title</abp-card-title> |
|||
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
|||
<a abp-button="Primary" href="#"> Go somewhere</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
```` |
|||
|
|||
|
|||
|
|||
##### Using Titles, Text and Links: |
|||
|
|||
Following tags can be used under main `abp-card` tag |
|||
|
|||
* `abp-card-title` |
|||
* `abp-card-subtitle` |
|||
* `a abp-card-link` |
|||
|
|||
Sample: |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
<abp-card-body> |
|||
<abp-card-title>Card title</abp-card-title> |
|||
<abp-card-subtitle class="mb-2 text-muted">Card subtitle</abp-card-subtitle> |
|||
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
|||
<a abp-card-link href="#">Card link</a> |
|||
<a abp-card-link href="#">Another link</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
```` |
|||
|
|||
|
|||
|
|||
##### Using List Groups: |
|||
|
|||
* `abp-list-group flush="true"` : `flush` attribute renders into bootstrap `list-group-flush` class which is used for removing borders and rounded corners to render list group items edge to edge in a parent container. |
|||
* `abp-list-group-item` |
|||
|
|||
Kitchen Sink Sample: |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
<img abp-card-image="Top" src="~/imgs/demo/300x200.png" /> |
|||
<abp-card-body> |
|||
<abp-card-title>Card Title</abp-card-title> |
|||
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
|||
</abp-card-body> |
|||
<abp-list-group flush="true"> |
|||
<abp-list-group-item>Cras justo odio</abp-list-group-item> |
|||
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> |
|||
<abp-list-group-item>Vestibulum at eros</abp-list-group-item> |
|||
</abp-list-group> |
|||
<abp-card-body> |
|||
<a abp-card-link href="#">Card link</a> |
|||
<a abp-card-link href="#">Another link</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
```` |
|||
|
|||
|
|||
|
|||
##### Using Header, Footer and Blockquote: |
|||
|
|||
* `abp-card-header` |
|||
* `abp-card-footer` |
|||
* `abp-blockquote` |
|||
|
|||
Sample: |
|||
|
|||
```xml |
|||
<abp-card style="width: 18rem;"> |
|||
<abp-card-header>Featured</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Special title treatment</abp-card-title> |
|||
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text> |
|||
<a abp-button="Primary" href="#"> Go somewhere</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
``` |
|||
|
|||
Quote Sample: |
|||
|
|||
```xml |
|||
<abp-card> |
|||
<abp-card-header>Quote</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-blockquote> |
|||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> |
|||
<footer>Someone famous in Source Title</footer> |
|||
</abp-blockquote> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
``` |
|||
|
|||
Footer Sample: |
|||
|
|||
```xml |
|||
<abp-card class="text-center"> |
|||
<abp-card-header>Featured</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-blockquote> |
|||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> |
|||
<footer>Someone famous in Source Title</footer> |
|||
</abp-blockquote> |
|||
</abp-card-body> |
|||
<abp-card-footer class="text-muted"> 2 days ago</abp-card-footer> |
|||
</abp-card> |
|||
``` |
|||
|
|||
|
|||
|
|||
## Demo |
|||
|
|||
See the [cards demo page](https://bootstrap-taghelpers.abp.io/Components/Cards) to see it in action. |
|||
|
|||
## abp-card Attributes |
|||
|
|||
- **background:** A value indicates the background color of the card. |
|||
- **text-color**: A value indicates the color of the text inside the card. |
|||
- **border:** A value indicates the color of the border inside the card. |
|||
|
|||
Should be one of the following values: |
|||
|
|||
* `Default` (default value) |
|||
* `Primary` |
|||
* `Secondary` |
|||
* `Success` |
|||
* `Danger` |
|||
* `Warning` |
|||
* `Info` |
|||
* `Light` |
|||
* `Dark` |
|||
|
|||
Example: |
|||
|
|||
````xml |
|||
<abp-card background="Success" text-color="Danger" border="Dark"> |
|||
```` |
|||
|
|||
### sizing |
|||
|
|||
Cards has default 100% with and can be changed with custom CSS, grid classes, grid Sass mixins or [utilities](https://getbootstrap.com/docs/4.0/utilities/sizing/). |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
```` |
|||
|
|||
### card-deck and card-columns |
|||
|
|||
`abp-card` can be used inside `card-deck` or `card-columns` aswell. |
|||
|
|||
````xml |
|||
<div class="card-deck"> |
|||
<abp-card background="Primary"> |
|||
<abp-card-header>First Deck</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Ace </abp-card-title> |
|||
<abp-card-text>Here is the content for Ace.</abp-card-text> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
<abp-card background="Info"> |
|||
<abp-card-header>Second Deck</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Beta </abp-card-title> |
|||
<abp-card-text>Beta content.</abp-card-text> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
<abp-card background="Warning"> |
|||
<abp-card-header>Third Deck</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Epsilon </abp-card-title> |
|||
<abp-card-text>Content for Epsilon.</abp-card-text> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
</div> |
|||
```` |
|||
@ -1,3 +1,148 @@ |
|||
# 自定义应用模块: 扩展实体 |
|||
|
|||
TODO... |
|||
在某些情况下你可能希望为依赖模块中定义的实体添加一些额外的属性(和数据库字段). 本节将介绍一些实现这一目标的不同方法. |
|||
|
|||
## Extra Properties |
|||
|
|||
[Extra properties](Entities.md)是一种存储实体的一些额外数据但不用更改实体的方式. 实体应该实现 `IHasExtraProperties` 接口. 所有预构建模块定义的聚合根实体都实现了 `IHasExtraProperties` 接口,所以你可以在这些实体中存储额外的属性. |
|||
|
|||
示例: |
|||
|
|||
````csharp |
|||
//SET AN EXTRA PROPERTY |
|||
var user = await _identityUserRepository.GetAsync(userId); |
|||
user.SetProperty("Title", "My custom title value!"); |
|||
await _identityUserRepository.UpdateAsync(user); |
|||
|
|||
//GET AN EXTRA PROPERTY |
|||
var user = await _identityUserRepository.GetAsync(userId); |
|||
return user.GetProperty<string>("Title"); |
|||
```` |
|||
|
|||
这种方法开箱即用并且非常简单,你可以使用不同的属性名称(如这里的`Title`)在同一时间存储多个属性. |
|||
|
|||
对于EF Core额外的属性被格式化成单个 `JSON` 字符值串存储在数据库中. 对于MongoDB它们做为单独的字段存储. |
|||
|
|||
参阅[实体文档](Entities.md)了解更多关于额外系统. |
|||
|
|||
> 可以基于额外的属性执行**业务逻辑**. 你可以**override**服务方法获取或设置值. 重写服务在下面进行讨论. |
|||
|
|||
## 创建新实体映射到同一个数据库表/Collection |
|||
|
|||
尽管额外属性方法**易于使用**并且适用于一些场景,但它具有[实体文档](Entities.md)中描述的一些缺点. |
|||
|
|||
另一个方法是**创建你自己的实体**映射到**同一个数据库库**(对于MongoDB数据库是collection) |
|||
|
|||
[应用程序启动模板](Startup-Templates/Application.md)的 `AppUser` 已经实现了这种方法. [EF Core迁移文档](Entity-Framework-Core-Migrations.md)描述了在这些情况下如何实现和管理**EF Core数据库迁移**. 这种方法同样适用于MongoDB,但你不需要处理数据库迁移问题. |
|||
|
|||
## 创建一个拥有自己数据库表/Collection的新实体 |
|||
|
|||
映射你的实体到依赖模块的**已存在的表**有一些缺点; |
|||
|
|||
* 你需要处理EF Core的**数据库迁移架构**. 需要特别注意迁移代码,特别是当你需要在实体间添加**关系**时. |
|||
* 你的应用程序数据库和模块数据库将是 **同一个物理数据库**. 通常需要时可以将模块数据库分开,但使用相同的表会对其进行限制. |
|||
|
|||
如果你想要使你的实体或模块定义的实体**低耦合**,那么可以创建自己的数据库表/collection并且将你的实体映射到自己的数据库表. |
|||
|
|||
在这种情况下你需要处理**同步问题**,尤其是你要**复制**相关实体的某些属性/字段时,有一些解决方案; |
|||
|
|||
* 如果你构建的是一个 **单体** 应用程序(或者在同一进程管理你的实体和依赖模块的实体),那么你可以使用[本地事件总线](Local-Event-Bus.md)监听实体更改. |
|||
* 如果你构建的是一个 **分布式** 系统,模块的实体和你的实体在不同的 进程/服务 管理(创建/更新/删除),那么你可以使用[分布式事件总线](Distributed-Event-Bus.md)订阅实体的更改事件. |
|||
|
|||
在你处理事件时,你可以在自己的数据库中更改自己的实体. |
|||
|
|||
### 订阅本地事件总线 |
|||
|
|||
[本地事件总线](Local-Event-Bus.md)系统是发布和订阅同一应用程序中发生的事件的方法. |
|||
|
|||
假设你想要获取 `IdentityUser` 实体的更改信息(创建,更改或删除). 你可以创建一个类实现 `ILocalEventHandler<EntityChangedEventData<IdentityUser>>` 接口. |
|||
|
|||
````csharp |
|||
public class MyLocalIdentityUserChangeEventHandler : |
|||
ILocalEventHandler<EntityChangedEventData<IdentityUser>>, |
|||
ITransientDependency |
|||
{ |
|||
public async Task HandleEventAsync(EntityChangedEventData<IdentityUser> eventData) |
|||
{ |
|||
var userId = eventData.Entity.Id; |
|||
var userName = eventData.Entity.UserName; |
|||
//... |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `EntityChangedEventData<T>` 涵盖了给定实体的创建,更新或删除事件. 如果你需要你可以分别订阅创建,更新或删除事件(在同一个类或不同的类中). |
|||
* 这里的代码在**本地事务之外执行**,因为它监听 `EntityChanged` 事件. 如果当前[工作单元](Unit-Of-Work.md)是事务性的,你可以订阅 `EntityChangingEventData<T>` 事件,它在**同一本地(进行)事务**中执行事件处理. |
|||
|
|||
> 提醒:这些方法需要在包含处理类的同一进程中更改 `IdentityUser` 实体. 即使在集群环境(同一应用程序的多个实例在不同的服务器进行),它也完美工作. |
|||
|
|||
### 订阅分布式事件总线 |
|||
|
|||
[分布式事件总线](Distributed-Event-Bus.md)是在一个应用程序中发布事件,并在相同服务器或不同服务器运行的相同应用程序或不同应用程序中接收事件的方法. |
|||
|
|||
假设你想要获取 `IdentityUser` 实体的创建,更改或删除信息. 你可以像以下一样创建一个类: |
|||
|
|||
````csharp |
|||
public class MyDistributedIdentityUserChangeEventHandler : |
|||
IDistributedEventHandler<EntityCreatedEto<EntityEto>>, |
|||
IDistributedEventHandler<EntityUpdatedEto<EntityEto>>, |
|||
IDistributedEventHandler<EntityDeletedEto<EntityEto>>, |
|||
ITransientDependency |
|||
{ |
|||
public async Task HandleEventAsync(EntityCreatedEto<EntityEto> eventData) |
|||
{ |
|||
if (eventData.Entity.EntityType == "Volo.Abp.Identity.IdentityUser") |
|||
{ |
|||
var userId = Guid.Parse(eventData.Entity.KeysAsString); |
|||
//...handle the "created" event |
|||
} |
|||
} |
|||
|
|||
public async Task HandleEventAsync(EntityUpdatedEto<EntityEto> eventData) |
|||
{ |
|||
if (eventData.Entity.EntityType == "Volo.Abp.Identity.IdentityUser") |
|||
{ |
|||
var userId = Guid.Parse(eventData.Entity.KeysAsString); |
|||
//...handle the "updated" event |
|||
} |
|||
} |
|||
|
|||
public async Task HandleEventAsync(EntityDeletedEto<EntityEto> eventData) |
|||
{ |
|||
if (eventData.Entity.EntityType == "Volo.Abp.Identity.IdentityUser") |
|||
{ |
|||
var userId = Guid.Parse(eventData.Entity.KeysAsString); |
|||
//...handle the "deleted" event |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* 它实现了多个 `IDistributedEventHandler` 接口: **创建**,**更改**和**删除**,因为分布式事件总线单独发布事件,没有本地事件总线那样的"Changed"事件. |
|||
* 它订阅了 `EntityEto`, 这是一个通用的事件类,ABP框架针对所有类型的实体**自动发布**. 这就是为什么它检查**实体类型**(因为我们没有假设有对 `IdentityUser` 实体有安全的类型引用,所以它是字符串类型的). |
|||
|
|||
预构建应用模块没有定义专门的事件类型(如`IdentityUserEto` - "ETO" 意思是 "事件传输对象"). 此功能在路线图上([关注这个issue](https://github.com/abpframework/abp/issues/3033)),一旦完成后,你就可以订阅独立的实体类型: |
|||
|
|||
````csharp |
|||
public class MyDistributedIdentityUserCreatedEventHandler : |
|||
IDistributedEventHandler<EntityCreatedEto<IdentityUserEto>>, |
|||
ITransientDependency |
|||
{ |
|||
public async Task HandleEventAsync(EntityCreatedEto<IdentityUserEto> eventData) |
|||
{ |
|||
var userId = eventData.Entity.Id; |
|||
var userName = eventData.Entity.UserName; |
|||
//...handle the "created" event |
|||
} |
|||
|
|||
//... |
|||
} |
|||
```` |
|||
|
|||
* 这个处理程序只会在新用户创建时执行. |
|||
|
|||
> 唯一预定义的专门事件类是 `UserEto`, 你可以订阅 `EntityCreatedEto<UserEto>` 获取用户创建时的通知. 此事件也适用于身份模块. |
|||
|
|||
## 另请参阅 |
|||
|
|||
* [自定义已存在的模块](Customizing-Application-Modules-Guide.md) |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Volo.Abp.Cli.ProjectBuilding.Files; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
|||
{ |
|||
public class RemoveRootFolderStep : ProjectBuildPipelineStep |
|||
{ |
|||
public override void Execute(ProjectBuildContext context) |
|||
{ |
|||
foreach (var file in context.Files) |
|||
{ |
|||
file.SetName(RemoveRootFolder(file.Name)); |
|||
} |
|||
} |
|||
|
|||
private string RemoveRootFolder(string fileName) |
|||
{ |
|||
if (!fileName.Contains('/')) |
|||
{ |
|||
return fileName; |
|||
} |
|||
|
|||
return string.Join("/", fileName.Split('/').Skip(1)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public static class ProjectFileNameHelper |
|||
{ |
|||
|
|||
public static string GetAssemblyNameFromProjectPath(string projectFile) |
|||
{ |
|||
return projectFile |
|||
.Substring(projectFile.LastIndexOf(Path.DirectorySeparatorChar) + 1) |
|||
.RemovePostFix(StringComparison.OrdinalIgnoreCase, ".csproj"); |
|||
} |
|||
} |
|||
} |
|||
@ -1,3 +1,10 @@ |
|||
const fse = require("fs-extra"); |
|||
const fse = require('fs-extra'); |
|||
|
|||
console.log(fse.readJSONSync("package.json").version); |
|||
const commonProps = fse.readFileSync('../common.props').toString(); |
|||
|
|||
const versionTag = '<Version>'; |
|||
const versionEndTag = '</Version>'; |
|||
const first = commonProps.indexOf(versionTag) + versionTag.length; |
|||
const last = commonProps.indexOf(versionEndTag); |
|||
|
|||
console.log(commonProps.substring(first, last)); |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/anchor-js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"anchor-js": "^4.2.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,11 +1,11 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/aspnetcore.mvc.ui.theme.basic", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.2.0" |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,24 +1,24 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/aspnetcore.mvc.ui.theme.shared", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui": "^2.2.0", |
|||
"@abp/bootstrap": "^2.2.0", |
|||
"@abp/bootstrap-datepicker": "^2.2.0", |
|||
"@abp/datatables.net-bs4": "^2.2.0", |
|||
"@abp/font-awesome": "^2.2.0", |
|||
"@abp/jquery-form": "^2.2.0", |
|||
"@abp/jquery-validation-unobtrusive": "^2.2.0", |
|||
"@abp/lodash": "^2.2.0", |
|||
"@abp/luxon": "^2.2.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.2.0", |
|||
"@abp/select2": "^2.2.0", |
|||
"@abp/sweetalert": "^2.2.0", |
|||
"@abp/timeago": "^2.2.0", |
|||
"@abp/toastr": "^2.2.0" |
|||
"@abp/aspnetcore.mvc.ui": "^2.3.0", |
|||
"@abp/bootstrap": "^2.3.0", |
|||
"@abp/bootstrap-datepicker": "^2.3.0", |
|||
"@abp/datatables.net-bs4": "^2.3.0", |
|||
"@abp/font-awesome": "^2.3.0", |
|||
"@abp/jquery-form": "^2.3.0", |
|||
"@abp/jquery-validation-unobtrusive": "^2.3.0", |
|||
"@abp/lodash": "^2.3.0", |
|||
"@abp/luxon": "^2.3.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.3.0", |
|||
"@abp/select2": "^2.3.0", |
|||
"@abp/sweetalert": "^2.3.0", |
|||
"@abp/timeago": "^2.3.0", |
|||
"@abp/toastr": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,13 +1,13 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/blogging", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.2.0", |
|||
"@abp/owl.carousel": "^2.2.0", |
|||
"@abp/tui-editor": "^2.2.0" |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.3.0", |
|||
"@abp/owl.carousel": "^2.3.0", |
|||
"@abp/tui-editor": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/bootstrap", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"bootstrap": "^4.3.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/clipboard", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"clipboard": "^2.0.4" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/codemirror", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"codemirror": "^5.49.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,8 +1,8 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/core", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/datatables.net-bs4", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/datatables.net": "^2.2.0", |
|||
"@abp/datatables.net": "^2.3.0", |
|||
"datatables.net-bs4": "^1.10.20" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/datatables.net", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"datatables.net": "^1.10.20" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,15 +1,15 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/docs", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/anchor-js": "^2.2.0", |
|||
"@abp/clipboard": "^2.2.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.2.0", |
|||
"@abp/popper.js": "^2.2.0", |
|||
"@abp/prismjs": "^2.2.0" |
|||
"@abp/anchor-js": "^2.3.0", |
|||
"@abp/clipboard": "^2.3.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.3.0", |
|||
"@abp/popper.js": "^2.3.0", |
|||
"@abp/prismjs": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/font-awesome", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"@fortawesome/fontawesome-free": "^5.11.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,11 +1,11 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/highlight.js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0" |
|||
"@abp/core": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery-form", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"jquery-form": "^4.2.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery-validation-unobtrusive", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery-validation": "^2.2.0", |
|||
"@abp/jquery-validation": "^2.3.0", |
|||
"jquery-validation-unobtrusive": "^3.2.11" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery-validation", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"jquery-validation": "^1.19.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"jquery": "^3.4.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/lodash", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"lodash": "^4.17.15" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/malihu-custom-scrollbar-plugin", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"malihu-custom-scrollbar-plugin": "^3.1.5" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/markdown-it", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"markdown-it": "^10.0.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/owl.carousel", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"owl.carousel": "^2.3.4" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/popper.js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"popper.js": "^1.16.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/prismjs", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"prismjs": "^1.17.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/select2", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"select2": "^4.0.12" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/sweetalert", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"sweetalert": "^2.1.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/timeago", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"timeago": "^1.6.7" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/toastr", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"toastr": "^2.1.4" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,15 +1,15 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/tui-editor", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/codemirror": "^2.2.0", |
|||
"@abp/highlight.js": "^2.2.0", |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/markdown-it": "^2.2.0", |
|||
"@abp/codemirror": "^2.3.0", |
|||
"@abp/highlight.js": "^2.3.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"@abp/markdown-it": "^2.3.0", |
|||
"tui-editor": "^1.4.8" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
Loading…
Reference in new issue