Xamarin Forms DependencyService with Prism with Autofac crash - Unhandled Exception - c#

So to continue on with my first foray into Xamarin, I'm trying to develop an Content page that will take a photo, and then save the photo on the device gallery for viewing. I'm using Prism with Autofac and I'm following the wiki documentation on DependencyService and the examples that was provided on GitHub, but the program is crashing without explaining why.
I hate that!
So, here's my interface:
public interface ISavePicture
{
void SavePictureToGallery(string path);
}
ViewModel:
public class PluginPageViewModel : BindableBase
{
private ISavePicture _savePicture;
public PluginPageViewModel(ISavePicture savePicture)
{
try
{
TakePicCommand = new DelegateCommand(TakePicture);
_savePicture = savePicture;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
public ICommand TakePicCommand { get; private set; }
private async void TakePicture()
{
try
// Code here for getting the camera to take a picture ...
_savePicture.SavePictureToGallery(filePath);
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
}
}
and the Android code:
using Android.App;
using Android.Content;
using Java.IO;
using RolodexDEMO_XF.Droid.Service;
using RolodexDEMO_XF.Services;
using Xamarin.Forms;
using Uri = Android.Net.Uri;
[assembly: Dependency(typeof(SavePicture_Android))]
namespace RolodexDEMO_XF.Droid.Service
{
public class SavePicture_Android : Activity, ISavePicture
{
public void SavePictureToGallery(string path)
{
Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
var file = new File(path);
Uri contentUri = Uri.FromFile(file);
mediaScanIntent.SetData(contentUri);
SendBroadcast(mediaScanIntent);
}
}
}
Notice that I DO have the assembly attribute for the DependencyService. I also wanted to note that I'm not using the emulator to test it out. Instead, I'm using my Galaxy Note 4 since I'm trying to test out the camera. For that part, I'm using Xamarin.Plugins from James Montemagno and that's working fine. I just can't save it, or see the pic if it is indeed saved to the device.
So where am I going wrong with it?
UPDATE: I was asked by others on what permissions I'm putting into my Android app, so in the AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" package="RolodexDEMOXF.RolodexDEMOXF">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:theme="#style/MyTheme" android:label="Rolodex DEMO">
<provider android:name="android.support.v4.content.FileProvider" android:authorities="RolodexDEMOXF.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="#xml/file_paths"></meta-data>
</provider>
</application>
</manifest>
and in the file_paths.xml (in the Resources\xml directory)
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/RolodexDEMOXF/files/Pictures" />
<external-path name="my_movies" path="Android/data/RolodexDEMOXF/files/Movies" />
</paths>

Various members of the Prism community are committed to making the Autofac implementation of Prism for Xamarin.Forms (Prism.Autofac.Forms) excellent; so using this IoC container option is not a bad choice. The issue has been that the first version of the Autofac implementation (for Prism.Forms) was written without the knowledge that the ContainerBuilder.Update() method was being deprecated; and that Autofac containers should be immutable (i.e. built once and not updated).
Also, the Autofac implementation does not have the built-in ability to resolve dependencies via the Xamarin.Forms DependencyService, if they cannot be resolved from the Autofac IoC container. This is a known issue and is not likely to be fixed, since - as Steve points out - the integration between Prism for Xamarin.Forms and the DependencyService is being re-considered (and possibly deprecated).
So, with Autofac, you just need to perform a secondary registration of your Xamarin.Forms dependency. You can do it as Steve showed - registering platform-specific implementations in the RegisterTypes() method of your IPlatformInitializer implementation; or you can use the Xamarin.Forms DependencyService to register it in the shared (PCL) project in the App.RegisterTypes() method (in your App.xaml.cs file).
Using the Xamarin.Forms DependencyService to register it in your shared project would look like this (again, in class that inherits from PrismApplication, which is generally the App.xaml.cs file):
protected override void RegisterTypes()
{
var builder = new ContainerBuilder();
builder.Register(ctx => Xamarin.Forms.DependencyService.Get<ISavePicture>())
.As<ISavePicture>();
builder.Update(Container);
}
Final note: The information provided above is correct for version 6.3.0 of Prism.Autofac.Forms. Since things are being re-worked (possibly for Prism 7.x) to remove the ability to update Autofac containers after they are built (i.e. make them immutable); it seems likely that at some point in the future, the code above would change to:
protected override void RegisterTypes()
{
Container.Register(ctx => Xamarin.Forms.DependencyService.Get<ISavePicture>())
.As<ISavePicture>();
}

I'm putting in the answer here just in case someone else has the same problem as I do in the future. Also, since there's little documentation, and I had to cobble together this alternative solution from a Chinese website (of all places!) and discussion with some of the guys on Slate Prism-Forms channel, I thought it would be best to put it out there on the alternative solution so at least you get an rudimentary idea on to resolve the DependencyService issues, and workarounds using Autofac DI.
I do want to note that there is discussion going on the Prism GitHub that Prism's implementation of DependencyService should be depreciated and go through this alternative that I'm describing here in this post. So hopefully one of the guys on the development team will document it and give better code examples on what I'm showing here.
That said, on with the show...
Okay, so I found out the answer to the problem. Long story short, the problem is the Autofac container.
And now the long winded version.
From what I gathered Dan Siegel, of all the containers that Prism can implement for a IoC/DI container, I had to pick the one that doesn't play well with others!
Why, do I say that it doesn't play well? According to the Autofac documentation, the container is Immutable which means it cannot be modified once it's been built. So my hypothesis is that when Prism goes through the project and tries to add in the registration types of DependencyService, Autofac is balking because the container is already built and therefore the "Unhandled Exception" is being thrown.
That explains the issue, but not the solution.
And what is the solution? Well, it turns out that Brian (Lagunas) and Brian (Noyes) have implemented a new interface called IPlatformInitializer, and I therefore have no choice but to use ContainerBuilder.Update(container) to add in the new RegisterType, which implements the additional RegisterTypes for the DependencyService, and I had to implement it like this:
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainer container)
{
var temp = new ContainerBuilder();
temp.RegisterType<SavePicture_Android>().As<ISavePicture>();
temp.RegisterType<MessageService_Android>().As<IMessageService>();
// ... add more RegisterTypes as needed ...
temp.Update(container);
}
}
This class is already included in the Prism template project. For Android, it's in MainActivity.cs file.
Just in case you're wondering, it's the same for iOS and UWP. So instead of being AndroidInitializer:
iOSInitializer : IPlatformInitializer (iOS) (inside AppDelegate.cs)
UWPInitializer : IPlatformInitializer (UWP) (inside MainPage.xaml.cs)
On last thing: You can dump the [Assembly Dependency(typeof(X))] attribute since it is no longer needed. But you still need to do constructor dependency injection as they stated in their documentation for DependencyService.
As I said, the Prism gang is kicking around the idea of getting rid of their implementation of DependencyService on the next build of Prism and go down this route that I've explained to you.
It's also interesting to note is that the guys over Autofac are ALSO discussing on getting rid of the ContainerBuilder.Update() on the next version release of Autofac 4.
Just some fair warning, because of what I've put here may go out the window in the future.
I hope it helps someone out!

