Fluent Definition Provider

The fluent definition provider enables parts to be defined programmatically using ordinary code, with the help of calling a sequence of methods on the provider. The method calls can be chained in a continuous sequence to provide a fluent experience. The fluent experience also gives a context aware intellisense experience, by only exposing valid methods for the current location of the call chain.

There are two types of contexts that the provider is aware of. The first one is for defining an export and the second one if for defining an import. Both contexts are entered by calling the appropriate methods on the provider. Each of the methods uses lambda expressions to isolate which member that is being exported or imported.

The methods that are available when defining an import are.

Name Description
AddRequiredMetadata Adds the name of a required metadata item.
AllowDefault Gets a value indicating whether the property or field will be set to its type's default value when an export with the contract name is not present in the container.
IsPrerequisite Sets a value indicating whether the import definition is required to be satisfied before a part can start producing exported objects.
IsRecomposable Sets a value indicating whether the import definition can be satisfied multiple times.
WithContract The contract attribute is the name of the contract which the import should conform to. There are two overloads of this method. The first accepts a plain string and the second one a type. If the type overload is used then the AssemblyQualifiedName will be extracted from the type and used as the contract.


The methods that are available when defining an export are.

Name Description
AddMetadata Adds metadata with a specified name and value.
WithContract The contract attribute is the name of the contract which the import should conform to. There are two overloads of this method. The first accepts a plain string and the second one a type. If the type overload is used then the AssemblyQualifiedName will be extracted from the type and used as the contract.

Defining imports and exports

The whole idea of the fluent definition provider is to define the exports and imports using nothing but ordinary code. In the following example the SerivceHost class contains a property called Service which will act as the import. The MessageService class is the export.

public class ServiceHost
{
    public Export<IMessageService> Service { get; set; }
}


public class MessageService : IMessageService
{
    public void Execute()
    {
    }
}

The first thing that needs to be done is to create an instance of the provider itself.

var provider = 
    new FluentDefinitionProvider();

Once the provider has been instantiated the import can be defined with the help of the Import method and a lambda expression which points to the member of the type which should be treated as an import

provider
    .Import<ServiceHost>(s => s.Service)
    .WithContract("NameOfContract");

The import is assigned a contract called “NameOfContract” and additional methods are also available in this context to further add information, such as default behavior and required metadata, to the import definition. Imports can be properties and fields of a type, including static member.

The export is defined by calling the Export method on the provider and supplying the type and lambda expression, just like an import

provider
    .Export<MessageService >(m => m)
    .WithContract("NameOfContract");

Notice that in the lambda expression, the input is just returned back out. This tells the provider that the entire type should be used as the export. Exports can be types, properties, fields and methods and either instance of static members.

When exporting a method you have to provide dummy values in the method call so that the provider can tell which method overload that should be exported. The values will be ignored and only serves the purpose of identifying the overload.

Just like the import, the export was assigned a contract called “NameOfContract” and additional methods are available to associate metadata with the method
The import and export were defined with separate calls on the provider for the sake of clarity, but it is possible continue the method chain for as long as we want. The previous example can be rewritten as follows

var provider = 
    new FluentDefinitionProvider();


provider
    .Import<ServiceHost>(s => s.Service)
    .WithContract("NameOfContract")
    .Export<MessageService >(m => m)
    .WithContract("NameOfContract");

Once the provider is configured you create the catalog by passing in the instance of the provider to the DefinitionProviderPartCatalog.

var fluentCatalog =
  new DefinitionProviderPartCatalog<FluentDefinitionProvider>(provider);

Another way of working with the fluent definition provider is to have the DefinitionProviderPartCatalog create the instance of the definition provider and access it using the Provider property of the catalog.

var fluentCatalog =
    new DefinitionProviderPartCatalog<FluentDefinitionProvider>();


fluentCatalog.Provider
    .Import<ServiceHost>(s => s.Service)
    .WithContract("NameOfContract")
    .Export<MessageService >(m => m)
    .WithContract("NameOfContract");

Last edited Mar 1, 2009 at 3:34 PM by TheCodeJunkie, version 3

Comments

cphoton Nov 11, 2009 at 5:31 PM 
Update #3: If I add this method to the ProviderComposablePart class, composing works:

public override void Activate()
{
OnComposed();
}

cphoton Nov 11, 2009 at 5:09 PM 
Additional note: after some debugging, I noticed that ProviderComposablePart.SetImport() will not assign imported values before the part is composed.

I believe the problem is that ProviderComposablePart.SetImport() is not being called from Activate() once the part is composed (in fact, there is no ProviderComposablePart.Activate() method defined, so it uses ComposablePart.Activate() which is empty).

If you look at the default MEF implementation in ReflectionComposablePart.Activate(), there is a call to set the imports again (SetNonPrerequisiteImports()).

cphoton Nov 11, 2009 at 1:41 PM 
Hi,

Is this functionality supposed to be working with MEF Preview 8 / 7 ? I have tried this example but trying to compose parts has no effect.

Another issue is that the Export<> type is not recognized on the following line (I am using VS 2008 with SP 3.5):

public Export<IMessageService> Service { get; set; }

akashchopra Aug 27, 2009 at 2:55 PM 
In the above example, how would I handle the case where ServiceHost requires a constructor parameter import? i.e. is there a fluent equivalent of adding an [Import] attribute to the constructor parameter?

Thanks
Akash