Skip to content

Commit 2c03fa2

Browse files
committed
Made generic controllers abstract
Generic controllers cannot be effectively routed to by ASP.NET MVC. To mitigate this, a non-generic derived type must be created that sets the generic type. To help enforce this, these classes have been marked as `abstract`. Further, they've been renamed to `*ControllerBase` to communicate that they're not meant as concrete implementations. This required creating concrete stubs to ensure that the unit tests continue to work.
1 parent fc7de07 commit 2c03fa2

6 files changed

Lines changed: 93 additions & 12 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*==============================================================================================================================
2+
| Author Ignia, LLC
3+
| Client Ignia, LLC
4+
| Project Topics Library
5+
\=============================================================================================================================*/
6+
using System.Web.Mvc;
7+
using Ignia.Topics.ViewModels;
8+
using Ignia.Topics.Web.Mvc.Controllers;
9+
10+
namespace Ignia.Topics.Tests {
11+
12+
/*============================================================================================================================
13+
| CLASS: ERROR CONTROLLER
14+
\---------------------------------------------------------------------------------------------------------------------------*/
15+
/// <summary>
16+
/// Concrete implementation of <see cref="ErrorControllerBase{T}"/> class, suitable for test purposes.
17+
/// </summary>
18+
public class ErrorController : ErrorControllerBase<PageTopicViewModel> {
19+
20+
} //Class
21+
} //Namespace
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*==============================================================================================================================
2+
| Author Ignia, LLC
3+
| Client Ignia, LLC
4+
| Project Topics Library
5+
\=============================================================================================================================*/
6+
using System.Web.Mvc;
7+
using Ignia.Topics.Mapping;
8+
using Ignia.Topics.Repositories;
9+
using Ignia.Topics.ViewModels;
10+
using Ignia.Topics.Web.Mvc.Controllers;
11+
12+
namespace Ignia.Topics.Tests {
13+
14+
/*============================================================================================================================
15+
| CLASS: ERROR CONTROLLER
16+
\---------------------------------------------------------------------------------------------------------------------------*/
17+
/// <summary>
18+
/// Concrete implementation of <see cref="LayoutControllerBase{T}"/> class, suitable for test purposes.
19+
/// </summary>
20+
public class LayoutController : LayoutControllerBase<NavigationTopicViewModel> {
21+
22+
/*==========================================================================================================================
23+
| CONSTRUCTOR
24+
\-------------------------------------------------------------------------------------------------------------------------*/
25+
/// <summary>
26+
/// Initializes a new instance of a Topic Controller with necessary dependencies.
27+
/// </summary>
28+
/// <returns>A topic controller for loading OnTopic views.</returns>
29+
public LayoutController(
30+
ITopicRepository topicRepository,
31+
ITopicRoutingService topicRoutingService,
32+
ITopicMappingService topicMappingService
33+
) : base(
34+
topicRepository,
35+
topicRoutingService,
36+
topicMappingService
37+
) {}
38+
39+
} //Class
40+
} //Namespace

Ignia.Topics.Tests/Ignia.Topics.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@
9494
<Otherwise />
9595
</Choose>
9696
<ItemGroup>
97+
<Compile Include="Controllers\LayoutController.cs" />
98+
<Compile Include="Controllers\ErrorController.cs" />
9799
<Compile Include="TopicRoutingServiceTest.cs" />
98100
<Compile Include="ITopicRepositoryTest.cs" />
99101
<Compile Include="TestDoubles\FakeTopicRepository.cs" />

