Development of architecture started in 2009. Few times was made full refactoring basic API layer, was added and removed key features from the system. Since the beginning of 2012, system is basically not changed. From which is follows that the base has become quite stable.
While creating backoffice software for the few companies I formulated the minimal features that has to be supported by any backoffice applications:
After analyzing these requirements, I came up with the idea to write a unified interface that solves a minimum number of tasks:
Centralized settings storage (if required).
ISettingsProvider
that will allow to load and save settings from other data source.
Download required modules for UI and internal logic process by user or server role.
IPluginProvider
we can write our own plugin, witch will load plugins from different source.Ability to use convenient user interface
(SDI/MDI/Dialog interface).Reusable codebase, witch can be used not only in WinForms applications, but also in ASP.NET or Win/Web services.
Unlike the common extension architecture, the basis for SAL architecture is the plugin with inherit IPluginKernel
interface. At the same time, the module itself can extend or use other modules.
Result:
UserControl
classes are used as UI components for WinForms applications (The use of the Form class as the basis does not allow some hosts. For example EnvDTE). If necessary, the code can be returned to the standard application at no extra cost.The running application looks like this:
Application can be a SAL host as in case of Flatbed.MDI, Flatbed.Dialog. And a separate plugin, as in the case of Flatbed.EnvDTE.
SAL hostISettingsProvider
) and plugins provider (IPluginProvider
). If different settings or plugins providers are found then default provider will became parent provider and new provider will be used as default provider.
ISettingsProvider
allows to load plugin settings from different sources. In the host itself is implemented default provider that loading data from XML file.IPluginProvider
allows to load plugins and update plugins from different sources. In the host itself is implemented default provider that loading plugins from current application folder.If plugin is referenced only to interfaces of the SAL.Flatbed assembly, then it can't use UI and can work in any environment. If plugin is referenced to upper level assembly, for example SAL.Windows (Which referenced to SAL.Flatbed), then plugin will need WinForms UI to implement the full functionality.
SAL Host can provide additional set of interfaces that inherit from the minimal interface base on the host itself. For example to get access to additional features of Flatbed.EnvDTE host, we need to cast IHost
interface to IHostAddIn
interface, which described in assembly SAL.EnvDTE.
If required, one plugin can invoke other plugin. But if required plugin is not loaded, then part of logic of UI will be unavailable and there will be no exception about the same situation.
SAL.Flatbed.dll
— Root assembly, that contains minimal set of interfaces to implement plugins and hosts.SAL.Windows.dll
— Assembly to develop components for WinForms applications.SAL.Web.dll
— Assembly to develop components for applications for ASP.NET.After all plugins are loaded, for each plugin method Boolean OnConnection(ConnectMode mode)
is called.
Interaction between plugins a based on public methods properties and events.
Interaction between plugin and host is available through IHost argument that can be received from plugin constructor method. For example if plugin can interact with WinForms host, then we can try to cast IHost
to IHostWindows
, which will add additional features related to WinForms.
Receive and load plugin settings. (With inheritance from IPluginSettings<T>
interface).
public class Plugin : IPlugin, IPluginSettings<PluginSettings> { ... private PluginSettings _settings; public PluginSettings Settings { get { if(this._settings == null) { this._settings = new PluginSettings(this); this.Host.Plugins.Settings(this).LoadAssemblyParameters(this._settings); } return this._settings; } } Object IPluginSettings.Settings { get { return this.Settings; } } ... }
Loading big object from settings (DataSet for example)
using(Stream stream = this.Plugin.Host.Plugins.Settings(this.Plugin).LoadAssemblyBlob("DataSet.xml")) if(stream != null) base.DataSet.ReadXml(stream);
Saving big object to settings. (DataSet for example)
using(MemoryStream stream = new MemoryStream()) { base.DataSet.WriteXml(stream); this.Plugin.Host.Plugins.Settings(this).SaveAssemblyBlob("DataSet.xml", stream); }
Adding elements to application that supports SAL.Windows interfaces.
internal IHost Host { get; private set; }//It's allowed to launch the plugin with any host, and at startup - the plugin decides how to start public Plugin(IHost host){ this.Host = host; } ... IHostWindows host = this.Host as IHostWindows; if(host != null) { IMenuItem menuTools = host.MainMenu.FindMenuItem("Tools"); if(menuTools != null) { this.RdpClientMenu = menuTools.Create("RDP Client"); this.RdpClientMenu.Name = "tsmiToolsRdpClient"; this.RdpClientMenu.Click += new EventHandler(RdpClientMenu_Click); menuTools.Items.Insert(0, this.RdpClientMenu); return true; } }
Opening window (We must inherit from UserControl
class)
internal IHostWindows HostWindows { get; private set; } public Plugin(IHostWindows host) { this.HostWindows = host; }//Creating an instance of a plugin is only possible in a host that implements the IHostWindows interface ... IWindow wnd = this.HostWindows.Windows.CreateWindow(this, typeof(PanelRdpClient).ToString(), false, DockState.DockLeft);
Recieveing objects IWindow
and IPlugin
in the opened window.
public partial class PanelRdpClient : UserControl { private IWindow Window { get { return (IWindow)base.Parent; } } private PluginRdp Plugin { get { return (PluginWindows)this.Window.Plugin; } } ... protected override void OnCreateControl() { this.Window.Caption = "I want cookie"; base.OnCreateControl(); } ... }
IPlugin
— An interface that should be implemented in any pluginIPluginSettings
, IPluginSettings<T>
— Interface for load and save plugin settings. Contains property Settings of type of Object.
IPluginKernel
— Is the marker interface that this plugin is required to load before other plugins.
IPluginProvider
— Interface to load plugins from various sources.
ISettingsProvider
— Interface to load and save plugins settings from various sources.Диаграмма классов сборки SAL.Flatbed
Диаграмма классов сборки SAL.Windows
Class diagram for SAL.Web assembly
All class diagram SAL, SAL.Windows, SAL.Web, SAL.EnvDTE.
SAL.Flatbed.dll
— БазаSAL.Windows.dll
— База для WinFormsSAL.Web.dll
— База для ASP.NET