Unit testing MVC controllers that use NHibernate, with and without implementing a repository pattern - asp.net-mvc-3

I have an MVC app that uses NHibernate for ORM. Each controller takes an ISession construction parameter that is then used to perform CRUD operations on domain model objects. For example,
public class HomeController : Controller
{
public HomeController(ISession session)
{
_session = session;
}
public ViewResult Index(DateTime minDate, DateTime maxDate)
{
var surveys = _session.CreateCriteria<Survey>()
.Add( Expression.Like("Name", "Sm%") )
.Add( Expression.Between("EntryDate", minDate, maxDate) )
.AddOrder( Order.Desc("EntryDate") )
.SetMaxResults(10)
.List<Survey>();
// other logic that I want to unit test that does operations on the surveys variable
return View(someObject);
}
private ISession _session;
}
I would like to unit test this controller in isolation, without actually hitting the database, by mocking the ISession object using Moq or RhinoMocks. However, it is going to be very difficult to mock the ISession interface in the unit test, because it is being used via a fluent interface that chains a number of calls together.
One alternative is to wrap the ISession usage via a repository pattern. I could write a wrapper class something like this:
public interface IRepository
{
List<Survey> SearchSurveyByDate(DateTime minDate, DateTime maxDate);
}
public class SurveyRepository : IRepository
{
public SurveyRepository(ISession session)
{
_session = session;
}
public List<Survey> SearchSurveyByDate(DateTime minDate, DateTime maxDate)
{
return _session.CreateCriteria<Survey>()
.Add( Expression.Like("Name", "Sm%") )
.Add( Expression.Between("EntryDate", minDate, maxDate) )
.AddOrder( Order.Desc("EntryDate") )
.SetMaxResults(10)
.List<Survey>();
}
private ISession _session;
}
I could then re-write my controller to take an IRepository constructor argument, instead of an ISession argument:
public class HomeController : Controller
{
public HomeController(IRepository repository)
{
_repository = repository;
}
public ViewResult Index(DateTime minDate, DateTime maxDate)
{
var surveys = _repository.SearchSurveyByDate(minDate, maxDate);
// other logic that I want to unit test that does operations on the surveys variable
return View(someObject);
}
private IRepository _repository;
}
This second approach would be much easier to unit test, because the IRepository interface would be much easier to mock than the ISession interface, since it is just a single method call. However, I really don't want to go down this route, because:
1) It seems like a really bad idea to create a whole new layer of abstraction and a lot more complexity just to make a unit test easier, and
2) There is a lot of commentary out there that rails against the idea of using a repository pattern with nHibernate, since the ISession interface is already a repository-like interface. (See especially Ayende's posts here and here) and I tend to agree with this commentary.
So my questions is, is there any way I can unit-test my initial implementation by mocking the ISession object? If not, is my only recourse to wrap the ISession query using the repository pattern, or is there some other way I can solve this?

Oren tends to wander around a lot. He used to be a huge proponent of Repositories and Unit of Work. He will probably swing back around again to it, but with a different set of requirements.
Repository has some very specific advantages that none of Oren's comments have quite found solutions for. Also, what he recommends has it's own set of limitaitons and problems. Sometimes I feel like he's just exchanging one set of problems for another. It's also good when you need to provide different views of the same data, such as a Web Service, or Desktop application while still keeping the web app.
Having said that, he has a lot of good points. I'm just not sure there are good solutions for them yet.
Repository is still very useful for highly test driven scenarios. It's still useful if you don't know if you will stick with a given ORM or persistence layer and might want to swap it out with another one.
Oren's solution tends to couple nHimbernate more tightly into the app. That may not be a problem in many situations, in others it might be.
His approach of creating dedicated query classes is interesting, and is sort of a first step to CQRS, which might be a better total solution. But Software development is still so much more art or craft than science. We're still learning.

Rather than mocking out ISession have you considered having your tests inherit from a base fixture that makes use of SQLite?
public class FixtureBase
{
protected ISession Session { get; private set; }
private static ISessionFactory _sessionFactory { get; set; }
private static Configuration _configuration { get; set; }
[SetUp]
public void SetUp()
{
Session = SessionFactory.OpenSession();
BuildSchema(Session);
}
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var cfg = Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.SQLiteConfiguration.Standard.ShowSql().InMemory())
.Mappings(configuration => configuration.FluentMappings.AddFromAssemblyOf<Residential>())
.ExposeConfiguration(c => _configuration = c);
_sessionFactory = cfg.BuildSessionFactory();
}
return _sessionFactory;
}
}
private static void BuildSchema(ISession session)
{
var export = new SchemaExport(_configuration);
export.Execute(true, true, false, session.Connection, null);
}
[TearDown]
public void TearDownContext()
{
Session.Close();
Session.Dispose();
}
}

