DryIoc with all classes Singleton and DbContext as WebRequest - dryioc

I am trying to create completely Singleton applications (Web and Console).
But the Entity DbContext should be used PerWebRequest on web.
How should I register it on container to support this?
I understand once the class has been initialized as a singleton, I will be running on a single instance on Memory with all injected classes as a Singleton too.
The following code is my Container Initialization for all web applications and console applications.
- How should I register when it is running in Console?
- When running on Web and Owin calls startup things, sometimes I need to resolve objects to use on Authentication, but Owin runs on "no context" environment. How to detect and use it?
private static IContainer Initialize(IContainer container)
{
if (container == null)
container = new Container(
rules => rules
.WithDefaultReuseInsteadOfTransient(Reuse.InWebRequest)
.WithFactorySelector(Rules.SelectLastRegisteredFactory())
.With(FactoryMethod.ConstructorWithResolvableArguments)
.WithoutThrowOnRegisteringDisposableTransient(),
scopeContext: new AsyncExecutionFlowScopeContext()
);
string prefix = GetPrefix();
var implementingClasses =
AppDomain.CurrentDomain.GetAssemblies().ToList()
.Where(x => x.FullName.StartsWith(prefix))
.SelectMany(x => x.GetTypes())
.Where(type =>
(type.Namespace != null && type.Namespace.StartsWith(prefix)) &&
type.IsPublic && // get public types
!type.IsAbstract && // which are not interfaces nor abstract
type.GetInterfaces().Length != 0); // which implementing some interface(s)
Parallel.ForEach(implementingClasses, implementingClass =>
{
container.RegisterMany(new[] { implementingClass }, Reuse.Singleton, serviceTypeCondition: type => type.IsInterface);
});
return container;
}

As you are using ambient scope context you can consume / inject DbContext as Func<DbContext> in your singletons. Then whenever Func is called, it will return value bound to current ambient scope / request.

Related

Container has different rules for .net 5 web app and test project

I have a small .net 5 WebApi app, using DryIoc. I am now trying to set up an xUnit test suite for this app, but the integration tests fail immediately because one of the registrations has multiple constructors, even though I am using the same rules for both containers and running the same registrations in the same order as the app.
The registration works fine for the app because the container has ConstructorWithResolvableArguments set for the factory method, but it's not being set from anywhere in our code. I know I can easily just add that rule to the container for the tests, but I don't understand why a container set up in the exact same way appears to have different rules, and I am concerned there may be other differences that could affect the tests.
In the app, the container is set up like so:
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new DryIocServiceProviderFactory(Startup.CreateContainer()))
.ConfigureWebHostDefaults(...);
StartUp.cs
...
public static IContainer CreateContainer() => new Container(DiConfiguration.SetRules);
public void ConfigureContainer(IContainer container)
{
container.RegisterCoreDependencies();
// Site specific registrations
container.ConfigureDomain();
}
DiConfiguration.cs
public static class DiConfiguration
{
public static Rules SetRules(Rules rules)
{
rules = rules.WithAutoConcreteTypeResolution();
rules = rules.WithVariantGenericTypesInResolve();
return rules;
}
public static void RegisterCoreDependencies(this IContainer container)
{
// dependency registrations, not much to see here, nothing clever
// just basic registrations e.g.
container.Register<IFoo, Foo>();
}
In the test suite I am setting up the container like so:
public class Test
{
private object _sut;
public Test()
{
var container = new Container(DiConfiguration.SetRules); // <--- Same rules as the app
container.RegisterCoreDependencies(); // <--- fails here
container.ConfigureDomain();
_sut = container.Resolve<Bar>();
}
[Fact]
public void Some_Test_Here()
{
...
}
}
As you can see, all the container registration code is abstracted out into a shared library (the DiConfiguration class). The test is failing calling RegisterCoreDependencies. I don't understand why the rules are different between the two scenarios, is it perhaps something introduced by the DryIocServiceProviderFactory call in the app setting some extra defaults? (DryIocServiceProviderFactory is part of the DryIoc.Microsoft.DependencyInjection package)
The DryIocServiceProviderFactory will override the existing container rules to conform to MS.DI container. Here is the code: https://github.com/dadhi/DryIoc/blob/5e3f1f7edfe237f69ba33c9166d17e284ca4781a/src/DryIoc.Microsoft.DependencyInjection/DryIocAdapter.cs#L97
Here how the rules are overriden in detail:
private static Rules WithMicrosoftDependencyInjectionRules(Rules rules)
{
rules = rules.Clone(cloneMade: true);
rules._settings |= Settings.TrackingDisposableTransients;
rules._settings &= ~Settings.ThrowOnRegisteringDisposableTransient;
rules._settings &= ~Settings.VariantGenericTypesInResolvedCollection;
rules._factorySelector = SelectLastRegisteredFactory;
rules._made._factoryMethod = DryIoc.FactoryMethod.ConstructorWithResolvableArguments;
return rules;
}

How to test method in XUnit that needs UserManager, but uses in-memory database

I'm using ASP.NET Core 3.1 and XUnit for my unit tests.
I built a database context factory class that instantiates an in-memory version of my database:
public static class DbContextFactory
{
public static ApplicationDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var modelBuilder = new ModelBuilder(new ConventionSet());
var dbContext = new ApplicationDbContext(options);
var onModelCreatingMethod = dbContext.GetType().GetMethod("OnModelCreating",
BindingFlags.Instance | BindingFlags.NonPublic);
onModelCreatingMethod.Invoke(dbContext,
new object[] { modelBuilder });
return dbContext;
}
}
This is the current test class I'm trying to use:
public class AdminServiceTests
{
public ApplicationDbContext context { get; set; }
public IAdminService adminService { get; set; }
public AdminServiceTests()
{
this.context = DbContextFactory.CreateDbContext();
this.adminService = new AdminService(userManager, context);
}
[Fact]
public async Task DeleteUserShouldDeleteUser()
{
// What to do ???
}
}
In order for me to test my admin service, I need to provide a user manager. It should be linked with the database I currently have created.
How can I make that happen?
You're making a common mistake of testing the framework. All your test needs to do is ensure that AdminService.DeleteUser calls UserManager.DeleteAsync. Whether or not that spirals down into actually removing the user from the database is 1) not a concern of the service and 2) an implementation detail of both ASP.NET Core Identity and EF Core, both of which have their own extensive test suites to ensure that happens.
As such, you can just use a library like Moq to create a mock of UserManager<TUser> and then do something like:
userManagerMock.Verify(x => x.DeleteAsync(user), Times.Once());
It's worth mentioning here that this also serves to point out a bit of a flaw in this kind of design. You have a dependency on ASP.NET Core Identity whether or not you put an AdminService wrapper around that. Unless your service is doing something special outside of just proxying to UserManager here (e.g. coordinating multiple actions, like maybe deleting the user triggers a notification or something), then your service is pointless, and you should just use UserManager directly. Developers make this kind of mistake constantly; abstraction for the sake of abstraction only hurts your code. It adds additional maintenance concerns, testing concerns, and obscures what the code is actually doing.

