Umbraco 10 Views and Dependency Injection - best practice?
Can someone explain to me how/why dependency injection is used in Views in Umbraco 10 (and I assume 9)?
Why are we not able to access the UmbracoHelper by default, for example?
I am currently doing this to get access to the Umbraco Helper:
@inject IUmbracoHelperAccessor umbracoHelperAccessor
@inherits UmbracoViewPage<BlockListItem>
@{
var row = (RoleInsightsCarousel)Model.Content;
var settings = (RoleInsightsCarouselSettings)Model.Settings;
if (settings != null && settings.DisableComponent) return;
var roleInsightPages = new List<RoleInsightsPage>();
if (umbracoHelperAccessor.TryGetUmbracoHelper(out var umbracoHelper))
{
if (umbracoHelper != null)
{
roleInsightPages = umbracoHelper.GetRoleInsights(row.RoleTypes);
}
}
}
GetRoleInsights() is an extension method I have created btw.
In another partial I need the context and could only get it by injecting in the
IUmbracoContextAccessor and then calling contextAccessor.TryGetUmbracoContext(out var umbracoContext)
Is there a better way? Any explaination for how this works would be much appreciated!
You should not have to use DI in your views to get at UmbracoHelper.
As long as your page inherits from Umbraco.Cms.Web.Common.Views.UmbracoViewPage you should be able to access it directly by using @Umbraco.
I am not sure if your entire view header is shown in your code example, but if you are not able to access @Umbraco methods, you can try something like (Umbraco 10):
Really though, ideally you should be creating a custom controller and view model for each of your views (either based on document type or template name). This was you can simplify or eliminate business logic from your views.
And make sure you check out "Returning a view with a custom model" that outlines a way you can create a view model that inherits from your modelsbuilder model that will allow you to add view specific properties that may not be available in your modelsbuilder models.
The issue with the above recommendation is that you then introduce multiple points of failure. You now have the view, the controller and the new viewmodel any of which may have an issue in them. Not to mention you are now overriding the routing again, and adding to memory usage by introducing more objects into RAM. I would only do this if the view is very complex.
Business logic in the view can be handled by extending the partial class of the page model, hitting a custom service or any number of other ways. This also means it can be cached (the controller methods are never cached).
If the logic is purely for the view, then pop it in the partial.
Hi Damien, maybe I am missing something - I'm not sure exactly what you are suggesting here by extending the partial class of the page model - no business logic should exist in the model.
Logic in the views (while it's very possible) is an MVC antipattern. A popular design pattern with Umbraco involves injecting some sort of custom data retrieval service in the view via DI; however, ideally you would override the Index controller, call your service from there (if desired), or execute custom logic in the controller to manipulate the existing modelsbuilder model or enhance the Umbraco generated models with your own view model that inherits from the generated model exposing not only the models builder model to the view but also custom properties from your own view model.
This is more work to be sure, but if anything, by putting BL in the views you are setting yourself up for potential runtime errors. The MVC approach at least gives you better separation of concerns, compile time errors and the ability to unit test your code. Any additional memory should be negligible and certainly worth the tradeoff.
Controllers are fully cacheable using the OutputCache Attribute. Something like:
public class MyPageController : RenderController
{
[OutputCache(Duration = 10)] // cache seconds
public override IActionResult Index()
{
// do stuff
myCustomProperty = "Test";
return View("MyPageView", new MyPageViewModel(CurrentPage!, _publishedValueFallback)
{
MyCustomProperty = myCustomProperty //assumes you have a custom viewmodel called "MyPageViewModel" with a MyCustomProperty prop.
});
}
}
Umbraco 10 Views and Dependency Injection - best practice?
Can someone explain to me how/why dependency injection is used in Views in Umbraco 10 (and I assume 9)?
Why are we not able to access the UmbracoHelper by default, for example?
I am currently doing this to get access to the Umbraco Helper:
GetRoleInsights() is an extension method I have created btw.
In another partial I need the context and could only get it by injecting in the
IUmbracoContextAccessor
and then callingcontextAccessor.TryGetUmbracoContext(out var umbracoContext)
Is there a better way? Any explaination for how this works would be much appreciated!
You should not have to use DI in your views to get at UmbracoHelper.
As long as your page inherits from Umbraco.Cms.Web.Common.Views.UmbracoViewPage you should be able to access it directly by using @Umbraco.
I am not sure if your entire view header is shown in your code example, but if you are not able to access @Umbraco methods, you can try something like (Umbraco 10):
Really though, ideally you should be creating a custom controller and view model for each of your views (either based on document type or template name). This was you can simplify or eliminate business logic from your views.
Have you looked into Umbraco route hijacking? Check out: https://our.umbraco.com/documentation/reference/routing/custom-controllers/
And make sure you check out "Returning a view with a custom model" that outlines a way you can create a view model that inherits from your modelsbuilder model that will allow you to add view specific properties that may not be available in your modelsbuilder models.
The issue with the above recommendation is that you then introduce multiple points of failure. You now have the view, the controller and the new viewmodel any of which may have an issue in them. Not to mention you are now overriding the routing again, and adding to memory usage by introducing more objects into RAM. I would only do this if the view is very complex.
Business logic in the view can be handled by extending the partial class of the page model, hitting a custom service or any number of other ways. This also means it can be cached (the controller methods are never cached).
If the logic is purely for the view, then pop it in the partial.
Hi Damien, maybe I am missing something - I'm not sure exactly what you are suggesting here by extending the partial class of the page model - no business logic should exist in the model.
Logic in the views (while it's very possible) is an MVC antipattern. A popular design pattern with Umbraco involves injecting some sort of custom data retrieval service in the view via DI; however, ideally you would override the Index controller, call your service from there (if desired), or execute custom logic in the controller to manipulate the existing modelsbuilder model or enhance the Umbraco generated models with your own view model that inherits from the generated model exposing not only the models builder model to the view but also custom properties from your own view model.
This is more work to be sure, but if anything, by putting BL in the views you are setting yourself up for potential runtime errors. The MVC approach at least gives you better separation of concerns, compile time errors and the ability to unit test your code. Any additional memory should be negligible and certainly worth the tradeoff.
Controllers are fully cacheable using the OutputCache Attribute. Something like:
is working on a reply...