-
Notifications
You must be signed in to change notification settings - Fork 184
Proposed Extension Mechanism
Eugene Bekker edited this page Jan 12, 2016
·
1 revision
The following code snippet defines a proposed extension mechanism called the ExtManager Pattern which is based on the MEF features of the .NET framework and common utilization pattern.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
// * Managed Extensibility Framework (MEF)
// https://msdn.microsoft.com/en-us/library/dd460648%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
// * Attributed Programming Model (MEF)
// https://msdn.microsoft.com/en-us/library/ee155691(v=vs.110).aspx
// Terms:
// * Component - target entity instance
// * Provider - a factory for resolving components
// * Scaffold? - the machinery for provider and compnent resolution
namespace AppDomain.FooArea
{
/*********************************************************************
** The two interfaces IFoo and IFooProvider represent the contract
** that needs to be satified by any extension implementations of the
** target extensible component.
*/
/// <summary>
/// Session-level interface or base-class that defines the <i>immediate
/// interaction</i> contract between a client and the extensible component.
/// </summary>
/// <remarks>
/// The terms <b>session-level</b> and <b>immediate</b> in the summary
/// description are relative to the domain and context of use of the
/// extensible component. In some cases, long-running interactions
/// (sessions) that span minutes, hours or days in real time, may be
/// acceptable. In other cases, a session may be confined to seconds
/// in real world scenarios.
/// </remarks>
public interface IFoo : IDisposable
{
string HowToSayHello
{ get; set; }
void SayHelloWorld();
}
/// <summary>
/// Provider interface for generating instances of the target
/// extensible component.
/// </summary>
/// <remarks>
/// This is an Application-level interface that is constructed at
/// the time the component instance needs to be resolved, and
/// persists until the ext management container is disposed (at
/// which time it would discard all providers that it instantiated
/// during its lifetime).
/// </remarks>
public interface IFooProvider // : IDisposable
/* The provider interface can optionally extend IDisposable
** if we want to support clean up of the provider instance,
** however it's important to rememeber that only the MEF
*/
{
/// <remarks>
/// An optional set of named initialization parameters may be
/// provided to further configure or qualify the Component
/// instance that is returned.
/// </remarks>
IFoo GetFoo(IDictionary<string, object> initParams = null);
}
/*********************************************************************
** The remaining components represent the supporing elements that help
** implement this "ExtManager" pattern. Once in place, they only need
** to be used by consumers of the ExtManager and by implemntors.
*/
public interface IFooProviderInfo
/* The property definitions of this interface should
** match up with the properties defined in the custom
** attribute defined below, except that they should
** only define getters as part of the property sig.
*/
{
[DefaultValue(null)]
string Name
{ get; }
string Label
{ get; }
string Description
{ get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class FooProviderAttribute : ExportAttribute
{
/* Any required attribute parameters are part of the
** constructor signature as per usual with attributes.
*/
public FooProviderAttribute(string name)
: base(typeof(IFooProvider))
{
Name = name;
}
public string Name
{ get; private set; }
public string Label
{ get; set; }
public string Description
{ get; set; }
}
public static class FooExtManager
{
private const string EXT_DIR = "ext";
private static Config _config;
private static CompositionContainer _cc;
public static string GetExtPath()
{
var thisAsm = Assembly.GetExecutingAssembly().Location;
if (string.IsNullOrEmpty(thisAsm))
return null;
var thisDir = Path.GetDirectoryName(thisAsm);
if (string.IsNullOrEmpty(thisDir))
return null;
return Path.Combine(thisDir, EXT_DIR);
}
/// <remarks>
/// An optional name may be given to distinguish between different
/// available provider implementations. Additionally, an optional
/// set of named initialization parameters may be provided to
/// further configure or qualify the Provider instance that is
/// returned and ultimately the Components that the Provider
/// produces.
/// </remarks>
public static IFooProvider GetFooProvider(string name = null)
/* The name can be an optional argument if the entity ext
** manager supports the notion of a "default" provider
** and/or entity implementation
*/
{
if (_config == null)
{
lock (typeof (Config))
{
if (_config == null)
{
InitConfig();
}
}
}
return _config[name]?.Value;
}
private static void InitConfig()
{
var aggCat = new AggregateCatalog();
// Add the assembly that contains the current Component/Provider Scaffold
var thisAsm = Assembly.GetExecutingAssembly();
aggCat.Catalogs.Add(new AssemblyCatalog(thisAsm));
// Add assemblies in the current apps path and runtime
aggCat.Catalogs.Add(new ApplicationCatalog());
// Add the local extension folder if it exists
var thisExt = GetExtPath();
if (Directory.Exists(thisExt))
aggCat.Catalogs.Add(new DirectoryCatalog(thisExt));
// Other possible folders to include:
// * Application CWD
// * PATH
// * User-specific ext folder
// * System-wide ext folder
_config = new Config();
// Guard this with a try-catch if we want to do something
// in an error situation other than let it throw up
_cc = new CompositionContainer(aggCat);
_cc.ComposeParts(_config);
}
class Config : Dictionary<string, Lazy<IFooProvider, IFooProviderInfo>>
{
private IEnumerable<Lazy<IFooProvider, IFooProviderInfo>> _Providers;
[ImportMany]
public IEnumerable<Lazy<IFooProvider, IFooProviderInfo>> Providers
{
get { return _Providers; }
set
{
_Providers = value;
Clear();
foreach (var x in Providers)
{
var m = x.Metadata;
if (!string.IsNullOrEmpty(m?.Name))
this[m.Name] = x;
}
}
}
}
}
}
namespace AppDomain.FooArea.Impl1
{
[FooProvider("FooImplName",
Label = "Foo Impl",
Description = "This is a Foo implementation.")]
[PartCreationPolicy(CreationPolicy.Shared)]
// This is an optional attribute to control creation lifetime
public class FooImplProvider : IFooProvider
{
public IFoo GetFoo(IDictionary<string, object> initParams = null)
{
return new FooImpl();
}
public void Dispose()
{
// Cleanup Provider if the provider extends an IDisposable interface
}
}
public class FooImpl : IFoo
{
public string HowToSayHello { get; set; }
public void SayHelloWorld()
{
throw new NotImplementedException();
}
public void Dispose()
{
// Cleanup Component
}
}
}
The following snippet shows a typical use case of a consumer of the ExtManager pattern.
using AppDomain.FooArea;
using System.Collections.Generic;
namespace ClientAppDomain.FooClient
{
public class FooClient
{
public static void Main(string[] args)
{
var fooProv = FooExtManager.GetProvider("someName");
var fooParams = new Dictionary<string, object>
{
["param1"] = "val1",
["param2"] = "val2",
["param3"] = "val3",
};
using (var foo = fooProv.GetFoo(fooParams))
{
foo.SayHelloWorld();
}
}
}
}
Docs
- Overview
- FAQ
- Let's Encrypt Reference Sheet
- Quick Start
- Requirements
- Basic Concepts
- Vaults, Vault Providers and Vault Profiles
- Challenge Types, Challenge Handlers and Providers
- Troubleshooting
- Contributions
Legacy Docs - out of date
Reference
- Good to Know
- Proposed Extension Mechanism
- PowerShell Module Design
- Style Guides and Conventions
- Documentation Resources
A bit dated