Related

How can I use Microsoft.Extensions.DependencyInjection in an UNO Platform app

I am new to UNO Platform and I am trying to develop my first application with this framework. I would like to use a DI toolkit also so I chose Microsoft.Extensions.DependencyInjection that should be compliant with this Platform.
By the way, I cannot understand how can I inject dependencies to my ViewModels.
I red this post:
How can I use Microsoft.Extensions.DependencyInjection in an .NET Core console app? and this is my approach:
App.xaml.cs:
public App()
{
ConfigureFilters(global::Uno.Extensions.LogExtensionPoint.AmbientLoggerFactory);
var services = new ServiceCollection()
.AddSingleton<IMessageBroker, MessageBroker>();
var serviceProvider = services.BuildServiceProvider();
this.InitializeComponent();
this.Suspending += OnSuspending;
}
where IMessageBroker and MessageBroker are the interface and the implementation of my class I would like to handle with DI.
Then on my ViewModel I do:
MainPageViewModel.cs
public MainPageViewModel(IMessageBroker broker)
{
// some code
}
So far, so good.
For MVVM toolkit, I chose MVVM helpers.
If I understood correctly, this MVVM toolkit does not provide any convention to attach View to ViewModel so I should do it manually:
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
// How can I get a reference to ServiceProvider for calling GetService
var broker = serviceProvider.GetService<IMessageBroker>()
this.DataContext = new MainPageViewModel(broker);
}
}
How can I get a reference here to ServiceProvider for calling GetService<IMessageBroker>()?
Any hint would be highly appreciated.
one way you could do is having a static class ServiceLocator, which is initialized only from the beginning. This class exposes a readonly ServiceProvider, and you can call it to get the thing you register.
You may read somewhere else, or someone else here may comment this is anti-pattern, which is generally out of context. Depending on what you are trying to do and how your solution is structured, this service locator pattern can be totally fine.
here is a sample project you can use for reference
https://github.com/weitzhandler/UnoRx/tree/f2a0771e6a513863108e58ac7087078f39f7e3ed
an alternative will be, writing a viewmodel locator, which automatically resolves all dependencies through your view. This, however, may or may not overkill your use case.
finally, my personal flavor, having a shell project that depends on auto discovery and auto resolving; and each discovered smaller projects will internally depends on either service locator or function compositions