Ignia.Topics.Tests/TopicControllerTest.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ public TopicControllerTest() {
5555
| TEST: ERROR
5656
\-------------------------------------------------------------------------------------------------------------------------*/
5757
/// <summary>
58-
/// Triggers the <see cref="ErrorController{T}.Error(string)" /> action.
58+
/// Triggers the <see cref="ErrorControllerBase{T}.Error(string)" /> action.
5959
/// </summary>
6060
[TestMethod]
6161
public void ErrorController_ErrorTest() {
6262

63-
var controller = new ErrorController<PageTopicViewModel>();
63+
var controller = new ErrorController();
6464
var result = controller.Error("ErrorPage") as ViewResult;
6565
var model = result.Model as PageTopicViewModel;
6666

@@ -73,12 +73,12 @@ public void ErrorController_ErrorTest() {
7373
| TEST: NOT FOUND ERROR
7474
\-------------------------------------------------------------------------------------------------------------------------*/
7575
/// <summary>
76-
/// Triggers the <see cref="ErrorController{T}.NotFound(string)" /> action.
76+
/// Triggers the <see cref="ErrorControllerBase{T}.NotFound(string)" /> action.
7777
/// </summary>
7878
[TestMethod]
7979
public void ErrorController_NotFoundTest() {
8080

81-
var controller = new ErrorController<PageTopicViewModel>();
81+
var controller = new ErrorController();
8282
var result = controller.Error("NotFoundPage") as ViewResult;
8383
var model = result.Model as PageTopicViewModel;
8484

@@ -91,12 +91,12 @@ public void ErrorController_NotFoundTest() {
9191
| TEST: INTERNAL SERVER ERROR
9292
\-------------------------------------------------------------------------------------------------------------------------*/
9393
/// <summary>
94-
/// Triggers the <see cref="ErrorController{T}.InternalServer(string)" /> action.
94+
/// Triggers the <see cref="ErrorControllerBase{T}.InternalServer(string)" /> action.
9595
/// </summary>
9696
[TestMethod]
9797
public void ErrorController_InternalServerTest() {
9898

99-
var controller = new ErrorController<PageTopicViewModel>();
99+
var controller = new ErrorController();
100100
var result = controller.Error("InternalServer") as ViewResult;
101101
var model = result.Model as PageTopicViewModel;
102102

@@ -183,7 +183,7 @@ public void LayoutController_MenuTest() {
183183
var topicRoutingService = new MvcTopicRoutingService(_topicRepository, uri, routes);
184184
var mappingService = new TopicMappingService(_topicRepository);
185185

186-
var controller = new LayoutController<NavigationTopicViewModel>(_topicRepository, topicRoutingService, mappingService);
186+
var controller = new LayoutController(_topicRepository, topicRoutingService, mappingService);
187187
var result = controller.Menu() as PartialViewResult;
188188
var model = result.Model as NavigationViewModel<NavigationTopicViewModel>;
189189

Ignia.Topics.Web.Mvc/Controllers/ErrorController.cs renamed to Ignia.Topics.Web.Mvc/Controllers/ErrorControllerBase.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,20 @@ namespace Ignia.Topics.Web.Mvc.Controllers {
1414
/// Provides access to the views associated with 400 and 500 error results.
1515
/// </summary>
1616
/// <remarks>
17-
/// Implementers may wish to provide derived classes which return more specific error messages. This class provides a
18-
/// generic implementation that should suit most requirements.
17+
/// <para>
18+
/// Implementers may wish to provide derived classes which return more specific error messages. This class provides a
19+
/// generic implementation that should suit most requirements.
20+
/// </para>
21+
/// <para>
22+
/// In order to remain view model agnostic, the <see cref="ErrorController{T}"/> does not assume that a particular view
23+
/// model will be used, and instead accepts a generic argument for any view model that implements the interface <see
24+
/// cref="IPageTopicViewModelCore"/>. Since generic controllers cannot be effectively routed to, however, that means that
25+
/// implementors must, at minimum, provide a local instance of <see cref="ErrorController{T}"/> which sets the generic
26+
/// value to the desired view model. To help enforce this, while avoiding ambiguity, this class is marked as
27+
/// <c>abstract</c> and suffixed with <b>Base</b>.
28+
/// </para>
1929
/// </remarks>
20-
public class ErrorController<T> : Controller where T : IPageTopicViewModelCore, new() {
30+
public abstract class ErrorControllerBase<T> : Controller where T : IPageTopicViewModelCore, new() {
2131

2232
/*==========================================================================================================================
2333
| GET: /Error/Error

Ignia.Topics.Web.Mvc/Controllers/LayoutController.cs renamed to Ignia.Topics.Web.Mvc/Controllers/LayoutControllerBase.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,16 @@ namespace Ignia.Topics.Web.Mvc.Controllers {
3030
/// assembling <see cref="Topic"/> and <see cref="INavigationTopicViewModelCore"/> references that are relevant to
3131
/// specific layout elements.
3232
/// </para>
33+
/// <para>
34+
/// In order to remain view model agnostic, the <see cref="LayoutController{T}"/> does not assume that a particular view
35+
/// model will be used, and instead accepts a generic argument for any view model that implements the interface <see
36+
/// cref="INavigationTopicViewModelCore"/>. Since generic controllers cannot be effectively routed to, however, that means
37+
/// implementors must, at minimum, provide a local instance of <see cref="LayoutController{T}"/> which sets the generic
38+
/// value to the desired view model. To help enforce this, while avoiding ambiguity, this class is marked as
39+
/// <c>abstract</c> and suffixed with <c>Base</c>.
40+
/// </para>
3341
/// </remarks>
34-
public class LayoutController<T> : Controller where T : class, INavigationTopicViewModelCore<T>, new() {
42+
public abstract class LayoutControllerBase<T> : Controller where T : class, INavigationTopicViewModelCore<T>, new() {
3543

3644
/*==========================================================================================================================
3745
| PRIVATE VARIABLES
@@ -48,7 +56,7 @@ namespace Ignia.Topics.Web.Mvc.Controllers {
4856
/// Initializes a new instance of a Topic Controller with necessary dependencies.
4957
/// </summary>
5058
/// <returns>A topic controller for loading OnTopic views.</returns>
51-
public LayoutController(
59+
protected LayoutControllerBase(
5260
ITopicRepository topicRepository,
5361
ITopicRoutingService topicRoutingService,
5462
ITopicMappingService topicMappingService

0 commit comments

Comments
 (0)