I'm fairly new to the IoC pattern and I've hit an issue on the way the following should be setup.
I've got a Service class which has the following constructor:
public BookingService(IBookingRepository bookingRepository, IUnitRepository unitRepository, IRateRepository rateRepository, IDiscountRepository discountRepository, IUnitOfWork unitOfWork)
{
this.bookingRepository = bookingRepository;
this.unitRepository = unitRepository;
this.rateRepository = rateRepository;
this.discountRepository = discountRepository;
this.unitOfWork = unitOfWork;
}
Now I've got this working with my controllers like so:
private IBookingService _bookingService;
public AdminBookingSurfaceController(IBookingService bookingService)
{
_bookingService = bookingService;
}
Where I've got stuck is when using the BookingService in an inherited class from a Third Party framework (Umbraco).
This is the current constructor:
public class Freedom2BookTree : umbraco.cms.presentation.Trees.BaseTree
{
public Freedom2BookTree(string application)
: base(application)
{
}
I wasn't sure how IoC would work with this, I tried like this but it didn't work:
As in, when I add the additional parameter the constructor never gets hit/called
public class Freedom2BookTree : umbraco.cms.presentation.Trees.BaseTree
{
private IBookingService _bookingService;
public Freedom2BookTree(string application, IBookingService bookingService)
: base(application)
{
_bookingService = bookingService;
}
If anyone could lend some advice on how this should be done or if I'm looking at it in the wrong way, that would great :)
Many Thanks,
Tom
Maybe that framework only executes a constructor with specific parameters.
You can make the IBookingService a property on the Freedom2BookTree and assign it outside of the constructor.
Related
I am in the process of migrating a project from .Net Framework to .Net Core. In the existing project we have a utility class with a few functions like below:
public static class BudgetUtilities
{
public static decimal CalculateBudgetRemaining(string fiscalYear = null)
{
if (string.IsNullOrWhiteSpace(fiscalYear))
fiscalYear = DateTime.Now.GetFiscalYear().ToString();
using (AppContext _context = new AppContext())
{
FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);
return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
}
}
// other functions removed for brevity
}
I can then reference it anywhere else using BudgetUtilities.CalculateBudgetRemaining(). Very simple and straightforward.
When migrating this function to .Net Core I need to use Dependency Injection so I have amended the class by removing the static modifier (since static constructors cannot have parameters) and injecting the AppContext into the constructor:
public class BudgetUtilities
{
private readonly AppContext _context;
public BudgetUtilities(AppContext context)
{
_context = context;
}
public decimal CalculateBudgetRemaining(string financialYear = null)
{
if (string.IsNullOrWhiteSpace(fiscalYear))
fiscalYear = DateTime.Now.GetFiscalYear().ToString();
FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);
return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
}
}
I then tried to call my code by doing the following:
BudgetUtilities utils = new BudgetUtilities();
decimal remaining = utils.CalculateBudgetRemaining();
But I cannot make a new instance of BudgetUtilities without providing an AppContext in the constructor which makes sense. Every method in this application is at some point initiated by a controller action, and I know that DbContexts are supposed to be short lived, so I assume passing the context the whole way down to this BudgetUtilities class from the initial controller is a bad idea.
The only other option I can see is to keep going back up the call stack from where CalculateBudgetRemaining() is referenced and keep adding in constructor injections until I get to a controller but this is not the only class I will have to inject like this so my constructors further up the chain are going to be really bloated and this will make my ConfigureServices() method bloated too.
I'm sure there's a simple way to do this but I just can't see it.
Don't manually create a new BudgetUtilities instance, that type should also be registered with the DI Framework, preferably interfaced:
public interface IBudgetUtilities
{
decimal CalculateBudgetRemaining(string financialYear);
}
public class BudgetUtilities : IBudgetUtilities
Then in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddScoped<IBudgetUtilities, BudgetUtilities>();
}
Then it can be injected into any class that needs it, such as a controller:
public class YourController : Controller
{
private readonly IBudgetUtilities _utils;
public YourController(IBudgetUtilities utils)
{
_utils = utils;
}
public ActionResult YourMethod()
{
//...
decimal remaining = _utils.CalculateBudgetRemaining();
}
}
By default, registered DbContexts have a scoped lifetime, which means a single instance is used for the entirety of a HTTP request.
I have some problem with InSingletonScope().
My interface:
public interface ISettingsManager
{
ApplicationSettings Application { get; }
}
and my class:
public class SettingsManager : ISettingsManager
{
private readonly IConfigurationService _configurationService;
private readonly Lazy<ApplicationSettings> _applicationSettings;
public ApplicationSettings Application { get { return _applicationSettings.Value; } }
private SettingsManager(IConfigurationService context)
{
_configurationService = context;
_applicationSettings = new Lazy<ApplicationSettings>(() => new ApplicationSettings(context));
}
}
and standard binding in NinjectWebCommon looks like this:
kernel.Bind<ISettingsManager>().To<SettingsManager>().InSingletonScope();
kernel.Bind<IConfigurationService >().To<ConfigurationService>().InRequestScope();
And when I use constructor injection or property injection in HomeController:
[Inject]
public ISettingsManager SettingsManager { private get;}
Then I get an error:
An error occurred when trying to create a controller of type 'Web.Controllers.HomeController'. Make sure that the controller has a parameterless public constructor.
Where is the problem? What is wrong with my Singleton?
I use in my project dependency injection and when I inject one interface in constructor, everything work fine. When I add ISettingsManagers I have many problems.
I know what was wrong. The private constructor was the main problem. When I change it to:
public SettingsManager(IConfigurationService context)
then it works like a charm.
The Error it self says its the home controller that needs to be parameterless, however if ninject is setup correctly that is not true, and the bug can be nested down to a single class that does not have the bindings needed.
However, there is one thing that might go wrong in what you show here.
Your configuration is in request scope and your settings is in singleton scope.
This means it might run fine the first time, however in the second attempt, its a new request and the configuration, could be disposed inside your singleton settings, and would proberbly break something.
Update:
This is an mvc plugin project, using MEF to get the controllers and actions at run time. http://www.fidelitydesign.net/?p=104
I added a new project and in its class I added an export of a type that is already being composed.
[Export(typeof(IController)), ExportMetadata("Name", "Clocks")]
public class ClocksController : Controller
{
public XmlActionResult Index()
{
var p = DeviceLogic.GetUnassigned;
}
[Import(typeof(DeviceLogic))]
DeviceLogic DeviceLogic { get; set; }
}
This gets composed in another project:
[Export]
public class ImportControllerFactory : DefaultControllerFactory
{
[ImportMany]
private IEnumerable<PartFactory<IController, IControllerMetadata>> ControllerFactories;
}
Application Start
[ImportMany]
private IEnumerable<ImportControllerFactory> ControllerFactories;
Controller factories is null, until I actually compose the parts
container.ComposeParts(this);
thats working fine, so I decided to try and emulate this to get devicelogic to appear in the project im having trouble with.
I created an emptry interface (IEmpty) for testing and tried this:
[Export(typeof(IEmpty))]
public class RequestProcessor : IEmpty
{
[Import(typeof(DeviceLogic))]
DeviceLogic DeviceLogic { get; set; }
}
and in my applciation start added
[ImportMany]
private IEnumerable<IEmpty> TestMef;
This is filled with the one instance after composition, so this seems to have worked. My question is does anyone have any suggestions as to why devicelogic is null in requestprocessor but not in clocksController.
You need to call MEF's SatisfyImportsOnce method after your instantiation :
YourMEFContainter.SatisfyImportsOnce(dataTransfer)
I was following this tutorial:
http://blog.johanneshoppe.de/2010/10/walkthrough-ado-net-unit-testable-repository-generator/
And I had this issue:
MVC3 & EF. Interface for TDD
However, now I have my interfaces setup (I am not using ninject due to project restrictions)
I am getting a null error here;
`Public partial class MyEntitiesRepository : MyEntitiesRepository
{
public IEnumerable<userdetails> getAlluserDetails()
{
return this.Context.userDetails.ToList();
}`
Context is null. I am using the exact same structure as the tutorial.
The header in my MVC controller that calls this is:
`
[HandleError]
public class HomeController : Controller
{
private MyEntitiesRepository _repository;
...
...
public HomeController() : this(new externalEntities(), new MyEntitiesRepository ()){}
public HomeController(externalEntities external, MyEntitiesRepository repository)
{
_repository = repository;
_ContextExt = external;
}
`
EDIT:
context is from:
[System.CodeDom.Compiler.GeneratedCode("ADO.NET Unit Testable Repository Generator", "0.5")]
public partial class MyEntitiesRepository
{
/// <summary>
/// Gets or sets the specialised object context
/// </summary>
/// <value>object context</value>
#if !DO_NOT_USE_UNITY
[Dependency]
#endif
public IMyEntities Context { get; set; }
}
}
I am guessing that in the example they pass the Context in the constructor. They can do this because they are using dependency injection and it will create that instance for you. Since you are not using Ninject, you will more than likely need to construct this Context yourself.
If you are unable to use Ninject or any other IoC container then you need to do a better job convincing your bosses to let you. If they still don't let you then you can do poor man's dependency injection I suppose:
public class MyEntitiesRepository
{
private MyDbContext context;
public MyEntitiesRepository() : this(new MyDbContext())
{ }
public MyEntitiesRepository(MyDbContext context)
{
this.context = context;
}
}
It's better than nothing I suppose?
Seeing the edit (the Dependency attribute) I guess the project restrictions you are referring to are that instead of Ninject you are to use Microsoft's Unity.
Now you can solve your problem using or not using Unity. To start with the latter: Adjust your HomeController and MyEntitiesRepository classes a little:
public HomeController() :
this(new externalEntities(),
new MyEntitiesRepository (new MyEntities()))
{
}
public HomeController(externalEntities external, MyEntitiesRepository repository)
{
_repository = repository;
_ContextExt = external;
}
public partial class MyEntitiesRepository
{
public MyEntitiesRepository(IMyEntities context)
{
this.Context = context;
}
public IMyEntities Context { get; private set; }
}
Here I made the assumption that you have a class MyEntities implementing the interface IMyEntities.
You could also use Unity. To get to know that framework a little better you could start at MSDN. I don't have any experience with Unity, but some things I noticied are that you need to create MyEntityRepository using a UnityContainer object:
IUnityContainer container = new UnityContainer();
...
MyEntityRepository repository = container.Resolve<MyEntityRepository>();
Before that works you need to register a mapping of MyEntities to IMyEntities:
container.RegisterType<IMyEntities, MyEntities>();
If you choose to try Unity I suggest you give it a try and ask a new question if you get stuck.
I’m fairly new to IoC and perhaps my understanding of generics and inheritance is not strong enough for what I’m trying to do. You might find this to be a mess. I have a generic Repository<TEntity> base class:
public class Repository<TEntity> where TEntity : class, IEntity
{
private Table<TEntity> EntityTable;
private string _connectionString;
private string _userName;
public string UserName
{
get { return _userName; }
set { _userName = value; }
}
public Repository() {}
public Repository(string connectionString)
{
_connectionString = connectionString;
EntityTable = (new DataContext(connectionString)).GetTable<TEntity>();
}
public Repository(string connectionString, string userName)
{
_connectionString = connectionString;
_userName = userName;
EntityTable = (new DataContext(connectionString)).GetTable<TEntity>();
}
// Data access methods ...
... }
and a SqlClientRepository that inherits Repository:
public class SqlClientRepository : Repository<Client>
{
private Table<Client> ClientTable;
private string _connectionString;
private string _userName;
public SqlClientRepository() {}
public SqlClientRepository(string connectionString) : base(connectionString)
{
_connectionString = connectionString;
ClientTable = (new DataContext(connectionString)).GetTable<Client>();
}
public SqlClientRepository(string connectionString, string userName)
: base(connectionString, userName)
{
_connectionString = connectionString;
_userName = userName;
ClientTable = (new DataContext(connectionString)).GetTable<Client>();
}
// data access methods unique to Client repository
... }
The Repository class provides some generics methods like Save<TEntity>, Delete<TEntity>, etc, that I want all my repository derived classes to share.
The TEntity parameter is constrained to the IEntity interface:
public interface IEntity
{
int Id { get; set; }
NameValueCollection GetSaveRuleViolations();
NameValueCollection GetDeleteRuleViolations();
}
This allows the Repository class to reference these methods within its Save and Delete methods. Unit tests work fine on mock SqlClientRepository instances as well as live unit tests on the real database. However, in the MVC context:
public class ClientController : Controller
{
private SqlClientRepository _clientRepository;
public ClientController(SqlClientRepository clientRepository)
{
this._clientRepository = clientRepository;
}
public ClientController() { }
// ViewResult methods ...
... }
... _clientRepository is always null. I’m using Windor Castle as an IoC container. Here is the configuration:
<component id="ClientRepository" service="DomainModel.Concrete.Repository`1[[DomainModel.Entities.Client, DomainModel]], DomainModel"
type="DomainModel.Concrete.SqlClientRepository, DomainModel" lifestyle="PerWebRequest">
<parameters>
<connectionString>#{myConnStr}</connectionString>
</parameters>
</component>
I’ve tried many variations in the Windsor configuration file. I suspect it’s more of a design flaw in the above code. As I'm looking over my code, it occurs to me that when registering components with an IoC container, perhaps service must always be an interface. Could this be it? Does anybody have a suggestion? Thanks in advance.
---- AMENDMENT ----
In response to Answer 1, I’ve appended a new code sample, since it won’t format properly in the comment sections below.
I can get this to work:
public class ClientController : Controller
{
private IClientRepository _clientRepository;
public ClientController(IClientRepository clientRepository) { ... }
}
public interface IClientRepository : IRepository<Client> { ... }
public class SqlClientRepository : IClientRepository { ... }
... but now I’m required to duplicate my Save and Delete methods inside of SqlClientRepository and the benefits of my generic Repository class are lost. Once I try and have SqlClientRepository inherit from Repository<Client> again, like this:
public class SqlClientRepository : Repository<Client>, IClientRepository { ... }
... my null value for _clientRepository in the controller returns. Is there a way I can do this or is it not possible? I feel like I’ve tried many variations and can’t get it right.
Thanks again for your help.
No, service does not have to be an interface, although usually it should. Service is what a component exposes to an outside world (see the doco, let me know if it makes sense).
So, constructor of your ClientController says "I depend on SqlClientRepository service" That's bad (because services should be abstract (preferably interfaces)), but that's besides the point.
The component you're registering ( named ClientRepository), you're registering as Repository<Client> service.
Hopefully by now you're seeing what the problem is. ClientComponent expects SqlClientRepository, but the component you have registered does not expose itself as SqlClientRepository, but as Repository<Client>, hence Windsor thinks: "Ok, since I have no service for SqlClientRepository I'm gonna use the other constructor, to create ClientController" (see the doco for explanation on how Windsor picks which constructor to use)