Microsoft DependencyInjection: ServiceProviderFactory doesn't work as expected? - c#

Please checkout the below codes
using Microsoft.Extensions.DependencyInjection;
IServiceProviderFactory<IServiceCollection> serviceProviderFactory =
new DefaultServiceProviderFactory(new ServiceProviderOptions {
ValidateOnBuild = true,
ValidateScopes = true
});
IServiceCollection oldServiceCollection = new ServiceCollection();
IServiceCollection newServiceCollection =
serviceProviderFactory.CreateBuilder(oldServiceCollection);
Assert.IsTrue(oldServiceCollection == newServiceCollection);
I wanted to create a newServiceCollection base on the oldServiceCollection (then modify the newServiceCollection). However (big surprise) the ServiceProviderFactory, despite being a "Factory" with "Create.." methods, it did not create anything...
The newServiceCollection IS the oldServiceCollection. (If I modified the newServiceCollection then the oldServiceCollection will be modified as well).
I think that "DefaultServiceProviderFactory" (of Microsoft) is at fault here, do anybody know a better implementation which can help me clone the oldServiceCollection to make a newServiceCollection?

This is by design. The DefaultServiceProviderFactory isn't useful and only exists for other DI Containers to intercept the creation of IServiceCollection instances.
To make a copy, you'll have to iterate the old collection and add all ServiceDescriptors to the new one:
var newServiceCollection = new ServiceCollection();
foreach (var descriptor in oldServiceCollection)
{
newServiceCollection.Add(descriptor);
}

Related

How to mock IConfiguration.GetValue

I tried in vain to mock a top-level (not part of any section) configuration value (.NET Core's IConfiguration). For example, neither of these will work (using NSubstitute, but it would be the same with Moq or any mock package I believe):
var config = Substitute.For<IConfiguration>();
config.GetValue<string>(Arg.Any<string>()).Returns("TopLevelValue");
config.GetValue<string>("TopLevelKey").Should().Be("TopLevelValue"); // nope
// non generic overload
config.GetValue(typeof(string), Arg.Any<string>()).Returns("TopLevelValue");
config.GetValue(typeof(string), "TopLevelKey").Should().Be("TopLevelValue"); // nope
In my case, I also need to call GetSection from this same config instance.
You can use an actual Configuration instance with in-memory data.
//Arrange
var inMemorySettings = new Dictionary<string, string> {
{"TopLevelKey", "TopLevelValue"},
{"SectionName:SomeKey", "SectionValue"},
//...populate as needed for the test
};
IConfiguration configuration = new ConfigurationBuilder()
.AddInMemoryCollection(inMemorySettings)
.Build();
//...
Now it is a matter of using the configuration as desired to exercise the test
//...
string value = configuration.GetValue<string>("TopLevelKey");
string sectionValue = configuration.GetSection("SectionName").GetValue<string>("SomeKey");
//...
Reference: Memory Configuration Provider
I do not have idea about NSubstitute, but this is how we can do in Moq.
Aproach is same in either cases.
GetValue<T>() internally makes use of GetSection().
You can Mock GetSection and return your Own IConfigurationSection.
This includes two steps.
1). Create a mock for IConfigurationSection (mockSection) & Setup .Value Property to return your desired config value.
2). Mock .GetSection on Mock< IConfiguration >, and return the above mockSection.Object
Mock<IConfigurationSection> mockSection = new Mock<IConfigurationSection>();
mockSection.Setup(x=>x.Value).Returns("ConfigValue");
Mock<IConfiguration> mockConfig = new Mock<IConfiguration>();
mockConfig.Setup(x=>x.GetSection(It.Is<string>(k=>k=="ConfigKey"))).Returns(mockSection.Object);
Mock IConfiguration
Mock<IConfiguration> config = new Mock<IConfiguration>();
SetupGet
config.SetupGet(x => x[It.Is<string>(s => s == "DeviceTelemetryContainer")]).Returns("testConatiner");
config.SetupGet(x => x[It.Is<string>(s => s == "ValidPowerStatus")]).Returns("On");
IConfiguration.GetSection<T> must be mocked indirectly. I don't fully understand why because NSubstitute, if I understand correctly, creates its own implementation of an interface you're mocking on the fly (in memory assembly). But this seems to be the only way it can be done. Including a top-level section along with a regular section.
var config = Substitute.For<IConfiguration>();
var configSection = Substitute.For<IConfigurationSection>();
var configSubSection = Substitute.For<IConfigurationSection>();
configSubSection.Key.Returns("SubsectionKey");
configSubSection.Value.Returns("SubsectionValue");
configSection.GetSection(Arg.Is("SubsectionKey")).Returns(configSubSection);
config.GetSection(Arg.Is("TopLevelSectionName")).Returns(configSection);
var topLevelSection = Substitute.For<IConfigurationSection>();
topLevelSection.Value.Returns("TopLevelValue");
topLevelSection.Key.Returns("TopLevelKey");
config.GetSection(Arg.Is<string>(key => key != "TopLevelSectionName")).Returns(topLevelSection);
// GetValue mocked indirectly.
config.GetValue<string>("TopLevelKey").Should().Be("TopLevelValue");
config.GetSection("TopLevelSectionName").GetSection("SubsectionKey").Value.Should().Be("SubsectionValue");
I could imagine in some rare scenarios it is needed but, in my humble opinion, most of the time, mocking IConfiguration highlight a code design flaw.
You should rely as much as possible to the option pattern to provide a strongly typed access to a part of your configuration. Also it will ease testing and make your code fail during startup if your application is misconfigured (instead than at runtime when the code reading IConfiguration is executed).
If you really(really) need to mock it then I would advice to not mock it but fake it with an in-memory configuration as explained in #Nkosi's answer
While Nkosi's answer works great for simple structures, sometimes you want to be able to have more complex objects (like arrays) without repeating the whole section path and to be able to use the expected types themselves. If you don't really care too much about performance (and should you in your unit tests?) then this extension method might be helpful.
public static void AddObject(this IConfigurationBuilder cb, object model) {
cb.AddJsonStream(new MemoryStream(Encoding.UTF8.GetString(JsonConvert.SerializeObject(model))));
}
And then use it like this
IConfiguration configuration = new ConfigurationBuilder()
.AddObject(new {
SectionName = myObject
})
.Build();
Use SetupGet method to mock the Configuration value and return any string.
var configuration = new Mock<IConfiguration>();
configuration.SetupGet(x => x[It.IsAny<string>()]).Returns("the string you want to return");
We need to mock IConfiguration.GetSection wichs is executed within GetValue extension method.
You inject the IConfiguration:
private readonly Mock<IConfiguration> _configuration;
and the in the //Arrange Section:
_configuration.Setup(c => c.GetSection(It.IsAny())).Returns(new Mock().Object);
It worked like a charm for me.
I've found this solution to work reliably for me for my XUnit C# tests & corresponding C# code:
In Controller
string authConnection = this._config["Keys:AuthApi"] + "/somePathHere/Tokens/jwt";
In XUnit Test
Mock<IConfiguration> mockConfig = new Mock<IConfiguration>();
mockConfig.SetupGet(x => x[It.Is<string>(s => s == "Keys:AuthApi")]).Returns("some path here");

