Browse Source

Merge branch 'dev' of https://github.com/abpframework/abp into docs/service-proxies

pull/3308/head
mehmet-erim 7 years ago
parent
commit
f7f7462e7c
  1. 209
      docs/en/UI/Angular/Http-Requests.md
  2. 6
      docs/en/UI/Angular/Localization.md
  3. 92
      docs/en/UI/AspNetCore/Tag-Helpers/Collapse.md
  4. 97
      docs/en/UI/AspNetCore/Tag-Helpers/Dropdowns.md
  5. 10
      docs/en/UI/AspNetCore/Tag-Helpers/Index.md
  6. 81
      docs/en/UI/AspNetCore/Tag-Helpers/Modals.md
  7. 57
      docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md
  8. 70
      docs/en/UI/AspNetCore/Tag-Helpers/Progress-Bars.md
  9. 35
      docs/en/UI/AspNetCore/Tag-Helpers/Tooltips.md
  10. 4
      docs/en/docs-nav.json
  11. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs
  12. 13
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js
  13. 1
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs
  14. 10
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/Data/MyProjectNameDbMigrationService.cs

209
docs/en/UI/Angular/Http-Requests.md

@ -0,0 +1,209 @@
# How to Make HTTP Requests
## About HttpClient
Angular has the amazing [HttpClient](https://angular.io/guide/http) for communication with backend services. It is a layer on top and a simplified representation of [XMLHttpRequest Web API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). It also is the recommended agent by Angular for any HTTP request. There is nothing wrong with using the `HttpClient` in your ABP project.
However, `HttpClient` leaves error handling to the caller (method). In other words, HTTP errors are handled manually and by hooking into the observer of the `Observable` returned.
```js
getConfig() {
this.http.get(this.configUrl).subscribe(
config => this.updateConfig(config),
error => {
// Handle error here
},
);
}
```
Although clear and flexible, handling errors this way is repetitive work, even when error processing is delegated to the store or any other injectable.
An `HttpInterceptor` is able to catch `HttpErrorResponse`  and can be used for a centralized error handling. Nevertheless, cases where default error handler, therefore the interceptor, must be disabled require additional work and comprehension of Angular internals. Check [this issue](https://github.com/angular/angular/issues/20203) for details.
## RestService
ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by deafult.
### Getting Started with RestService
In order to use the `RestService`, you must inject it in your class as a dependency.
```js
import { RestService } from '@abp/ng.core';
@Injectable({
/* class metadata here */
})
class DemoService {
constructor(private rest: RestService) {}
}
```
You do not have to provide the `RestService` at module or component/directive level, because it is already **provided in root**.
### How to Make a Request with RestService
You can use the `request` method of the `RestService` is for HTTP requests. Here is an example:
```js
getFoo(id: number) {
const request: Rest.Request<null> = {
method: 'GET',
url: '/api/some/path/to/foo/' + id,
};
return this.rest.request<null, FooResponse>(request);
}
```
The `request` method always returns an `Observable<T>`. Therefore you can do the following wherever you use `getFoo` method:
```js
doSomethingWithFoo(id: number) {
this.demoService.getFoo(id).subscribe(
foo => {
// Do something with foo.
}
)
}
```
**You do not have to worry about unsubscription.** The `RestService` uses `HttpClient` behind the scenes, so every observable it returns is a finite observable, i.e. it closes subscriptions automatically upon success or error.
As you see, `request` method gets a request options object with `Rest.Request<T>` type. This generic type expects the interface of the request body. You may pass `null` when there is no body, like in a `GET` or a `DELETE` request. Here is an example where there is one:
```js
postFoo(body: Foo) {
const request: Rest.Request<Foo> = {
method: 'POST',
url: '/api/some/path/to/foo',
body
};
return this.rest.request<Foo, FooResponse>(request);
}
```
You may [check here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L23) for complete `Rest.Request<T>` type, which has only a few chages compared to [HttpRequest](https://angular.io/api/common/http/HttpRequest) class in Angular.
### How to Disable Default Error Handler of RestService
The `request` method, used with defaults, always handles errors. Let's see how you can change that behavior and handle errors yourself:
```js
deleteFoo(id: number) {
const request: Rest.Request<null> = {
method: 'DELETE',
url: '/api/some/path/to/foo/' + id,
};
return this.rest.request<null, void>(request, { skipHandleError: true });
}
```
`skipHandleError` config option, when set to `true`, disables the error handler and the returned observable starts throwing an error that you can catch in your subscription.
```js
removeFooFromList(id: number) {
this.demoService.deleteFoo(id).subscribe(
foo => {
// Do something with foo.
},
error => {
// Do something with error.
}
)
}
```
### How to Get a Specific API Endpoint From Application Config
Another nice config option that `request` method receives is `apiName` (available as of v2.4), which can be used to get a specific module endpoint from application configuration.
```js
putFoo(body: Foo, id: string) {
const request: Rest.Request<Foo> = {
method: 'PUT',
url: '/' + id,
body
};
return this.rest.request<Foo, void>(request, {apiName: 'foo'});
}
```
`putFoo` above will request `https://localhost:44305/api/some/path/to/foo/{id}` as long as the environment variables are as follows:
```js
// environment.ts
export const environment = {
apis: {
default: {
url: 'https://localhost:44305',
},
foo: {
url: 'https://localhost:44305/api/some/path/to/foo',
},
},
/* rest of the environment variables here */
}
```
### How to Observe Response Object or HTTP Events Instead of Body
`RestService` assumes you are generally interested in the body of a response and, by default, sets `observe` property as `'body'`. However, there may be times you are rather interested in something else, such as a custom proprietary header. For that, the `request` method receives `observe` property in its config object.
```js
getSomeCustomHeaderValue() {
const request: Rest.Request<null> = {
method: 'GET',
url: '/api/some/path/that/sends/some-custom-header',
};
return this.rest.request<null, HttpResponse<any>>(
request,
{observe: Rest.Observe.Response},
).pipe(
map(response => response.headers.get('Some-Custom-Header'))
);
}
```
You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10).
## What's Next?
* [Localization](./Localization.md)

6
docs/en/UI/Angular/Localization.md

@ -133,4 +133,8 @@ Localization resources are stored in the `localization` property of `ConfigState
## See Also ## See Also
* [Localization in ASP.NET Core](../../Localization.md) * [Localization in ASP.NET Core](../../Localization.md)
## What's Next?
* [Permission Management](./Permission-Management.md)

92
docs/en/UI/AspNetCore/Tag-Helpers/Collapse.md

@ -0,0 +1,92 @@
# Collapse
## Introduction
`abp-collapse-body` is the main container for showing and hiding content. `abp-collapse-id` is used to show and hide the content container. Can be triggered with both `abp-button` and `a` tags.
Basic usage:
````xml
<abp-button button-type="Primary" abp-collapse-id="collapseExample" text="Button with data-target" />
<a abp-button="Primary" abp-collapse-id="collapseExample"> Link with href </a>
<abp-collapse-body id="collapseExample">
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-collapse-body>
````
## Demo
See the [collapse demo page](https://bootstrap-taghelpers.abp.io/Components/Collapse) to see it in action.
## Attributes
### show
A value indicates if the collapse body will be initialized visible or hidden. Should be one of the following values:
* `false` (default value)
* `true`
### multi
A value indicates if an `abp-collapse-body` can be shown or hidden by an element that can show/hide multiple collapse bodies. Basically, this attribute adds "multi-collapse" class to `abp-collapse-body`. Should be one of the following values:
* `false` (default value)
* `true`
Sample:
````xml
<a abp-button="Primary" abp-collapse-id="FirstCollapseExample"> Toggle first element </a>
<abp-button button-type="Primary" abp-collapse-id="SecondCollapseExample" text="Toggle second element" />
<abp-button button-type="Primary" abp-collapse-id="FirstCollapseExample SecondCollapseExample" text="Toggle both elements" />
<abp-row class="mt-3">
<abp-column size-sm="_6">
<abp-collapse-body id="FirstCollapseExample" multi="true">
Curabitur porta porttitor libero eu luctus. Praesent ultrices mattis commodo. Integer sodales massa risus, in molestie enim sagittis blandit
</abp-collapse-body>
</abp-column>
<abp-column size-sm="_6">
<abp-collapse-body id="SecondCollapseExample" multi="true">
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et.
</abp-collapse-body>
</abp-column>
</abp-row>
````
## Accordion example
`abp-accordion` is the main container for the accordion items.
Basic usage:
````xml
<abp-accordion>
<abp-accordion-item title="Collapsible Group Item #1">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry rtat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
<abp-accordion-item title="Collapsible Group Item #2">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
<abp-accordion-item title="Collapsible Group Item #3">
Anim pariatur wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
</abp-accordion>
````
## Attributes
### active
A value indicates if the accordion item will be initialized visible or hidden. Should be one of the following values:
* `false` (default value)
* `true`
### title
A value indicates the visible title of the accordion item. Should be a string value.

97
docs/en/UI/AspNetCore/Tag-Helpers/Dropdowns.md

@ -0,0 +1,97 @@
# Dropdowns
## Introduction
`abp-dropdown` is the main container for dropdown content.
Basic usage:
````xml
<abp-dropdown>
<abp-dropdown-button text="Dropdown button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
````
## Demo
See the [dropdown demo page](https://bootstrap-taghelpers.abp.io/Components/Dropdowns) to see it in action.
## Attributes
### direction
A value indicates which direction the dropdown buttons will be displayed to. Should be one of the following values:
* `Down` (default value)
* `Up`
* `Right`
* `Left`
### dropdown-style
A value indicates if an `abp-dropdown-button` will have split icon for dropdown. Should be one of the following values:
* `Single` (default value)
* `Split`
## Menu items
`abp-dropdown-menu` is the main container for dropdown menu items.
Basic usage:
````xml
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" text="Dropdown"/>
<abp-dropdown-menu>
<abp-dropdown-header>Dropdown Header</abp-dropdown-header>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item active="true" href="#">Active action</abp-dropdown-item>
<abp-dropdown-item disabled="true" href="#">Disabled action</abp-dropdown-item>
<abp-dropdown-divider/>
<abp-dropdown-item-text>Dropdown Item Text</abp-dropdown-item-text>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
````
## Attributes
### align
A value indicates which direction `abp-dropdown-menu` items will be aligned to. Should be one of the following values:
* `Left` (default value)
* `Right`
### Additional content
`abp-dropdown-menu` can also contain additional HTML elements like headings, paragraphs, dividers or form element.
Example:
````xml
<abp-dropdown >
<abp-dropdown-button button-type="Secondary" text="Dropdown With Form"/>
<abp-dropdown-menu>
<form class="px-4 py-3">
<abp-input asp-for="EmailAddress"></abp-input>
<abp-input asp-for="Password"></abp-input>
<abp-input asp-for="RememberMe"></abp-input>
<abp-button button-type="Primary" text="Sign In" type="submit" />
</form>
<abp-dropdown-divider></abp-dropdown-divider>
<abp-dropdown-item href="#">New around here? Sign up</abp-dropdown-item>
<abp-dropdown-item href="#">Forgot password?</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
````

10
docs/en/UI/AspNetCore/Tag-Helpers/Index.md

@ -15,6 +15,16 @@ Here, the list of components those are wrapped by the ABP Framework:
* [Buttons](Buttons.md) * [Buttons](Buttons.md)
* [Cards](Cards.md) * [Cards](Cards.md)
* [Alerts](Alerts.md) * [Alerts](Alerts.md)
* [Tabs](Tabs.md)
* [Grids](Grids.md)
* [Modals](Modals.md)
* [Collapse](Collapse.md)
* [Dropdowns](Dropdowns.md)
* [List Groups](List-Groups.md)
* [Paginator](Paginator.md)
* [Popovers](Popovers.md)
* [Progress Bars](Progress-Bars.md)
* [Tooltips](Tooltips.md)
* ... * ...
> Until all the tag helpers are documented, you can visit https://bootstrap-taghelpers.abp.io/ to see them with live samples. > Until all the tag helpers are documented, you can visit https://bootstrap-taghelpers.abp.io/ to see them with live samples.

81
docs/en/UI/AspNetCore/Tag-Helpers/Modals.md

@ -0,0 +1,81 @@
# Modals
## Introduction
`abp-modal` is a main element to create a modal.
Basic usage:
````xml
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button>
<abp-modal centered="true" size="Large" id="myModal">
<abp-modal-header title="Modal title"></abp-modal-header>
<abp-modal-body>
Woohoo, you're reading this text in a modal!
</abp-modal-body>
<abp-modal-footer buttons="Close"></abp-modal-footer>
</abp-modal>
````
## Demo
See the [modals demo page](https://bootstrap-taghelpers.abp.io/Components/Modals) to see it in action.
## Attributes
### centered
A value indicates the positioning of the modal. Should be one of the following values:
* `false` (default value)
* `true`
### size
A value indicates the size of the modal. Should be one of the following values:
* `Default` (default value)
* `Small`
* `Large`
* `ExtraLarge`
### static
A value indicates if the modal will be static. Should be one of the following values:
* `false` (default value)
* `true`
### Additional content
`abp-modal-footer` can have multiple buttons with alignment option.
Add `@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal` to your page.
Example:
````xml
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button>
<abp-modal centered="true" size="Large" id="myModal" static="true">
<abp-modal-header title="Modal title"></abp-modal-header>
<abp-modal-body>
Woohoo, you're reading this text in a modal!
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)" button-alignment="Between"></abp-modal-footer>
</abp-modal>
````
### button-alignment
A value indicates the positioning of your modal footer buttons. Should be one of the following values:
* `Default` (default value)
* `Start`
* `Center`
* `Around`
* `Between`
* `End`

57
docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md

@ -0,0 +1,57 @@
# Paginator
## Introduction
`abp-paginator` is the abp tag for pagination. Requires `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model.
Basic usage:
````xml
<abp-paginator model="Model.PagerModel" show-info="true"></abp-paginator>
````
Model:
````xml
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
{
public class PaginatorModel : PageModel
{
public PagerModel PagerModel { get; set; }
public void OnGet(int currentPage, string sort)
{
PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort);
}
}
}
````
## Demo
See the [paginator demo page](https://bootstrap-taghelpers.abp.io/Components/Paginator) to see it in action.
## Attributes
### model
`Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model can be initialized with the following data:
* `totalCount`
* `shownItemsCount`
* `currentPage`
* `pageSize`
* `pageUrl`
* `sort` (default null)
### show-info
A value indicates if an extra information about start, end and total records will be displayed. Should be one of the following values:
* `false` (default value)
* `true`

70
docs/en/UI/AspNetCore/Tag-Helpers/Progress-Bars.md

@ -0,0 +1,70 @@
# Progress Bars
## Introduction
`abp-progress-bar` is the abp tag for progress bar status.
Basic usage:
````xml
<abp-progress-bar value="70" />
<abp-progress-bar type="Warning" value="25"> %25 </abp-progress-bar>
<abp-progress-bar type="Success" value="40" strip="true"/>
<abp-progress-bar type="Dark" value="10" min-value="5" max-value="15" strip="true"> %50 </abp-progress-bar>
<abp-progress-group>
<abp-progress-part type="Success" value="25"/>
<abp-progress-part type="Danger" value="10" strip="true"> %10 </abp-progress-part>
<abp-progress-part type="Primary" value="50" animation="true" strip="true" />
</abp-progress-group>
````
## Demo
See the [progress bars demo page](https://bootstrap-taghelpers.abp.io/Components/Progress-Bars) to see it in action.
## Attributes
### value
A value indicates the current progress of the bar.
### type
A value indicates the background color of the progress bar. Should be one of the following values:
* `Default` (default value)
* `Secondary`
* `Success`
* `Danger`
* `Warning`
* `Info`
* `Light`
* `Dark`
### min-value
Minimum value of the progress bar. Default is 0.
### max-value
Maximum value of the progress bar. Default is 100.
### strip
A value indicates if the background style of the progress bar is stripped. Should be one of the following values:
* `false` (default value)
* `true`
### animation
A value indicates if the stripped background style of the progress bar is animated. Should be one of the following values:
* `false` (default value)
* `true`

35
docs/en/UI/AspNetCore/Tag-Helpers/Tooltips.md

@ -0,0 +1,35 @@
# Tooltips
## Introduction
`abp-tooltip` is the abp tag for tooltips.
Basic usage:
````xml
<abp-button abp-tooltip="Tooltip">
Tooltip Default
</abp-button>
<abp-button abp-tooltip-top="Tooltip">
Tooltip on top
</abp-button>
<abp-button abp-tooltip-right="Tooltip">
Tooltip on right
</abp-button>
<abp-button abp-tooltip-bottom="Tooltip">
Tooltip on bottom
</abp-button>
<abp-button disabled="true" abp-tooltip="Tooltip">
Disabled button Tooltip
</abp-button>
````
## Demo
See the [tooltips demo page](https://bootstrap-taghelpers.abp.io/Components/Tooltips) to see it in action.

4
docs/en/docs-nav.json

@ -316,6 +316,10 @@
"text": "Service Proxies", "text": "Service Proxies",
"path": "UI/Angular/Service-Proxies.md" "path": "UI/Angular/Service-Proxies.md"
}, },
{
"text": "HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
},
{ {
"text": "Localization", "text": "Localization",
"path": "UI/Angular/Localization.md" "path": "UI/Angular/Localization.md"

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs

@ -9,7 +9,7 @@ namespace Volo.Abp.Cli.ProjectModification
{ {
public ILogger<NpmGlobalPackagesChecker> Logger { get; set; } public ILogger<NpmGlobalPackagesChecker> Logger { get; set; }
public NpmGlobalPackagesChecker(PackageJsonFileFinder packageJsonFileFinder) public NpmGlobalPackagesChecker()
{ {
Logger = NullLogger<NpmGlobalPackagesChecker>.Instance; Logger = NullLogger<NpmGlobalPackagesChecker>.Instance;
} }

13
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js

@ -119,16 +119,17 @@
}; };
var setQueryString = function () { var setQueryString = function () {
clearQueryString();
var uri = window.location.href.toString();
var comboboxes = $(".doc-section-combobox"); var comboboxes = $(".doc-section-combobox");
if (comboboxes.length < 1) { if (comboboxes.length < 1) {
return; return;
} }
var hash = document.location.hash;
clearQueryString();
var uri = window.location.href.toString();
var new_uri = uri + "?"; var new_uri = uri + "?";
for (var i = 0; i < comboboxes.length; i++) { for (var i = 0; i < comboboxes.length; i++) {
@ -142,7 +143,7 @@
} }
} }
window.history.replaceState({}, document.title, new_uri); window.history.replaceState({}, document.title, new_uri + hash);
}; };
var getTenYearsLater = function () { var getTenYearsLater = function () {

1
modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs

@ -44,6 +44,7 @@ namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants
public string AdminEmailAddress { get; set; } public string AdminEmailAddress { get; set; }
[Required] [Required]
[DataType(DataType.Password)]
[MaxLength(128)] [MaxLength(128)]
public string AdminPassword { get; set; } public string AdminPassword { get; set; }
} }

10
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/Data/MyProjectNameDbMigrationService.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
@ -38,15 +39,14 @@ namespace MyCompanyName.MyProjectName.Data
await MigrateHostDatabaseAsync(); await MigrateHostDatabaseAsync();
var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
var i = 0; var i = 0;
var tenants = await _tenantRepository.GetListAsync(); foreach (var tenant in tenants.Where(t => t.ConnectionStrings.Any()))
foreach (var tenant in tenants)
{ {
i++;
using (_currentTenant.Change(tenant.Id)) using (_currentTenant.Change(tenant.Id))
{ {
Logger.LogInformation($"Migrating {tenant.Name} database schema... ({i} of {tenants.Count})"); Logger.LogInformation($"Migrating {tenant.Name} database schema... ({++i} of {tenants.Count})");
await MigrateTenantDatabasesAsync(tenant); await MigrateTenantDatabasesAsync(tenant);
Logger.LogInformation($"Successfully completed {tenant.Name} database migrations."); Logger.LogInformation($"Successfully completed {tenant.Name} database migrations.");
} }

Loading…
Cancel
Save