MEF can't find module's views when dll in subdirectory - wpf

I try to make a little application with Prism and MEF in order to learn how it works. I'm stuck on a fairly frustrating problem.
I would like to have a "Modules" subdirectory in my base app directory where I copy all the module's dll as a post build event.
These modules are MVVM app with View and ViewModel.
My problem is : When I copy my module's dll in the main app directory, the views are displayed in the shell, but when my modules are in the subdirectory, nothing is displayer.
My modules and their parts are found but according to fuslogvw the views can't be found :
* Assembly Binder Log Entry (27/11/2015 # 16:45:28) *
The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.
Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable C:\Users\mouarf\Downloads\Prism-Samples-Wpf-master\Prism-Samples-Wpf-master\HelloWorld\HelloWorld\bin\Debug\HelloWorld.vshost.exe
--- A detailed error log follows.
=== Pre-bind state information ===
LOG: DisplayName = ModuleB.resources, Version=1.0.0.0, Culture=en-US, PublicKeyToken=null
(Fully-specified)
LOG: Appbase = file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = HelloWorld.vshost.exe
Calling assembly : ModuleB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: C:\Users\mouarf\Downloads\Prism-Samples-Wpf-master\Prism-Samples-Wpf-master\HelloWorld\HelloWorld\bin\Debug\HelloWorld.vshost.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources.EXE.
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources/ModuleB.resources.EXE.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources.EXE.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources/ModuleB.resources.EXE.
LOG: All probing URLs attempted and failed.
I don't know why MEF look in "modules\en-US\", I think it's probably why it doesn't find any views, but I couldn't find how to specify otherwise.
My bootstrapper :
public class Bootstrapper : MefBootstrapper
{
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules");
DirectoryCatalog catalog = new DirectoryCatalog(path, "*.dll");
this.AggregateCatalog.Catalogs.Add(catalog);
}
protected override DependencyObject CreateShell()
{
return this.Container.GetExportedValue<MainWindow>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (MainWindow)this.Shell;
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
}
My modules :
[ModuleExport(typeof(ModuleAModule))]
public class ModuleAModule : IModule
{
IRegionManager _regionManager;
[ImportingConstructor]
public ModuleAModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.RegisterViewWithRegion(RegionNames.RightRegion, typeof(ViewA));
}
}
My views :
/// <summary>
/// Interaction logic for ViewA.xaml
/// </summary>
[Export]
public partial class ViewA : UserControl
{
public ViewA()
{
InitializeComponent();
}
}
My viewmodels :
[Export]
public class ViewAViewModel : BindableBase
{
private string _title = "Module A";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
}
Anyone ?
Edit :
Here's the solution for who whould like to take a look : HelloWorldPrismMef
Edit 2 :
The investigation still goes on, I discovered the really handy mefx ! So my problem seems to be :
[Part] ModuleA.ModuleAModule from: DirectoryCatalog (Path="Modules")
[Primary Rejection]
[Export] ModuleA.ModuleAModule (ContractName="Prism.Modularity.IModule")
[Import] ModuleA.ModuleAModule..ctor (Parameter="regionManager", ContractName="Prism.Regions.IRegionManager")
[Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint contract name
ContractName Prism.Regions.IRegionManager
RequiredTypeIdentity Prism.Regions.IRegionManager n'a été trouvée.
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)
Does that mean that I need to Export a IRegionManager class ?