Introducing repositories with named query methods does not add complexity to your system. Actually it reduces complexity and makes your code easier to understand and maintain. Compare original version:
public ViewResult Index(DateTime minDate, DateTime maxDate)
{
var surveys = _session.CreateCriteria<Survey>()
.Add(Expression.Like("Name", "Sm%"))
.Add(Expression.Between("EntryDate", minDate, maxDate))
.AddOrder(Order.Desc("EntryDate"))
.SetMaxResults(10)
.List<Survey>();
// other logic which operates on the surveys variable
return View(someObject);
}
Frankly speaking all my memory slots where already occupied BEFORE I got to the actual logic of your method. It takes time for reader to understand which criteria you are building, what parameters are you passing and which values are returned. And I need to switch contexts between lines of code. I start thinking in terms of data access and Hibernate, then suddenly I'm back to the business logic level. And what if you have several places where you need to search surveys by date? Duplicate all this staff?
And now I'm reading version with repository:
public ViewResult Index(DateTime minDate, DateTime maxDate)
{
var surveys = _repository.SearchSurveyByDate(minDate, maxDate);
// other logic which operates on the surveys variable
return View(someObject);
}
It takes me zero efforts to understand what happening here. This method has single responsibility and single level of abstraction. All data access related logic gone. Query logic is not duplicated in different places. Actually I don't care how it is implemented. Should I care at all, if main goal of this method is some other logic?
And, of course, you can write unit test for your business logic with no efforts (also if you are using TDD repository gives you ability to test your controller before you actually write data access logic, and when you will start writing repository implementation, you will have already designed repository interface):
[Test]
public void ShouldDoOtherLogic()
{
// Arrange
Mock<ISurveryRepository> repository = new Mock<ISurveryRepository>();
repository.Setup(r => r.SearchSurveyByDate(minDate, maxDate))
.Returns(surveys);
// Act
HomeController controller = new HomeController(repository.Object);
ViewResult result = controller.Index(minDate, maxDate);
// Assert
}
BTW In-memory database usage is good for acceptance testing, but for unit-testing I think its an overkill.
Also take a look at NHibernate Lambda Extensions or QueryOver in NHibernate 3.0 which use expressions to build criteria instead of strings. Your data access code will not break if you rename some field.
And also take a look on Range for passing pairs of min/max values.

Related

How to reduce number of injected dependencies on controller