ASP.NET Web API Unit Test Autofac Module with BuildManager.GetReferencedAssemblies()

Working on a project in ASP.NET Web API 2 which has Autofac as my IoC container. This project is hosted on IIS and in my Autofac module I use the following method to scan for assemblies:
var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
Why?
https://docs.autofac.org/en/latest/register/scanning.html#iis-hosted-web-applications
But now we are making Unit Tests using NUnit, during my setup I register my module which uses this method. Now I receive the following exception when running my tests:
System.InvalidOperationException: 'This method cannot be called during the application's pre-start initialization phase.'
I understand why I have this exception but I don't have a clue how to make my code work in tests and for deployment environments.
Setup method of NUnit:
[TestFixture]
public abstract class ApplicationTestBase
{
[SetUp]
public override void Init()
{
var builder = new ContainerBuilder();
// If the class requires auto mapper mapping, initialize them
// We do this in order not to init them for every test => optimalisation!
if (GetType().GetCustomAttributes<RequiresAutoMapperMappingsAttribute>(false) != null)
{
builder.RegisterModule<AutoMapperModule>();
}
this.Container = builder.Build();
}
}
Do I need to create a new module specific for my Unit tests or is there another way for this?
AutoMapperTest
[RequiresAutoMapperMappings]
[TestFixture]
public class AutoMapperTests : ApplicationTestBase
{
[Test]
public void Assert_Valid_Mappings()
{
Mapper.AssertConfigurationIsValid();
}
}
UPDATE
Like Cyril mentioned: Why do you need Ioc in your unit tests? I went searching and indeed you don't have to use the Ioc in your tests. So I ditched the Ioc and initialized my mapper configuration byy doing:
Mapper.Initialize(configuration =>
{
var asm = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.StartsWith("ProjectWebService."));
configuration.AddProfiles(asm);
});
I would recommend separating the "how to load assemblies" logic from the "do assembly scanning and register modules logic."
Right now I'm guessing you have something like this all in one method.
public IContainer BuildContainer()
{
var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(asm);
var container = builder.Build();
}
Not exactly that, but something similar - the loading of assemblies is inlined and directly used.
Separate that so you can swap that logic in for testing. For example, consider allowing a parameter to be optionally passed so you can override the logic in test.
public IContainer BuildContainer(Func<IEnumerable<Assembly>> assemblyLoader = null)
{
IEnumerable<Assembly> asm = null;
if (assemblyLoader != null)
{
asm = assemblyLoader();
}
else
{
asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
}
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(asm);
var container = builder.Build();
}
Your default logic will work the way you want, but then in testing you can swap in something else.
var container = BuildContainer(() => AppDomain.GetAssemblies());
There are lots of ways you can do that swap-in. It could be anything from a static property you can set somewhere to a virtual method you can override somewhere. The point is, by separating the assembly loading logic you can get the test-time behavior to work but still use the registration behavior you're after.

