Skip to content

jasonwoods-7/Vandelay

Repository files navigation

Vandelay Maintainability

What is Vandelay?

Vandelay is an add-in to the Fody IL weaving project framework. Vandelay is an Importer\Exporter.

The NuGet Package NuGet Status

https://www.nuget.org/packages/Vandelay.Fody/

PM> Install-Package Fody
PM> Install-Package Vandelay.Fody

What type of problem does Vandelay solve?

Vandelay aims to solve the Open Closed Principle from the SOLID principles. Behind the scenes, it uses the Managed Extensibility Framework (MEF) to provide importing\exporting.

However, MEF can be a quite verbose framework, and it may be easy to forget to mark a class as exportable. Vandelay seeks to simplify using MEF.

How does using Vandelay simplify my exporting needs?

Vandelay will automatically mark your classes with the appropriate Export attribute.

What you write:

[assembly: Vandelay.Exporter(typeof(IFoo))]

public interface IFoo {}

public class Foo : IFoo {}
What gets compiled:
public interface IFoo {}

[Export(typeof(IFoo))]
public class Foo : IFoo {}

I have abstract classes which implement the interface type I want to have exported. Will they be exported too?

No, Vandelay only exports instances of classes. Abstract classes will be skipped.

What if I want to export the types manually?

Not a problem. Vandelay first checks whether the type is already exported and only exports unexported classes.

How does using Vandelay simplify my importing needs?

Vandelay will automatically add the code to import a specified type.

What you write:

public class SomeClass
{
  public void SomeMethod()
  {
    var foos = Vandelay.Importer.ImportMany<IFoo>("");
  }
}
What gets compiled:
public class SomeClass
{
  public void SomeMethod()
  {
    var foos = IFooRetriever.IFooRetriever(new object[0]);
  }
}

internal sealed class ExportValueProvider
{
  private readonly object _value;

  ExportValueProvider(object value)
  {
    this._value = value;
  }

  public object GetValue()
  {
    return this._value;
  }
}

internal static class CompositionBatchHelper
{
  public static CompositionBatch CreateCompositionBatch(object[] exports)
  {
    var compositionBatch = new CompositionBatch();
    for (var i = 0; i < exports.Length; i++)
    {
      var obj = exports[i];
      var type = obj.GetType();
      compositionBatch.AddExport(new Export(
        AttributedModelService.GetContractName(type),
        new Dictionary<string, object>
        {
          ["ExportTypeIdentity"] = AttributedModelService.GetTypeIdentity(type)
        }, new Func<object>(new ExportValueProvider(obj).GetValue)));
    }
    return compositionBatch;
  }
}

internal sealed class IFooRetriever
{
  [ImportMany(typeof(IFoo))]
  private IFoo[] _imports;

  private IFooRetriever(object[] exports)
  {
    using (var aggregateCatalog = new AggregateCatalog)
    {
      aggregateCatalog.Catalogs.Add(new DirectoryCatalog(           // *
        Directory.GetParent(new Uri(Assembly.GetExecutingAssembly()
        .EscapedCodeBase).LocalPath).FullName));
      using (var compositionContainer = new CompositionContainer(
        aggregateCatalog, new ExportProvider[0]))
      {
        compositionContainer.Compose(CreateCompositionBatch(exports));
        compositionContainer.ComposeParts(this);
      }
    }
  }

  public static IFoo[] IFooRetriever(object[] exports)
  {
    return new IFooRetriever(exports)._imports;
  }
}

What does the string searchPatterns argument in ImportMany do?

It allows you to specify file patterns to match when searching the directory for files. Multiple entries are separated by a vertical pipe (|).

For example:

Vandelay.Importer.ImportMany<IFoo>("*.exe|*.dll")

changes the Retriever code (above at *) to

var catalogPath = Directory.GetParent(new Uri(
  Assembly.GetExecutingAssembly()
  .EscapedCodeBase).LocalPath).FullName;
aggregateCatalog.Catalogs.Add(new DirectoryCatalog(
  catalogPath, "*.exe"));
aggregateCatalog.Catalogs.Add(new DirectoryCatalog(
  catalogPath, "*.dll"));

What does the object[] exports argument in ImportMany do?

This allows you to specify objects you want your imported classes to use.

For example, given the following

public class FooWithImport : IFoo
{
  [Import]
  Bar MyBar { get; set; }
}

public class Importer
{
  public IReadOnlyList<IFoo> Imports { get; } =
    Vandelay.Importer.ImportMany<IFoo>("*.dll", new Bar());
}

the Imports collection will contain a FooWithImport object with the MyBar property filled in.

Are there limitation to what I can export?

  1. Objects which contain a string in the constructor aren't currently working when you inline the object array. The current work-around would be to create the array before the call to ImportMany such as:

    var exports = new object[]
    {
      "string export",
      42,
      new Bar()
    };
    
    var imports = Vandelay.Importer.ImportMany<IFoo>("*.dll", exports);
  2. You cannot currently specify the contract name or type, so if you had an import expecting a type of IBar you would have to explicitly specify the contract name and type, such as:

    [Import("Fully.Qualified.Namespace.Bar", typeof(Bar))]
    IBar MyBar { get; set; }
  3. If you're using .Net Core and your assembly's runtime path contains a #, Vandelay won't be able to find the assemblies to scan.

Will Vandelay be my latex salesman?

No.