I'm using my app.config to tell Unity my interface to type mappings...
<unity>
<containers>
<container>
<types>
<type type="UnityDAL.Interfaces.IDataContextFactory, UnityDAL"
mapTo="UnityDAL.UnityDemoDataContextFactory, UnityDAL" />
<type type="UnityDAL.Interfaces.IProductRepository, UnityDAL"
mapTo="UnityDAL.ProductRepository, UnityDAL" />
<type name="productRepo"
type="UnityDAL.Interfaces.IProductRepository, UnityDAL"
mapTo="UnityDAL.ProductRepository, UnityDAL" />
and so on...
using this code
var wrapper = UnityWrapper.Create();
var productRepository =
wrapper.Container.Resolve<IProductRepository>("productRepo");
var productsBO = new ProductBO(productRepository);
var products = productsBO.GetAllProducts();
Here is the constructor for the wrapper object...
public UnityWrapper()
{
_container = new UnityContainer();
var section =
(UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(_container);
}
but I get an exception that says...
{"Resolution of the dependency failed, type = \"IProductRepository\",
name = \"productRepo\". Exception message is: The current build operation
(build key Build Key[UnityDAL.ProductRepository, productRepo]) failed:
The parameter dataContextFactory could not be resolved when attempting
to call constructor UnityDAL.ProductRepository(UnityDAL.Interfaces.
IDataContextFactory dataContextFactory). (Strategy type
Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy, index 2)"}
I thought this node was wiring that up
<type type="UnityDAL.Interfaces.IDataContextFactory, UnityDAL"
mapTo="UnityDAL.UnityDemoDataContextFactory, UnityDAL" />
The idea here was originally to create a nice dependency chain. Any idea what I'm doing wrong? If you have any advice or tips on how I can correct the problem, I would like to hear them. Thanks for any help.
Cheers,
~ck in San Diego
This is a mapping problem.
Trying to resolve the UnityDAL.Interfaces.IProductRepository you need to first resolve UnityDAL.Interfaces.IDataContextFactory. Next trying to resolve UnityDAL.UnityDemoDataContextFactory you miss some mapping. Probably the ctor of the UnityDAL.UnityDemoDataContextFactory requires something that has being not registered.
By the way: what you do here is using
a service location. I avoid this
practice if possible but if you
absolutely need this then try to
expose the common service
locator. This dll ships with Unity
and provides a simple service locator
ONLY interface.
Related
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!
I am a little lost as to where I need to look to resolve the error message below.
The history is that I added a new service object to the project and instantiate / inject it when a new section of the site is accessed. (MVC 3 site repository/service architecture).
The error is:
Cannot resolve parameter 'XService pl_xService' of constructor 'Void .ctor(.....
Now this is straight forward enough to understand. The newly added parameter XService can't be resolved...
The error is thrown by Autofac.core.dependency but I am not sure where / how I would register my new object for Autofac.
I understand you have no clue how my project is setup so I'm just looking for greater understanding of what is going on thus enabling me to track down where I need to register my new service object with Autofac etc.
FWIW:
Controller for new section of the site:
public partial class NewSectionController : BaseController
{
private readonly IPL_XService _pl_xService;
#region Constructors
public NewSectionController(IPL_XService pl_xService,....)
{
this._pl_forumService = pl_forumService;
}
.....controller actions, helpers, utilities etc.........
}
Since I am in waters I have not spent much time (if any really) in please let me know of any other code, style, architecture etc. that I can post to help make the question better.
Thank You
Just search for ContainerBuilder class, and then add this code:
var builder = new ContainerBuilder();
builder.RegisterType<PL_XService>().As<IXService>().InstancePerHttpRequest();
When I try to set a PARAMETER using the Xml Configuration I get the following error:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'LM.AM.Core.Services.EmailService' can be invoked with the available
services and parameters: Cannot resolve parameter 'System.String
testSmtp' of constructor 'Void .ctor(System.String)'.
Here are the relevant files:
web.config
<configSections>
<section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration" />
</configSections>
<autofac>
<components>
<component type="LM.AM.Core.Services.EmailService , LM.AM.Core" service="LM.AM.Core.Infrastructure.Services.IEmailService , LM.AM.Core.Infrastructure">
<parameters>
<parameter name="testSmtp" value="abc" />
</parameters>
</component>
</components>
</autofac>
Service Class
public class EmailService : IEmailService
{
public string _testSmtp;
public EmailService (string testSmtp)
{
_testSmtp = testSmtp;
}
}
Registration
builder.RegisterType<EmailService>().As<IEmailService>().SingleInstance();
Global.asax
var builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
builder.RegisterModule<Core.ModuleInstaller>();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
AutofacContainer.Container = builder.Build();
var emailSvc = AutofacContainer.Container.Resolve<IEmailService>();
I've checked the container is aware of the xml parameter and I've followed the Wiki as close as I can, but for some reason the parameter is not resolving on the only constructor and I'm receiving the above error.
This should be pretty simple to get going. Can anyone provide some suggestions on what I
can try to get this working?
You have regiestered your EmailService two times.
Once in the web.config and once with
builder.RegisterType<EmailService>().As<IEmailService>().SingleInstance();
If you have the line above in the Core.ModuleInstaller then it will override the web.config configuration. And because here you haven't specified the parameter Autofac throws an exception.
So to solve this just remove the EmailService registration from the Core.ModuleInstaller module.
If you use the Core.ModuleInstaller multiple places and you need to have the EmailService registration there then you need to change the order of the Module loading:
var builder = new ContainerBuilder();
builder.RegisterModule<Core.ModuleInstaller>();
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
or tell Autofac to not override the registration of EmailService if it already exists with PreserveExistingDefaults:
builder.RegisterType<EmailService>().As<IEmailService>()
.SingleInstance().PreserveExistingDefaults();
I had created a constructor where there was none before and made it private, therefore there was default constructor so I got this error. Had to make my constructor public.
My registrations were all good, but I had referenced the concrete class instead of the interface in my constructor - Missed the 'I'.
Hope this answer helps someone save 5 minutes of effort one day.
Adding the service with appropriate scope(Singleton,Scoped,Transient) to the specified IServiceCollection instance helped to resolve this issue.
Something like below:
public static IServiceCollection YourServiceCollectionExtensionMethod(this IServiceCollection services)
{
services.AddTransient<IEmailService, EmailService>();
}
Hope this helps!
I have two implementations of IEmailService, one for testing and one for live (is-A). And I have a BusinessService that has-A IEmailService reference.
BusinessService
IEmailService (has-A)
IEmailService
TestEmailService (is-A)
LiveEmailService (is-A)
In unity config, I register the two IEmailService implementations as follows.
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="DataAccess.IEmailService, DataAccess"
mapTo="DataAccess.LiveEmailService, DataAccess"
name="Live">
<lifetime type="singleton" />
</register>
<register type="DataAccess.IEmailService, DataAccess"
mapTo="DataAccess.TestEmailService, DataAccess"
name="Test">
<lifetime type="singleton" />
</register>
<container>
</unity>
Based on the appSetting for IEmailService I want Unity to pick the correct implementation. This will help while testing.
<appSettings>
<add key="IEmailService" value="Test"/>
</appSettings>
The issue is when unity resolves BusinessService, it tries to resolve (none) named mapping of IEmailService instead of Live or Test and throws an ResolutionFailedException.
container.Resolve<BusinessService>(); throws below exception:
BusinessServices.Test.BusinessServiceTest_Integration.Test103:
Microsoft.Practices.Unity.ResolutionFailedException : Resolution of the dependency failed, type = "BusinessServices.BusinessService", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, DataAccess.IEmailService, is an interface and cannot be constructed. Are you missing a type mapping?
-----------------------------------------------
At the time of the exception, the container was:
Resolving BusinessServices.BusinessService,(none)
Resolving parameter "emailService" of constructor BusinessServices.BusinessService(DataAccess.IEmailService emailService)
Resolving DataAccess.IEmailService,(none)
----> System.InvalidOperationException : The current type, DataAccess.IEmailService, is an interface and cannot be constructed. Are you missing a type mapping?
The workaround I came up with is to specify the registrations in code as well and have a wrapper method around container.RegisterType to register IEmailService with (none) named mapping as well based on the appSetting value.
IUnityContainer container;
// registering unity
static void Load()
{
container = new UnityContainer().LoadConfiguration();
RegisterType<IEmailService, TestEmailService>("Test");
RegisterType<IEmailService, LiveEmailService>("Live");
}
// register the `Test` or `Live` implementation with `(none)` named mapping as per appSetting
static void RegisterType<TFrom, TTo>(string name)
where TTo : TFrom
{
var tFromAppSetting= ConfigurationManager.AppSettings[typeof(TFrom).Name];
if (!string.IsNullOrEmpty(tFromAppSetting) && tFromAppSetting == name)
container.RegisterType<TFrom, TTo>();
}
This works, but I end up specifying the registrations in two places - config as well as code. Is there a better way for doing this?
Update
I actually had got it correct by code. I do not need the unity config at all. The RegisterType<TFrom, TTo>(string name) registers either the Test or Live implementation as (none) named mapping depending on appSetting value. BusinessService is also resolved without exception.
As there is no unity config, I do not have load the configuration.
container = new UnityContainer();
In my opinion the only point of having registrations in config is to not have them in code and being able to replace implementation without recompilation. So you are write in trying to remove it form code. What I don't understand is why you want to have both registrations in config in the first place. Simply remove the Live one from config for tests and the Test from config for application and register them both without name.
So for instance in application app.config:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="DataAccess.IEmailService, DataAccess"
mapTo="DataAccess.LiveEmailService, DataAccess">
<lifetime type="singleton" />
</register>
Since you really are determent to do it your way:
The other way around this is to register in code only a way of determining which instance is the default one:
container.RegisterType<IEmailService>(new InjectionFactory((c)=>
{
var name = GetImplementationsNameFromAppSettings();
return c.Resolve<IEmailService>(name);
});
I am trying to use container.LoadConfiguration and I dont see it in the intellisense. Do I need to install or download something?
Config
<unity>
<containers>
<container>
<types>
<type type="IMyInterface, someAssembly"
mapTo="MyObject, someAssembly" />
</types>
</container>
</containers>
code
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity.InterceptionExtension.Configuration;
...
IUnityContainer _container = new UnityContainer();
UnityConfigurationSection section = (UnityConfigurationSection)System.
Configuration.ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(_container);
IEnumerable<IMyInterface> serviceList = _container.ResolveAll<IMyInterface>();
foreach (IMyInterface item in serviceList)
{
Console.Write("tet");
}
It does not go into the loop. It seems to configure ok. no errors.
If I understand your problem correctly, LoadConfiguration is not showing up in Intellisense.
I would try two things:
First makes sure you have included the Microsoft.Practices.Unity.Configuration assembly
Add an using statement for Microsoft.Practices.Unity.Configuration
LoadConfiguration is an extension method defined there.