How to Mock an AutoMapper IMapper object in Web API Tests With StructureMap Dependency Injection?

So I've build a WebAPI from scratch, including some best practices that I've found online such as Dependency Injection and Domain<->DTO mapping using auto mapper etc.
My API Controllers now look similar to this
public MyController(IMapper mapper)
{
}
and AutoMapper Registry:
public AutoMapperRegistry()
{
var profiles = from t in typeof(AutoMapperRegistry).Assembly.GetTypes()
where typeof(Profile).IsAssignableFrom(t)
select (Profile)Activator.CreateInstance(t);
var config = new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
});
For<MapperConfiguration>().Use(config);
For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));
}
I'm also building a few test cases, implementing MOQ, and this is where i feel a little unsure. whenever calling my controllers, I need to pass in an IMapper like this:
var mockMapper = new Mock<IMapper>();
var controller = new MyController(mockMapper.Object);
But then, how do i configure the IMapper to have the correct mappings? It feels redundant to recreate the same logic I've already created before to configure the Mapper. so I am wondering what is the recommended approach to do this?
That's pretty simple: if you mock IMapper and imagine it as a fully abstract concept of mapping data from one object to another, then you have to treat is an abstraction and not imply there's a real automapper behind it.
First you should not register any existing profile at all, you should instead setup IMapper.Map method to return specific object when given another object.
So for each profile used for specific method you have to do a setup, looking approximately like this:
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DestinationClass>(It.IsAny<SourceClass>()))
.Returns((SourceClass source) =>
{
// abstract mapping function code here, return instance of DestinationClass
});
In this case, your test knows nothing about actual IMapper implementation - it just uses it methods to get the data you expect from actual IMapper implementation to receive.
This might me another solution
//auto mapper configuration
var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile()); //your automapperprofile
});
var mapper = mockMapper.CreateMapper();
And then call then controller like so
var controller = new YourController(imapper:mapper,..otherobjects..);
This way it will serve the purpose or else if you create mock object for IMapper then it will return what you ask it to return.

creating own service with default doctrine2 connection in zf2

i'm learning zf2 with doctrine2.
Doctrine2 entities is not good place for putting logic there, so i have to create something like services.
I created new catalog in src - Service, and i thought that i will create abstractService class, which will have by default access to doctrine2 entity manager, and other usefull stuff further.
But i'm not sure how to achieve it, i created constructor, where i should pass instance of object manager through injection, but i don't know how to do it. All found examples are for controllers.
Can somebody show me way ho to manage/organise it?
I didn't found any solutions.
You can retrieve the default doctrine entity manager from the service manager using eighter doctrine.entitymanager.orm_default or the alias Doctrine\ORM\EntityManager.
$serviceLocator->get('Doctrine\ORM\EntityManager');
Best practice for dependency injection in ZF2 is to use service factories which you register on the service manager. There is plenty of information about this available.
Anyway I'll explain the steps.
Your service
namespace MyNamespace
class MyService
{
public function __construct(EntityManager $em)
{
$this->em = $em;
}
}
Factory
Define the factory in your module.php
class Module
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MyNamespace\MyService' => function($serviceLocator) {
return new MyService($serviceLocator->get('Doctrine\ORM\EntityManager');
}
)
);
}
}
I have used a closure for the factory for this example, but it's recommended to use a dedicated factory class.
Now you can retrieve your fully composed service from the service manager.
$serviceManager->get('MyNamespace\MyService');
If you want to use this service in your controller you'll need to define a factory for this in the same way I did above. There's just one small difference, because all controllers are managed by a seperate pluginManager (a serviceManager dedicated to creating classes of a certain type). You can use the method getControllerConfig in your module.php to define factories on the controller plugin manager.
Add this to your module.php
public function getControllerConfig()
{
return array(
'factories' => array(
'MyNamespace\Controller\MyController' => function($serviceLocator)
{
$rootLocator = $serviceLocator->getServiceLocator();
$service = $rootLocator->get('MyNamespace\MyService');
return new MyController($service);
}
)
);
}
Hope this helps.