In my App.xaml.cs I have
private void InitializeContainer()
{
var catalogs = new AggregateCatalog();
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
catalogs.Catalogs.Add(catalog);
// Also adding Interactions project
catalog = new AssemblyCatalog(typeof(InteractionsService).Assembly);
catalogs.Catalogs.Add(catalog);
// initialize the main application composition host (container)
CompositionHost.Initialize(catalogs);
}
However, when I try to get object initialized down a line like so:
this.InteractionsService = ServiceLocator.Current.GetInstance<IInteractionsService>();
I get exception that my ServiceLocator.Current is null.
How do I make it work?
I had the very same question..
and found this which might be helpful,
http://www.itwox.com/post/Using-MEF-with-Common-Service-Locator.aspx
the key statement is
ServiceLocator.SetLocatorProvider(() => whateveryourlocatorinstanceis );
I think you're missing the call to ComposeParts or Compose in the set up of the CompositionContainer . Before you start to resolve instances via GetInstance.
There's an MSDN walk through here, and some other samples here and here. The relevant code snippet:
private CompositionContainer _container
//your code
var batch = new CompositionBatch();
batch.AddPart(this);
_container.Compose(batch);
//or more simply
_container.ComposeParts(this) //if this method is supported
Related
I'm using SolrNet with Autofac DI in my web application like this:
var solrNetModule = new SolrNetModule(ConfigurationManager.ConnectionStrings["solr"].ConnectionString);
solrNetModule.HttpWebRequestFactory = new BasicAuthHttpWebRequestFactory("****", "****");
builder.RegisterModule(solrNetModule);
My queries are quite long and sometimes end up with a 404 in Jetty, I'm pretty sure this happens because the length of the URL.
I've been reading about the SolrPostConnection and that sounds like a proper solution to my problem, but I'm having trouble implementing it in my Autofac setup.
I know the Autofac SolrNetModule integration internally uses
builder.RegisterInstance(solrConnectionInstance).As<ISolrConnection>();
But I can't figure out how to override it or .Register Autofac to use the SolrPostConnection instead.
Looks like you need to override registration for interface ISolrConnection. The main problem is that SolrNetModule doesn't provide mapping of SolrConnection to itself. Try out the example below. I added another registration of SolrConnection to make it available for injecting to contructor of PostSolrConnection without dependency loops
[Test]
public void SolrRegistrationOverride()
{
// Arrange
var builder = new ContainerBuilder();
var serverUrl = ConfigurationManager.ConnectionStrings["solr"].ConnectionString;
var httpWebRequestFactory = new BasicAuthHttpWebRequestFactory("****", "****");
var solrNetModule = new SolrNetModule(serverUrl);
solrNetModule.HttpWebRequestFactory = httpWebRequestFactory;
builder.RegisterModule(solrNetModule);
builder.RegisterType<SolrConnection>().AsSelf()
.WithParameter(new NamedParameter("serverURL", serverUrl))
.WithProperty("HttpWebRequestFactory", httpWebRequestFactory).AsSelf()
.SingleInstance();
builder.RegisterType<PostSolrConnection>().As<ISolrConnection>()
.WithParameters(new Parameter[]
{
new ResolvedParameter((prm, сtx) => prm.Name == "conn", (prm, ctx) => ctx.Resolve<SolrConnection>()),
new NamedParameter("serverUrl", serverUrl)
});
var container = builder.Build();
// Act
var conn = container.Resolve<ISolrConnection>();
// Assert
Assert.IsInstanceOf<PostSolrConnection>(conn);
}
Hope it helps.
I have already looked at some similar answers but I cannot get it to work.
I am attempting to make the following more maintainable:
var modules = new INinjectModule[]
{
new ServiceModule(),
new ApplicationSettingsModule(),
new SerializerModule(),
new LoggerModule(),
new SqliteModule(),
new SetupModule(),
new CacheModule(),
new AuthenticationModule(),
};
Every time I add a new NinjectModule I need to modify this array to include it.
I want to be able to find all types that derive from NinjectModule and activate them and put them all into a collection.
This is what I have tried but I am not getting any of my classes that derive from NinjectModule
var classes = (from domainAssembly in AppDomain.CurrentDomain.GetAssemblies()
from assemblyType in domainAssembly.GetTypes()
where typeof(NinjectModule).IsAssignableFrom(assemblyType)
select assemblyType).ToArray();
Please note that the classes that I want to find are in a different assembly...
I suggest you to use kernel like that, so the Ninject will take care about the NinjectModules:
public static IKernel ConfigureKernel(IKernel kernel)
{
kernel.Load(Assembly.Load("NZBDash.DependencyResolver.Modules"));
return kernel;
}
Fixed it.
I used Assembly.Load():
var result = Assembly.Load("NZBDash.DependencyResolver.Modules").GetTypes()
.Where(a =>
a.IsClass &&
a.BaseType == typeof(NinjectModule))
.ToArray();
I want register type, than resolve type, and then register instance using resolved values. Something like this:
//Register type:
builder.RegisterType<ValidateImportMandatoryColumns>().Named<IValidateImport>("MandatoryColumn").As<IValidateImport>();
builder.RegisterType<ValidateImportNonMandatoryColumns>().Named<IValidateImport>("NonMandatoryColumns").As<IValidateImport>();
//Resolve
var t1 = Container.ResolveNamed<IValidateImport>("MandatoryColumn");
var t2 = Container.ResolveNamed<IValidateImport>("NonMandatoryColumns");
//Create list with resolved values:
List<IValidateImport> allValidators = new List<IValidateImport>(){t1,t2};
//Register Instance:
builder.RegisterInstance(allValidators).As<List<IValidateImport>>();
This is not working. I can't resolve and than register again. Do you know how to do this with Autofac? Maybe is approach wrong, so please tell me if you have better idea. Goal is to inject list of validators with different types that use same interface.
Autofac has built in support for collection. If you want to resolve all IValidateImport, you can resolve IEnumerable<IValidateImport>
var allValidators = container.Resolve<IEnumerable<IValidateImport>>();
See Implicit Relationship Types for more information for more information.
By the way, if you want to update a container, which is not required here, you can use the following piece of code.
var builder = new ContainerBuilder();
// do some registration
var container = builder.Build();
var updater = new ContainerBuilder();
// do other registraitons
// update the container
updater.Update(container);
I have found what I think may be a bug in Autofac, but I wanted to see if anybody had a possible solution or workaround so I can make this work.
Basically I have set up a generic decorator, which works fine. The problem is that as soon as I call BeginLifetimeScope() with a configuration delegate, it erroneously resolves multiple components of the same type. If I don't use a configuration delegate with BeginLifetimeScope(), then it works correctly. Unfortunately, I need to add additional dependencies to my child scope, so not using a configuration delegate is not an option.
Here is an example that demostrates the problem:
var builder = new ContainerBuilder();
builder.RegisterType<Dependency>()
.Named<IDependency<object>>("service");
builder.RegisterGenericDecorator(
typeof(Decorator<>), typeof(IDependency<>), "service", "decorated");
var container = builder.Build();
// Returns 1
var scope1 = container.BeginLifetimeScope();
Console.WriteLine(
scope1.ResolveNamed<IEnumerable<IDependency<object>>>("decorated").Count());
// Returns 2 - notice the configAction doesn't even have to do anything
var scope2 = container.BeginLifetimeScope(r => { });
Console.WriteLine(
scope2.ResolveNamed<IEnumerable<IDependency<object>>>("decorated").Count());
And here are my fake types:
interface IDependency<T> { }
class Dependency : IDependency<object> { }
class Decorator<T> : IDependency<T> {}
Any help would be greatly appreciated!
It does just seem like a bug. As a workaround, I ended up doing the following:
var param = new TypedParameter(typeof(IDecoratorDependency), new DecoratorDependency());
var decorated = scope.ResolveNamed<IEnumerable<IDependency<object>>>("decorated", param);
That was good enough for my use case. However, this method is inflexible because it only allows me to supply parameters to the root object, in this case Decorator<T>, but not any of its dependencies.
With the new fluent MEF programming model, if I have multiple catalogs:
To which catalog do I pass my RegistrationBuilder to???
Do I need to pass RegistrationBuilder to SatisfyImportsOnce call?
Which of SatisfyImportsOnce or ComposeParts do I use? (has anything changed with this in fluent mef?)
E.g. Here is an example to llustrate my confusion (see comments on the r.h.s):
// Get pre-wired registration builder
RegistrationBuilder rb = new MefCompositionRoot().CommonRegistrationBuilder();
// Register this WCF service class
rb.ForType<LogService>().Export<LogService>();
var assembly = typeof (LogService).Assembly;
var assemblyCatalog = new AssemblyCatalog(assembly, rb); // <-- HERE?
var dirCatalog = new DirectoryCatalog("bin", rb); // <-- and HERE?
// Combine catalogs
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(dirCatalog);
catalog.Catalogs.Add(assemblyCatalog);
var container = new CompositionContainer(catalog);
container.SatisfyImportsOnce(this, rb);// THIS?
container.ComposeParsts(this); ///or THIS?
You can put the RegistrationBuilder to any of the catalogs you want to use MEF Conventions with. If you want to use only the Conventions Model, then use it in all catalogs. If the DirectoryCatalog in your sample code, will load assemblies containing only Attributed exports/imports then you do not need the RegistrationBuilder. Note that both models (Attributed and Contentions) can coexist. So, you could add the RegistrationBuilder to all catalogs unless there might be a type that satisfies a rule (configured using the RegistrationBuilder) that you do not want to use in your CompositionContainer.
This overload of SatisfyImportsOnce is a riddle. From the documentation (and from a quick look at MEF's source) it looks like you can use a specific RegistrationBuilder ad-hoc. In reality I have only managed to use it with the code sample that follows.
SatisfyImportsOnce disables recomposition. Check out this excellent answer on this subject.
Sample using SatisfyImportsOnce(Object, ReflectionContext)
private static void TestLateRegistration_SameBuilder_Ok()
{
var rb = new RegistrationBuilder();
var assemblyCatalog = new AssemblyCatalog(typeof(LogService).Assembly, rb);
using (var container = new CompositionContainer(assemblyCatalog))
{
rb.ForType<LogService>().Export();
var server = new TypeImportingLogService();
//Use the same RegistrationBuilder.
container.SatisfyImportsOnce(server, rb);
}
}