Currently, I'm developing an extension to Visual Studio 2010 using MEF and I need to initialize my global state. I'm trying to do it in Package.Initialize method
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0.0.0", IconResourceID = 400)]
[Guid("1AF4B41B-F2DF-4F49-965A-816A103ADFEF")]
public sealed class MyPackage : Package
{
protected override void Initialize()
{
ContainerConfigurator.Configure();
ContainerConfigurator.IsInitialized = true;
base.Initialize();
}
}
Also I have a MEF classifier provider that uses this state
[Export(typeof(IClassifierProvider))]
[Name("This is my provider")]
[ContentType("DebugOutput")]
[ContentType("Output")]
public class MyClassifierProvider : IClassifierProvider
{
[Import]
private IClassificationTypeRegistryService _classificationRegistry = null; // MEF
public IClassifier GetClassifier(ITextBuffer textBuffer)
{
// This always false
if (!ContainerConfigurator.IsInitialized)
throw new InvalidOperationException();
return textBuffer.Properties.GetOrCreateSingletonProperty(() => new TypedClassifier(ServiceLocator.Current, _classificationRegistry));
}
}
Both of package and MEF classifier are in the same assembly. When I start debugging and place a breakpoint I see that this assemly is loaded. But MyClassifierProvider has been initialized before MyPackage.Initialize call. So I can't initialize my global state before any of MEF components is started. Can anyone explain why and how can I avoid that behavior?
Thanks
I've found the answer. It is necessary to add ProvideAutoLoad attribute
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.vsconstants(v=vs.80).aspx
http://dotneteers.net/blogs/divedeeper/archive/2008/03/23/LVNSideBar1.aspx
so the final class declaration is
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0.0.0", IconResourceID = 400)]
[Guid("1AF4B41B-F2DF-4F49-965A-816A103ADFEF")]
[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]
public sealed class MyPackage : Package
Related
I have a custom Roslyn analyzer which inherits from DiagnosticAnalyzer. It is packed in VSIX extension which contains custom Package class. I'd like to pass an instance of a class with the settings (CodeAnalysisSettings instance) from the package to my DiagnosticAnalyzer.
I've tried to use MEF for that purpose. I've registered an instance of my settings class in VS Package using the following code:
protected override void Initialize()
{
base.Initialize();
IComponentModel componentModel = Package.GetGlobalService(typeof(SComponentModel)) as IComponentModel;
new CompositionContainer(componentModel.DefaultCatalog).ComposeExportedValue(
new CodeAnalysisSettings(...));
}
Analyzer looks as follows:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MyAnalyzer : DiagnosticAnalyzer
{
[Import]
public CodeAnalysisSettings Settings { get; set; }
}
Settings class:
[Export]
public class CodeAnalysisSettings
{
public CodeAnalysisSettings()
{
}
public bool RecursiveAnalysisEnabled { get; }
}
For some reason Settings property is not imported - its value is always null.
Please help.
I've ended up using a service locator (CommonServiceLocator package) that consumes MEF container as a source.
Since VS infrastructure doesn't allow adding new registrations (IComponentModel.DefaultCatalog throws an exception which tells that this functionality is not supported anymore), I've created a new container inside Package.Initialize():
var container = new CompositionContainer(CompositionOptions.Default, componentModel.DefaultExportProvider);
container.ComposeExportedValue<CodeAnalysisSettings>(new CodeAnalysisSettings(...));
var serviceLocator = new MefServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => serviceLocator);
Roslyn diagnostic consumes this dependency:
var settings = CodeAnalysisSettings.Default;
if (ServiceLocator.IsLocationProviderSet)
settings = ServiceLocator.GetInstance<CodeAnalysisSettings>();
I'm starting learning MVVM cross, In the android app, I have a splash screen class:
[Activity(MainLauncher = true,
Label = "#string/app_name",
Theme = "#style/Theme.Splash",
NoHistory = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Portrait)]
public class SplashScreen : MvxSplashScreenActivity
{
public SplashScreen() : base(Resource.Layout.SplashScreen)
{
}
}
and this is the Setup class:
public class Setup : MvxAndroidSetup
{
protected Setup(Context applicationContext) : base(applicationContext)
{
}
protected override IMvxApplication CreateApp()
{
return null;
}
}
the problem is that the debugger doesn't hit the constructor of the Setup Class, instead I get "An unhandled exception" after the constructor of the splash screen
EDIT
I've already defined the App class in the PCL project:
public class App : MvxApplication
{
public override void Initialize()
{
base.Initialize();
}
also defined the AppStart:
public class AppStart : MvxNavigatingObject, IMvxAppStart
{
public async void Start(object hint = null)
{
//hardcoded login for this demo
//var userService = Mvx.Resolve<IUserDataService>();
//await userService.Login("gillcleeren", "123456");
ShowViewModel<MainViewModel>();
}
}
The main reason behind this project is to understand the sequence of code required and executed by MVVM Cross, so I provide the minimum code till it runs successfully without runtime errors.
Update
I have read your code again more thoroughly and I can see the issue now. You defined the constructor of the Setup class as protected, which makes it invisible for activation.
On MvvmCross for Android the magic happens inside MvxAndroidSetupSingleton class (see the source code here) which searches for the Setup type you defined. The FindSetupType method looks for your defined Setup class first and then inside the CreateSetup method Activator.CreateInstance is used to build the Setup instance. The CreateInstance method variant used however searches only for public constructors, which means it doesn't find your protected one. The result is that it cannot build the Setup class and crashes.
Original answer
The reason this happens is that you have no Core libary that would define the MvvmCross App class and would initialize other required setup. I suggest you to start with a simple tutorial or to look into the official sample projects to see what is necessary to make MvvmCross work in a Xamarin.Android app.
I am currently working on a new Prism project in C# and wanted to create an NLog Logger in my Bootstrapper class. Unfortunately, no matter what I do it refuses to build, telling me...
'Logger.Logger()' is inaccessible due to its protection level.
The fragment of my code in question is listed below...
namespace PrismTemplate
{
public class Bootstrapper : UnityBootstrapper
{
#region Method Overrides
protected override ILoggerFacade CreateLogger()
{
//ILoggerFacade logger = new NLogLogger();
ILoggerFacade logger = new Logger();
logger.Log("Prism Template Logger was Created.", Category.Info, Priority.None);
return logger;
//return new Logger();
//return null;
}
protected override DependencyObject CreateShell()
{
return new ShellWindow();
}
protected override void InitializeShell()
{
base.InitializeShell();
///Assign the shell which is a window to the apps main window, and show it
App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}
#endregion Method Overrides
}
}
I downloaded both Prism and NLog via Nuget in Visual Studio. Any help attacking this problem would be most appreciated.
Thanks,
Tesnich
Due to the heavy configuration of loggers, NLog has chosen to delegate the log creation responsibility to the sealed LogManager class.
You can use either to get an instance:
LogManager.GetCurrentClassLogger()
or
LogManager.GetLogger(string name)
The WPF application I am working on will have numerous add-ins to extend its functionality. Each add-in will consist of one or several assemblies (usually a "main" add-in assembly with separate assemblies for presentation-layer components and views) but will be treated by the application as a single Prism module. Prism will be used to discover and load the add-ins, with MEF used to ensure that the Prism module can access dependencies in the add-in's related assemblies. The Prism module's Initialize method will be responsible for, amongst other things, configuring the IoC container (in this case, Unity). In a deployed scenario, this will all be loaded and managed with a MefBootstrapper at startup.
My problem occurs when trying to unit test an add-in. To keep the add-in code semi-isolated from the main application, each add-in will also have its own unit test assemblies. One of these test assemblies will be responsible for checking registration of services with the IoC container. In the test scenario, I don't want to use a bootstrapper to load the Prism modules as they have dependencies that I don't want to introduce to my test assemblies. I have therefore written my test fixture base class such that it creates its own MefModuleManager to load the module to be tested.
ResolutionTestBase.cs
public abstract class ResolutionTestBase
{
[ClassInitialize]
public static void TestFixtureInitialise(TestContext context)
{
// Create the main resolution container.
var container = new UnityContainer();
// Install the service locator.
var locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
}
// Here go some helper methods for performing resolution tests.
protected IUnityContainer Container
{
get { return ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer; }
}
}
AddInResolutionTestBase.cs
public abstract class AddInResolutionTestBase:ResolutionTestBase
{
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
var aggregateCatalog = new AggregateCatalog();
foreach (var testAssembly in TestAssemblies)
{
aggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
// Load module manager.
var container = new CompositionContainer(aggregateCatalog);
var serviceLocator = new MefServiceLocatorAdapter(container);
var parts = new DownloadedPartCatalogCollection();
var moduleInitialiser = new MefModuleInitializer(serviceLocator, Logger, parts, aggregateCatalog);
var moduleManager = new MefModuleManager(moduleInitialiser, ModuleCatalog, Logger);
moduleManager.ModuleTypeLoaders = new[] { new MefFileModuleTypeLoader() };
moduleManager.Run();
}
protected static ILoggerFacade Logger { get; private set; }
protected abstract IModuleCatalog ModuleCatalog { get; }
protected abstract IEnumerable<Assembly> TestAssemblies { get; }
}
For the add-ins, they have a stand-alone class in their main assembly to implement the requirements of a Prism module, and Unity extensions to configure the container.
Module.cs
[ModuleExport("AddInModule", typeof(Module), InitializationMode = InitializationMode.OnDemand)]
public class Module : IModule
{
private readonly IEnumerable<IUnityContainerExtensionConfigurator> _extensions;
[ImportingConstructor]
public Module([ImportMany]IEnumerable<IUnityContainerExtensionConfigurator> extensions)
{
_extensions = extensions;
}
public void Initialize()
{
// Load the dependency injection container.
var container = ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer;
if (container != null)
{
foreach (var extension in _extensions)
{
container.AddExtension((UnityContainerExtension) extension);
}
}
}
}
ContainerInstallerExtension.cs (in the add-in's main assembly)
[Export(typeof(IUnityContainerExtensionConfigurator))]
public class ContainerInstallerExtension : UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}
PresentationInstallerExtension.cs (in the add-in's presentation assembly)
[Export(typeof(IUnityContainerExtensionConfigurator))]
public class PresentationInstallerExtension:UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}
AddInResolutionTest.cs (in the add-in's IoC test assembly)
[TestClass]
public class AddInResolutionTest : AddInResolutionTestBase
{
private IEnumerable<Assembly> _testAssemblies;
private IModuleCatalog DoGetModuleCatalog()
{
var moduleInfo = new ModuleInfo("AddInModule", typeof (Module).AssemblyQualifiedName)
{
InitializationMode = InitializationMode.WhenAvailable,
Ref = typeof (Module).Assembly.CodeBase
};
return new ModuleCatalog(new[] {moduleInfo});
}
protected override IModuleCatalog ModuleCatalog
{
get { return DoGetModuleCatalog(); }
}
protected override IEnumerable<Assembly> TestAssemblies
{
get { return _testAssemblies ?? (_testAssemblies = new[] { typeof(ContainerInstallerExtension).Assembly, typeof(PresentationInstallerExtension).Assembly }); }
}
[TestMethod]
public void ResolveSomeService()
{
// perform resolution test here.
}
}
Of note with the resolution test fixture, the "test assemblies" are linked to the IoC test assembly with project references and referred to directly by type (rather than using a directory-scan catalog) so I could avoid having to use a post-build event to copy assemblies to a common folder for testing.
When I run the unit tests (as-is), I get an exception indicating the module manager failed to load the Prism module:
Initialization method AddInResolutionTest.TestInitialise threw exception. Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Failed to load type for module AddInModule.
If this error occurred when using MEF in a Silverlight application, please ensure that the CopyLocal property of the reference to the MefExtensions assembly is set to true in the main application/shell and false in all other assemblies.
Error was: Object reference not set to an instance of an object.. ---> System.NullReferenceException: Object reference not set to an instance of an object..
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo)
--- End of inner exception stack trace ---
at Microsoft.Practices.Prism.Modularity.ModuleManager.HandleModuleTypeLoadingError(ModuleInfo moduleInfo, Exception exception)
at Microsoft.Practices.Prism.Modularity.ModuleManager.IModuleTypeLoader_LoadModuleCompleted(Object sender, LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(ModuleInfo moduleInfo, Exception error)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo)
at Microsoft.Practices.Prism.Modularity.ModuleManager.BeginRetrievingModule(ModuleInfo moduleInfo)
at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModuleTypes(IEnumerable`1 moduleInfos)
at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesWhenAvailable()
at Microsoft.Practices.Prism.Modularity.ModuleManager.Run()
at AddInResolutionTestBase.TestInitialise() in AddInResolutionTestBase.cs: line xx
At the point of calling moduleManager.Run() nothing in my code is null so it is not clear to me what the "real" problem is.
I have tried various changes to resolve the problem including:
calling moduleManager.LoadModule() instead of moduleManager.Run() in AddInResolutionTestBase.cs
manipulating the State of the ModuleInfo created in AddInResolutionTest to bypass the problem in the module manager
Any other changes I've made have resulted in different errors, but still indicate a problem with the module manager trying to load the Prism module.
Is there some additional step required to correctly configure the module manager to be able to load modules in this way, bearing in mind that some of the usual overhead (such as the logger) is not required for the unit tests?
With the aid of a de-compiler, I was able to figure out the "missing pieces" and make some changes to ensure that all the required components are registered/installed for the module manager to be able to initialise the module(s) for testing. For anyone who is interested:
public abstract class AddInResolutionTestBase:ResolutionTestBase
{
private CompositionContainer _container;
private IModuleCatalog _moduleCatalog;
private IEnumerable<object> _testEntities;
private IEnumerable<ModuleInfo> _testModuleInformation;
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
AggregateCatalog = CreateAggregateCatalog();
ConfigureAggregateCatalog();
AggregateCatalog = DefaultPrismServiceRegistrar.RegisterRequiredPrismServicesIfMissing(AggregateCatalog);
ConfigureContainer();
// Initialise modules to be tested.
CompositionContainer.GetExportedValue<IModuleManager>().Run();
}
#region Protected Methods
protected virtual void ConfigureAggregateCatalog()
{
var testAssemblies = TestEntities.OfType<Assembly>();
foreach (var testAssembly in testAssemblies)
{
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
if (TestEntities.Any(entity => entity is System.Type))
{
var catalog = new TypeCatalog(TestEntities.OfType<System.Type>());
AggregateCatalog.Catalogs.Add(catalog);
}
}
protected virtual void ConfigureContainer()
{
CompositionContainer.ComposeExportedValue<ILoggerFacade>(Logger);
CompositionContainer.ComposeExportedValue<IModuleCatalog>(ModuleCatalog);
CompositionContainer.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(CompositionContainer));
CompositionContainer.ComposeExportedValue<AggregateCatalog>(AggregateCatalog);
}
protected virtual AggregateCatalog CreateAggregateCatalog()
{
return new AggregateCatalog();
}
protected virtual CompositionContainer CreateContainer()
{
return new CompositionContainer(AggregateCatalog);
}
protected virtual IModuleCatalog CreateModuleCatalog()
{
return new ModuleCatalog(TestModuleInformation);
}
protected abstract IEnumerable<object> GetTestEntities();
protected abstract IEnumerable<ModuleInfo> GetTestModuleInformation();
#endregion
#region Protected Properties
protected AggregateCatalog AggregateCatalog { get; set; }
protected CompositionContainer CompositionContainer
{
get { return _container ?? (_container = CreateContainer()); }
}
protected static ILoggerFacade Logger { get; private set; }
protected IModuleCatalog ModuleCatalog
{
get { return _moduleCatalog ?? (_moduleCatalog = CreateModuleCatalog()); }
}
protected IEnumerable<object> TestEntities
{
get { return _testEntities ?? (_testEntities = GetTestEntities()); }
}
protected IEnumerable<ModuleInfo> TestModuleInformation
{
get { return _testModuleInformation ?? (_testModuleInformation = GetTestModuleInformation()); }
}
#endregion
}
This test base class now mimics to some extent what normally goes on in the boostrapper when the application normally starts. The (resolution) tests in each of the add-ins now only need to provide a list of the (exported) container extensions and module information for the Prism module that represents the add-in (in addition to the actual resolution tests!)
I've implemented a very small plugin system in C# with MEF. But my plugins won't be loaded. In the Aggregate-Catalog I can see my plugin listed. But, after I'll compose these parts, there isn't my plugin in the plugin list, what I'm doing wrong?
Here's a snippet of my code:
Plugin-Loader:
[ImportMany(typeof(IFetchService))]
private IFetchService[] _pluginList;
private AggregateCatalog _pluginCatalog;
private const string pluginPathKey = "PluginPath";
...
public PluginManager(ApplicationContext context)
{
var dirCatalog = new DirectoryCatalog(ConfigurationManager.AppSettings[pluginPathKey]);
//Here's my plugin listed...
_pluginCatalog = new AggregateCatalog(dirCatalog);
var compositionContainer = new CompositionContainer(_pluginCatalog);
compositionContainer.ComposeParts(this);
}
...
And here, the plugin itself:
[Export(typeof(IFetchService))]
public class MySamplePlugin : IFetchService
{
public MySamplePlugin()
{
Console.WriteLine("Plugin entered");
}
...
}
You're doing this wrong. The ImportMany attribute of the _pluginList field doesn't make any sense, since the the plugin manager instance will be created by you, not by the DI container.
You have to create another class that will import all your plugins.
[Export]
class SomeClass
{
readonly IFetchService[] pluginList;
[ImportingConstructor]
public SomeClass([ImportMany(typeof(IFetchService))]IFetchService[] pluginList)
{
this.pluginList = pluginList;
}
}
Now, you can let the DI container compose an instance of this SomeClass for you. You will see that its pluginList field contains your plugin reference.