I have .Net Core Web API application. There is some Get method in controller and injected IRepositoryProviderFactory. Nothing special.
[ApiController]
public class DataController : ControllerBase
{
private readonly ILogger<DataController> _logger;
private readonly IRepositoryProviderFactory _repositoryProviderFactory;
#region ctor
/// <summary>
/// ctor
/// </summary>
public DataController(ILogger<DataController> logger, IRepositoryProviderFactory repositoryProviderFactory)
{
_logger = logger;
_repositoryProviderFactory = repositoryProviderFactory;
}
[HttpPost]
public async Task<IActionResult> GetData([FromBody] SearchModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var data = await _repositoryProviderFactory.GetDataAsync(model);
return Ok(data);
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
}
}
There are repositories based on the same Interface to get data from different data sources.
public Repository1: IDataRepository {}
public Repository2: IDataRepository {}
public Repository3: IDataRepository {}
I'm using DI so all parts are registered in services as Scoped or Transients. Some repositories are using EntityFramework.
services.AddScoped<IDataRepository, Repository1>();
services.AddScoped<IDataRepository, Repository2>();
services.AddScoped<IDataRepository, Repository3>();
Ok, now I need to implement RepositoryProviderFactory to return repository. But there is one required functionality: it must return for every call different repository.
I have injected IEnumerable and I need to return Repository1, Repository2, Repository3 and again Repository1, … etc. So all repositores are used the same time.
/// <summary>
/// ctor
/// </summary>
public RepositoryProviderFactory(
ILogger<RepositoryProviderFactory> logger,
IEnumerable<IDataRepository> dataRepositories)
{
_logger = logger;
_dataRepositories = dataRepositories;
}
public IDataRepository GetRepository()
{
// TODO: Logic to cycle repositories
var instance = dataRepositories.Where();
return instance;
}
How to do this?
Factory can't be registered as Singleton, because repositories have another dependencies that have Scoped Lifetime (DbContext etc.)
How can I create some thread safe singleton object to be able persists last used repository and serve another one, for another call?
Well, I did it this way.
I made class GlobalAppData and registered is as Singleton.
services.AddSingleton<GlobalAppData>();
Then I made simple implementation (not finished yet).
public class GlobalAppData
{
private IDataRepository[] _availableRepositories;
private IDataRepository_lastUsedRepository;
/// <summary>
/// ctor
/// </summary>
public GlobalAppData()
{
_availableRepositories = new IDataRepository[0];
}
public void TryRegisterRepositories(IEnumerable<IDataRepository> repositories)
{
if (_availableRepositories.Length > 0)
{
return;
}
_availableRepositories = repositories.ToArray();
_lastUsedRepository = null;
}
public IDataRepository GetNextRepository()
{
if (_lastUsedRepository == null)
{
_lastUsedRepository = _availableRepositories[0];
return _lastUsedRepository;
}
var lastUsedIndex = _availableRepositories.IndexOf(_lastUsedRepository);
if (lastUsedIndex < _availableRepositories.Length - 1)
{
lastUsedIndex++;
}
else
{
lastUsedIndex = 0;
}
_lastUsedRepository = _availableRepositories[lastUsedIndex];
return _lastUsedRepository;
}
}
Then because of DI there will be stored original instances, not injected ones, I made just simple compare in factory.
var instanceData = _globalAppData.GetNextRepository();
var instance = _repositories.SingleOrDefault(r => r.GetType() == instanceData.GetType());
Not perfect, but it works as a start.
Related
I have base Attachment controller
Here is code of it
public class ApiAttachmentControllerBase<T> : PM101MobileApiController where T : Entity<int>
{
private readonly IObjectStorageManager _storageManager;
private readonly IRepository<T> _repository;
public ApiAttachmentControllerBase(IObjectStorageManager storageManager, IRepository<T> repository)
{
_storageManager = storageManager;
_repository = repository;
}
private void CheckFileSize(IFormFile file)
{
if (file.Length > PM101Consts.MaxAttachmentSize)
{
throw new UserFriendlyException(L("Attachment_Warn_SizeLimit", PM101Consts.MaxAttachmentSizeMb.ToString()));
}
}
private void CheckFileType(IFormFile file, params string[] supportedTypes)
{
if (supportedTypes.Any())
{
var extention = Path.GetExtension(file.FileName);
if (!supportedTypes.ToList().Contains(extention))
{
throw new UserFriendlyException(L("Attachment_Warn_Type", extention));
}
}
}
}
}
I inherited it in another controller like this
public class InspectionsController : ApiAttachmentControllerBase<Inspection>
{
private readonly IRepository<Inspection> _inspectionRepository;
public InspectionsController(IObjectStorageManager storageManager, IRepository<Inspection> repository,
IRepository<Inspection> inspectionRepository) : base(storageManager, repository)
{
_inspectionRepository = inspectionRepository;
}
/// <summary>
/// Method for posting pre-inspection
/// </summary>
/// <remarks>When you start pre inspection you send just jobId, tenantId, and status
/// When you finishing inspection you send full DTO with all fields</remarks>
/// <response code="200">Returns if pre inspection created
/// </response>
[HttpPost]
public async Task<IActionResult> AddPreInspection(CreatePreInspectionDto input)
{
var preInspection = new Inspection();
ObjectMapper.Map(input, preInspection);
await _inspectionRepository.InsertAsync(preInspection);
return Ok();
}
In AddPreInspection I tried to use repository like repository.InsertAsync
But it not works, so I make DI for repository like private read-only IRepository<Inspection> _inspectionRepository;
Is this a good practice or I can use the repository from the base class?
If yes, how I can do it?
If you make the base ApiAttachmentControllerBase have a protected, rather than private, repository then the InspectionsController will be able to access it.
The protected access modifier allows the member to be accessed by the current class, or a derived class.
public class ApiAttachmentControllerBase<T> : PM101MobileApiController where T : Entity<int>
{
private readonly IObjectStorageManager _storageManager;
protected readonly IRepository<T> Repository;
public ApiAttachmentControllerBase(IObjectStorageManager storageManager, IRepository<T> repository)
{
_storageManager = storageManager;
Repository = repository;
}
....
iam using ninject.web in my aspx page in this way
my problem is with Nhibernate session management.
this is my ninject module:
public override void Load()
{
Bind<IUnitOfWork>().To<UnitOfWork>();
Bind<IAttivitaRepository>().To<AttivitaRepository>();
}
the page_load and quite every button in a single page create and dispose a unit of work at this way:
using (iuow)
{
iuow.DoSomething();
iuow.SaveAll();
}
in pageLoad works, but every other attempt in the page to use iuow with a new Using block, return that session is closed
this is my UoW impl:
public UnitOfWork()
{
_nhHelper = new SessionFactory();
InizializzaSessione();
_transaction = _session.BeginTransaction();
}
private void InizializzaSessione()
{
if (_session == null)
{
_session = _nhHelper.OpenSession();
}
Area = new AreaRepository(this._session);
Attivita = new AttivitaRepository(this._session);
Societa = new SocietaRepository(this._session);
Persona = new PersonaRepository(this._session);
}
/// <summary>
/// Salva le modifiche sulla base dati
/// </summary>
public void SaveAll()
{
if (_transaction != null)
{
_transaction.Commit();
_transaction = null;
}
}
it seems to me that iuow is resolved (whith a call to New) only at page load, so every new attempt to create Uow return last used one with session disposed.
before attimpting to use ninject what i do is simply:
using (Iuow = new UnitOfWork())
{
....
}
and all works fine
p.s.
i have to remove InRequestScope from binding since it prevent even the page load to work
replace
using (iuow)
{
...
}
by
using (IResolutionRoot.Get<IUnitOfWork>())
{
...
}
Whereas you can inject IResolutionRoot into your classes. No extra bindings necessary (it's a Ninject type).
Caveat: this is service locator.
Alternatively you can hide the IResolutionRoot.Get<IUnitOfWork>() behind an IUnitOfWorkFactory which you can either implement manually or use Ninject.Extensions.Factory to do it for you.
Example
using Ninject;
using Ninject.Syntax;
namespace NinjectTest.SO38013150
{
public interface IUnitOfWork { }
internal class UnitOfWork : IUnitOfWork { }
public interface IUnitOfWorkFactory
{
IUnitOfWork Create();
}
internal class UnitOfWorkFactory : IUnitOfWorkFactory
{
private readonly IResolutionRoot resolutionRoot;
public UnitOfWorkFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IUnitOfWork Create()
{
return this.resolutionRoot.Get<IUnitOfWork>();
}
}
}
with a test showing that it works (this uses xunit for testing and FluentAssertions for assertions.. they are nuget packages):
using FluentAssertions;
using Ninject;
using Xunit;
namespace NinjectTest.SO38013150
{
public class Test
{
[Fact]
public void Foo()
{
var kernel = new StandardKernel();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
kernel.Bind<IUnitOfWorkFactory>().To<UnitOfWorkFactory>();
var factory = kernel.Get<IUnitOfWorkFactory>();
var unitOfWork1 = factory.Create();
var unitOfWork2 = factory.Create();
unitOfWork1.Should().NotBeSameAs(unitOfWork2);
}
}
}
The code is also available as part of my examples collection, here
I have the following architecture:
Data
Database Layer
WebAPI
Presentation Layer
Resolver
IoC Register Layer
Services
Business Layer
In WebApiConfig.cs(App_Start) i register the unity container the following way:
// Unity Container Resolver
var container = new UnityContainer();
//Registers the repository interface in Resolver(IoC Register Layer)
var UResolver = new UnityRegisterContainer();
UResolver.RegisterContainer(ref container);
//Configures WebAPI DependecyResolver to use UnityResolver
config.DependencyResolver = new UnityResolver(container);
My Resolver(IoC Register Layer):
public class UnityRegisterContainer
{
public void RegisterContainer(ref UnityContainer container)
{
container.RegisterType<IUnitOfWork>(new HierarchicalLifetimeManager());
container.RegisterType<IService>(new HierarchicalLifetimeManager());
}
}
Controller:
public static KeyService KeyLibrary{ get; set; }
// GET api/values
[Route("Keys")]
public IEnumerable<KeyDTO> Get()
{
var Keys = KeyLibrary.GetAllKeys();
return Keys;
}
KeyService:
public class KeyService: IService
{
IUnitOfWork UOW { get; set; }
/// <summary>
/// Get all Keys
/// </summary>
/// <returns></returns>
public IEnumerable<KeyDTO> GetAllKeys()
{
return Mapper.Map<IEnumerable<Key>, IEnumerable<KeyDTO>>(UOW.Keys.GetAllKeys());
}
}
IService
public interface IService
{
}
IUnitOfWork
public interface IUnitOfWork : IDisposable
{
IKeyRepository Keys { get; }
int Complete();
}
How can i inject the class libraries and repositories with unity?
You can use constructor injection and let the DependencyResolver do it job and pass the necessary dependencies to the classes.
public class KeyController : ApiController {
IKeyService keyService;
public KeyController(IKeyService keyService) {
this.keyService = keyService
}
// GET api/values
[Route("Keys")]
public IEnumerable<KeyDTO> Get() {
var Keys = keyService.GetAllKeys();
return Keys;
}
}
public interface IKeyService : IService {
IEnumerable<KeyDTO> GetAllKeys();
}
public class KeyService: IKeyService {
IUnitOfWork UOW;
public KeyService(IUnitOfWork uow) {
this.UOW = uow
}
/// <summary>
/// Get all Keys
/// </summary>
/// <returns></returns>
public IEnumerable<KeyDTO> GetAllKeys() {
return Mapper.Map<IEnumerable<Key>, IEnumerable<KeyDTO>>(UOW.Keys.GetAllKeys());
}
}
public class UnitOfWork: IUnitOfWork {
public UnitOfWork(IKeyRepository repository) {
Keys = repository;
}
IKeyRepository Keys { get;private set }
public int Complete(){...}
}
Though constructor injection is preferred (and property injection is sometimes not recommended), you can also use the [Dependency] attribute in implementation classes that have dependencies, like this:
public class KeyService: IService
{
// Public setter, private getter, so you can mock and manually assing in Unit Tests
[Dependency]
public IUnitOfWork UOW { private get; set; }
public IEnumerable<KeyDTO> GetAllKeys()
{
return Mapper.Map<IEnumerable<Key>, IEnumerable<KeyDTO>>(UOW.Keys.GetAllKeys());
}
}
See Annotating Objects for Property Injection
I am trying to make sense of mocking in unit testing and to integrate the unit testing process to my project. So I have been walking thru several tutorials and refactoring my code to support mocking, anyway, I am unable to pass the tests, because the DB method I am trying to test is using a transaction, but when creating a transaction, I get
The underlying provider failed on Open.
Without transaction everything works just fine.
The code I currently have is:
[TestMethod]
public void Test1()
{
var mockSet = GetDbMock();
var mockContext = new Mock<DataContext>();
mockContext.Setup(m => m.Repository).Returns(mockSet.Object);
var service = new MyService(mockContext.Object);
service.SaveRepository(GetRepositoryData().First());
mockSet.Verify(m => m.Remove(It.IsAny<Repository>()), Times.Once());
mockSet.Verify(m => m.Add(It.IsAny<Repository>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
// gets the DbSet mock with one existing item
private Mock<DbSet<Repository>> GetDbMock()
{
var data = GetRepositoryData();
var mockSet = new Mock<DbSet<Repository>>();
mockSet.As<IQueryable<Repository>>().Setup(m => m.Provider).Returns(data.Provider);
// skipped for brevity
return mockSet;
}
Code under test:
private readonly DataContext _context;
public MyService(DataContext ctx)
{
_context = ctx;
}
public void SaveRepositories(Repository repo)
{
using (_context)
{
// Here the transaction creation fails
using (var transaction = _context.Database.BeginTransaction())
{
DeleteExistingEntries(repo.Id);
AddRepositories(repo);
_context.SaveChanges();
transaction.Commit();
}
}
}
I was trying to mock the transaction part as well:
var mockTransaction = new Mock<DbContextTransaction>();
mockContext.Setup(x => x.Database.BeginTransaction()).Returns(mockTransaction.Object);
but this is not working, failing with:
Invalid setup on a non-virtual (overridable in VB) member: conn =>
conn.Database.BeginTransaction()
Any ideas how to solve this?
As the second error message says, Moq can't mock non-virtual methods or properties, so this approach won't work. I suggest using the Adapter pattern to work around this. The idea is to create an adapter (a wrapper class that implements some interface) that interacts with the DataContext, and to perform all database activity through that interface. Then, you can mock the interface instead.
public interface IDataContext {
DbSet<Repository> Repository { get; }
DbContextTransaction BeginTransaction();
}
public class DataContextAdapter {
private readonly DataContext _dataContext;
public DataContextAdapter(DataContext dataContext) {
_dataContext = dataContext;
}
public DbSet<Repository> Repository { get { return _dataContext.Repository; } }
public DbContextTransaction BeginTransaction() {
return _dataContext.Database.BeginTransaction();
}
}
All of your code that previously used the DataContext directly should now use an IDataContext, which should be a DataContextAdapter when the program is running, but in a test, you can easily mock IDataContext. This should make the mocking way simpler too because you can design IDataContext and DataContextAdapter to hide some of the complexities of the actual DataContext.
I've tried the wrapper/adapter approach, but came up against the problem that when you then go to test the code:
using (var transaction = _myAdaptor.BeginTransaction())
Your mock/fake still needs to return something so the line transaction.Commit();
can still execute.
Normally I'd set the fake of my adapter to return an interface from BeginTransaction() at that point (so I can fake that returned object too), but the DbContextTransaction returned by BeginTransaction() only implements IDisposable so there was no interface that could give me access to the Rollback and Commit methods of DbContextTransaction.
Furthermore, DbContextTransaction has no public constructor, so I couldn't just new up an instance of it to return either (and even if I could, it wouldn't be ideal as I couldn't then check for calls to commit or rollback the transaction).
So, in the end I took a slightly different approach and created a separate class altogether to manage the transaction:
using System;
using System.Data.Entity;
public interface IEfTransactionService
{
IManagedEfTransaction GetManagedEfTransaction();
}
public class EfTransactionService : IEfTransactionService
{
private readonly IFMDContext _context;
public EfTransactionService(IFMDContext context)
{
_context = context;
}
public IManagedEfTransaction GetManagedEfTransaction()
{
return new ManagedEfTransaction(_context);
}
}
public interface IManagedEfTransaction : IDisposable
{
DbContextTransaction BeginEfTransaction();
void CommitEfTransaction();
void RollbackEfTransaction();
}
public class ManagedEfTransaction : IManagedEfTransaction
{
private readonly IDataContext _context;
private DbContextTransaction _transaction;
public ManagedEfTransaction(IDataContext context)
{
_context = context;
}
/// <summary>
/// Not returning the transaction here because we want to avoid any
/// external references to it stopping it from being disposed by
/// the using statement
/// </summary>
public void BeginEfTransaction()
{
_transaction = _context.Database.BeginTransaction();
}
public void CommitEfTransaction()
{
if (_transaction == null) throw new Exception("No transaction");
_transaction.Commit();
_transaction = null;
}
public void RollbackEfTransaction()
{
if (_transaction == null) throw new Exception("No transaction");
_transaction.Rollback();
_transaction = null;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
if (_transaction != null)
{
_transaction.Dispose();
_transaction = null;
}
}
}
}
I then inject that service class into whatever classes need to use a transaction. For example, using the code from the original question:
private readonly DataContext _context;
private readonly IEfTransactionManager _transactionManager;
public MyService(DataContext ctx, IEfTransactionManager transactionManager)
{
_context = ctx;
_transactionManager = transactionManager;
}
public void SaveRepositories(Repository repo)
{
using (_context)
{
// Here the transaction creation fails
using (var managedEfTransaction = _transactionManager.GetManagedEfTransaction())
{
try
{
managedEfTransaction.BeginEfTransaction();
DeleteExistingEntries(repo.Id);
AddRepositories(repo);
_context.SaveChanges();
managedEfTransaction.CommitEfTransaction();
}
catch (Exception)
{
managedEfTransaction.RollbackEfTransaction();
throw;
}
}
}
}
You can find a pretty good solution here.
In short, you need to create proxy class for DbContextTransaction and use it instead of an original one. So that you can mock your proxy and test your method with BeginTransaction().
PS. In article which I linked above, author forgot about the virtual keyword for BeginTransaction() method placed in dbContext class:
// <summary>
/// When we call begin transaction. Our proxy creates new Database.BeginTransaction and gives DbContextTransaction's control to proxy.
/// We do this for unit test.
/// </summary>
/// <returns>Proxy which controls DbContextTransaction(Ef transaction class)</returns>
public virtual IDbContextTransactionProxy BeginTransaction()
{
return new DbContextTransactionProxy(this);
}
This is a long shot because there is so much custom code, but hopefully it is something simple.
I have this DatabaseInitializer class:
/// <summary>
/// Our Database initialsizer, which inherits CreateDatabaseIfNotExists. We could use DropCreateDatabaseWhenModelChanges or DropCreateDatabaseAlways as well.
/// </summary>
public class DatabaseInitializer : CreateDatabaseIfNotExists<DatabaseContext>
{
/// <summary>
/// Method to insert our data upon initialization
/// </summary>
/// <param name="context">Our DbConext</param>
protected override void Seed(DatabaseContext context)
{
// Create our User
CreateUser();
// Seed
base.Seed(context);
}
/// <summary>
/// Private method which creates the user
/// </summary>
private void CreateUser()
{
// Create our unit of work
var unitOfWork = new UnitOfWork<DatabaseContext>();
// Create our user service
var service = new UserService<User>(unitOfWork, true, true);
// Get our current date
var now = DateTime.UtcNow;
// Map the user model out
var user = new User()
{
UserName = "j***#*****.co.uk",
Email = "j***#*****.co.uk",
DateCreated = now,
DateModified = now,
LastLoginDate = now
};
// Run our task
var task = Task.Run(async () => {
// Create our user
await service.CreateAsync(user, "********");
// Save the changes to our DbSet
await unitOfWork.SaveChangesAsync();
});
// Wait for the async task to complete
task.Wait();
}
}
Now the problem is, that it never seems to complete.
I am thinking it might be because of the async tasks, but I can't be sure.
I know there is no a lot to go on, but like I said I hope that it is something simple because showing you how my UnitOfWork class works along with my UserService would be a lot of code. Rest assured that they have worked fine in other projects.
UnitOfWork
This is what makes up my UnitOfWork class:
public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext, new()
{
private readonly DbContext context;
private Dictionary<Type, object> repositories;
public DbContext Context { get { return this.context; } }
public UnitOfWork()
{
this.context = new TContext();
repositories = new Dictionary<Type, object>();
}
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
if (repositories.Keys.Contains(typeof(TEntity)))
return repositories[typeof(TEntity)] as IRepository<TEntity>;
var repository = new Repository<TEntity>(context);
repositories.Add(typeof(TEntity), repository);
return repository;
}
public async Task SaveChangesAsync()
{
try
{
await this.context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
ex.Entries.First().Reload();
}
}
public void Dispose()
{
this.context.Dispose();
}
}
As stated in the comments, you need to use the same datacontext.
Add a constructor to your UnitOfWork<TContext> class that can take an existing context as parameter:
public UnitOfWork(TContext context)
{
this.context = context;
repositories = new Dictionary<Type, object>();
}
Then in you CreateUser method:
private void Createuser(DatabaseContext context)
{
var unitOfWork = new UnitOfWork<DatabaseContext>(context);
...
}