I am working on refactoring my 80+ custom modules to remove all deprecated DNN calls. I am coding against DNN 9.8.0. I have researched and understand that I am supposed to use interfaces in the abstractions namespace but I am not sure how to use the interfaces. I have read up on dependency injection and still don't quite understand. The item I am working on at the moment is simple and an explanation would help me understand how to use these interfaces in general.
I am currently trying to get a list of portal aliases using portal ID. My old code is:
Dim portal As New Portals.PortalAliasController Dim aliases As List(Of Portals.PortalAliasInfo) = portal.GetPortalAliasesByPortalId(ActiveModule.PortalID)
My new code is:
Dim portalService As Abstractions.Portals.IPortalAliasService Dim aliases As List(Of Abstractions.Portals.IPortalAliasInfo) = portalService.GetPortalAliasesByPortalId(ActiveModule.PortalID)
Since interfaces are just abstract classes, I know I am missing something here. As expected, I am getting a "Object reference not set to an instance of an object" on the second line of the new code since I have no concrete instance.
How do I implement these interfaces correctly? How do I get a list of portal aliases using dependency injection?
It depends on how you're running this code. If this code is inside of a Web API controller, an MVC controller, or a scheduled task, you can add IPortalAliasService as a parameter to the constructor, and DNN will automatically supply a value when it creates the component.
IPortalAliasService
We expect to also support constructor injection for WebForms modules in DNN 10, but for now these dependencies need to be explicitly requested in WebForms modules. PortalModuleBase exposes a DependencyProvider property, which you can use to get the dependency. Here's an example:
PortalModuleBase
DependencyProvider
Imports Microsoft.Extensions.DependencyInjection Public Partial Class View Inherits PortalModuleBase Private ReadOnly portalAliasService As IPortalAliasService Public Sub New() Me.portalAliasService = DependencyProvider.GetService(Of IPortalAliasService)() End Sub End Class
There are other components which do not currently have access to the dependency injection container, and in those components it is not entirely possible to remove these warnings. We expect to resolve these issues in DNN 10. Some examples of these include the business controller class (where you would implement searching, import/export, and upgrade logic, among other things), folder providers, prompt commands, and connectors.
Note that you can create your own classes that you register with the dependency injection container, and those classes can also have constructor dependencies on other classes registered with the dependency injection container. Here's an example with a custom class that's used from multiple places:
Imports DotNetNuke.DependencyInjection Imports Microsoft.Extensions.DependencyInjection Public Class Startup Inherits IDnnStartup Public Sub ConfigureServices(ByVal services As IServiceCollection) services.AddScoped(Of MyService)() End Sub End Class Public Class MyService Private ReadOnly portalAliasService As IPortalAliasService Public Sub New(ByVal portalAliasService As IPortalAliasService) Me.portalAliasService = portalAliasService End Sub End Class Public Partial Class View Inherits PortalModuleBase Private ReadOnly myService As MyService Public Sub New() Me.myService = DependencyProvider.GetService(Of MyService)() End Sub End Class Public Class MyController Inherits DnnApiController Private ReadOnly myService As MyService Public Sub New(ByVal myService As MyService) Me.myService = myService End Sub End Class Public Class MyBusinessController Inherits IUpgradeable Private ReadOnly myService As MyService Public Sub New() Me.myService = New MyService(New PortalAliasController()) End Sub Public Function UpgradeModule(ByVal Version As String) As String End Function End Class
Hope it helps!
Thanks Brian! That's exactly what I needed. This code above was indeed in a WebAPI class that inherited from DnnApiController. I added this code:
Protected portalAliasService As Abstractions.Portals.IPortalAliasService Public Sub New(ByVal portalAliasService As Abstractions.Portals.IPortalAliasService) Me.portalAliasService = portalAliasService End Sub
Public Sub New(ByVal portalAliasService As Abstractions.Portals.IPortalAliasService)
Me.portalAliasService = portalAliasService
End Sub
Protected portalAliasService As Abstractions.Portals.IPortalAliasService Protected navigationManager As Abstractions.INavigationManager Public Sub New(ByVal portalAliasService As Abstractions.Portals.IPortalAliasService, ByVal navigationManager as Abstractions.INavigationManager) Me.portalAliasService = portalAliasService Me.navigationManager = navigationManager End Sub
I understood MyService above to be "creating" a new custom service and that is registered using the ConfigureServices method. However, I need to know how to pull or expose that service in a custom class that does not inherit from portalModuleBase or DnnApiController. After reading through the code once more, I see you are showing 3 ways to pull the service. The first way requires inheriting portalModuleBase, the second requires inheriting DnnApiController, the third is manual. Since my class would not inherit portalModuleBase nor DnnApiController, my only option is the manual option.
I guess the hard part of the manual option is knowing which controller implements the interface I need. I will start working to figure that out. I really appreciate the feedback. Thanks again.
Using a manual approach is always available, and what you'll need to fall back to if another approach won't work. That said, my intention above is that MyService would represent your custom class that doesn't inherit from PortalModuleBase, etc.
MyService
All of your code that runs inside DNN should be getting called by some component within DNN itself. So, ultimately, after some changes in DNN 10, there's nowhere that your custom code can't take advantage of dependency injection. If you design your custom code to take any dependencies it needs as constructor parameters, you can supply constructor arguments either via DI support or manually. That may happen transitively, if you class relies on another class that relies on a separate interface.
Hope that helps!
Okay I think I see now. My custom class would use the constructor above. Then, instead of creating a new instance of my class elsewhere, I would call an instance of it through the dependency provider using the methods you showed. I will give it a try.
I have tried the DependencyProvider.getService(of MyService) method in modulebase, but I get a "Function GetService(serviceType As Type) As Object' has no type parameters and so cannot have type arguments" error in visual studio. It doesn't like the "Of MyService" parameter for some reason. Any chance there is a typo in the code above or is the MyService class missing an implementation of something?
Yes, sorry, that should be GetRequiredService. It's an extension method, so you need the Microsoft.Extensions.DependencyInjection import.
GetRequiredService
Microsoft.Extensions.DependencyInjection
That got it. Appreciate your time.
These Forums are dedicated to the discussion of DNN Platform.
For the benefit of the community and to protect the integrity of the ecosystem, please observe the following posting guidelines:
Awesome! Simply post in the forums using the link below and we'll get you started.