Base solution for your next web application
Open Closed

How do you find the source of a dependency injection error? #9308


User avatar
0
michael.pear created

I'm extending a unit test project that I created separately from the <ANZProject>.Tests and <ANZProject>.Test.Base projects in order to test a separate DbContext, other than the ApsNetZero DbContext. I've been trying to keep dependencies to a minimum, so I'm not using the <ANZProject>.Test.Base module, but am depending on the AbpTestBaseModule. Now, I'm adding tests for an AppService, which is adding depenencies on the <ANZProject>ApplicationModule. And I'm getting an exception when running the new test:

  Message: 
    Abp.AbpException : Could not resolve DbContextOptions for NexusApps.EntityFrameworkCore.NexusAppsDbContext, NexusApps.EntityFrameworkCore, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null.
  Stack Trace: 
    DefaultDbContextResolver.CreateOptions[TDbContext](String connectionString, DbConnection existingConnection)    

The DbContext named is the original AspNetZero context for my project, not my separate DbContext, so this is due to the introduction of the AppService and module. The error/failure is reported on the new test case method, and my previous test case methods with the separate DbContext are working and passing.

What approach can I take to find the source of this dependency injection error? Is there a way to trace/debug the resolution problem to determine where this is happening?


5 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Did you configure your second DbContext in PreInitialize method of *EntityFrameworkCoreModule.cs ? If so, could you share it ?

  • User Avatar
    0
    michael.pear created

    Yes, I do for normal operation. I duplicated the configuration configuration code for the AZN project DB Context. But for the test cases I set "SkipDbContextRegistration" to true so that neither context is registered in the Core module, and I handle that in the TestBase class.

    In my TestModule class:

            public TrainingManagerTestModule(NexusAppsEntityFrameworkCoreModule dbModule)
            {
                //This keeps core module from setting setting up db context. 
                //Will do that within this class.
                dbModule.SkipDbContextRegistration = true;
            }
    

    In my TestBase class: <br>

     public TrainingManagerTestBase() : base()
            {
    
    
                if (_dbContext == null)
                {
                    var builder = new DbContextOptionsBuilder<TrainingManagerDbContext>();
                    builder.UseSqlServer(connectionString);
                    _dbContext = new TrainingManagerDbContext(builder.Options);
                    _dbContext.Database.EnsureCreated(); //Make sure database schema is created
                    //Check to see if the data is seeded already
                    var countTrainees = _dbContext.Trainees.Count();
                    if (countTrainees >0 )
                    {
                        IsSeeded = true;
                    } else
                    {
                        IsSeeded = false; //Indicate that data seeding is needed
                    }
    
    
                }
            }
    

    I did reduce my test case, and found that the error actually happens when the service is called. Here is the minimal test class that causes failure. If the call to the service is removed, there is no failure.  It would be nice to see where the IOC resolver is called that is resulting in the error. <br>

        public class TraineeAppService_Tests : TrainingManagerTestBase
        {
            private ITraineeAppService _traineeAppService;
            private TrainingManagerDbContext _trainingSchemaDb;
            private void InitializeDatabase()
            {
                _trainingSchemaDb = GetContext();
     
            }
    
            [Fact]
            public async Task GetTrainees_FromService()
            {
                InitializeDatabase();
                _traineeAppService = LocalIocManager.Resolve<ITraineeAppService>();
                var traineesAll = await _traineeAppService.GetTrainees(new NexusApps.Dto.PagedAndFilteredInputDto { Filter = String.Empty });
            }
        }
    
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @michael.pear,

    Is it possible to share your project with [email protected] ? If not, can you create a reproduction project ?

    Thanks,

  • User Avatar
    0
    michael.pear created

    I sent a link to a folder with my project to [email protected]. Please confirm that you have access.

    I am still interested in general approaches to finding dependency injection errors. Given the large number of classes in the dependency injection container, trial and error approaches are not feasible. I can see in the debugger that the LocalIocManager.IocContainer provides debugging views, and I can locate the component NexusAppsDbContext that is listed in the original error message. However, it doesn't tell me what triggers the resolution of the dependency that fails. I see mention of "diagnostic logging" in Castle Windsor which the Abp dependency injection is based on. How is that turned on? I would expect that to show me the information needed and give me an idea of how I can avoid the unnecessary attempted instantiation of NexusAppsDbContext that appears to be causing the problem.

  • User Avatar
    0
    michael.pear created

    Although I've not how to find the source of the dependency injection that is causing the error, I was able to satisfy the dependency by adding the component being requested in my test case with:

                var inMemorySqlite = new SqliteConnection("Data Source=:memory:");
                inMemorySqlite.Open();
                var options = new DbContextOptionsBuilder<NexusAppsDbContext>()
                    .UseSqlite(inMemorySqlite)
                    .Options;
    
                 LocalIocManager.IocContainer.Register(
                    Component
                    .For<DbContextOptions<NexusAppsDbContext>>()
                    .Instance(options)
                    .IsDefault()
                    .Named("OverridingDbContextOptions")
                    .LifestyleSingleton()
                    );
    

    <br> I'm looking at ways to avoid bringing in the entire ".Application" module, which seems to create the requirement for initializing the database context I do not need for my targeted tests. That sounds like another question, so I'm marking this question as answered.