Skip to content

Managed Extensibility Framework Integration

yallie edited this page May 21, 2018 · 1 revision

This documentation page describes features currently in development and is a subject to change.

Managed Extensibility Framework integration

Managed Extensibility Framework (MEF) is an advanced IoC container and add-in framework available in .NET 4.0. Server-side MEF integration allows to set up server application declaratively by decorating public services with special attributes. Client-side integration provides abstraction layer over remote services: you use them just like local services.

Server-side: ZyanComponentHost + MEF

MEF infrastructure discovers and assembles server components, and ZyanComponentHost publishes them automatically. ZyanComponentHost and ComponentCatalog pull components out of MEF's CompositionContainer or ComposablePartCatalog instances.

Component lifetime is managed by MEF, as specified by PartCreationPolicyAttribute. Shared parts act like singletons (single instance per root CompositionContainer), NonShared parts are created and disposed of within nested CompositionContainers on every remote call. NonShared part creation policy should be applied to single-call components, and Shared policy — to singletons.

Server-side setup code example:

{code:c#} // MEF integration resides in a separate namespace using Zyan.Communication.Composition;

// create any kind of MEF catalogs and/or containers var catalog = new AggregateCatalog(...); var rootContainer = new CompositionContainer(catalog);

// instantiate component host using (var host = new ZyanComponentHost(...)) { // register all public components from the container host.RegisterComponents(rootContainer);

// ... or catalog itself
host.RegisterComponents(catalog);

} {code:c#}

Server components need to be decorated with special attributes. There are three options available, each with its own pros and contras:

Zyan built-in attributes (ZyanComponent, ZyanInterface)

Standard MEF attributes (Export, InheritedExport, ExportMetadata)

Custom attributes derived from MEF's ExportAttribute

1.1 Using Zyan built-in attributes

Pros: short, clear, ready to use out-of-the-box Contras: server components need to reference Zyan.Communication.dll

1.1.1 ZyanComponent attribute

Decorate a class with ZyanComponent attribute, specify component interface and optional unique name. More than one ZyanComponent attribute can be applied to a class. Sample code:

{code:c#} // Shared assembly interface ISampleService { ... }

// Server implementation assembly ZyanComponent(typeof(ISampleService)) class SampleService : ISampleService { ... }

// This component is published twice, under different names ZyanComponent("SomeUniqueName", typeof(ISampleService)) ZyanComponent(typeof(ISomeAnotherService)) class AnotherService : ISampleService, ISomeAnotherService, INonPublicService { ... } {code:c#}

1.1.2 ZyanInterface attribute (not recommended)

Decorate an interface with ZyanInterface attribute, specify interface type and optional unique name. Server component implementing this interface will be published automatically.

This method of registration is not recommended for two reasons:

Attribute constructor requires interface type parameter to export ComponentInterface metadata. It's inevitable because the attribute don't have access to the object it is applied to. This is obviously redundant, error-prone and simply doesn't look nice.

If more than one component implements interface decorated with ZyanInterface attribute, Zyan will not be able to decide which component to choose, which will result in ImportCardinalityMismatchException thrown at runtime.

Sample code:

{code:c#} // Shared assembly will also need to reference Zyan.Communication.dll ZyanInterface(typeof(ISampleService)) interface ISampleService { ... }

// Component implementation don't need any attributes class SampleService : ISampleService { ... } {code:c#}

1.1.3 Disabling automatic publication of the component

Remove ZyanComponent / ZyanInterface attributes or set IsPublished parameter to false:

{code:c#} ZyanComponent(typeof(ISampleService), IsPublished = false) class SampleService : ISampleService { ... } {code:c#}

1.2 Using MEF metadata attributes

Pros: server components don't need to reference Zyan.Communication.dll Contras: verbose (more attributes), error-prone (hardcoded string constants)

1.2.1 Using MEF metadata with Export attribute

Sample code:

{code:c#} interface ISampleService { ... }

Export(typeof(ISampleService)) ExportMetadata("ComponentInterface", typeof(ISampleService)) class SampleService : ISampleService { ... } {code:c#}

1.2.2 Using MEF metadata with InheritedExport attribute (not recommended)

This registration option is not recommended: if more than one class implements interface marked with InheritedExport, Zyan will not be able to choose which component to register and ImportCardinalityMismatchException will be thrown.

Sample code:

{code:c#} InheritedExport ExportMetadata("ComponentInterface", typeof(ISampleService)) interface ISampleService { ... }

// Component implementation don't need attributes class SampleService : ISampleService { ... } {code:c#}

1.1.3 Disabling automatic publication

Remove ComponentInterface metadata or add IsPublished metadata attribute set to false:

{code:c#} Export(typeof(ISampleService)) [ExportMetadata("ComponentInterface", typeof(ISampleService)](ExportMetadata(ComponentInterface,-typeof(ISampleService)) ExportMetadata("IsPublished", false) class SampleService : ISampleService { ... } {code:c#}

1.3 Using custom MEF metadata attributes

Pros: short, no hardcoded strings, no reference to Zyan.Communication.dll needed Contras: require manual coding

Write your own attribute similar to ZyanComponent or ZyanInterface. Inherit from MEF's ExportAttribute or InheritedExportAttribute, decorate with MetadataAttribute as follows:

{code:c#} MetadataAttribute, AttributeUsage(AttributeTargets.Class, Inherited = false) public class MyComponentAttribute : ExportAttribute { public MyComponentAttribute(Type componentInterface) : base(componentInterface) { if (!componentInterface.IsInterface) { throw new InvalidOperationException("Interface type required: " + componentInterface); }

    ComponentInterface = componentInterface;
}

// This property is required
public Type ComponentInterface { get; private set; }

}

// Server component implementation MyComponent(typeof(ISampleService)) class SampleService : ISampleService { ... } {code:c#}

1.4 Queryable components registration

Zyan can automatically discover queryable components (i. e., server classes you can query via Linq). Since release:68252 release queryable components don't need any special handling. To use Linq, register a component with one or more parameterless methods returning either IEnumerable or IQueryable as follows:

{code:C#} // Shared interfaces assembly public interface ISampleService { IEnumerable GetList() where T : class;

IQueryable<T> Query<T>() where T : class;

}

// Server implementation assembly ZyanComponent(typeof(ISampleService)) public class SampleService : ISampleService { // implementation details are omitted }

// Client assembly using (var connection = new ZyanConnection(url)) { var proxy = connection.CreateProxy(); var query =     from c in proxy.Query()     where c.BirthDate > DateTime.Today.AddYears(-18)     select c; ... } {code:C#}

The following section only applies to release:62104 release and is subject for removal.

Queryable component should implement either IObjectSource or IEntitySource interface and be decorated with ZyanComponent attribute, as usual:

{code:c#} // Server implementation assembly ZyanComponent(typeof(IEntitySource)) class SampleService : IEntitySource { public IQueryable Get() where T : class { return MyDataContext.GetTable(); } .... }

// Client assembly var proxy = connection.CreateQueryableComponent(); var query = from c in proxy.Get() where c.Login == "yallie" select new { c.FirstName, c.LastName }; {code:c#}

1.5 Specifying component creation policy

Part creation policy is a strategy MEF uses to instantiate composable parts. Shared creation policy means that there will be not more than one instance per CompositionContainer, NonShared policy indicates that every export operation should create a new instance. Default policy is CreationPolicy.Any, which acts like CreationPolicy.Shared if import is not constrained to some specific creation policy.

Zyan host delegates part lifetime management to MEF container, but it prefers NonShared creation policy to emulate default SingleCall component behavior. If part creation policy is not specified or specified as CreationPolicy.Any, Zyan will assume that the component can be instantiated as a NonShared part.

You may wish to override part creation policy via PartCreationPolicy attribute. It can be freely mixed with ZyanComponentAttribute, ExportAttribute, or your own export attributes:

{code:c#} // This component will act like singleton MyComponent(typeof(ISampleService)), PartCreationPolicy(CreationPolicy.Shared) class SampleService : ISampleService { ... } {code:c#}

Client-side: ZyanConnection + MEF

Client-side integration will be implemented as custom MEF catalog linked to ZyanConnection instance or factory.

Further ideas

The goal is to make distributed application truly modular and loosely coupled. Zyan bootstrapper uses MEF to assemble application on both sides and provides communication infrastructure. Client and server components can be made totally independent of communication layer.

TODO:

Server-side

Server loader class

Declarative hosts (endpoints)

Declarative serialization handlers

Component categories (to filter components for the certain host)

Client-side

Client bootstrapper

Declarative connections

Declarative call interceptors

Declarative serialization handlers

Clone this wiki locally