How to resolve list of dependencies in Autofac?

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);

How to register a named instance with Simple Injector

I'm developing a WinForm application and I'm using Repository Pattern , I'm using Servicestack OrmLite for data access and Simple Injector for IoC.
In my program.cs I have 2 register 2 different OrmLiteConnectionFactory but currently I'm not able to since I can't register named... with FunQ I can do this with
container.Register<IDbConnectionFactory>("db1", _ => new OrmLiteConnectionFactory(
ConfigurationManager.ConnectionStrings["db1"].ConnectionString,
SqlServerDialect.Provider));
container.Register<IDbConnectionFactory>("db2", _ => new OrmLiteConnectionFactory(
ConfigurationManager.ConnectionStrings["db2"].ConnectionString,
SqlServerDialect.Provider));
here is mine program.cs Bootstrap method
private static void Bootstrap()
{
// Create the container as usual.
Container = new Container();
string conn1 = ConfigurationManager.ConnectionStrings["dbconn1"].ConnectionString;
string conn2 = ConfigurationManager.ConnectionStrings["dbconn2"].ConnectionString;
OrmLiteConnectionFactory connectionFactory = new OrmLiteConnectionFactory(conn1,
ServiceStack.OrmLite.SqlServer.SqlServerOrmLiteDialectProvider.Instance);
Container.RegisterSingle<OrmLiteConnectionFactory>(connectionFactory1);
OrmLiteConnectionFactory connectionFactory2 = new OrmLiteConnectionFactory(conn2,
ServiceStack.OrmLite.SqlServer.SqlServerOrmLiteDialectProvider.Instance);
// how do I register this one without a name?
Container.RegisterSingle<OrmLiteConnectionFactory>(connectionFactory2);
Container.Register<MainForm>();
}
Thanks in advance
Since you have have two different databases with each a different schema, the different OrmLiteConnectionFactory aren't interchangable. This means that if you swap them around, your system will stop working. That's an indication that you are breaking the Liskov Substitution Principle. So the general solution in that case is to give each factory its own interface and let each repository depend on that particular interface. That allows you to register them uniquely and let the container resolve the object graph without doubt.
If giving each factory its own abstraction is not an option, or convenient, another option is to register your repositories without auto-wiring. By registering your repository with a delegate, you are completely in control over what to inject into your repository. For instance:
container.Register<IOrderRepository>(() => new SqlOrderRepo(connectionFactory1));
container.Register<IUserRepository>(() => new SqlUserRepo(
connectionFactory2,
container.GetInstance<ILogger>()));
A third option is to use context based injection, were you do the following:
var f1 = Lifestyle.Singleton.CreateRegistration<IDbConnectionFactory>(
() => new OrmLiteConnectionFactory(
conn1,
ServiceStack.OrmLite.SqlServer.SqlServerOrmLiteDialectProvider.Instance),
container);
var f2 = Lifestyle.Singleton.CreateRegistration<IDbConnectionFactory>(
() => new OrmLiteConnectionFactory(
conn2,
ServiceStack.OrmLite.SqlServer.SqlServerOrmLiteDialectProvider.Instance),
container);
container.RegisterConditional(typeof(IDbConnectionFactory), f1, InjectedInto<SqlOrderRepo>);
container.RegisterConditional(typeof(IDbConnectionFactory), f2, InjectedInto<SqlUserRepo>);
// Helper method.
static bool InjectedInto<T>(PredicateContext c) =>
c.Consumer.ImplementationType == typeof(T);
Out of the box Simple Injector does not support registering keyed registrations, but there are several ways to do this as explained on this page. That page also explains why there is no built-in support for this. This is a deliberate design decision.

Autofac generic decorator is duplicating components

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.

When do I pass the RegistrationBuilder to Catalog?

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);
}
}

Categories

Resources