Below is a simple but functional example of roughly how I would do Dependency Injection. This works great when my DbContext connection string is not dynamic. Even if it's passed in to the factory through a config file or whatever, it doesn't matter so long as it's the same one all the time.
What I need is to wrap my head around how to make (ideally minor) modifications to the below code to allow for the connection string to be determined dynamically at run time.
For example, say on the View the user was able to not only select the teacher to be passed into the Post method of the controller, but also the school. If, for simplicity sake, there are 2 schools that have the exact same database structure, but have different connection strings how do I get that down from the controller to the factory?
I've experimented with passing a value from method to method, but this isn't really sustainable for large projects, increases the likelihood of errors and overall is just messy (besides violations of SOLID) to be passing something from layer to layer like that. (If desired I can add the not exactly ideal attempts I've made, I've omitted them for brevity sake since this is already a fairly long question what with the code examples and all).
Controller
public class HomeController : Controller
{
private readonly IDataService _dataService;
public HomeController(IDataService dataService)
{
_dataService = dataService;
}
public ActionResult Index()
{
var results = _dataService.GetTeachers();
var model = new ViewModel
{
Teachers = results
};
return View(model);
}
[HttpPost]
public ActionResult Index(ViewModel model)
{
var results = _dataService.GetCourses(model.Teacher);
model.Courses = new List<string>(results);
return View(model);
}
}
Service
public class DataService : IDataService
{
private readonly IDataRepo _dataRepo;
public DataService(IDataRepo dataRepo)
{
_dataRepo = dataRepo;
}
public List<string> GetCourses(string teacherName)
{
return _dataRepo.GetCourses()
.Where(c => c.Teacher.FirstName == teacherName)
.Select(c => c.Name)
.ToList();
}
public List<string> GetTeachers()
{
return _dataRepo.GetCourses()
.Select(c => c.Teacher.FirstName)
.ToList();
}
}
Repository
public class DataRepo : IDataRepo
{
private readonly SchoolContext _context;
public DataRepo()
{
_context = ContextFactory.MakeContext();
}
public IEnumerable<Course> GetCourses()
{
return _context.Courses;
}
}
Context Factory
public static class ContextFactory
{
public static SchoolContext MakeContext()
{
var connString =
"connStringA";
return new SchoolContext(connString);
}
}
UnityConfig
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IDataService, DataService>();
container.RegisterType<IDataRepo, DataRepo>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
First, you have to decide how are you going to get the current connection string to use. Is it through a URL? or using the current user or whatever other way.
Then, create another database that has a mapping between the connection strings and the method you chose (user, url ...)
Lastly, implement a way to get the record from the database.
so, assuming that you will use the URL as an identifier for the current tenant, your entity class should be like this:
public class Tenant
{
public string Url {get;set;}
public string ConnectionString {get;set;}
}
An interface that represents the logic to get the current tenant:
public interface ICurrentTenantService
{
Tenant GetCurrentTenant();
}
And now you will put its implementation
public class CurrentTenantService : ICurrentTenantService
{
public Tenant GetCurrentTenant()
{
string currentUrl = HttpContext.Current.Url; //make sure to get only the base URL here
return TenantDbContext.Tenants.FirstOrDefault(t=>t.Url == url); //TenantDbContext should be a class that has the Tenant entity
}
}
Now you have to wire up the context factory to the tenant service like this
public static class ContextFactory
{
private readonly ICurrentTenantService currentTenantService;
//Inject it in the constructor
public static SchoolContext MakeContext()
{
var currentTenant= currentTenantService.GetCurrentTenant(); //Check for NULL
return new SchoolContext(currentTenant.ConnectionString);
}
}
Related
I have MemoryCache objects (Application,Configuration etc) which I registered them as Singleton. Also there are scoped repositories which selects data from db to fill cache.
For example here is the Singleton registered class,
public class ApplicationCache : MultipleLoadCache<Application>
{
public ApplicationCache()
{
}
}
MultipleLoadCache overrides the CacheItemPolicy, (there is also SingleLoadCache),
public class MultipleLoadCache<TEntity> : SmartCache<TEntity> where TEntity : class
{
public MultipleLoadCache()
{
}
protected override CacheItemPolicy SetPolicy()
{
return new CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(15)
};
}
}
And base class is,
public class SmartCache<TEntity> : IDisposable where TEntity : class
{
public bool TryGetList(IRepository<TEntity> repository, out List<TEntity> valueList)
{
valueList = null;
lock (cacheLock)
{
GenerateCacheIfNotExists(repository, out valueList);
if (valueList == null || valueList.Count == 0)
{
valueList = (List<TEntity>)_memoryCache.Get(key);
}
}
return valueList != null;
}
I know that scoped services can't be injected to singleton class. So I prefer to use method injection.
private void GenerateCacheIfNotExists(IRepository<TEntity> repository, out List<TEntity> list)
{
list = null;
if (!_memoryCache.Any(x => x.Key == key)) // if key not exists, get db records from repo.
{
IEnumerable<TEntity> tempList = repository.GetList();
list = tempList.ToList();
_cacheItemPolicy = SetPolicy();
SetCacheList(list);
}
}
}
And at controller I try to get cache values, but this part seems wrong to me. If I try to get cache values, I shouldn't pass repository as parameter.
private readonly ApplicationCache _appCache;
public LogController(ApplicationCache appCache)
{
_appCache = appCache;
}
[HttpPost]
[Route("Register")]
public List<Application> Register([FromServices] IApplicationRepository repository)
{
List<Application> cf;
_appCache.TryGetList(repository, out cf);
return cf;
}
Also, by doing Method Injection. I am also unable to use RemovedCallBack event of CacheItemPolicy. Because, when callback triggers (reload cache), I need repository to get records from db again.
Is this design seems nice, what is the best design to do this by using callback events of MemoryCache?
Update 1-
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache();
services.AddSingleton(x => new ApplicationCache());
services.AddScoped<IApplicationRepository, ApplicationRepository>();
}
Thanks,
I had the same issue. Since static classes is compiled at the beginning it cannot inject the required services later. I figured it out by using IServiceScopeFactory.
You basically inject IServiceScopeFactory serviceScopeFactory in the constructer .
static SampleClass(IServiceScopeFactory serviceScopeFactory){
//serviceScopedFactory will act as Singleton, since it is a static class
_serviceScopeFactory = serviceScopeFactory;
}
And use it like this in the method :
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<IService>();
//Here you can use the service. This will be used as Scoped since it will be
//recreated everytime it is called
}
Any better examples or tutorials available on Unit testing projects using Entity framework than this
http://www.asp.net/web-api/overview/testing-and-debugging/mocking-entity-framework-when-unit-testing-aspnet-web-api-2
In my case API project is using Entity framework file Edmx file and accessing the tables from the edmx file from Repository class. [ Not really like the codefirst or dbfirst approach ]
The structre of repo class looks like
public class AppBackendRepository
{
// modify the type of the db field
private AppDBEntities db_context = new AppDBEntities();
public List<Student> Get()
{
return db_context.Students.ToList();
}
}
public class StudentController
{
private static AppBackendRepository repo;
public StudentController()
{
repo = new AppBackendRepository();
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = repo.Get();
return students;
}
}
How can i write a proper Unit testing against this way of code architecture
The quick answer is: You don't.
Now, I say this because I tend to regard "Unit Tests" as something that is quick and can be used in continuous integration, while "Integration tests" are the slow tests that only run at night and, of course, when you're working with them.
The problem you're creating here is that you're using untestable code.
Take your method, "GetStudents()" as an example. You're depending on the repo to actually exist before calling this method. Any unit-test will depend on Entity Framework being installed, AND of course, this will be super-slow. Imagine a few hundred of these, and your unit test framework is now a serious clog in your system that makes people say "IT's so slow that we don't use it"
A better approach would be to implement the Dependency Inversion Principle
First, define an interface:
public interface IStudentRepository
{
IEnumerable<Student> GetStudents();
}
Now, your class is just an implementation detail of that contract, for example:
public class StudentRepository : DbContext, IStudentRepository
{
private DbSet<Student> Students;
public IEnumerable<Student> GetStudents()
{
return Students;
}
}
In the class that uses your repository, you can now inject your instance by constructor injection, and end up with something that is fully unit-testable:
public class StudentEnrollment
{
private readonly IStudentRepository _studentRepository;
// Inject the contract here
public StudentEnrollment(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public IEnumerable<Student> GetStudentsForClass(StudentClass studentClass)
{
return _studentRepository.GetStudents().Where(student => student.class == studentClass);
}
}
And now, as the added bonus, you can Unit-Test every last bit of logic, for example:
[TestMethod]
public void GetStudentsForClass_GetStudentsThrowsException_ResultIsNull()
{
// Arrange
var mock = Mock.Create<IStudentRepository();
var badException = new Exception("I'm bad");
mock.Setup(repo => repo.GetStudents()).Throws(badException);
var someClass = new StudentClass();
var instance = new StudentEnrollment(mock.object);
// Act
var result = instance.GetStudentsForClass(studentClass);
// Assert
result.ShouldBeEmpty();
}
I'm of the opinon that all your code should be tested. This way you can easily detect when some developer breaks an expected chain. Because of that I always add tests for both repositories and controllers. In your case I would add a test that ensures that your controller uses your repository in a correct way, and that your repository uses EF the right way. However, you should not test EF itself. That's Microsofts problem.
First you must abstract the DbContext.
public class YourContext : DbContext, IDbContext
{
public virtual IDbSet<Student> Students { get; set; }
}
public interface IDbContext
{
IDbSet<Student> Students;
}
// Util for creating a testable context.
public class ContextUtils
{
internal static IDbSet<T> GetMockDbSet<T>(IEnumerable<T> data) where T : class
{
IQueryable<T> queryable = data.AsQueryable();
IDbSet<T> dbSet = MockRepository.GenerateMock<IDbSet<T>, IQueryable>();
dbSet.Stub(m => m.Provider).Return(queryable.Provider);
dbSet.Stub(m => m.Expression).Return(queryable.Expression);
dbSet.Stub(m => m.ElementType).Return(queryable.ElementType);
dbSet.Stub(m => m.GetEnumerator()).Return(queryable.GetEnumerator());
return dbSet;
}
public static IDbContext GetMockDbContext()
{
var dbContext = MockRepository.GenerateMock<IDbContext>();
dbContext.Stub(x => x.Student).PropertyBehavior();
dbContext.Students = GetMockDbSet(GetStudents());
return dbContext;
}
private static IEnumerable<Student> GetStudents()
{
// Create some mock data.
return new List<Student>
{
new Student()
{
StudentID = 1,
Name = "Student One",
},
new Student()
{
StudentID = 2,
Name = "Student Two",
},
new Student()
{
StudentID = 3,
Name = "Student Three",
}
};
}
}
Now you have a DbContext that can be tested. More information regarding the mocking of DbContext can be found on this blog.
http://aikmeng.com/post/62817541825/how-to-mock-dbcontext-and-dbset-with-moq-for-unit
Then make sure that you can test your repository.
public class AppBackendRepository
{
private IDbContext _dbContext;
// With injection.
public AppBackendRepository(IDbContext context)
{
_dbContext = context;
}
public List<Student> Get()
{
return _dbContext.Students.ToList();
}
}
It can also be done with a factory.
public class AppBackendRepository
{
public List<Student> Get()
{
using (var context = DbContextFactory.GenerateContext())
{
return context .Students.ToList();
}
}
}
public interface IDbContextFactory
{
/// <summary>
/// Creates a new context.
/// </summary>
/// <returns></returns>
IDbContext GenerateContext();
/// <summary>
/// Returns the previously created context.
/// </summary>
/// <returns></returns>
IDbContext GetCurrentContext();
}
public class DbContextFactory : IDbContextFactory
{
private IDbContext _context;
public IDbContext GenerateContext()
{
_context = new DbContext();
return _context;
}
public IDbContext GetCurrentContext()
{
if (_context == null)
_context = GenerateContext();
return _context;
}
}
Now you can test the repository and make sure that it's using EF the right way.
[TestMethod]
public void ShouldReturnAllValues()
{
int correctAmount = 3; // The number specified in MockUtils.
var dbContext = MockUtils.GetMockDbSet();
var repo = new AppBackendRepository(dbContext);
var result = repo.Get();
Assert.IsTrue(result.Count() == correctAmount);
}
What you actually tested is that no developer broke the intended code with something like:
public class AppBackendRepository
{
private IDbContext _dbContext;
// With injection.
public AppBackendRepository(IDbContext context)
{
_dbContext = context;
}
public List<Student> Get()
{
// Only active...
return _dbContext.Students.Where(x => x.Active).ToList();
}
}
Now that you know that the repo is doing what it's supposed to, you can simply make sure that your controller is calling the repo and actually returns the value.
public class StudentController
{
private static IAppBackendRepository _repo;
public StudentController(IAppBackendRepository repo)
{
_repo = repo;
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = _repo.Get();
return students;
}
}
[TestMethod]
public void ShouldCallRepo()
{
// With Rhino
var mockRepo = MockRepository.GenerateStub<IAppBackendRepository>();
var expectedResult = new List<Student>();
mockRepo.Expect(x => x.Get()).Return(expectedResult);
var controller = new StudentController(mockRepo);
var actualResult = controller.GetStudents();
mockRepo.VerifyAllExpectations();
Assert.AreEqual(actualResult, expectedResult); // Possible in it's own method.
}
What you actually tested here is that your controller doesn't manipulate the list before returning it, and that it's actually using the repo as intended.
Also, you might consider using an IoC like Structuremap or Unity. It makes it much easier to make testable applications.
For every page request, I need to get some current user permissions data from the database and then use this information several times in the logic
(which is made of several objects)
I want to get data from the database once per request, and reuse it where needed by injecting it.
Is the following the best way to do this?
1) define a object like this
class ApplicationUser : IApplicationUserProvider
{
private UserPermissions _userPermissions;
...
public ApplicationUser(IDatabase userService)
{
_userPermissions = userService.GetUserPermission(); // this would executed for every request once
}
UserRoles GetRoles()
{
return _userPermissions;
}
}
2) define the ioc something like this
var container1 = new Container(c =>
{
c.For<IDatabase>().Use<Database>();
c.For<IApplicationUser>().Use<ApplicationUser >();
c.For<IApplicationLogic1>().Use<ApplicationLogic1>(); // this has IApplicationUser in ctor
c.For<IApplicationLogic2>().Use<ApplicationLogic2>(); // this has IApplicationUser in ctor
});
3) the controller would be
void PageController(IApplicationLogic1 l1)
{
l1.Process();
l2.Process();
}
The UserPermissions information is runtime data, and as explained here, runtime data should not be injected or resolved during construction of the object graphs.
Instead, the call to userService.GetUserPermission() should be moved out of the constructor. For instance by delaying the call by using a Lazy<T>:
class ApplicationUser : IApplicationUserProvider
{
private Lazy<UserPermissions> _userPermissions;
public ApplicationUser(IDatabase userService) {
_userPermissions = new Lazy<UserPermissions>(userService.GetUserPermission);
}
UserRoles GetRoles() {
return _userPermissions.Value.GetRoles();
}
}
Another option is to define a decorator on IDatabase that will implement the caching:
public class PerRequestCacheDatabaseDecorator : IDatabase
{
private IDatabase _decoratee;
public PerRequestCacheDatabaseDecorator(IDatabase decoratee) {
_decoratee = decoratee;
}
public UserPermissions GetUserPermission() {
var items = HttpContext.Current.Items;
if (items["permissions"] == null)
items["permissions"] = _decoratee.GetUserPermission();
return (UserPermissions)items["permissions"];
}
}
By wrapping the real database inside the PerRequestCacheDatabaseDecorator, you can simplify the ApplicationUser to the following:
class ApplicationUser : IApplicationUserProvider
{
private IDatabase _userService;
public ApplicationUser(IDatabase userService) {
_userService = userService;
}
public UserRoles GetRoles() {
return _userService.GetUserPermission().GetRoles();
}
}
Now neither the ApplicationUser nor the Database class are concerned with this performance optimization, which is a good thing. The decorator allows us to plugin this performance optimization without having to change any of the existing classes.
I am attempting to configure a web service with Autofac so that I can map a different connection context for each controller:
// database connections
container.Register(c => new DocumentControllerActivator()).As<IHttpControllerActivator>().InstancePerApiControllerType(typeof(DocumentController));
container.Register(c => new WorkflowControllerActivator()).As<IHttpControllerActivator>().InstancePerApiControllerType(typeof(WorkflowController));
and:
public class WorkflowControllerActivator : IHttpControllerActivator
{
// snip...
var connectionString = "workflow connection string";
var container = new ContainerBuilder();
container.Register(c =>
{
var newConnectionContext = new SqlServerConnectionContext(connectionString) {ProductID = productId};
newConnectionContext.Open();
return newConnectionContext;
}).As<ISqlServerConnectionContext>().As<IConnectionContext>().InstancePerApiRequest();
var dr = (AutofacWebApiDependencyResolver)GlobalConfiguration.Configuration.DependencyResolver;
container.Update(dr.Container.ComponentRegistry);
return (IHttpController)request.GetDependencyScope().GetService(typeof(WorkflowController));
}
The DocumentControllerActivator differs only in the connection string the the return object type.
[AutofacControllerConfiguration]
public class WorkflowController : ApiController
When I attempt to access the service, the DocumentController throws an error saying that "Unable to cast object of type 'SearchService.Controllers.WorkflowController' to type 'SearchService.Controllers.DocumentController'." It's as if the second InstancePerApiControllerType registration is overwriting the first (i.e. it is doing it for all controllers).
Any suggestions where I've gone wrong? Or, an alternate solution? (Other than a service locator pattern in each controller.)
Not sure why the ControllerActivator approach isn't working, but a simpler and less web api stack specific alternative could be:
public interface ISqlServerConnectionContextFactory
{
ISqlServerConnectionContext Create();
}
// Register this with your ContainerBuilder
public class SqlServerConnectionContextFactory : ISqlServerConnectionContextFactory
{
private string _connectionString;
public SqlServerConnectionContextFactory(string connectionString)
{
_connectionString = connectionString;
}
public ISqlServerConnectionContext Create()
{
var connectionContext = new SqlServerConnectionContext(_connectionString);
connectionContext.Open();
return connectionContext;
}
}
public class MyController : ApiController
{
private ISqlServerConnectionContext _sqlServerConnectionContext;
public MyController(Func<string, ISqlServerConnectionContextFactory> connectionFactory)
{
_sqlServerConnectionContext = connectionFactory("MyConnectionString");
}
}
See http://nblumhardt.com/2010/01/the-relationship-zoo/ for more information about AutoFac relationships and auto generated factories
In this case, when the AutoFac Container sees the Func parameter, it passes in a delegate that acts as a factory method that returns a ISqlServerConnectionContextFactory and passes the string through to its constructor. AutoFac really is rather clever!
I have a service defined like this:
public class MentorService : IMentorService
{
private readonly Func<MentorContext> _dbFactory;
public MentorService(string connectionString)
{
this._dbFactory = () => new MentorContext(connectionString);
}
public MentorService()
{
this._dbFactory = () => new MentorContext();
}
public MentorContext CreateContext()
{
return _dbFactory.Invoke();
}
public IList<User> GetUsers()
{
using (var db = CreateContext())
{
return db.Users.ToList();
}
}
// etc etc
}
Within my API controllers, I have the following:
public class UserController : ApiController
{
private readonly IMentorService _mentorService;
public UserController()
{
_mentorService = new MentorService();
}
public UserController(IMentorService mentorService)
{
_mentorService = mentorService;
}
}
I'd like to break the service up into sub-services for easier maintenance and testing - something like UserService, BookService, etc. Then I just want to use a single service in my controllers.
How can this be done?
I am a huge fan of factories for this sort of thing.
(Please note, I am assuming you have some sort of DI/IoC setup, considering you have an interface-based example)
Basically, you first split your code into separate classes UserService, BookService, etc. Once you've done that, you create a factory that creates these services for you. Something like this:
public class UserControllerServiceFactory : IUserControllerServiceFactory {
public IUserService CreateUserService() {
return _container.Get<IUserService>();
}
public IBookService CreateBookService() {
return _container.Get<IBookService>();
}
// etc.
}
We use Ninject for our DI/IoC, and prefer to use the Factories extensions for Ninject. This means we don't have to implement the factories ourselves.. we only define the interfaces.
Then you have the factory injected into your controller:
public UserController(IUserControllerServiceFactory factory) {
_factory = factory;
}
And then, your Action Methods use the factory to access the services you require:
[HttpGet]
public ActionResult ViewAllBooks() {
return View(_factory.CreateBookService().GetAll());
}
etc.
I hope that is what you meant.