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.
- 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
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?