I've created an injectable dbcontext
Startup.cs:
public void ConfigureServices(IServiceCollection services) {
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddDbContext<DBContext>(options => options.UseSqlServer("Server=localhost;Database=mydb;Trusted_Connection=True;"));
}
UnitOfWork:
public class UnitOfWork : IUnitOfWork {
private readonly DBContext _context;
public UnitOfWork(DBContext context) {
_context = context;
}
The injection working fine in the controller:
public class UserController : ControllerBase {
private readonly IUnitOfWork unitOfWork;
public UserController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
}
How can I create custom class that takes IUnitOfWork in the constructor and call it from main program?
CustomClass :
public class CustomClass {
private readonly IUnitOfWork unitOfWork;
public CustomClass(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
}
Main program:
public class Program {
public static void Main(string[] args) {
var unitOfWork=new UnitOfWork() // Here I don't want to pass new DBContext I want to reach the same injected DBContext
var customClass =new CustomClass (unitOfWork);
}
}
Firstly, move out your code from ConfigureServices to some shared library that can be used both by Web and Console project. Create extension method to configure all your services.
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp13
{
public static class ConfigureServicesExtensions
{
public static void ConfigureMyServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddDbContext<ApplicationDbContext>();
serviceCollection.AddScoped<IUnitOfWork, UnitOfWork>();
serviceCollection.AddScoped<CustomClass>();
}
}
}
This is how your Console app will look like
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp13
{
class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.ConfigureMyServices();
using var serviceProvider = serviceCollection.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var myService = scope.ServiceProvider.GetService<CustomClass>();
}
}
}
And your web project
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureMyServices();
}
What you can do is just add your class to the dependecy injection container and inject it into your constructor, you wouldn't need to inject the IUnitOfWork in you controller.
services.AddScoped<CustomClass>();
and then in your constroller constructor
public class UserController : ControllerBase {
private readonly CustomClass _CustomClass;
public UserController(CustomClass customClass) {
_CustomClass = customClass;
}
}
after that you are able to use this class in your class methods
I would try something like this:
public class Program {
public static void Main(string[] args) {
var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
optionsBuilder.UseSqlServer(connectionstring);
using(DbContext dbContext = new DbContext(optionsBuilder.Options))
{
var unitOfWork=new UnitOfWork(dbContext)
var customClass =new CustomClass (unitOfWork);
.....
}
}
}
Related
I am using Autofac for IoC
Here is my container initiator class, which the responsibility is to register the dependencies.
public class ContainerInit
{
public static IContainer BuildContainer()
{
var conFac = new ContainerFactory();
var builder = new ContainerBuilder();
builder.Register(conFac).As<IContainerFactory>().SingleInstance();
builder.Register(c=> new MainClass(conFac)).As<IMainClass>().SingleInstance();
builder.Register(c=> new Database(conFac)).As<IDatabase>().SingleInstance();
var logger = LoggUtil.CreateLogger();
builder.Register(logger).As<ILogger>().SingleInstance();
var container = builder.Build();
ContainerFactory.SetContainer(container);
return container;
}
}
Problem with this approach is, I need to pass IContainerFactory to the constructor of every class I use in my application as follow
public class MainClass: IMainClass
{
private readonly ILogger _logger;
private readonly IDatabase _db;
public MainClass(IContainerFactory containerFactory)
{
_logger = containerFactory.GetInstance<ILogger>();
_db = containerFactory.GetInstance<IDatabase>(); //example
}
public AddDetails(Data data)
{
//do some business operations
_db.Add(data);
_logger.Information("added");
}
}
So it is difficult to unit test these classes.
How can come up with a good solution?
A better approach would be to pass the dependencies you need in your class into your constructor:
public class MainClass : IMainClass
{
private readonly ILogger _logger;
private readonly IDatabase _db;
public MainClass(ILogger logger, IDatabase db)
{
_logger = logger;
_db = db;
}
public void AddDetails(Data data)
{
//do some business operations
_db.Add(data);
_logger.Information("added");
}
}
Then you could use a mocking framework such as Moq to mock your class dependencies and perform verifications on whether the dependencies were called:
[TestClass]
public class UnitTest1
{
private Mock<ILogger> _mockLogger = new Mock<ILogger>();
private Mock<IDatabase> _mockDb = new Mock<IDatabase>();
[TestMethod]
public void TestMethod1()
{
// arrange
var mainClass = new MainClass(_mockLogger.Object, _mockDb.Object);
var data = new Data();
// act
mainClass.AddDetails(data);
// assert
_mockDb
.Verify(v => v.Add(data), Times.Once);
}
}
I would not verify your log message though as this could change and make the test brittle. Only verify functionality which is essential to doing what the method is intended for.
Your current Service Locator Anti-Pattern is what makes your code difficult to test in isolation as well as makes the class misleading about what it actually depends on.
MainClass should be refactored to follow Explicit Dependencies Principle
public class MainClass : IMainClass
private readonly ILogger logger;
private readonly IDatabase db;
public MainClass(ILogger logger, IDatabase db) {
this.logger = logger;
this.db = db;
}
public void AddDetails(Data data) {
//do some business operations
db.Add(data);
logger.Information("added");
}
}
The same pattern should also be followed for any other class you have that depends on the container factory, like Database.
You would however need to also refactor the container registration accordingly
public class ContainerInit {
public static IContainer BuildContainer() {
var builder = new ContainerBuilder();
builder.RegisterType<MainClass>().As<IMainClass>().SingleInstance();
builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
var logger = LoggUtil.CreateLogger();
builder.Register(logger).As<ILogger>().SingleInstance();
var container = builder.Build();
return container;
}
}
Testing MainClass would required you to mock only the necessary dependencies of the class under test.
[TestClass]
public class MainClassTests {
[TestMethod]
public void Should_AddDetails_To_Database() {
// Arrange
var mockDb = new Mock<IDatabase>();
var data = new Data();
var mainClass = new MainClass(Mock.Of<ILogger>(), mockDb.Object);
// Act
mainClass.AddDetails(data);
// Assert
mockDb.Verify(_ => _.Add(data), Times.Once);
}
}
Here I would like to share solution, which I use in my project
To do unit testing of particular function, I use below structure
[TestClass]
public class TestSomeFunction
{
public IComponentContext ComponentContext { get; set; }
[TestInitialize]
public void Initialize()
{
//Registering all dependencies required for unit testing
this.ComponentContext = builder.Build(); //You have not build your container in your question
}
[TestMethod]
public void Testfunction()
{
//Resolve perticular dependency
var _logger = containerFactory.Resolve<ILogger>();
//Test my function
//use _logger
}
}
I am writing my ASP.NET Core Web API and I want to start writing units tests with XUnit (I know, it's a little late for that, but hey, better later than never), but I have a little complicated Dependency Injections in here. So here is my sample Controller class:
public class UnitController : MyController {
private readonly MyContext _context;
public UnitController(MyContext context) {
_context = context;
}
}
And MyContext class:
public class MyContext : DbContext {
private readonly IHttpContextAccessor _httpContext;
public MyContext(DbContextOptions options, IHttpContextAccessor httpContext) : base(options) {
_httpContext = httpContext;
}
}
And my ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services) {
services.AddDbContext<MyContext>(options => options.UseMySql(Configuration.GetConnectionString("MyContext"))
.ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.QueryClientEvaluationWarning)));
services.AddHttpContextAccessor();
services.AddMvc();
}
And my poor XUnit class:
public class UnitControllerTest {
private readonly MyContext _context;
public UnitControllerTest() {
var configuration = new Mock<IConfiguration>().Object;
var dbOptions = new DbContextOptionsBuilder().UseMySql(configuration.GetConnectionString("MyContext"));
_context = new Mock<MyContext>(dbOptions).Object;
}
[Fact]
public void AddUnit_InvalidModel_ShouldReturnBadRequest() {
var controller = new UnitController(_context);
var testUnit = new Unit();
var result = controller.AddUnit(testUnit);
Assert.Equal(typeof(BadRequestObjectResult), result.GetType());
}
}
And my question is,
how to write tests properly?
I have a WebApi done in Core.net 2.0, with UOW , and automapper.
Everything is working fine, but now I want to implement Unit Test with Nunit, and I have this error of automapper
Message: System.InvalidOperationException : Mapper not initialized.
Call Initialize with appropriate configuration. If you are trying to
use mapper instances through a container or otherwise, make sure you
do not have any calls to the static Mapper.Map methods, and if you're
using ProjectTo or UseAsDataSource extension methods, make sure you
pass in the appropriate IConfigurationProvider instance.
How can I solve this. Thanks in advance .
Jolynice
Class AutoMapperProfile.cs
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Cars, CarsDTO>()
.ReverseMap();
}
}
class Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
//removed configurations
// Add cors
services.AddCors();
// Add framework services.
services.AddMvc();
Mapper.Initialize(cfg =>
{
cfg.AddProfile<AutoMapperProfile>();
});
// Repositories
services.AddScoped<IUnitOfWork, HttpUnitOfWork>();
services.AddScoped<IAccountManager, AccountManager>();
}
}
class carsController.cs
[Authorize]
[Route("api/[controller]")]
public class CarsController : Controller
{
private IUnitOfWork _unitOfWork;
readonly ILogger _logger;
private readonly IAccountManager _accountManager;
public CarsController(
IUnitOfWork unitOfWork,
ILogger<CarsController> logger,
IAccountManager accountManager)
{
_unitOfWork = unitOfWork;
_logger = logger;
_accountManager = accountManager;
}
[HttpGet]
public IActionResult GetAll()
{
var allCars = _unitOfWork.CarsRepository.GetAllCarsData();
if (allCars == null)
{
return NotFound();
}
return Ok(Mapper.Map<IEnumerable<CarsDTO>>(allCars));
}
and this is my unit test
[TestFixture]
public class CarsControllerTest
{
#region private variables
List<Cars> cars = new List<Cars>();
CarsController _carsController = null;
IUnitOfWork _unitOfWork;
ICarsRepository _carsRepository;
#endregion
[SetUp]
public void SetUp()
{
cars = new List<Cars>
{
new Cars
{
Alias = "406Moq",
BrandId = 1,
ModelId = 1,
Plate = "00-00-01",
AltranVehicle = 0,
DefaultCar = 0,
Active = 1,
ColorId = 1
}
};
}
[Test]
public void GetAllCarsControllerTest()
{
//Arrange
_carsRepository = SetupCarsRepository();
var unityOfWork = new Mock<IUnitOfWork>();
var _logger = new Mock<ILogger<CarsController>>();
var accountManager = new Mock<IAccountManager>();
unityOfWork.SetupGet(c => c.CarsRepository).Returns(_carsRepository);
_unitOfWork = unityOfWork.Object;
_carsController = new CarsController(_unitOfWork, _logger.Object, accountManager.Object);
//Act
var carsResult = _carsController.GetAll();
//Assert
carsResult.StatusCode.Should().Be(HttpStatusCode.OK);
}
private ICarsRepository SetupCarsRepository()
{
//initialize repository
var mockRepo = new Mock<ICarsRepository>(MockBehavior.Default);
//Setup mocking behavior
mockRepo.Setup(c => c.GetAllCarsData()).Returns(cars);
return mockRepo.Object;
}
//Cleanup
[TearDown]
public void TearDown()
{
cars = null;
}
}
}
You are missing initialization of your mapper in your unit test. The following initializes the mapper in the CarsControllerTest class constructor.
[TestFixture]
public class CarsControllerTest
{
public CarsControllerTest()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<AutoMapperProfile>();
});
}
}
I have a console application that Autofac DI is used to inject data and service layer from web application project.
here is the setup on console application:
public static class ContainerConfig
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<DbFactory>().As<IDbFactory>();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
builder.RegisterType<Application>().As<IApplication>();
builder.RegisterType<DataRepository>().As<IDataRepository>();
builder.RegisterType<DataService>().As<IDataService>();
return builder.Build();
}
}
public interface IApplication
{
void Run();
}
public class Application : IApplication
{
private readonly IDataService _dataService;
public Application(IDataService dataService)
{
_dataService = dataService;
}
public void Run()
{
var data = _dataService.GetDataById(1);
var task = new TestTask("test");
data.AddTask(task);
_dataService.Update(data);
_dataService.SaveChanges();
}
}
main Program class:
class Program
{
static void Main(string[] args)
{
var container = ContainerConfig.Configure();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<IApplication>();
app.Run();
}
}
}
When the application is run loading the data works fine. However, saving a new entry does not seem to do the work.
However, when I remove DI and use simple class initializing in the Run method as below the save works fine:
IDbFactory dbFactory = new DbFactory();
IDataRepository dataRepository = new DataRepository(dbFactory);
var unitOfWork = new UnitOfWork(dbFactory);
IDataService service = new DataService(dataRepository, unitOfWork);
var data = service.GetDataById(1);
var task = new TestTask("test");
data.AddTask(task);
service.Update(data);
service.SaveChanges();
Am I missing something while I setup the autofac? It seems to access the data fine but when it comes to save it does not save the data. I debugged to see any issue but the program runs fine with no error. How can I debug this sort of issues to find more details?
Updated
public interface IDataService
{
void Add(TestTask task);
void SaveChanges();
}
public class DataService : IDataService
{
private readonly IDataRepository _dataRepository;
private readonly IUnitOfWork _unitOfWork;
public DataService(IDataRepository dataRepository, IUnitOfWork unitOfWork)
{
_dataRepository = dataRepository;
_unitOfWork = unitOfWork;
}
public void Add(TestTask task)
{
_dataRepository.Add(task);
}
public void SaveChanges()
{
_unitOfWork.Commit();
}
}
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory _dbFactory;
private ApplicationDbContext _dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this._dbFactory = dbFactory;
}
public ApplicationDbContext DbContext => _dbContext ?? (_dbContext = _dbFactory.Init());
public void Commit()
{
DbContext.Commit();
}
}
After reading autofac scopes here
I found out that default scope is Instance Per Dependency. Which means that a unique instance will be returned from each request for a service. DbFactory should be for InstancePerLifetimeScope.
So changing configuration below fixes the issue:
public static class ContainerConfig
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
builder.RegisterType<Application>().As<IApplication>();
builder.RegisterType<DataRepository>().As<IDataRepository>();
builder.RegisterType<DataService>().As<IDataService>();
return builder.Build();
}
}
I try to inject log4net in a ILogger property of my service class but the property is always NULL!
I've seen this topic but it doesn't help me!
How can I get Castle Windsor to automatically inject a property?
this is Program.cs
CastleContainer.Instance
.Install(
new RepositoriesInstaller(),
new PersistenceInstaller(),
new LoggerInstaller(),
new FormInstaller(),
new ServiceInstaller()
);
FrmStart form1 = CastleContainer.Resolve<FrmStart>(new {Id="666" });
I use log4net.config external file and this is my installer:
public class LoggerInstaller : IWindsorInstaller
{
#region IWindsorInstaller Members
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility("logging", new LoggingFacility(LoggerImplementation.Log4net, "log4net.config"));
}
#endregion
}
This is the class contains the property I want Windsor to inject:
public partial class FrmStart : Form
{
private EventService EventService;
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public FrmStart(EventService eventService, string Id)
: this()
{
Logger.Debug("xxx");
this.EventService = eventService;
this.id = Id;
}
Note that "eventService" and "Id" in the constructor are correctly injected!
If I try to inject the Logger in the constructor it works and I've the Logger object:
{log4net.Repository.Hierarchy.DefaultLoggerFactory+LoggerImpl}! :-(
I've tried to create a public property for EventService and Windsor can inject it properly! So I think the problem is related only to the ILogger interface.
I prepared a simple full-code example here:
using Castle.Core.Logging;
using Castle.Facilities.Logging;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace IocTest
{
public class LoggerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility("logger", new LoggingFacility(LoggerImplementation.Log4net, "log4net.config"));
}
}
public class LogicInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(AllTypes.FromThisAssembly()
.Pick()
.If(t => t.Name.StartsWith("Logic"))
.Configure((c => c.LifeStyle.Transient)));
}
}
class Program
{
static void Main(string[] args)
{
IWindsorContainer container = new WindsorContainer();
container.Install(
new LoggerInstaller(),
new LogicInstaller()
);
LogicClass1 logic1 = container.Resolve<LogicClass1>();
LogicClass2 logic2 = container.Resolve<LogicClass2>();
}
}
public class LogicClass1
{
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public LogicClass1()
{
logger.Debug("Here logger is NullLogger!");
}
}
public class LogicClass2
{
public LogicClass2(ILogger logger)
{
logger.Debug("Here logger is properly injected!");
}
}
}
What's wrong?
A problem is where you are checking it:
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public LogicClass1()
{
logger.Debug("Here logger is NullLogger!");
}
The property injection will not happen until after the constructor is run, so checking the property value in the constructor will never show the value you are expecting
I was having the same problem. It was always null.
I managed to solve the problem by injecting the logger in the constructor this way:
public ILogger logger;
public MyController(ILogger logger)
{
this.logger = logger;
logger.Info("Something");
}
You could also initialize your Logger by using:
public ILogger Logger { get; set; }
public MyController()
{
Logger = NullLogger.Instance;
}