I am using MVC3, Entity Framework v4.3 Code First, and SimpleInjector. I have several simple classes that look like this:
public class SomeThing
{
public int Id { get; set; }
public string Name { get; set; }
}
I have another entity that looks like this:
public class MainClass
{
public int Id { get; set; }
public string Name { get; set; }
public virtual AThing AThingy { get; set; }
public virtual BThing BThingy { get; set; }
public virtual CThing CThingy { get; set; }
public virtual DThing DThingy { get; set; }
public virtual EThing EThingy { get; set; }
}
Each Thingy (currently) has its own Manager class, like so:
public class SomeThingManager
{
private readonly IMyRepository<SomeThing> MyRepository;
public SomeThingManager(IMyRepository<SomeThing> myRepository)
{
MyRepository = myRepository;
}
}
My MainController consequently follows:
public class MainController
{
private readonly IMainManager MainManager;
private readonly IAThingManager AThingManager;
private readonly IBThingManager BThingManager;
private readonly ICThingManager CThingManager;
private readonly IDThingManager DThingManager;
private readonly IEThingManager EThingManager;
public MainController(IMainManager mainManager, IAThingManager aThingManager, IBThingManager bThingManager, ICThingManager cThingManager, IDThingManager dThingManager, IEThingManager eThingManager)
{
MainManager = mainManager;
AThingManager = aThingManager;
BThingManager = bThingManager;
CThingManager = cThingManager;
DThingManager = dThingManager;
EThingManager = eThingManager;
}
...various ActionMethods...
}
In reality, there are twice as many injected dependencies in this controller. It smells. The smell is worse when you also know that there is an OtherController with all or most of the same dependencies. I want to refactor it.
I already know enough about DI to know that property injection and service locator are not good ideas.
I can not split my MainController, because it is a single screen that requires all these things be displayed and editable with the click of a single Save button. In other words, a single post action method saves everything (though I'm open to changing that if it makes sense, as long as it's still a single Save button). This screen is built with Knockoutjs and saves with Ajax posts if that makes a difference.
I humored the use of an Ambient Context, but I'm not positive it's the right way to go.
I humored the use of injecting a Facade as well.
I'm also wondering if I should implement a Command architecture at this point.
(Don't all of the above just move the smell somewhere else?)
Lastly, and perhaps independent of the three above approaches, is should I instead have a single, say, LookupManager with explicit methods like GetAThings(), GetAThing(id), GetBThings(), GetBThing(id), and so on? (But then that LookupManager would need several repositories injected into it, or a new type of repository.)
My musings aside, my question is, to reiterate: what's a good way to refactor this code to reduce the crazy number of injected dependencies?
Using a command architecture is a good idea, since this moves all business logic out of the controller, and allows you to add cross-cutting concerns without changes to the code. However, this will not fix your problem of constructor over-injection. The standard solution is to move related dependencies into a aggregate service. However, I do agree with Mark that you should take a look at the unit of work pattern.
Have you considered using a unit of work design pattern? There is a great MSDN post on what a unit of work is. An excerpt from that article:
In a way, you can think of the Unit of Work as a place to dump all
transaction-handling code. The responsibilities of the Unit of Work
are to:
Manage transactions.
Order the database inserts, deletes, and updates.
Prevent duplicate updates. Inside a single usage of a Unit of Work object, different parts of the code may mark the same Invoice
object as changed, but the Unit of Work class will only issue a
single UPDATE command to the database.
The value of using a Unit of Work pattern is to free the rest of your
code from these concerns so that you can otherwise concentrate on
business logic.
There are several blog posts about this, but the best one I've found is on how to implement it is here. There are some other ones which have been referred to from this site here, and here.
Lastly, and perhaps independent of the three above approaches, is
should I instead have a single, say, LookupManager with explicit
methods like GetAThings(), GetAThing(id), GetBThings(), GetBThing(id),
and so on? (But then that LookupManager would need several
repositories injected into it, or a new type of repository.)
The unit of work would be able to handle all of these, especially if you're able to implement a generic repository for most of your database handling needs. Your tag mentions you're using Entity Framework 4.3 right?
Hope this helps!
I think your main issue is too many layers of abstraction. You are using Entity Framework, so you already have a layer of abstraction around you data, adding two more layers (one per entity) via a Repository and a Manager interface has led to the large number of interfaces your controller depends upon. It doesn't add a whole lot of value, and besides, YAGNI.
I would refactor, getting rid of your repository and manager layers, and use an 'ambient context'.
Then, look at the kinds of queries your controller is asking of the manager layers. Where these are very simple, I see no problems querying your 'ambient context' directly in your controller - this is what I would do. Where they are more complicated, refactor this into a new interface, grouping things logically (not necessarily one per Entity) and use your IOC for this.

TDD : tests too close of method implementations

We are doing TDD for quite a while and we are facing some concerns when we refactor. As we are trying to respect as much as we can the SRP (Single responsibility principle), we created a lot of composition that our classes use to deal with common responsibilities (such as validation, logging, etc..).
Let's take a very simple example :
public class Executioner
{
public ILogger Logger { get; set; }
public void DoSomething()
{
Logger.DoLog("Starting doing something");
Thread.Sleep(1000);
Logger.DoLog("Something was done!");
}
}
public interface ILogger
{
void DoLog(string message);
}
As we use a mocking framework, the kind of test that we would do for this situation would be somthing like
[TestClass]
public class ExecutionerTests
{
[TestMethod]
public void Test_DoSomething()
{
var objectUnderTests = new Executioner();
#region Mock setup
var loggerMock = new Mock<ILogger>(MockBehavior.Strict);
loggerMock.Setup(l => l.DoLog("Starting doing something"));
loggerMock.Setup(l => l.DoLog("Something was done!"));
objectUnderTests.Logger = loggerMock.Object;
#endregion
objectUnderTests.DoSomething();
loggerMock.VerifyAll();
}
}
As you can see, the test is clearly aware of the method implementation that we are testing. I have to admit that this example is too simple, but we sometimes have compositions that cover responsibilities that don't add any value to a test.
Let's add some complexity to this example
public interface ILogger
{
void DoLog(LoggingMessage message);
}
public interface IMapper
{
TTarget DoMap<TSource, TTarget>(TSource source);
}
public class LoggingMessage
{
public string Message { get; set; }
}
public class Executioner
{
public ILogger Logger { get; set; }
public IMapper Mapper { get; set; }
public void DoSomething()
{
DoLog("Starting doing something");
Thread.Sleep(1000);
DoLog("Something was done!");
}
private void DoLog(string message)
{
var startMessage = Mapper.DoMap<string, LoggingMessage>(message);
Logger.DoLog(startMessage);
}
}
Ok, this is an example. I would include the Mapper stuff within the implementation of my Logger and keep a DoLog(string message) method in my interface, but it's an example to demonstrate my concerns
The corresponding test leads us to
[TestClass]
public class ExecutionerTests
{
[TestMethod]
public void Test_DoSomething()
{
var objectUnderTests = new Executioner();
#region Mock setup
var loggerMock = new Mock<ILogger>(MockBehavior.Strict);
var mapperMock = new Mock<IMapper>(MockBehavior.Strict);
var mockedMessage = new LoggingMessage();
mapperMock.Setup(m => m.DoMap<string, LoggingMessage>("Starting doing something")).Returns(mockedMessage);
mapperMock.Setup(m => m.DoMap<string, LoggingMessage>("Something was done!")).Returns(mockedMessage);
loggerMock.Setup(l => l.DoLog(mockedMessage));
objectUnderTests.Logger = loggerMock.Object;
objectUnderTests.Mapper = mapperMock.Object;
#endregion
objectUnderTests.DoSomething();
mapperMock.VerifyAll();
loggerMock.Verify(l => l.DoLog(mockedMessage), Times.Exactly(2));
loggerMock.VerifyAll();
}
}
Wow... imagine that we would use another way to translate our entities, I would have to change every tests that has some method that uses the mapper service.
Anyways, we really feel some pain when we do major refactoring as we need to change a bunch of tests.
I'd love to discuss about this kind of problem. Am I missing something? Are we testing too much stuff?
Tips:
Specify exactly what should happen and no more.
In your fabricated example,
Test E.DoSomething asks Mapper to map string1 and string2 (Stub out Logger - irrelevant)
Test E.DoSomething tells Logger to log mapped strings (Stub/Fake out Mapper to return message1 and message2)
Tell don't ask
Like you've yourself hinted, if this was a real example. I'd expect Logger to handle the translation internally via a hashtable or using a Mapper. So then I'd have a simple test for E.DoSomething
Test E.DoSomething tells Logger to log string1 and string2
The tests for Logger would ensure L.Log asks mapper to translate s1 and log the result
Ask methods complicate tests (ask Mapper to translate s1 and s2. Then pass the return values m1 and m2 to Logger) by coupling the collaborators.
Ignore irrelevant objects
The tradeoff for isolation via testing interactions is that the tests are aware of implementation.
The trick is to minimize this (via not creating interfaces/specifying expectations willy-nilly). DRY applies to expectations as well. Minimize the amount of places that an expectation is specified... ideally Once.
Minimize coupling
If there are lots of collaborators, coupling is high which is a bad thing. So you may need to rework your design to see which collaborators don't belong at the same level of abstraction
Your difficulties come from testing behavior rather than state. If you would rewrite the tests so that you look at what's in the log rather than verifying that the call to the log is made, your tests wouldn't break due to changes in the implementation.

Is there a recommended base repository class to use with Entity Framework?

I'm using an EF Code First approach with an ASP.NET MVC 3 application, and instead of re-creating the wheel, I was wondering if there already exists a solid base Repository class that my custom Repository classes could extend in order to provide default functionality out of the box (e.g. basic CRUD, etc...).
So something like this ...
public class CustomerRepository : BaseRepository { ... }
... would therefore provide a default way to work with Customer objects out of the box. I'd like to then inject an ICustomerRepository into my MVC controllers and have the functionality available to me there.
I'm sure something like this already exists out there as I've done something similar with NHibernate.
Thanks
No, there is no built-in repository, other than EF itself (which is in and of itself an implementation of the Unit of Work pattern, and DbSet's are basically Repositories).
There is currently a debate in the software community over whether generic repositories have much real value. For testing purposes, many argue, they provide easy unit testing. Others say that unit testing repositories doesn't help because mocked repositories don't behave the same way that real ones do (because of the linq -> Sql translation layer, which doesn't exist in a mocked repository).
Many are suggesting that you do integration testing against EF using an in-memory database like SqlLite rather than unit testing it.
Still, if you are intent on using repositories, there are many examples out there on the net, with varying styles and methods. Or you can roll your own. MS does not provide one.
In my experience, write your own repositories is redundant because EF implements this pattern already through DbSet's.
I worked with MVC3 + EF Code Fisrt in a recent project. We started implementing a generic repository following some tutorials and soon we realized that we are writing a lot of unnecessary and redundant code. Actually, the repositories were given us nothing but hiding a lot of the DbSet's functionality. Finally, we decided to remove them and work with our DbContext and DbSet's directly.
But, how about complex business logic beyond simple CRUD operations?
Well, we exposed all complex functionality like queries and multiple CRUD operations through a service layer. You can build different service classes by functionality. By example, you can write an AccountService to manage all functionality related with user accounts. Something like this:
public class AccountService {
private MyContext ctx;
public AccountService(DbContext dbContext) {
this.ctx = (MyContext)dbContext;
}
/// <summary>
/// Gets the underlying DbContext object.
/// </summary>
public DbContext DbContext {
get { return ctx; }
}
/// <summary>
/// Gets the users repository.
/// </summary>
public DbSet<User> Users {
get {return ctx.Users;}
}
public bool ValidateLogin(string username, string password) {
return ctx.Users.Any(u => u.Username == username && u.Password == password);
}
public string[] GetRolesForUser(string username) {
var qry = from u in ctx.Users
from r in u.Roles
where u.Username == username
select r.Code;
return qry.ToArray<String>();
}
public User CreateUser(string username, string password) {
if (String.IsNullOrWhiteSpace(username)) throw new ArgumentException("Invalid user name");
if (String.IsNullOrWhiteSpace(password)) throw new ArgumentException("Invalid password");
User u = new User {
Username = username.Trim().ToLower(),
Password = password.Trim().ToLower(),
Roles = new List<Role>()
};
ctx.Users.Add(u);
ctx.SaveChanges();
return u;
}
How about dependency injection?
Using this approach, the only thing we need to inject is the DbContext. The service classes has a constructor that takes a DbContext. So, when your controller constructor takes a service instance the DbContext will be injected to it.
Edit: Example code
This is an example code about how you controller could look:
public class HomeController : Controller {
private readonly AccountService accountService;
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
}
And this could be the DI configuration using NInject:
private static void RegisterServices(IKernel kernel) {
kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<MyContext>());
}
How about unit testing?
You could build specific interfaces for each service layer class and mock it where you need.
A friend of mine, Sacha Barber wrote a nice article covering some of these ideas.
Link can be found here.
RESTful WCF / EF POCO / Unit of Work / Repository / MEF: 1 of 2
EF has a base class called DbContext. You can add properties of type DbSet<TEntity>
This allows you to do something like this:
public class User {
public int Id { get; set; }
public string Name { get; set; }
}
public class DatabaseContext : DbContext {
public DbSet<User> Users { get; set; }
}
You can now use this like so:
using(var db = new DatabaseContext()) {
User jon = new User {Name = "Jon Smith"};
db.Users.Add(jon);
db.SaveChanges();
var jonById = db.Users.Single(x => x.Id == 1);
}
If you want more abstraction see this post about building a generic repository around EF Entity Framework 4 CTP 4 / CTP 5 Generic Repository Pattern and Unit Testable Just to note, this level of abstraction is not always needed. You should decide if your abblication will truly benefit from adding a generic repository over just using DbContext directly.

What is the best way to create EF DbContext instance for ASP.NET MVC

In order to support lazy loading feature in EF, what is the best way to instantiate DbContext?
I know HttpContext's current item is good place to create DbContext via Application_BeginRequest method and Application_EndRequest method, but in some sample codes of MSDN and official asp.net mvc site, they just create DbContext in Controller's constructor and dispose it in controller's Dispose() method.
I think the both ways are not too different because all of those all implement session per request pattern.
I just want to make sure that my understanding is correct or not.
The Dispose() method in the controller isn't always reliable. By the same token, Session is probably not a good idea either. "Best" is probably subjective, but we've had the best success by using dependency injection (Castle Windsor) and following a Unit of Work Repository pattern.
Setup the unit of work along the following lines:
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork()
{
this.Context = new MyEFEntities();
this.Context.ContextOptions.LazyLoadingEnabled = true;
}
public void Dispose()
{
this.Context.Dispose();
}
public ObjectContext Context { get; internal set; }
}
Setup your repository:
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
public Repository(IUnitOfWork unitOfWork)
{
Context = unitOfWork.Context;
ObjectSet = Context.CreateObjectSet<TEntity>();
}
public ObjectContext Context { get; set; }
public IObjectSet<TEntity> ObjectSet { get; set; }
}
Register with Castle in Global.asax:
void Application_Start()
{
this.Container.Register(
Component.For<IUnitOfWork>()
.UsingFactoryMethod(() => new UnitOfWork())
.LifeStyle
.Is(LifestyleType.PerWebRequest)
);
ControllerBuilder.Current.SetControllerFactory(
new WindsorControllerFactory(this.Container));
}
And use in your controller (or wherever you're using it, as long as it's injectable):
public class SomeController
{
public SomeController(IRepository<MyEntity> repository)
{
this.Repository = repository;
}
public IRepository<MyEntity> Repository { get; set; }
public ActionResult MyAction()
{
ViewData.Model = this.Repository.ObjectSet.Single(x => x.Condition); //or something...
}
}
Any lazy loading here could potentially be a trap for a future issue. Without DI, without a repository - its hard to see anything working without it being a hack for lazy loading. Also do you you plan on passing your entities to your view. If so this is going to create a bad overlap. The controller should package data for your view, not have things evaluated later in your view.
For MVC best practices, you should flatten out your domain model as much as possible into a viewmodel (if flattening makes sense) and use the view model. Since you would ideally then know what would be lazy loaded, it may make more sense to take the hit up front and use .Include() in your query to eager load, otherwise you can issue many many queries to the database.
I've used a session factory pattern and saved the DBContext in the session object. It will stay open per session. I haven't had problems with it so far.

Using DI to cache a query for application lifetime

Using a DI container (in this case, Ninject) is it possible - - or rather, wise to cache a frequently used object for the entire application lifetime (or at least until it is refreshed)?
To cite example, say I have a Template. There are many Template objects, but each user will inherit at least the lowest level one. This is immutable and will never change without updating everything that connects to it (so it will only change on administration demand, never based on user input). It seems foolish to keep querying the database over and over for information I know is not changed.
Would caching this be best done in my IoC container, or should I outsource it to something else?
I already store ISessionFactory (nHibernate) as a Singleton. But that's a little bit different because it doesn't include a query to the database, just the back-end to open and close ISession objects to it.
So basically I would do something like this..
static class Immutable
{
[Inject]
public IRepository<Template> TemplateRepository { get; set; }
public static ITemplate Template { get; set; }
public void Initialize()
{
if(Immutable.Template == null)
{
Immutable.Template = TemplateRepository.Retrieve(1); // obviously better logic here.
}
}
class TemplateModule : Module
{
public void Load()
{
Bind<ITemplate>().ToMethod(() => Immutable.Initialize())InSingletonScope();
}
}
Is this a poor approach? And if so, can anyone recommend a more intelligent one?
I'd generally avoid using staticness and null-checking from your code - create normal classes without singleton wiring by default and layer that aspect on top via the container. Ditto, remove reliance on property injection - ctor injection is always better unless you have no choice
i.e.:
class TemplateManager
{
readonly IRepository<Template> _templateRepository;
public TemplateManager(IRepository<Template> templateRepository)
{
_templateRepository = templateRepository;
}
public ITemplate LoadRoot()
{
return _templateRepository.Retrieve(1); // obviously better logic here.
}
}
class TemplateModule : Module
{
public void Load()
{
Bind<ITemplate>().ToMethod(() => kernel.Get<TemplateManager>().LoadRoot()).InSingletonScope();
}
}
And then I'd question whether TemplateManager should become a ninject provider or be inlined.
As for the actual question... The big question is, how and when do you want to control clearing the cache to force reloading if you decided that the caching should be at session level, not app level due to authorization influences on the template tree? In general, I'd say that should be the Concern of an actual class rather than bound into your DI wiring or hardwired into whether a class is a static class or is a Singleton (as in the design pattern, not the ninject Scope).
My tendency would be to have a TemplateManager class with no static methods, and make that a singleton class in the container. However, to get the root template, consumers should get the TemplateManager injected (via ctor injection) but then say _templateManager.GetRootTemplate() to get the template.
That way, you can:
not have a reliance on fancy ninject providers and/or tie yourself to your container
have no singleton cruft or static methods
have simple caching logic in the TemplateManager
vary the Scoping of the manager without changing all the client code
have it clear that getting the template may or may not be a simple get operation
i.e, I'd manage it like so:
class TemplateManager
{
readonly IRepository<Template> _templateRepository;
public TemplateManager(IRepository<Template> templateRepository)
{
_templateRepository = templateRepository;
}
ITemplate _cachedRootTemplate;
ITemplate FetchRootTemplate()
{
if(_cachedRootTemplate==null)
_cachedRootTemplate = LoadRootTemplate();
return _cachedRootTemplate;
}
ITemplate LoadRoot()
{
return _templateRepository.Retrieve(1); // obviously better logic here.
}
}
register it like so:
class TemplateModule : Module
{
public void Load()
{
Bind<TemplateManager>().ToSelf().InSingletonScope();
}
}
and then consume it like so:
class TemplateConsumer
{
readonly TemplateManager _templateManager;
public TemplateConsumer(TemplateManager templateManager)
{
_templateManager = templateManager;
}
void DoStuff()
{
var rootTempalte = _templateManager.FetchRootTemplate();
Wild speculation: I'd also consider not having a separate IRepository being resolvable in the container (and
presumably having all sorts of ties into units of work). Instead, I'd have the TemplateRepository be a longer-lived thing not coupled to an ORM layer and Unit Of Work. IOW having a repository and a Manager none of which do anything well defined on their own isnt a good sign - the repository should not just be a Table Data Gateway - it should be able to be the place that an Aggregate Root such as Templates gets cached and collated together. But I'd have to know lots more about your code base before slinging out stuff like that without context!

Resources