MEF is actually a good way for such an application architecture. Saying "not so many applications use MEF" is not quite correct, because the (probably) largest and mostly known application using MEF is Visual Studio itself (starting with version 12.0 - 2013).
Now, there is some misunderstanding about MEF. There are 3 (well, three-and-a-half) versions (flavors) of MEF available. This often confuses people.
Let me try to explain:
- MEF 1.0, also known as .NET Framework MEF. Initially released with .NET Framework 4.0; namespace
System.ComponentModel.Composition
.
- pros: a part of the .NET Framework; very flexible and dynamic
- cons: (relatively) slow; no further development
- MEF 2.0, also known as NuGet MEF. Microsoft wanted a much faster version for Windows Phone apps and didn't need that fully dynamic approach. First released for mobile platforms only, then made available for other frameworks. It can be obtained via NuGet or using .NET Core FX. Namespace
System.Composition
.
- pros: fast; now a part of .NET Core FX
- cons: relatively "static"; poor startup performance
- MEF 1.0+, sometimes wrongly referred to as MEF 2.0. This was the MEF 1.0 update released with .NET Framework 4.5. Pros and cons see MEF 1.0.
- VS-MEF, a special MEF flavor that is used in Visual Studio. Can be obtained via NuGet; also see GitHub. Namespace
Microsoft.VisualStudio.Composition
.
- pros: combines good performance of MEF 2.0 with almost the same flexibility as MEF 1.0; is under active development
- cons: has no dynamic recomposition
These MEF versions have of course some differences, but any of those versions can be used for a dynamic plugin-based application. Depending on your needs, you can choose one of them. Nowadays, I would recommend either MEF 2.0 (NuGet MEF) or VS-MEF. I have a practical experience with VS-MEF and am totally satisfied with its feature set and performance.
However, MEF is not the only way. There is always an option for creating a home-grown platform for plugin-based applications. In fact, many companies go this way.
Another possibility (if you have an IoC based architecture) is to use some IoC container available (like Unity) and manually extend its functionality when needed.