The log you have posted is for an attempt to load a resource .dll, something that MEF will never load (resource .dlls are used to store application resource information, like strings for internationalization). You should look for errors that do not mention resource .dlls.
Also, it seems to me you are attempting to edit the Prism Library HelloWorld example from GitHub. This particular example has tight coupling with ModuleA (by that I mean that ModuleA is used as a project dependency in HelloWorld) and to my knowledge you can not simply move the ModuleA.dll from the main folder to a modules folder and expect it to work.
My suggestion would be to add a new project, set that to output to a modules folder and see if that loads (leaving the ModuleA project alone). Or you could remove the reference from the HelloWorld project and use the post build event.
Now regarding the loading of modules from a directory, in my humble opinion, you are over complicating it. All you need is
AgregateCatalog.Catalogs.Add(new DirectoryCatalog(#".\Modules"));
Or presuming you have a convention that specifies a pattern for module file names that resembles AppName.Modules.[ModuleNameHere].dll (eg: AppName.Modules.LoginModule.dll you could use something like this to load the modules
AgregateCatalog.Catalogs.Add(new DirectoryCatalog(#".\Modules", "AppName.Modules.*Module.dll"));
Although this does not seem the case here, if you ever try to load modules from a zone deemed as untrustworthy, the default security policy is to ignore the module. This would happen if you attempt to run the application over a network connection like Windows Share. For this scenario you need to add these instructions to App.config
<runtime>
<loadFromRemoteSources enabled="true" />
</runtime>
These should be added after <startup /> section.
Hope this helps you.
Edit:
Does that mean that I need to Export a IRegionManager class ?
No, that's just complaining because mefx has not loaded the assembly that exports it (Prism.Wpf.dll I think it is called).
Personally I found mefx.exe to be cumbersome; I prefer the GUI version
Now regarding your code, I took a look at the GitHub repository and made some changes but not that many (had some issues with references with ModuleC so I had to remove and add again Prism.Mef & company):
Removed the PostBuildEvent from Infrastructure project
Changed the PostBuildEvent from the module projects. This needs some explaining:
all macros come appended with the directory delimiter "\" so you do not need to add it (I am reffering to $(OutDir)\Modules => $(OutDir)Modules).
COPY/XCOPY require the destination path to end with a delimiter or the destination path will be intepreted as a destinaiton directory ( $(OutDir)Modules => *$(OutDir)Modules* ).
Destination directory needs to exist (so first command should be MKDIR)
I also commented (lines that start with REM are comments) out the command that copies the .pdb because I do not think it is needed and added the /z flag to XCOPY.
Added ILoggerFacade as a dependency to demonstrate that the modules actually load. If you run the application from the Visual Studio Debugger, you will see some messages in the debug window.
Added <loadFromRemoteSources enabled="true" /> in App.config => <configuration /> => <runtime /> so I can run the app over a mounted partition where the project is stored.
All of this is in the PR.
Now regarding why it will not auto-display the views in the regions, I can not say yet. I will keep investigating during my free time, but you might have better luck asking Brian Lagunas as he is one of the developers of Prism.
Edit 2:
As I was looking at Brian Lagunas's profile I saw he answered this post that luckily solves the issue.
Will also add a PR to GitHub.

Related

Cannot access files in output directory when running under Desktop Bridge

In my WPF project, I have some JSON files that are set as Content/Copy to Output Folder. When running as standard WPF, I access them as follows and it works fine.
foreach (var config in Directory.GetFiles("HostConfigs", "*.json"))
But when I run the app under the Desktop Bridge using the packaging project, it throws the following exception
System.IO.DirectoryNotFoundException: 'Could not find a part of the path 'C:\WINDOWS\SysWOW64\HostConfigs'.'
Desktop Bridge projects don't automatically set your current directory to your project's output folder... they use Windows' default directory instead.
To fix this across your project, at the main launching point (App.xaml.cs), simply add the following...
public partial class App : Application
{
public App()
{
SetCurrentDirectory();
}
/// <summary>
/// Sets the current directory to the app's output directory. This is needed for Desktop Bridge, which
/// defaults to the Windows directory.
/// </summary>
private void SetCurrentDirectory()
{
// Gets the location of the EXE, including the EXE name
var exePath = typeof(App).Assembly.Location;
var outputDir = Path.GetDirectoryName(exePath);
Directory.SetCurrentDirectory(outputDir);
}
}

Registering Startup Class In Nancy Using AutoFac Bootstrapper

I've been reading through a lot of the Jabbr code to learn Nancy and trying to implement many of the same patterns in my own application. One of the things I can't seem to get working is the concept of an on application start class. The Jabbr code base has an App_Start folder with a Startup.cs file (here) in it with the following implementation.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
...
SetupNancy(kernel, app);
...
}
}
private static void SetupNancy(IKernel kernel, IAppBuilder app)
{
var bootstrapper = new JabbRNinjectNancyBootstrapper(kernel);
app.UseNancy(bootstrapper);
}
When I tried to do something similar to that in my project the Startup.cs file was just ignored. I searched the Jabbr code base to see if it was used anywhere but I wasn't able to find anything and the only differences I could see is Jabbr uses Ninject while I wanted to use AutoFac
Is there a way to register a startup class in nancy?
Take a look at my project over on GitHub, you'll be interested in the Spike branch and may have to unload the ChainLink.Web project to run I can't remember.
I had some trouble finding a way to configure the ILifetimeScope even after reading the accepted answer here by TheCodeJunkie. Here's how you do the actual configuration:
In the bootstrapper class derived from the AutofacNancyBootstrapper, to actually configure the request container, you update the ILifetimeScope's component registry.
protected override void ConfigureRequestContainer(
ILifetimeScope container, NancyContext context)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>();
builder.Update(container.ComponentRegistry);
}
The application container can be updated similarly in the ConfigureApplicationContainer override.
You should install the Nancy.Bootstrappers.Autofac nuget, inherit from the AutofacNancyBootstrapper type and override the appropriate method (depending on your lifetime scope requirements: application or request). For more info check the readme file https://github.com/nancyfx/nancy.bootstrappers.autofac
HTH
After following the advice from TheCodeJunkie you can use the Update method on the ILifetimeScope container parameter which gives you a ContainerBuilder through an Action:
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
container.Update(builder =>
{
builder.RegisterType<MyType>();
});
}

MEF and loading EntityTypeConfiguration in runtime

You cannot vote on your own post
0
Hi.
I am developing this (http://arg-co.com/SabteNam%20-%20Copy.zip) windows application, and for my DAL I use Entity Framework. But every single extension has its own EntityTypeConfiguration, so I decided to use [Import] and [Export] to add them in OnModelCreating method of my DbContext.The problem here is that, in 'SabteNamDbContext' class which is located on 'SabteNamDataAccess' library, the '_Configs' is not initialized so I cant iterate it and add its items to 'modelBuilder.Configurations'.
In the source code of 'SampleConfiguration' class, I commented out '[Export(typeof(IDbConfiguration))]' but even Uncommenting this part of code, do not cause application to work properly.
Intresting point is that, if I use the following code in 'Main' windows form, the '_Configs' would be initialized :
[ImportMany(typeof(IDbConfiguration))]
public IEnumerable<EntityTypeConfiguration<object>> _Configs { get; set; }
How can this be fixed ?
While I realize this is probably no longer of use to you, we use a variation on this model from OdeToCode, which I advise you read.
In our case, we have created an interface for our extensions in general, not just for the entity configuration like Scott did, which allows us not only to load the configurations, but also factory and test data per extension, add new permission types to the core application, etc.
Our OnModelCreating looks something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// load core object mappings
modelBuilder.Configurations.Add(new UserConfiguration());
modelBuilder.Configurations.Add(new PermissionConfiguration());
// get plugin assemblies
var catalog = new DirectoryCatalog("bin");
var container = new CompositionContainer(catalog);
container.ComposeParts();
var plugins = container.GetExportedValues<IPlugin>();
// load plugin object mappings
foreach (IPlugin plugin in plugins)
{
plugin.RegisterDomainEntities(modelBuilder.Configurations);
}
base.OnModelCreating(modelBuilder);
}

Caliburn.Micro + MEF + SQL CE together produce exception Cannot open 'Db\Some.sdf'. Provider 'System.Data.SqlServerCe.3.5' not installed

Hi I am really confuse with this behavior. I use Caliburn.Micro as MVVM framework in my WPF app, MEF is used on export view models classes.
Now I need use SQL compact DB in my app. So I create *dbml file with sqlmetal, because on DB access I would like to use LINQ TO SQL.
First I create simple class which do CRUD operations DB.
Here is it:
public interface IDbManager{}
[Export(typeof(IDbManager))]
public class DbManager : IDbManager
{
//_dc is DataContext class
private Spiri_SQL_CE_DB _dc;
public DbManager()
{
//string connStr = System.Configuration.ConfigurationManager.AppSettings["connstr"];
//_dc = new Spiri_SQL_CE_DB(connStr);
_dc = new Spiri_SQL_CE_DB(#"Db\Spiri_SQL_CE_DB.sdf");
}
}
This class is used in view model class which is WPF window.
[Export(typeof(IArchiveViewModel))]
public class ArchiveViewModel :Screen,IArchiveViewModel
{
private IDbManager _dbManager;
[ImportingConstructor]
public ArchiveViewModel(IDbManager dbManager)
{
_dbManager = dbManager;
}
}
And this window I open from screen with WindowManager class.
[Export(typeof(IMessengerViewModel))]
public class MessengerViewModel : Screen, IMessengerViewModel
{
private IWindowManager _windowManager;
[ImportingConstructor]
public MessengerViewModel(IWindowManager windowManager)
{
_windowManager = windowManager;
OpenArchive();
}
public void OpenArchive()
{
var w = IoC.Get<IArchiveViewModel>();
_windowManager.ShowWindow(w);
}
If I run app I get this error:
The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) Cannot open 'Db\Spiri_SQL_CE_DB.sdf'. Provider 'System.Data.SqlServerCe.3.5' not installed.
Resulting in: An exception occurred while trying to create an instance of type 'Spirit.DbManager.DbManager'.
Resulting in: Cannot activate part 'Spirit.DbManager.DbManager'.
Element: Spirit.DbManager.DbManager --> Spirit.DbManager.DbManager --> AssemblyCatalog (Assembly="Spirit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
Resulting in: Cannot get export 'Spirit.DbManager.DbManager (ContractName="Spirit.DbManager.IDbManager")' from part 'Spirit.DbManager.DbManager'.
Element: Spirit.DbManager.DbManager (ContractName="Spirit.DbManager.IDbManager") --> Spirit.DbManager.DbManager --> AssemblyCatalog (Assembly="Spirit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
}
I am really confuse with this error:
1) Cannot open 'Db\Spiri_SQL_CE_DB.sdf'. Provider 'System.Data.SqlServerCe.3.5' not installed.
Beacause:
I use Window 7 x64, I have installed SQL Server CE for .NET4.0, .NET3.5
WPF use .NET4.0 and is x86
In WPF I have reference on assembly system.data.sqlserverce.dll
I create small repo project but I get same error:
Repo project you can find here: http://netload.in/dateiy4s4jdPyCj/DbTest.7z.htm
I think problem code is here:
_dc = new Spiri_SQL_CE_DB(#"Db\Spiri_SQL_CE_DB.sdf");
Spiri_SQL_CE_DB class is from *.dbml file.
I create view model class without MEF I get the same error!
So I try use DbManager class in WPF app without Calibur.Micro and MEF and it works good.
I really don’t know what is bad, I search with google 2 hours but any advice don’t solve this error.
If anybody can help I would be very glad.
Reinstall the 3.5 SP2 MSI, and if you have a x64 system, install both the x86 and x64 MSIs: http://www.microsoft.com/downloads/en/details.aspx?familyid=E497988A-C93A-404C-B161-3A0B323DCE24&displaylang=en

silverlight 4, dynamically loading xap modules

I know that it is possible to load xap modules dynamically using Prism or MEF framework. However, I'd like not to use those frameworks; instead load my xap files manually. So, I created the following class (adapted from internet):
public class XapLoader
{
public event XapLoadedEventHandler Completed;
private string _xapName;
public XapLoader(string xapName)
{
if (string.IsNullOrWhiteSpace(xapName))
throw new ArgumentException("Invalid module name!");
else
_xapName = xapName;
}
public void Begin()
{
Uri uri = new Uri(_xapName, UriKind.Relative);
if (uri != null)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += onXapLoadingResponse;
wc.OpenReadAsync(uri);
}
}
private void onXapLoadingResponse(object sender, OpenReadCompletedEventArgs e)
{
if ((e.Error == null) && (e.Cancelled == false))
initXap(e.Result);
if (Completed != null)
{
XapLoadedEventArgs args = new XapLoadedEventArgs();
args.Error = e.Error;
args.Cancelled = e.Cancelled;
Completed(this, args);
}
}
private void initXap(Stream stream)
{
string appManifest = new StreamReader(Application.GetResourceStream(
new StreamResourceInfo(stream, null), new Uri("AppManifest.xaml",
UriKind.Relative)).Stream).ReadToEnd();
XElement deploy = XDocument.Parse(appManifest).Root;
List<XElement> parts = (from assemblyParts in deploy.Elements().Elements()
select assemblyParts).ToList();
foreach (XElement xe in parts)
{
string source = xe.Attribute("Source").Value;
AssemblyPart asmPart = new AssemblyPart();
StreamResourceInfo streamInfo = Application.GetResourceStream(
new StreamResourceInfo(stream, "application/binary"),
new Uri(source, UriKind.Relative));
asmPart.Load(streamInfo.Stream);
}
}
}
public delegate void XapLoadedEventHandler(object sender, XapLoadedEventArgs e);
public class XapLoadedEventArgs : EventArgs
{
public Exception Error { get; set; }
public bool Cancelled { get; set; }
}
The above code works fine; I can load any xap the following way:
XapLoader xapLoader = new XapLoader("Sales.xap");
xapLoader.Completed += new XapLoadedEventHandler(xapLoader_Completed);
xapLoader.Begin();
Now, I have a UserControl called InvoiceView in the Sales.xap project, so I would like to instantiate the class. In the current project (Main.xap) I added reference to Sales.xap project, however, since I load it manually I set "Copy Local = False". But when executed, the following code throws TypeLoadException:
Sales.InvoiceView view = new Sales.InvoiceView();
It seems the code can't find InvoiceView class. But I checked that XapLoader's initXap() method was successfully executed. So why the code can't find InvoiceView class? Can someone help me with this problem?
This is based on the asker's self-answer below, rather than the question.
If you delete a project/module the output DLLs/XAP files do hang around. If you click the "show all files" button you will see some these left-over output files in your clientbin, bin and obj folders of related projects.
You can delete them individually from the project, or, when in doubt, search for all BIN and OBJ (e.g. using desktop explorer) and delete all those folders. The BIN/CLIENTBIN/OBJ folders will be recreated when needed (this the job that the "clean" option in Visual Studio should have done!)
Hope this helps.
Ok, I found the cause. The above code works. After creating a new silverlight project (Sales.xap) I happened to compile my solution once. Then I deleted App class in the Sales.xap and renamed default MainPage class to SalesView. However, no matter how many times I compile my solution, Visual Studio's development web server was loading the first version of Sales.xap (where from?), so my code couldn't find SalesView. In my host Asp.Net project I set development server's port to a different port number, and the problem gone. So the problem was with Visual Studio's development server. Apparently it is keeping compiled xap files in some temporary folder, and doesn't always update those xap files when source code changed.
What I do to avoid such problems when executing freshly compiled Silverlight is clear the browser cache, chrome even has a clear silverlight cache ;)
this XAP Cache phenomena is often due to the visual studio embedded web server (ASP.NET Development Server).
Just stop the occurence of this server and the cache will be cleared.
Start again your project and the latest build of your xap is called.

Resources