Integration test with Bounded DbContexts - c#

I'm using multiple DbContext(Bounded DbContext) in my application. So I have following codes:
public class EfUnitOfWork<TContext> : IUnitOfWork where TContext : BaseDbContext, new()
{
...
}
//*******************************Module1**************************************
public interface IModule1UnitOfWork:IUnitOfWork
{
}
public class Module1EfUnitOfWork : EfUnitOfWork<Module1DbContext>,IModule1UnitOfWork
{
public Module1EfUnitOfWork ()
: base(new Module1DbContext())
{
}
}
public class Module1DbContext: BaseDbContext
{
static Module1DbContext()
{
Database.SetInitializer<Module1DbContext>(null);
}
public Module1DbContext()
: base("name=MyDatabase")
{
}
}
//*******************************Module2**************************************
public interface IModule2UnitOfWork:IUnitOfWork
{
}
public class Module2EfUnitOfWork : EfUnitOfWork<Module2DbContext>,IModule2UnitOfWork
{
public Module2DbContext()
: base(new PlanningDbContext())
{
}
}
public class Module2DbContext: BaseDbContext
{
static Module2DbContext()
{
Database.SetInitializer<Module2DbContext>(null);
}
public Module1DbContext()
: base("name=MyDatabase")
{
}
}
I'm using StructureMap 3 as my IoC container too and used following codes:
ObjectFactory.Configure(x =>x.For(typeof(IModule1UnitOfWork))
.Use(typeof(Module1EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton));
x.For(typeof(IUnitOfWork))
.Use(typeof(Module1EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton)));
ObjectFactory.Configure(x =>x.For(typeof(IModule2UnitOfWork))
.Use(typeof(Module2EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton));
x.For(typeof(IUnitOfWork))
.Use(typeof(Module2EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton)));
It works fine, but when I want to use these codes for Integration Test, I have one problem.
I want to use Sql Server CE for my Integration Test, so I have to pass my test's ConnectionString(that specify the Sql Server CE database file) to the module's DbContexts, I used my module's DbContexts as Generic Parameters in the EfUnitOfWork<> class and so it should has parameterless constructor, but as you see I used my module's DbContext parameterless constructor for specifying my Database:
public Module1DbContext()
: base("name=MyDatabase");
and couldn't pass anything to it.
So, how could I use my existing DbContexts for Integration Test?

"name=" only designate a connection string, so it is an indirect parameter read from an app.config file.
So imho, you should just use a different app.config for your tests.

Related

ServiceStack IAppSettings was not ready and would result NULL reference exception if used in constructor

