I've created issue #3481 in Github for this:
https://github.com/aspnetzero/aspnet-zero-core/issues/3481
At the time I posted, I did not have a ServiceCollectionRegistrar in my test module. I added one that mimics the one provided with the AspnetZero project, and removed the flag I had added to skip the Resolve<ChatUserStateWatcher>, and it works now.
Is there someplace that explains what adding this does in the dependency injection process? I'd like to better understand the dependency injection system in Aspnetzero.
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.
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.
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 });
}
}
Subsitute for or whatever you ended up using for your Module file and Test base class. <br>
namespace <YourProjectName>
{
public class <YourProjectName>Base: <YourProjectName>Base<<YourProjectName>Module>
{
string connectionString = "YourConnectionString ";
private YourDbContext _dbContext = null;
public Boolean IsSeeded { get; set; }
public <YourProjectName>Base() : base()
{
if (_dbContext == null)
{
var builder = new DbContextOptionsBuilder<YourDbContext>();
builder.UseSqlServer(connectionString);
_dbContext = new YourrDbContext(builder.Options);
_dbContext.Database.EnsureCreated(); //Make sure database schema is created
//Check to see if the data is seeded already
var count = _dbContext.<SomeEntityThatWouldIndicateSeedingAlreadyDone>.Count();
if (count >0 )
{
IsSeeded = true;
} else
{
IsSeeded = false; //Indicate that data seeding is needed
}
}
}
public TrainingManagerDbContext GetContext()
{
//Check for seeding, and load data if not
if (!IsSeeded)
{
//Alternative to external data loader is to build in the seeding operation directly here
using (var loader = LocalIocManager.ResolveAsDisposable<YourDataLoader>())
{
loader.Object.CreateDbContext(connectionString);
loader.Object.LoadData(@"data\SeedData.json");
}
IsSeeded = true;
}
//Make sure _dbContext is not disposed during seeding operation!
return _dbContext;
}
}
public abstract class <YourProjectName>Base<T> : AbpIntegratedTestBase<T> where T : AbpModule
{
protected <YourProjectName>Base()
{
}
}
I replaced my project name with "<YourProjectName>" for your test project. Also, the data loading module reference can be left out if you will seed data directly in your test base class.
namespace <YourProjectName>
{
[DependsOn(
typeof(<DataLoadingModuleIfYouHaveOne>), //This is a module with data loading utilities
typeof(NexusAppsEntityFrameworkCoreModule),
typeof(AbpTestBaseModule))]
public class <YourProjectName>Module : AbpModule
{
public <YourProjectName>Module(<AspNetZeroProject>EntityFrameworkCoreModule dbModule)
{
//This keeps core module from setting setting up db context.
//Will do that within this class.
dbModule.SkipDbContextRegistration = true;
}
public override void PreInitialize()
{
var configuration = GetConfiguration();
//Use as means to communicate configinfo
LocalConfiguration = configuration;
//May not be needed.
Configuration.Modules.AbpAutoMapper().UseStaticMapper = false;
Configuration.ReplaceService<IAppConfigurationAccessor, <YourProjectName>ConfigurationAccessor>();
Configuration.Modules.AspNetZero().LicenseCode = configuration["AbpZeroLicenseCode"];
}
public IConfigurationRoot LocalConfiguration { get; set; }
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(<YourProjectName>Module).GetAssembly());
}
private static IConfigurationRoot GetConfiguration()
{
return AppConfigurations.Get(Directory.GetCurrentDirectory(), addUserSecrets: false);
}
}
}
I recently did this in order to allow for using my DB Context with MSSQLocalDb rather than SQLLite or an InMemory database, due to some of the limitations of those. I also had the objective of keeping the project as simple as possible, without all the cpmplexities that come along with the AspNetZero test. I found that modeling it after the ".GraphQL.Tests" was an easier starting point. ( is your project name) Here are the steps I followed: <br>
Confirmed that the order of the mapping in the "CreateMap" method written in CustomDtoMapper.cs by the Rad Power Tools has the Dto and Entity reversed, so either the order of the mapping needs to be changed to CreateMap<Entity,Dto> or add ".ReverseMap()". Hoping this can be fixed in the Power Tools quickly
I am seeing this problem using the Rad Power Tools 2.0.2.1 with a new V7.1.0 AspNetZero project. I can enter new entity through the generated interface with no problem, but an Automapper exception is thrown when attempting to edit the entity. The problem appears to be with the generated "CreateOrEdit" Dto. Here is exception (without full stacktrace) showing in log:
ERROR 2019-07-26 11:55:26,310 [121 ] Mvc.ExceptionHandling.AbpExceptionFilter - Missing type map configuration or unsupported mapping.
Mapping types:
Trainee -> CreateOrEditTraineeDto
NexusApps.TrainingManager.Trainee -> NexusApps.TrainingManager.Dtos.CreateOrEditTraineeDto
AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Mapping types:
Trainee -> CreateOrEditTraineeDto
NexusApps.TrainingManager.Trainee -> NexusApps.TrainingManager.Dtos.CreateOrEditTraineeDto