5
\$\begingroup\$

I'm trying to create a "perfect" generic solution structure for MVC (5.2.3.0) projects using MEF.

Currently my solution looks like this.

solution structure

  • Core layer contains generic classes that could be used in any project
  • Data layer contains a ApplicationContext (: DbContext) and models
  • Repositories layer contains Repository classes to access data
  • Service layer contains business rules
  • TreeOfMana is the presentation (MVC)
  • Dependencies is where I'm adding all interfaces (IUser, IRepository, IApplicationContext...)

They don't know each other

layer dependencies

The layers "export" their DLLs on the Post Build event to the presentation bin folder.

copy "$(TargetFileName)" "$(SolutionDir)$(SolutionName)\bin"

This allows me to load assemblies and add to MEF

public static class MefConfig
{
    public static CompositionContainer Container { get; private set; }

    public static void Register()
    {
        var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "TreeOfMana*.dll", SearchOption.AllDirectories)
            .Where(o => !o.Replace(AppDomain.CurrentDomain.BaseDirectory, "").Contains("obj\\"));

        var catalogAggregator = new AggregateCatalog();

        foreach (var file in files)
        {
            catalogAggregator.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFrom(file)));
        }

        Container = new CompositionContainer(catalogAggregator);
    }
}

To wire-up with MVC I'm using a custom controller factory. For that first I need to export the controller:

public class ControllerExportAttribute : ExportAttribute
{
    public ControllerExportAttribute(Type concreteType)
        : base(concreteType.FullName, typeof(IController))
    {

    }
}

[ControllerExportAttribute(typeof(HomeController))]
[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class HomeController : Controller { }

By exporting as IController and having the contract name I can find it on the exports list.

My ControllerFactory is:

public class MefControllerFactory : DefaultControllerFactory
{
    private readonly CompositionContainer compositionContainer;

    public MefControllerFactory(CompositionContainer compositionContainer)
    {
        this.compositionContainer = compositionContainer;
    }

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        IController result = null;

        var export = compositionContainer.GetExports(typeof(IController), null, controllerType.FullName).SingleOrDefault();

        if (null != export)
            result = export.Value as IController;

        if (null != result)
            compositionContainer.SatisfyImportsOnce(result);

        return result;
    }
}

The controller factory finds the exported controller where the contract name ends with the controller name requested.

Question

I'm looking for advice on how to separate the layers, how to properly use MEF on an architecture like this.

  • Does this structure I created look decent?
  • Does it make sense?
  • Should I change something?
  • Am I going mad?
\$\endgroup\$
2
\$\begingroup\$

Security

MEF is a lightweight framework for discovering plugins at runtime. Be wary that plugins are loaded using the same security and evidence of the container code. Anyone deploying malicious TreeOfMana*.ddl in your output folder has the same access rights to the system as your application.

var files = Directory.GetFiles(
    AppDomain.CurrentDomain.BaseDirectory, "TreeOfMana*.dll",
        SearchOption.AllDirectories)
            .Where(o => !o.Replace(AppDomain.CurrentDomain.BaseDirectory,
                 "").Contains("obj\\"));

MAF comes with built-in functionality to provide separate and limited permissions for addins. But I'm not sure whether or to which degree this framework is still supported. If you keep using MEF, make sure no malicious code could get access to the system. Some considerations:

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.