It seems like the IAppSettings implementation was not ready from IoC in the constructor.
Before I go into details, I've read similar problems:
ServiceStack doesn't auto-wire and register AppSettings
Instantiation of POCO objects with ServiceStack's IAppSettings is not working
Both were answered by #mythz that he was not able to reproduce it.
From the Doc
"ServiceStack made AppSettings a first-class property, which defaults to looking at .NET's App/Web.config's.": https://docs.servicestack.net/appsettings#first-class-appsettings
And there is default IoC registration already in Funq to give you AppSettings when you ask for IAppSettings:
What I have
All my codes are in the repo: https://github.com/davidliang2008/MvcWithServiceStack
The demo app is just an ASP.NET MVC app (.NET 4.8) that built using the template, the simplest you can get, with ServiceStack (5.12.0) installed:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
new AppHost().Init();
}
}
public class AppHost : AppHostBase
{
public AppHost() : base("MvcWithServiceStack", typeof(ServiceBase).Assembly) { }
public override void Configure(Container container)
{
SetConfig(new HostConfig
{
HandlerFactoryPath = "api";
}
ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
}
}
Then I have a base class for the ServiceStack Service, and a HelloService just to demo:
public abstract class ServiceBase : Service { }
public class HelloService : ServiceBase
{
public IAppSettings AppSettings { get; set; }
public object Get(HelloRequest request)
{
return new HelloResponse
{
Result = $"Hello, { request.Name }! Your custom value is { AppSettings.Get<string>("custom") }."
};
}
}
[Route("/hello/{name}")]
public class HelloRequest : IReturn<HelloResponse>
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
What works
When you're not using IAppSettings in the constructor, whether in the HelloService or its base class ServiceBase, everything works out fine.
When you clone the project to your local, if you navigate to /api/hello/{your-name}, you will see its response would be able to get the custom value from web.config:
What doesn't work
When you're trying to get the IAppSettings and initialize something else with some app setting values in the constructor - whether it's in the child class or the base class, IAppSettings will fail to get the implementation from IoC, and result a NULL reference exception:
public abstract class ServiceBase : Service
{
public IAppSettings AppSettings { get; set; }
public ServiceBase()
{
// AppSettings would be NULL
var test = AppSettings.Get<string>("custom");
}
}
OR
public class HelloService : ServiceBase
{
public HelloService()
{
// AppSettings would be NULL
var test = AppSettings.Get<string>("custom");
}
}
You cannot use any property dependency in the constructor since the properties can only be injected after the class is created and the constructor is run.
You'll only be able to access it in the Constructor by using constructor injection, e.g:
public class HelloService : ServiceBase
{
public HelloService(IAppSettings appSettings)
{
var test = appSettings.Get<string>("custom");
}
}
Or accessing the dependency via the singleton:
public class HelloService : ServiceBase
{
public HelloService()
{
var test = HostContext.AppSettings.Get<string>("custom");
}
}

MOQ SetupGet dbContext with constructor parameters

Situation
Here I am, trying to write some unit tests for my GroupService with the use of MOQ.
To create an instance of my GroupService, I mocked 4 interfaces that needed to be passed through the constructor. Now on one of the mocks (IGroupRepository) a property called Context is called and my idea was to SetupGet this property and just simply return a fake list of GroupUser. But I keep getting errors, whatever I try.
Code
public class GroupServiceTests
{
private readonly GroupService _groupService;
private readonly Mock<AppDbContext> _dbContext;
private readonly Mock<IGroupRepository> _groupRepository;
private readonly Mock<IComponentService> _componentService;
private readonly Mock<IUserContextService> _userContextService;
private readonly Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>> _mapper;
public GroupServiceTests()
{
var groupUsersMock = CreateDbSetMock(GetFakeListOfGroupUsers());
_dbContext = new Mock<AppDbContext>(new DbContextOptions<AppDbContext>());
_dbContext.SetupGet(x => x.GroupUser).Returns(groupUsersMock.Object);
_groupRepository = new Mock<IGroupRepository>();
_groupRepository.SetupGet(repo => repo.Context).Returns(_dbContext.Object);
_componentService = new Mock<IComponentService>();
_userContextService = new Mock<IUserContextService>();
_mapper = new Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>>();
_groupService = new GroupService(_groupRepository.Object, _componentService.Object, _userContextService.Object, _mapper.Object);
}
}
In the GroupService this line is called:
// _repository reffers to IGroupRepository
userIdsForContextReset.AddRange(_repository.Context.GroupUser.Where(x => groupIds.Contains(x.GroupId)).Select(x => x.UserId));
And the GroupRepository and EntityRepository look like this:
public interface IGroupRepository : IEntityRepository<AppDbContext, Group>
{
List<GroupPermission> GetInheritedGroupPermissions(int groupId);
}
public class GroupRepository : EntityRepository<AppDbContext, Group>, IGroupRepository
{
public GroupRepository(AppDbContext dbContext) : base(dbContext)
{
}
public List<GroupPermission> GetInheritedGroupPermissions(int groupId)
{
// Removed for brevity
}
}
public class EntityRepository<TDbContext, TEntity> : EntityRepository<TDbContext, TEntity, int>, IEntityRepository<TDbContext, TEntity>
where TDbContext : DbContext
where TEntity : class, IEntity<int>
{
public EntityRepository(TDbContext dbContext) : base(dbContext)
{
}
}
public class EntityRepository<TDbContext, TEntity, TId> : IEntityRepository<TDbContext, TEntity, TId>
where TDbContext : DbContext
where TEntity : class, IEntity<TId>
where TId : IComparable
{
public EntityRepository(TDbContext context)
{
Context = context;
}
public TDbContext Context { get; }
}
And last but not least, the AppDbContext and SqlDbContext:
public class AppDbContext : Shared.DbContexts.SqlDbContext
{
public virtual DbSet<GroupUser> GroupUser { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
}
public class SqlDbContext : DbContext
{
public SqlDbContext(DbContextOptions options) : base(options)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
ChangeTracker.StateChanged += ChangeTracker_StateChanged;
}
}
Error
The error that I am getting is inside the SqlDbContext on the 1st line inside the constructor and says the following:
System.InvalidOperationException: 'No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.'
What am I doing wrong?
When you mock an implementation it creates the object using the constructor matching the parameters provided; it runs that code.
Additionally, anything not able to be mocked (not virtual or abstract) will run as is. In this case, you're passing in DbContextOptions and you haven't specified a provider, and something needs that.
This can be an opinionated topic, however to solve your problem there are a number of ways you could do it:
Add a parameterless constructor to your DbContext for testing. I wouldn't recommend this as I follow the mantra of not changing your SUT for a test.
Use an in-memory provider; EF Core In-Memory Database Provider or SQLite EF Core Database Provider are two that I have used. They do have limitations but for the OP usage would probably be fine and addresses Microsofts notes about how you shouldn't mock the DbContext.
Use an existing library such as EntityFrameworkCore.Testing (disclaimer, I am the author) which will extend in-memory providers to address their limitations.

Simple Injector - Inject service based on the specified generic type at runtime

I was a long time user of Autofac that recently switched to Simple Injector for my DI container needs. When I used Autofac, I was able to do something I'm still not able to do with Simple Injector, maybe because I do not yet perfectly understand the API.
Let's say I have the service IEntityRepository of TEntity and TDbContext. It's implementation looks like that:
public class EntityRepository<TEntity, TDbContext> : IEntityRepository<TEntity, TDbContext>
where TDbContext : IEntityDbContext where TEntity : class
{
public EntityRepository(TDbContext dbContext)
{
}
}
With Autofac, I was able to register the open generic implementation EntityRepository as the open generic interface IEntityRepository, so when I would inject say, IEntityRepository of Product and IProductsDbContext, the DI container would automatically guess that I inject through the constructor an instance of ProductsDbContext.
Is this possible with Simple Injector? I tries these, but it still fails:
container.Register(typeof(IEntityRepository<,>), typeof(EntityRepository<,>).Assembly);
container.Register(typeof(IEntityRepository<,>), typeof(EntityRepository<,>));
Thanks in advance for you help!
EDIT:
So here's a full exemple with Autofac as requested by Steven. Create a new .NET Core Console Application. You'll need to install the NuGet Package Autofac.
Program.cs:
internal class Program
{
private static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<ProductsDbContext>().AsImplementedInterfaces();
builder.RegisterGeneric(typeof(EntityRepository<,>)).As(typeof(IEntityRepository<,>));
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var productsRepository = scope.Resolve<IEntityRepository<Product, IProductsDbContext>>();
Console.WriteLine($"Resolved IEntityRepository is of type: {productsRepository.GetType()}");
}
}
}
ProductsDbContext.cs
public class ProductsDbContext : IProductsDbContext
{
public void Dispose()
{
// Demo, do nothing.
}
public int SaveChanges()
{
throw new System.NotImplementedException();
}
}
Product.cs
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
EntityRepository.cs
public class EntityRepository<TEntity, TDbContext> : IEntityRepository<TEntity, TDbContext>
where TDbContext : IEntityDbContext where TEntity : class
{
private readonly TDbContext _dbContext;
public EntityRepository(TDbContext dbContext)
{
_dbContext = dbContext;
Console.WriteLine($"Database context is of type {dbContext.GetType()}.");
}
public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> whereClause)
{
throw new NotImplementedException();
}
}
IEntityDbContext.cs
public interface IEntityDbContext : IDisposable
{
int SaveChanges();
}
IProductsDbContext.cs
public interface IProductsDbContext : IEntityDbContext
{
}
IEntityRepository.cs
public interface IEntityRepository<TEntity, TDbContext> where TDbContext : IEntityDbContext where TEntity : class
{
IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> whereClause);
}
The final console output should ressemble to:
Database context is of type
GenericTypeDiTester.DbContexts.ProductsDbContext. Resolved
IEntityRepository is of type:
GenericTypeDiTester.Repositories.EntityRepository`2[GenericTypeDiTester.Models.Product,GenericTypeDiTester.Interfaces.DbContexts.IProductsDbContext]
You can download the full example there: https://drive.google.com/file/d/1UkIYxLsY6YGwo5jOB5TyyncXc6yho8X5/view?usp=sharing
EDIT 2:
The problem wasn't with the Simple Injector library at the end. It seems that mixing the usage of Microsoft.DependencyInjection and SimpleInjector isn't really a good thing. As suggested by Steven, you should exclusively use SI to register the majority of your services and in rare case, MS.DI (by example for using AddDbContext).
As for my part, I have in my project MediatR, a library that implements the Mediator pattern. This library offers a NuGet package with an extension method AddMediatR for the IServiceCollection of MS.DI, which is supposed to register all handlers properly, but it wasn't the case for me. So I ended up registering the module all by myself using SI.
At the end it everything worked perfectly. You really need to call these lines at the end of the registering process: EnableSimpleInjectorCrossWiring and UseSimpleInjectorAspNetRequestScoping. Nothing else must be registered using the IServiceCollection afterwards. That way, the cross wiring of both DI framework ends up to work beautifully.
The way to register this in Simple Injector is:
container.Register(typeof(IEntityRepository<,>), typeof(EntityRepository<,>));
container.Register<IProductsDbContext, ProductsDbContext>();
There is no AsImplementedInterfaces equivalent in Simple Injector, although there are several ways to achieve rhe same. In the case that ProductsDbContext has multiple interfaces that need to be registered, the most obvious way is to register each interface explicitly:
container.Register<IProductsDbContext, ProductsDbContext>();
container.Register<IUsersDbContext, ProductsDbContext>();
container.Register<ICustomersDbContext, ProductsDbContext>();

StructureMap 4 hiding real exceptions - how do I make it throw the correct ones?

I have a web project containing 3 layers: Web (MVC5), BusinessLayer, DataAccess. I use StructureMap 4, Structuremap.MVC5 and StructureMap.WebApi2 to provide the default IoC configuration.
This is my configuration:
public static class IoC {
public static IContainer Initialize() {
var container = new Container(c => c.AddRegistry<DefaultRegistry>());
return container;
}
}
public class DefaultRegistry : Registry {
public DefaultRegistry() {
this.IncludeRegistry<DataAccessLayerRegistry>();
this.IncludeRegistry<BusinessLayerRegistry>();
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
}
}
The DataAccessLayerRegistry and BusinessLayerRegistry don't really do anything apart from scanning their respective dlls with DefaultConventions
Everything else is as generated by templates.
I inject dependencies in such hierarchical way:
Web:
public class HomeController : Controller
{
private ITestClass _myTest;
public HomeController(ITestClass testClass)
{
_myTest = testClass;
}
}
BusinessLayer:
public class TestClass : ITestClass
{
public TestClass(ITestValueRepository repo)
{
}
}
DataAccess:
public class TestValueRepository : ITestValueRepository
{
IMyContext _dbContext;
public TestValueRepository(IMyContext dbContext)
{
_dbContext = dbContext;
}
}
This all works fine and the dependencies are resolved correctly but when there is an error in one of the constructors somewhere down the road, for example an error creating the IMyContext instance (which is an EntityFramework DbContext), I don't get to see the real exception that happened there (for example issue with EF configuration). Instead this is what I see:
No parameterless constructor defined for this object.
[InvalidOperationException: An error occurred when trying to create a
controller of type 'XXX.Web.Controllers.HomeController'. Make sure
that the controller has a parameterless public constructor.]
There is no inner exception nor additional stack trace info that could lead to the real problem. Why is StructureMap hiding the real exception? Is there any way that I can set the StructureMap configuration to make it throw the real exceptions?

Accessing strongly typed configuration settings directly into class library in ASP.NET 5 (vNext)?

I have an ASP.NET 5 MVC 6 application. It has a Data Access library which needs a connection string to make a connection to the database.
Currently I am passing a strongly typed configuration settings class with connection string as a public property all the way up from the MVC controllers (Where it is received through DI) to the Data Access Class library.
I want to know if there is a better way for a class library to access strongly typed configuration settings using dependency injection or any other mechanism ?
Thank you.
EDIT : Code Example
This is a generic DbTransaction class which is called from the business layer.
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(string connectionString)
{
_query = new Query(connectionString);
}
public TEntity GetById(long id)
{
var sqlGenerator = new SqlGenerator<TEntity>();
var sql = sqlGenerator.GetSelectByIdQuery();
var mapper = new NMapper.Mapper<TEntity>();
var cmd = _query.GetNpgsqlCommand(sql, new { id });
return mapper.GetObject(cmd);
}
}
The query class creates the connection object from the connection string that is provided to it.
I agree with #Steven that using IOptions<T> is a bad idea. You can however use the ConfigurationBinder extensions to read out a specific section of configuration into a strongly-typed POCO class. Just make sure you have this somewhere in your project.json's dependencies section:
"dependencies": {
[other dependencies],
"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final",
[other dependencies]
}
Just build up your configuration as normal. For example, say you had a Database.json configuration file that looked like this:
{
"Database": {
"ConnectionInfo": {
"connectionString": "myConnectionString"
}
}
}
You can build your configuration from the Startup method in Startup.cs:
public IConfiguration Configuration { get; private set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("Database.json")
.AddEnvironmentVariables()
Configuration = configBuilder.Build();
}
Now we can make a POCO class to match the "Database:ConnectionInfo" section of the JSON configuraiton file. You can match it to an interface as #janhartmann suggests, but it may or may not be necessary.
public class DatabaseConnectionInfo {
public string ConnectionString { get; set; }
}
Now, how can we get that DatabaseConnectionInfo class populated with the data from the JSON config file? One way is to use the IOptions<T> framework type, but I don't like using framework types when I can avoid them. Instead, you can get an instance like so:
DatabaseConnectionInfo dbConnInfo = Configuration
.GetSection("Database:ConnectionInfo")
.Get<DatabaseConnectionInfo>();
Now you can just register the dbConnInfo type as a singleton of the type DatabaseConnectionInfo (or as a singleton of an interface type if you prefer to have an immutable configuration settings object). Once it's registered in the IoC container, you can constructor inject it where needed:
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(DatabaseConnectionInfo dbConnInfo)
{
_query = new Query(dbConnInfo.ConnectionString);
}
public TEntity GetById(long id) { ... }
}
You can let your service class depend on a an interface, e.g.:
public interface IConnectionFactory {
string ConnectionString();
}
public class MyDataAccessClass {
private readonly IConnectionFactory _connectionFactory
public MyDataAccessClass(IConnectionFactory connectionFactory) {
_connectionFactory = connectionFactory;
}
public void Whatever() {
var connectionString = _connectionFactory.ConnectionString();
}
}
And then make an implementation of it (as near to your composition root as possible):
public class SqlConnectionFactory : IConnectionFactory {
public string ConnectionString() {
return "myConnectionString";
}
}
Let the interface have the methods or properties you need.
Wire like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionFactory, SqlConnectionFactory>();
}
I use a similar method to some of those listed earlier, but I think its sufficiently different to warrant another answer.
Firstly I define an interface with all the configuration that my class needs. In this case
public interface IDbTransactionsConfiguration {
string ConnectionString { get; }
}
Then I alter my class to take this configuration via constructor injection
public class DbTransactions<TEntity> where TEntity : DbEntity, new() {
public DbTransactions(IDbTransactionsConfiguration configuration) {
...
}
}
Then I define a class that handles all the configuration for my application.
public class MyApplicationConfiguration : IDbTransactionsConfiguration, ISomeOtherConfiguration, etc {
public string ConnectionString { get; }
... other configuration
}
Then I pass this class into all classes that need it using some kind of Depenendency Injection (normally Castle Windsor or AutoFac for me).
If it is too difficult to construct DbTransactions for legacy type reasons, I define a static version of MyApplicationConfiguration and access this directly.
More details on this blog post.

Categories