How do I get a reference to the application under test?

I'm using specflow with the NUnit test runner. When I write my feature file and ask specflow to generate the steps, it outputs the following code:
using System;
using TechTalk.SpecFlow;
using Xamarin.UITest.Android;
namespace UITest1
{
[Binding]
public class CategoryPagerSteps
{
[Given(#"The (.*)st category is selected")]
public void GivenTheStCategoryIsSelected(int p0)
{
ScenarioContext.Current.Pending();
}
[When(#"I swipe left")]
public void WhenISwipeLeft()
{
ScenarioContext.Current.Pending();
}
[Then(#"The (.*)nd category is selected")]
public void ThenTheNdCategoryIsSelected(int p0)
{
ScenarioContext.Current.Pending();
}
}
}
This is fine, and I understand that these are "Steps" which will be called when my cucumber file with scenarios written in Gherkin calls for them.
However, being that this is a fully-integrated UI test, I need to be able to use Xamarin.UITest.Android to click on views and such.
So I need to somehow grab the object that represents the application that is under test so I can perform UI operations on it.
Now, I can see that this object is being initialized in another auto-generated test fixture file called "Tests.cs":
using NUnit.Framework;
using Xamarin.UITest;
using Xamarin.UITest.Android;
namespace UITest1
{
[TestFixture]
public class Tests
{
AndroidApp app;
[SetUp]
public void BeforeEachTest()
{
// TODO: If the Android app being tested is included in the solution then open
// the Unit Tests window, right click Test Apps, select Add App Project
// and select the app projects that should be tested.
app = ConfigureApp
.Android
// TODO: Update this path to point to your Android app and uncomment the
// code if the app is not included in the solution.
//.ApkFile ("../../../Android/bin/Debug/UITestsAndroid.apk")
.StartApp();
}
[Test]
public void AppLaunches()
{
app.Screenshot("First screen.");
}
}
}
I can see that the property AndroidApp app is the object that I need access to, but how do I access that property from the CategoryPagerSteps code above? Tests is not static nor are any of the methods or properties. I'm nervous to simply instantiate it myself because that should probably be done by the test runner, right? One of the other auto-generated files contains a testRunner property, but it is marked private.
So every avenue I've gone down appears blocked and I feel that I'm missing something obvious.
Here's how I solved it, in case anyone else might find it useful:
Following up on the link provided by #CheeseBaron from arteksoftware, the trick is to use SpecFlow's FeatureContext.Current to hold the value. This is one of the intended uses of FeatureContext.
The reference from arteksoftware used this method, as shown in this code:
[SetUp]
public void BeforeEachTest ()
{
app = AppInitializer.StartApp (platform, iOSSimulator);
FeatureContext.Current.Add ("App", app);
//This next line is not relevant to this post.
AppInitializer.InitializeScreens (platform);
}
However, it didn't work immediately for me because the [Setup] binding would not be called as part of a specflow test. Changing the binding to the SpecFlow [BeforeFeature] binding and making the method static solved the problem.
[BeforeFeature]
public static void Before()
{
AndroidApp app;
Console.WriteLine("** [BeforeFeature]");
app = ConfigureApp
.Android
// TODO: Update this path to point to your Android app and uncomment the
// code if the app is not included in the solution.
.ApkFile(<Path to APK>)
.StartApp();
FeatureContext.Current.Add("App", app);
}
Then, in the feature code itself, the app could be extracted from the FeatureContext dictionary like so:
[Binding]
public class FeatureSteps
{
AndroidApp app;
public FeatureSteps()
{
app = FeatureContext.Current.Get<AndroidApp>("App");
}
//Code for the rest of your feature steps.
}
I imagine that the selection of one's test runner is relevant to the bindings that are used, so here's my "App.config". I'm using NUnit with a SpecFlow plugin. I didn't try it with other test runner configurations.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow" />
</configSections>
<specFlow>
<!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
<!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
<!-- use unit test provider SpecRun+NUnit or SpecRun+MsTest for being able to execute the tests with SpecRun and another provider -->
<unitTestProvider name="NUnit" />
<plugins>
<add name="SpecRun" />
</plugins>
</specFlow>
</configuration>
The repo mentioned above https://github.com/RobGibbens/BddWithXamarinUITest last updated 2015 and things have moved on to POP architecture, which I would recommend.
Have a look here - POP architecture with SpecFlow, https://github.com/xamarin-automation-service/uitest-specflow-example
You can see there is a static class called AppManager which has the App property
There a lot of videos and blog post on this style:
https://www.youtube.com/watch?v=4VR861BWkiU
https://www.youtube.com/watch?v=i0UNQvfXZhM
https://xamarindevelopersummit.com/page-object-pattern-and-uitest-best-practicies/
https://devblogs.microsoft.com/xamarin/best-practices-tips-xamarin-uitest/

Issues when renaming WCF Service

first of all, I am well aware that there are two other questions about the same topic I'm about to inquire, and I must sadly say that they haven't worked for me.
As it stands I am trying to create a simple WCF service which is to be consumed by a desktop application. I created it off the WCF Service Application that is offered through the New -> Project in Visual Studio 2013. That project has a template which has these two files: An interface called IService1.cs and the Service which is called Service1.svc.
Right out of the box this whole package works, but when I decide to rename those files with "IReservationService" and "ReservationService" respectively and put in my own code (which I can't find any mistakes in, not yet), when I decide to see if it's all working I get this bad boy:
I regret to tell you that some parts of it are in Spanish, if you can't interpret them just yell at me and I will translate all of it for you.
This is the code involved:
IReservationService.cs
namespace WebService
{
[ServiceContract]
public interface IReservationService
{
[OperationContract]
List<Business.Reservation> RequestReservationRetrieval();
}
}
ReservationService.svc
namespace WebService
{
public class ReservationService : IReservationService, IDisposable
{
ReservationRepository repo;
public List<Business.Reservation> RequestReservationRetrieval()
{
//code goes here, skipped it so it doesn't clutter
}
private void ChangeSyncDate(DateTime date)
{
//code goes here, skipped it so it doesn't clutter
}
public void Dispose()
{
repo.Dispose();
}
}
}
As soon as I change the code in those two classes (IService1.cs and Service1.svc) which I assumed would have been refactored for mine to fit somewhat perfectly, that doesn't seem to be the case, and I get the error that is displayed in the image.
Any ideas on how to fix this?
This was pretty much a refactoring nightmare, because the issue was somewhat hidden.
There's two files in ReservationService, or rather the Service that's to be consumed, in my case:
ReservationService.svc and ReservationService.svc.cs
ReservationService.svc contains just this tiny line:
<%# ServiceHost Language="C#" Debug="true" Service="WebService.Service1" CodeBehind="ReservationService.svc.cs" %>
And oh, surprise, there it is. Service="WebService.Service1" is exactly what was causing all these issues.
In order to get to ReservationService.svc, or whatever it's called on your Project you need to Right Click the .svc and click "View Markup".
This is an issue no one should ever have to deal with, I swear.

MvvmCross vnext: merge plugins with monodroid

I'm trying to merge plugins library projects into a single one (for example, Location + PhoneCallTask). It works perfectly with wp7, but I get an unhandled exception with monodroid:
Could not load file or assembly 'Cirrious.MvvmCross.Plugins.Location.Droid.dll'
Of course, the location plugin is referenced in 'Cirrious.MvvmCross.Plugins.Droid.dll', the merged library.
Is there a way to point to the merged library path?
Having considered your question more fully...
I'm still not entirely sure what a merge plugin is, but I think the problem you are seeing must be down to the way that MvvmCross-MonoDroid uses file conventions to load plugins while all the other platforms force the user to provide explicit factory methods for each plugin.
The reason for this difference is because the file conventions are (IMO) the nicest way of doing this... but all the other platforms put security and/or compilation issues in the way which meant that alternative mechanisms had to be used...
The easiest thing for you to do is probably to switch the setup of your MonoDroid app to use the loader conventions too.
To do this:
in Setup.cs override CreatePluginManager() to:
protected override IMvxPluginManager CreatePluginManager()
{
var toReturn = new MvxLoaderBasedPluginManager();
var registry = new MvxLoaderPluginRegistry(".Droid", toReturn.Loaders);
AddPluginsLoaders(registry);
return toReturn;
}
and then provide a AddPluginsLoaders() implementation like:
protected virtual void AddPluginsLoaders(Cirrious.MvvmCross.Platform.MvxLoaderPluginRegistry loaders)
{
loaders.AddConventionalPlugin<Cirrious.MvvmCross.Plugins.Visibility.Droid.Plugin>();
loaders.AddConventionalPlugin<Cirrious.MvvmCross.Plugins.Location.Droid.Plugin>();
loaders.AddConventionalPlugin<Cirrious.MvvmCross.Plugins.Phone.Droid.Plugin>();
loaders.AddConventionalPlugin<AlphaPage.MvvmCross.Plugins.Mega.Droid.Plugin>();
// etc
}
Short answer:
I'm guessing you need to:
check that your namespaces and assembly names are all of the same convention
check that you have referenced both the core plugin assembly and the correct plugin implementation within the UI.Droid project
Longer answer (based on some notes I already had - will be published soon):
If you were to build an entirely new plugin, then you would:
1. Create a central shared plugin
This would be Portable Class library - say AlphaPage.MvvmCross.Plugins.Mega
Within that central shared PCL, you would put whatever portable code was available - often this might only be a few service Interface definitions - e.g.
public interface IAlphaService { ... }
and
public interface IPageService { ... }
You'd then add the PluginManager for that plugin which would just add the boiler-plate of:
public class PluginLoader
: IMvxPluginLoader
, IMvxServiceConsumer<IMvxPluginManager>
{
public static readonly PluginLoader Instance = new PluginLoader();
#region Implementation of IMvxPluginLoader
public void EnsureLoaded()
{
var manager = this.GetService<IMvxPluginManager>();
manager.EnsureLoaded<PluginLoader>();
}
#endregion
}
2. Create the specific plugin implementations
For each platform, you would then implement the plugin - e.g. you might implement AlphaPage.MvvmCross.Plugins.Mega.WindowsPhone and AlphaPage.MvvmCross.Plugins.Mega.Droid
Within each of these you will implement the native classes which provide the services:
public class MyAlphaService : IAlphaService { ... }
and
public class MyPageService : IPageService { ... }
Finally each plugin would then provide the boilerplate plugin implementation:
public class Plugin
: IMvxPlugin
, IMvxServiceProducer
{
#region Implementation of IMvxPlugin
public void Load()
{
// alpha registered as a singleton
this.RegisterServiceInstance<IAlphaService>(new MyAlphaService());
// page registered as a type
this.RegisterServiceType<IPageService, MyPageService>();
}
#endregion
}
3. Instantiation of plugins
Each UI client will have to initialise the plugins.
This is done by the end UI client adding library references to:
the shared core plugin
the appropriate plugin implementation
3.1 WinRT, WindowsPhone and MonoTouch
Then, for WinRT, WindowsPhone and MonoTouch clients, you also need to provide a Loader accessor in setup.cs - like:
protected override void AddPluginsLoaders(Cirrious.MvvmCross.Platform.MvxLoaderPluginRegistry loaders)
{
loaders.AddConventionalPlugin<AlphaPage.MvvmCross.Plugins.Mega.WindowsPhone.Plugin>();
base.AddPluginsLoaders(loaders);
}
Note that Convention is used here - so it's important that AlphaPage.MvvmCross.Plugins.Mega.WindowsPhone.Plugin implements the WindowsPhone plugin for AlphaPage.MvvmCross.Plugins.Mega.PluginLoader
3.2 MonoDroid
For MonoDroid clients, you don't need to add this setup step - because MonoDroid has less Assembly.Load restrictions than the other platforms - and ao can load the plugins from file. But for this to work, it's important that the assembly names match - if the PluginLoader is AlphaPage.MvvmCross.Plugins.Mega.PluginLoader then the conventions will try to load the plugin from AlphaPage.MvvmCross.Plugins.Mega.Droid.dll
4. Use of plugin services
After this setup, then applications should finally be able to access the plugins by:
adding a reference the Shared core portable library
at some time calling AlphaPage.MvvmCross.Plugins.Mega.PluginLoader.Instance.EnsureLoaded()
then accessing the individual services using this.GetService<IAlphaService>() or this.GetService<IPageService>()
5. Pure portable plugins
Some plugins can be 'pure portable'
In this case they don't need any specialization for each platform, and no step 3 is required.
For an example of this, see the Json implementation - https://github.com/slodge/MvvmCross/tree/vnext/Cirrious/Plugins/Json

Trying to inject dependency into IIS hosted WCF service using MEF

I'm trying to inject specific class into my WCF service but it doesn't work and I can't understand why. I'm VERY NEW to MEF and patterns, just trying to make it work. Watched series of videos to understand what it is about but bootstraping won't work in my case since it is not Silverlight
http://channel9.msdn.com/blogs/mtaulty/mef--silverlight-4-beta-part-1-introduction
Here is my Web application's Global.asax code. This is non MVC, just regular ASP.NET app:
private void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
var catalog = new WebScopedCatalog(new DirectoryCatalog(Server.MapPath("~\\bin")));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
FIRST, I'm not sure I even bootrstrap it properly.. SECOND, I'm using http://www.timjroberts.com/2011/02/web-scoped-mef-parts/ as a guidance for web-scoped parts. I need that because some injected objects supposed to live during request only.
Now, I have following class:
[Export(typeof(ITest))]
[WebPartCreationPolicy(WebCreationPolicy.Session)]
public class Test : ITest
{
public string TestMe()
{
return "Hello!";
}
}
And my service looks like:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MobileService
{
[Import]
public ITest MyTestClass { get; set; }
public MobileService()
{
int i = 10;
}
When breakpoint hits at i=10 - I have NULL inside MyTestClass. Clearly, MEF does not initialize this class for me. Am I missing something?
EDIT:
When I examine catalog - I see my class Exported but I don't see it imported anywhere...
EDIT 2:
Daniel, Thank you. It makes sense. It still feels litle like a magic for me at this point. Yes, WCF creates this service. Than I have MembershipProvider and various Utility classes that ALL need the same import. And I'm not creating neither of those classes so I can't have Mef creating them. (Service created by WCF, MembershipProvider created by who-knows-what, Utility classes have static methods..) I wanted to use Mef instead of storing my stuff in HttpContext.Current. My container object doesn't have SatisfyImports. It has SatisfyImportsOnce. What if I do it in many different constructors? Will Mef share same singleton or it will be creating new instance every time?
At this point I wonder if I should even use Mef in this particular scenario..
MEF won't satisfy imports for objects it doesn't create. What is it that creates MobileService? If it's not MEF, then the import won't be satisfied by default. Even if it is MEF, the import wouldn't be satisfied in the constructor- you can't set properties on an object you create until it is done being created (ie the constructor has finished).
You can call container.SatisfyImportsOnce(mobileService) to satisfy the imports of a part. You should try to avoid doing this everywhere you need a MEF import. If you can, you should export a part and import it from another part so that MEF handles the creation for you. However, it looks like this part may be created by WCF so you can't have MEF create it, and in that case it would be OK to use SatisfyImportsOnce.
Response to EDIT2: If you use the same container each time, MEF will by default only create one instance of the part with the export. You can set the CreationPolicy to NonShared (in either the export or import attribute) to change this.
As for whether it makes sense to use MEF the way you are trying to, I don't know enough about writing web services to give you any advice on that.
The web-scoped part creation policy that I wrote won't help with WCF services.
I've posted a new blog post that documents an approach to composeable WCF services:
http://www.timjroberts.com/2011/08/wcf-services-with-mef/
Basically, you need to implement a custom Instance Provider that is MEF-aware and can compose the WCF service object when it is created.
I wrote about how to compose your WCF in a generic way that its configuration based here:
Unit Testing, WCF and MEF
I actually built my solution on the code samples Tim Robert's provided in his post. only that I took it a bit further and instead of using code in the global asax i moved the configuration into the web.config so its easier to maintain.

Categories