A provider is a design pattern to integrate and swap a code extension more easily and flexibly. A good example are payment methods. If a developer wants to implement multiple payment methods of one payment company, he can create a single module (representing the payment company), which contains a provider for each payment method. Besides IPaymentMethod, there are other providers like IExportProvider, IShippingRateComputationMethod, IExchangeRateProvider, IExternalAuthenticationMethod etc.
Each of these provider interfaces is derived from the marker interface IProvider in order to be able to identify it uniformly as a provider. The generic provider class encapsulates the respective interface and enriches it with general properties like ProviderMetadata. As a result you get an abstract, handy and API friendly construct such as Provider<IPaymentMethod>
.
Name | Implement | Description |
---|---|---|
FriendlyName | FriendlyNameAttribute | The English name of the provider. Localizable by using resource key Plugins.FriendlyName.<ProviderSystemName> |
Description | FriendlyNameAttribute | The English description of the provider. Localizable by using resource key Plugins.Description.<ProviderSystemName> |
DisplayOrder | OrderAttribute | The display order in the providers list. |
DependentWidgets | DependentWidgetsAttribute | Widgets which are automatically (de)activated when the provider gets (de)activated. Useful in scenarios where separate widgets are responsible for displaying provider data. |
ExportFeatures | ExportFeaturesAttribute | Data processing types supported by an export provider. |
IsConfigurable | IConfigurable | A value indicating whether the provider is configurable. |
IsEditable | IUserEditable | A value indicating whether the metadata is editable by the user. |
IsHidden | IsHiddenAttribute | A value indicating whether the provider is hidden. A hidden provider can only be used programmatically but not by the user through the user interface. |
SystemName * | SystemNameAttribute | Unique SystemName of the provider, e.g. Payments.AmazonPay. |
{% hint style="info" %} A provider can be activated or deactivated using the provider list. For example, a deactivated payment provider does not appear in the checkout payment method list. {% endhint %}
Providers are configurable via IConfigurable
. It specifies a RouteInfo
that points to an action method used for configuration.
public RouteInfo GetConfigurationRoute()
=> new(nameof(MyModuleAdminController.Configure), "MyModuleAdmin", new { area = "Admin" });
Typically the configuration consists of two Configure methods, one called by HttpGet
to load the configuration and one called by HttpPost
to save it. Since we are configuring a provider and its configuration view is just a portion of the actual Razor layout page, we need to tell it what provider we are configuring. This is done via the metadata and ViewBag.Provider
.
private readonly IProviderManager _providerManager;
[LoadSetting]
public IActionResult Configure(MyProviderSettings settings)
{
var model = MiniMapper.Map<MyProviderSettings, MyConfigurationModel>(settings);
ViewBag.Provider = _providerManager.GetProvider("MyProviderSystemName").Metadata;
return View(model);
}
Use IProviderManager to load any provider (including its metadata) or all providers of a certain type. Since we are configuring settings inherited from ISetting
, we can use the LoadSettingAttribute
to load an instance of our settings as an action parameter. The HttpPost
method for saving may look like this:
[HttpPost, SaveSetting]
public IActionResult Configure(MyConfigurationModel model,
MyProviderSettings settings)
{
if (!ModelState.IsValid)
{
return Configure(settings);
}
MiniMapper.Map(model, settings);
return RedirectToAction(nameof(Configure));
}
The SaveSettingAttribute
automatically saves the updated settings to database. The configuration view may look like this:
@model MyConfigurationModel
@{
Layout = "_ConfigureProvider";
}
<widget target-zone="admin_button_toolbar_before">
<button id="SaveConfigButton" type="submit" name="save" value="save"
class="btn btn-warning">
<i class="fa fa-check"></i>
<span>@T("Admin.Common.Save")</span>
</button>
</widget>
@await Component.InvokeAsync("StoreScope")
<form asp-action="Configure">
<div asp-validation-summary="All"></div>
@* TODO: multi-store configuration using setting-editor tag helper. *@
</form>