I've a requirement where I've to support two types of container in my application. Therefore I've created a custom ServiceLocator that serves 2 types of containers i.e.
SimpleIoc (Galasoft)
MefServiceLocatorAdapter
Therefore I've created a common service locator class i.e.
public class CommonServiceLocator : ServiceLocatorImplBase
{
private IServiceLocator _iocContainer, _mefContainer;
public CommonServiceLocator(IServiceLocator iocLocator, IServiceLocator mefLocator)
{
_iocContainer = iocLocator;
_mefContainer = mefLocator;
}
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
IEnumerable<object> services;
try
{
services = _iocContainer.GetAllInstances(serviceType);
}
catch (Exception)
{
//Current assumption is that if IocContainer doesn't contain an instance then
//it should look for MefContainer for values
services = _mefContainer.GetAllInstances(serviceType);
}
return services;
}
protected override object DoGetInstance(Type serviceType, string key)
{
object service;
try
{
service = _iocContainer.GetInstance(serviceType, key);
}
catch (Exception)
{
//Current assumption is that if IocContainer doesn't contain an instance then
//it should look for MefContainer for values
service = _mefContainer.GetInstance(serviceType, key); //<== This fails to get an instance
}
return service;
}
public override object GetInstance(Type serviceType, string key)
{
return DoGetInstance(serviceType, key);
}
public override object GetInstance(Type serviceType)
{
return GetInstance(serviceType, null);
}
}
Now, the problem is when I try to get a class instance through IOC container using GetInstance() method it works fine. However, obtaining a class instance through MefContainer throws some error ("A first chance exception of type 'System.InvalidOperationException' occurred in System.Core.dll"). I've created a test class to test this scenario:
public class ServiceLocatorTest
{
public void CommonServiceLocatorInstanceTest()
{
var serviceLocator = new SimpleIoc();
serviceLocator.Register<GalaSoftIocData>();
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(MefCompositionAddin).Assembly));
CompositionContainer compContainer = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();
batch.AddPart(AttributedModelServices.CreatePart(new MefCompositionAddin()));
compContainer.Compose(batch);
var vl = compContainer.GetExportedValue<MefCompositionAddin>(); // <== This gives instance correctly
var mefserviceLocator = new MefServiceLocatorAdapter(compContainer);
var commonServiceLocator = new CommonServiceLocator(serviceLocator, mefserviceLocator);
var galaSoftData = commonServiceLocator.GetInstance(typeof(GalaSoftIocData));
var mefData = commonServiceLocator.GetInstance(typeof(MefCompositionAddin));
}
}
[Export]
class MefCompositionAddin
{
public string MyData { get { return "Mef's Data composed"; } }
[Import]
public MefCompositionAddin MyObj { get; set; }
}
class MefCompositionData
{
[Import]
public MefCompositionAddin MyAddin { get; set; }
}
class GalaSoftIocData
{
public string MyData { get { return "Galasoft's Data composed"; } }
}
Kindly let me know if i've missed anything. and what could be the possible cause. Thanks everyone for your help in advance.
I believe that you are using the MefServiceLocatorAdapter provided in the Prism library. This is the code for its DoGetInstance method:
protected override object DoGetInstance(Type serviceType, string key)
{
IEnumerable<Lazy<object, object>> exports = this.compositionContainer.GetExports(serviceType, null, key);
if ((exports != null) && (exports.Count() > 0))
{
// If there is more than one value, this will throw an InvalidOperationException,
// which will be wrapped by the base class as an ActivationException.
return exports.Single().Value;
}
throw new ActivationException(
this.FormatActivationExceptionMessage(new CompositionException("Export not found"), serviceType, key));
}
As you can see in the comment, if you have more than one type exported for the corresponding service type and key it will thrown the exception you are describing due to the Single method.
You can either review the exports in you app to see if there is some type mapped to multiple classes (or the same class exported multiple times,) or rewrite that method so that it doesn't throw an exception (for example, using FirstOrDefault instead of Single.)
Note: I did not add the aforementioned comment, it is included in the library's code.
Related
Note: using .NET Core 2.0 [Microsoft.Extensions.DependencyInjection].
Here's what I would like to do:
IServiceCollection collection = new ServiceCollection();
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel>(nameof(View1));
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel>(nameof(View2));
But I can't figure out a way to name services added to the collection. If the type used repeats without a name specified, the collection appears to simply overwrite the existing service with the new one. Ideally, I would be able to reuse a type, but differentiate it by supplying a name.
Please ignore the contrived example.
Q: Is this possible?
Given the fact that the ServiceDescriptor class doesn't have a Name property or any way to set a string identifier and the classes for resolving services are marked internal, I would say the answer is no.
However, it's not very difficult to build your own extensions to fake it.
NamedServiceDescriptor
class NamedServiceDescriptor
{
public NamedServiceDescriptor(string name, Type serviceType)
{
this.Name = name;
this.ServiceType = serviceType;
}
public string Name { get; private set; }
public Type ServiceType { get; private set; }
public override bool Equals(object obj)
{
if (!(obj is NamedServiceDescriptor))
return false;
var other = (NamedServiceDescriptor)obj;
return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) &&
ServiceType.Equals(other.ServiceType);
}
public override int GetHashCode()
{
return Name.GetHashCode() ^
ServiceType.GetHashCode();
}
}
Extension Methods
public static class ServiceCollectionExtensions
{
internal static readonly IDictionary<NamedServiceDescriptor, Type> nameToTypeMap
= new ConcurrentDictionary<NamedServiceDescriptor, Type>();
public static IServiceCollection AddSingleton<TService, TImplementation>(
this IServiceCollection serviceCollection,
string name)
where TService : class where TImplementation : class, TService
{
nameToTypeMap[new NamedServiceDescriptor(name, typeof(TService))]
= typeof(TImplementation);
return serviceCollection.AddSingleton<TImplementation>();
}
}
public static class ServiceProviderExtensions
{
public static T GetService<T>(this IServiceProvider provider, string name)
{
if (provider == null)
throw new ArgumentNullException(nameof(provider));
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
ServiceCollectionExtensions.nameToTypeMap.TryGetValue(
new NamedServiceDescriptor(name, typeof(T)), out Type implementationType);
return (T)provider.GetService(implementationType);
}
}
Usage
public interface IMyReusableViewModel { }
public class MyReusableViewModel1 : IMyReusableViewModel { }
public class MyReusableViewModel2 : IMyReusableViewModel { }
IServiceCollection collection = new ServiceCollection();
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel1>("View1");
collection.AddSingleton<IMyReusableViewModel, MyReusableViewModel2>("View2");
public class MyService
{
private readonly IServiceProvider provider;
public MyService(IServiceProvider provider)
{
this.provider = provider;
}
public void DoSomething()
{
var view1 = provider.GetService<IMyReusableViewModel>("View1");
var view2 = provider.GetService<IMyReusableViewModel>("View2");
// ...
}
}
NOTE: That said, I wouldn't recommend this approach. If you require such functionality, it is a sign that the application design is inadequate. A design pattern such as Abstract Factory or Strategy may be what is needed to fill the void without resorting to naming type registrations or abusing the container as a Service Locator.
Alternatively, you could use a 3rd party DI container that supports this functionality.
Im messing round in autofac and im having some issues binding to a specific constructor.
I have the following code:
var builder = new ContainerBuilder();
builder
.RegisterType<GenericIocFactory>()
.As<IGenericIocFactory>();
builder
.RegisterType<Product>()
.As<IProduct>()
.PropertiesAutowired();
IContainer Container = builder.Build();
IGenericIocFactory Fac = Container.Resolve<IGenericIocFactory>();
_product = Fac.Get<IProduct>(new Dictionary<string,object>() { {"returnEmpty" , false} }) as Product;
Then in the factory:
public interface IGenericIocFactory
{
T Get<T>(Dictionary<string,object> options) where T: class;
}
public class GenericIocFactory : IGenericIocFactory
{
private readonly IComponentContext _icoContext;
private object _options;
public GenericIocFactory(IComponentContext icoContext,bool isInjected = true)
{
_icoContext= icoContext;
}
public T Get<T>(Dictionary<string,object> options) where T: class
{
var _parameters = new List<Parameter>();
foreach (var parameter in options)
{
_parameters.Add(new NamedParameter(parameter.Key, parameter.Value));
}
return _icoContext.Resolve<T>(_parameters);
//operate on new object
// tried this as well
//return _icoContext.Resolve<T>(
//new NamedParameter("returnEmpty" , false)
//new TypedParameter(typeof(bool),false)
//);
}
}
This resolves a product but not with the constructor i expect.
Target constructor
public Product(bool returnEmpty)
Resolving constructor
public Product(IList<string> productCodes, string fields = "", string orderBy = "ProductCode")
There is a total of 23 constructors and the one resolving is not the biggest(so i don't think its being greedy)
ie
public Product(string strFields, string strFrom, string strFilter, string strOrderBy, string whseCode,
bool addExistsInWharehouse, string additionalAfterorderBy, bool forceUniqueRecords = false)
Nor is it the first or last in or of definition.
Im stumped can anyone see what im doing wrong.
Unfortunately Autofac doesn't provide this mechanism.
You could have implemented IConstructorSelector which select a constructor when more than one constructor is available and set it to the registration by using the UsingSelector method but unfortunately there is no way to access the available parameters of the current resolve operation.
Another solution would be to implement IInstanceActivator which is responsible of creating the instance based on the type and parameters. to use a custom IInstanceActivator you also need to implement IRegistrationBuilder which is quite difficult. To guarantee good performance, I would also recommend the use of ConstructorParameterBinding which will create an optimized factory using dynamic compiled expression.
If you can't change your constructor, the only solution I can see is to implement your own factory. Because your object don't have any dependencies, you can create them without using Autofac.
public class GenericIocFactory : IGenericIocFactory
{
public GenericIocFactory(ILifetimeScope scope)
{
this._scope = scope;
}
private readonly ILifetimeScope _scope;
public T Get<T>(params object[] args) where T: class
{
ConstructorInfo ci = this.GetConstructorInfo(args);
if (ci == null)
{
throw ...
}
var binder = new ConstructorParameterBinding(ci, args, this._scope);
T value = binder.Instanciate() as T;
if (value == null)
{
throw ...
}
if(value is IDisposable)
{
this._scope.Disposer.AddInstanceForDisposal(value);
}
return value;
}
protected virtual ConstructorInfo GetConstructorInfo<T>(params object[] args)
{
// TODO
}
}
So having read up again on the doco. I needed to bind the constructor at the start. But this wont fix my problem so I made another container ever single time an instance is requested and construct it based on the params. Its a little bit incorrect but this is a real world solution to anyone who is transitioning to autofac for a very large existing solution.
Hope this helps someone.
public interface IGenericIocFactory
{
T Get<T>(params object[] constructorParams) where T: class;
}
public interface ICustomAutoFacContainer
{
IContainer BindAndReturnCustom<T>(IComponentContext context, Type[] paramsList);
}
public class CustomAutoFacContainer : ICustomAutoFacContainer
{
public IContainer BindAndReturnCustom<T>(IComponentContext context, Type[] paramsList)
{
if (context.IsRegistered<T>())
{
// Get the current DI binding type target
var targetType = context
.ComponentRegistry
.Registrations
.First(r => ((TypedService) r.Services.First()).ServiceType == typeof(T))
.Target
.Activator
.LimitType;
// todo: exception handling and what not .targetType
var builder = new ContainerBuilder();
builder
.RegisterType(targetType)
.As<T>()
.UsingConstructor(paramsList)
.PropertiesAutowired();
return builder.Build();
}
return null;
}
}
public class GenericIocFactory : IGenericIocFactory
{
private ICustomAutoFacContainer _iCustomContainer;
private readonly IComponentContext _icoContext;
public GenericIocFactory(ICustomAutoFacContainer iCustomContainer, IComponentContext icoContext)
{
_iCustomContainer = iCustomContainer;
_icoContext = icoContext;
}
public T Get<T>(params object[] constructorParams) where T: class
{
//TODO handle reflection generation? ?? ?not needed?? ??
var parameters = constructorParams
.Select((t, index) => new PositionalParameter(index, t))
.Cast<Parameter>()
.ToList();
var parameterTypes = constructorParams
.Select((t, index) => t.GetType())
.ToArray();
return _iCustomContainer
.BindAndReturnCustom<T>(_icoContext,parameterTypes)
.Resolve<T>(parameters);
}
}
Setup and usage looks something like this:
var builder = new ContainerBuilder();
// Usually you're only interested in exposing the type
// via its interface:
builder
.RegisterType<GenericIocFactory>()
.As<IGenericIocFactory>();
builder
.RegisterType<CustomAutoFacContainer>()
.As<ICustomAutoFacContainer>();
builder
.RegisterType<Product>()
.As<IProduct>()
.PropertiesAutowired();
var container = builder.Build();
var factory = container.Resolve<IGenericIocFactory>();
_product = factory.Get<IProduct>(false) as Product;
_product = factory.Get<IProduct>("","") as Product;
A module has registered a type that looks like this:
class MyType
{
public IEnumerable<IServer> Servers { get { ... } }
}
I would like to register each of these server instances with the container:
builder.Register(x => x.Resolve<MyType>().Servers).As<IEnumerable<IServer>>();
Separately I would like to register another implementation of IServer
builder.RegisterType<AnotherServer>().As<IServer>().SingleInstance;
Then I would like to get all the servers - the union os Server property and an instance of AnotherServer:
public MyClass(IEnumerable<IServer> allServers)
{
}
However, the above setup doesn't work because registering an actual instance of IEnumerable seems to override the Autofac behaviour of providing all registered implementations when an IEnumerable is requested.
Is there any way I can achieve what I want?
It's not possible to have one registration to produce multiple component.
I can see two ways for resolving your issue
Create a new IRegistrationSource to resolve IEnumerable<T>. You can have a look at the CollectionRegistrationSource.cs to see how to do it
Use a module to replace the IEnumerable during the activation.
Something like this :
public interface IContainer<T> {
IEnumerable<T> Values { get; }
}
public class MyType : IContainer<IServer>
{
public IEnumerable<IServer> Servers
{
get
{
yield return new Server1();
yield return new Server2();
}
}
IEnumerable<IServer> IContainer<IServer>.Values
{
get { return this.Servers; }
}
}
public class AggregatedEnumerableModule<T> : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
if (registration.Activator.LimitType.IsArray)
{
Type elementType = registration.Activator.LimitType.GetElementType();
if (elementType == typeof(T))
{
registration.Activating += (sender, e) =>
{
IEnumerable<T> originalValues = (IEnumerable<T>)e.Instance;
IEnumerable<T> newValues = e.Context.Resolve<IContainer<T>>().Values;
IEnumerable<T> aggregatedValues = newValues.Concat(originalValues);
e.ReplaceInstance(aggregatedValues);
};
}
}
base.AttachToComponentRegistration(componentRegistry, registration);
}
}
Then
ContainerBuilder builder = new Autofac.ContainerBuilder();
builder.RegisterModule(new AggregatedEnumerableModule<IServer>());
builder.RegisterType<MyType>().As<IContainer<IServer>>();
builder.RegisterType<Server3>().As<IServer>();
IContainer container = builder.Build();
IEnumerable<IServer> pouets = container.Resolve<IEnumerable<IServer>>();
I have a class that calls out to an internet service to get some data:
public class MarketingService
{
private IDataProvider _provider;
public MarketingService(IDataProvider provider)
{
_provider = provider;
}
public string GetData(int id)
{
return _provider.Get(id);
}
}
Currently I have two providers: HttpDataProvider and FileDataProvider. Normally I will wire up to the HttpDataProvider but if the external web service fails, I'd like to change the system to bind to the FileDataProvider . Something like:
public string GetData(int id)
{
string result = "";
try
{
result = GetData(id); // call to HttpDataProvider
}
catch (Exception)
{
// change the Windsor binding so that all future calls go automatically to the
// FileDataProvier
// And while I'm at it, retry against the FileDataProvider
}
return result;
}
So when this has been executed all future instances of MarketingService will automatically be wired up to the FileDataProvider. How to change a Windsor binding on the fly?
One solution would be using selector
public class ForcedImplementationSelector<TService> : IHandlerSelector
{
private static Dictionary<Type, Type> _forcedImplementation = new Dictionary<Type, Type>();
public static void ForceTo<T>() where T: TService
{
_forcedImplementation[typeof(TService)] = typeof(T);
}
public static void ClearForce()
{
_forcedImplementation[typeof(TService)] = null;
}
public bool HasOpinionAbout(string key, Type service)
{
return service == typeof (TService);
}
public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
{
var tService = typeof(TService);
if (_forcedImplementation.ContainsKey(tService) && _forcedImplementation[tService] != null)
{
return handlers.FirstOrDefault(handler => handler.ComponentModel.Implementation == _forcedImplementation[tService]);
}
// return default
return handlers[0];
}
}
Test and usage
[TestFixture]
public class Test
{
[Test]
public void ForceImplementation()
{
var container = new WindsorContainer();
container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
container.Register(Component.For<IFoo>().ImplementedBy<Bar>());
container.Kernel.AddHandlerSelector(new ForcedImplementationSelector<IFoo>());
var i = container.Resolve<IFoo>();
Assert.AreEqual(typeof(Foo), i.GetType());
ForcedImplementationSelector<IFoo>.ForceTo<Bar>();
i = container.Resolve<IFoo>();
Assert.AreEqual(typeof(Bar), i.GetType());
ForcedImplementationSelector<IFoo>.ClearForce();
i = container.Resolve<IFoo>();
Assert.AreEqual(typeof(Foo), i.GetType());
}
}
Alternatively you could create a proxy:
public class AutoSelectingDataProvider : IDataProvider
{
public AutoSelectingDataPovider(HttpDataProvider httpDataProvider, FallBackDataProvider fallBackProvider)
{
_httpDataProvider = httpDataProvider;
_fallBackDataProvider = fallBackDataProvider;
}
public string GetData(int id)
{
try
{
return _httpDataProvider.GetData(id);
}
catch (Exception)
{
return _fallBackDataProvider.GetData(id);
}
return result;
}
}
container.Register(
Component.For<HttpDataProvider>(),
Component.For<FallBackDataProvider>(),
Component.For<IDataProvider>().ImplementedBy<FallBackDataProvider>());
This will always first try to get data from the HttpDataProvider if not succesfull use the fallback. If you want you can introduce state and after a failure always use the fallback. This way you can keep using the IDataProvider in your application without needing to obtain a new one from the container.
I'd like to have an instance per matching lifetime scoped registration in Autofac, but occasionally need to request an instance from a global container (where there is no matching lifetime scope). In scenarios where no matching lifetime scope exists, I want to give a top-level instance instead of throwing an exception.
Is this possible?
I think you'd better extend Autofac by introducing a new lifetime option. I took the Autofac sources and modified them a bit:
public static class RegistrationBuilderExtensions
{
public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InstancePerMatchingOrRootLifetimeScope<TLimit, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> builder, params object[] lifetimeScopeTag)
{
if (lifetimeScopeTag == null) throw new ArgumentNullException("lifetimeScopeTag");
builder.RegistrationData.Sharing = InstanceSharing.Shared;
builder.RegistrationData.Lifetime = new MatchingScopeOrRootLifetime(lifetimeScopeTag);
return builder;
}
}
public class MatchingScopeOrRootLifetime: IComponentLifetime
{
readonly object[] _tagsToMatch;
public MatchingScopeOrRootLifetime(params object[] lifetimeScopeTagsToMatch)
{
if (lifetimeScopeTagsToMatch == null) throw new ArgumentNullException("lifetimeScopeTagsToMatch");
_tagsToMatch = lifetimeScopeTagsToMatch;
}
public ISharingLifetimeScope FindScope(ISharingLifetimeScope mostNestedVisibleScope)
{
if (mostNestedVisibleScope == null) throw new ArgumentNullException("mostNestedVisibleScope");
var next = mostNestedVisibleScope;
while (next != null)
{
if (_tagsToMatch.Contains(next.Tag))
return next;
next = next.ParentLifetimeScope;
}
return mostNestedVisibleScope.RootLifetimeScope;
}
}
Just add these classes to your project and register you component as:
builder.RegisterType<A>.InstancePerMatchingOrRootLifetimeScope("TAG");
I haven't tried it myself, but it should work.
Possible solution is to override registration in child lifetime scope.
Sample:
public enum Scopes
{
TestScope
}
public class Test
{
public string Description { get; set; }
}
public class Tester
{
public void DoTest()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Test>()
.OnActivating(args => args.Instance.Description = "FromRoot")
.SingleInstance();
var container = builder.Build();
var scope = container.BeginLifetimeScope(Scopes.TestScope, b => b
.RegisterType<Test>()
.InstancePerMatchingLifetimeScope(Scopes.TestScope)
.OnActivating(args => args.Instance.Description = "FromScope"));
var test1 = container.Resolve<Test>();
Console.WriteLine(test1.Description); //writes FromRoot
var test2 = scope.Resolve<Test>();
Console.WriteLine(test2.Description); //writes FromScope
Console.ReadLine();
}
}
The root container itself is a lifetime scope with the tag name: root, defined in LifetimeScope.RootTag
So you can just do:
using Autofac.Core.Lifetime;
builder.RegisterType<Service>().As<IService>()
.InstancePerMatchingLifetimeScope(LifetimeScope.RootTag, "foobar");