Single Responsibility delivers more dependencies, how fix this [duplicate] - c#

This question already has answers here:
How to avoid Dependency Injection constructor madness?
(10 answers)
Closed 7 years ago.
I have an MVC controller holding several abstractions (components). I want to reduce the amount of params to max 4 params.
To do this, I have several strategies that do not satisfy:
Bundling a set of params into a new class where the params will be properties: that just masks the complexity.
Resolve dependencies not by constructor injection but resolve by using factories: that also masks complexity.
Is there a better alternative approach ?
This the code of the controller:
public abstract class BaseVerificatieController : ExtendedController
{
private readonly IReferentieTabellenSysteemSettings referentieTabellenSysteemSettings;
private readonly IProcessManager processManager;
private readonly INavigationManager navigationManager;
private readonly ITransactionFacade transactionFacade;
private readonly IUISupportFacade uiSupportFacade;
private readonly ProcessIndex process;
protected BaseVerificatieController(
IBackHandler backHandler,
IReferentieTabellenSysteemSettings referentieTabellenSysteemSettings,
IProcessManager processManager,
INavigationManager navigationManager,
ITransactionFacade transactionFacade,
IUISupportFacade uiSupportFacade,
ProcessIndex process)
: base(backHandler)
{
this.referentieTabellenSysteemSettings = referentieTabellenSysteemSettings;
this.processManager = processManager;
this.navigationManager = navigationManager;
this.transactionFacade = transactionFacade;
this.uiSupportFacade = uiSupportFacade;
this.process = process;
}
[HttpGet]
public ActionResult Index()
{
var processStep = processManager.StartProcess(process);
return navigationManager.RedirectFromProcessStep(process, processStep);
}
[HttpGet]
public ActionResult Oe()
{
var model = new OeViewModel();
var transactionResult = transactionFacade.VerifyIdentityStart();
model.SetTransactionResult(transactionResult);
return View(model);
}
[HttpPost]
public ActionResult Oe(OeViewModel viewModel)
{
if (viewModel == null)
{
throw new ArgumentNullException("viewModel");
}
var transactionResult = transactionFacade.VerifyIdentityCheckRegisters(viewModel.SKN, null);
if (transactionResult.MoveToStep != Business.Models.ProcessStepIndex.NoStep)
{
return navigationManager.RedirectFromTransactionResult(process, transactionResult);
}
var model = new OeViewModel();
model.SetTransactionResult(transactionResult);
return View(model);
}
[HttpGet]
public ActionResult Oz()
{
var model = new OzViewModel(uiSupportFacade, referentieTabellenSysteemSettings);
var idStaatResult = transactionFacade.IdStaatStart();
model.SetIdStaatResult(idStaatResult);
return View("Oz_SKDB", model);
}
[HttpPost]
public ActionResult Oz(OzViewModel viewModel)
{
return RedirectToAction("Index", "Home");
}

As #Maarten says in comments, I would get rid of the injection of the process and inject it where its needed (not passing it around).
I would further move all the view model logic into "view model handlers" and use a mediator for executing the view model handlers.
The dependencies such as the transactionFacade and uiSupportFacade would then be injected in the view model handlers. This can be achieved with the following:
/// <summary>
/// Specifices that the target class is a view model. This is a marker interface and has no methods.
/// </summary>
public interface IViewModel
{
// Marker interface
}
/// <summary>
/// Handles the <typeparamref name="TViewModel"/>.
/// </summary>
/// <typeparam name="TViewModel">The view model which should be handled.</typeparam>
public interface IHandleViewModel<out TViewModel> where TViewModel : IViewModel
{
/// <summary>
/// Creates a <typeparamref name="TViewModel"/>.
/// </summary>
/// <returns>An instance of the <typeparamref name="TViewModel"/>.</returns>
TViewModel Handle();
}
/// <summary>
/// Handles the <typeparamref name="TViewModel"/> with the argument of <typeparamref name="TInput"/>
/// </summary>
/// <typeparam name="TInput">The argument for the view model</typeparam>
/// <typeparam name="TViewModel">The view model which should be handled.</typeparam>
public interface IHandleViewModel<out TViewModel, in TInput> where TViewModel : IViewModel
{
/// <summary>
/// Creates a <typeparamref name="TViewModel"/>.
/// </summary>
/// <returns>An instance of the <typeparamref name="TViewModel"/>.</returns>
TViewModel Handle(TInput input);
}
/// <summary>
/// Processes and creates view models.
/// </summary>
public interface IProcessViewModels
{
/// <summary>
/// Creates the <typeparamref name="TViewModel"/>.
/// </summary>
/// <returns>The view model</returns>
TViewModel Create<TViewModel>() where TViewModel : IViewModel;
/// <summary>
/// Create the <typeparamref name="TViewModel"/> with an argument of type <typeparamref name="TInput"/>
/// </summary>
/// <typeparam name="TViewModel">The view model which should be constructed</typeparam>
/// <typeparam name="TInput">The type of argument for the view model</typeparam>
/// <param name="input">The argument for the view model</param>
/// <returns>The view model</returns>
TViewModel Create<TViewModel, TInput>(TInput input) where TViewModel : IViewModel;
}
This means you can inject a single IProcessViewModels into your controller and execute the handlers. E.g. by filling the dependency container (here Simple Injector):
/// <summary>
/// Registers the view models in the Simple Injector container
/// </summary>
/// <param name="container">The Simple Injector container</param>
/// <param name="viewModelAssemblies">The assembly location of the view models</param>
public static void RegisterViewModels(this Container container, Assembly[] viewModelAssemblies)
{
if (container == null)
throw new ArgumentNullException("container");
if (viewModelAssemblies == null)
throw new ArgumentNullException("viewModelAssemblies");
container.RegisterSingle<IProcessViewModels, ViewModelProcessor>();
container.RegisterManyForOpenGeneric(typeof(IHandleViewModel<>), viewModelAssemblies);
container.RegisterManyForOpenGeneric(typeof(IHandleViewModel<,>), viewModelAssemblies);
}
The above code is from my "Nerve Framework" here: https://github.com/janhartmann/nerve-framework/tree/master/NerveFramework.Web.Mvc

Related

Is is possible to change the contents of the Example Value section generated by NSwag?

We are using NSwag to generate swagger pages for our API. We have a fictitious asp.net core 2.2 controller and the generated swagger pages looks like this:
Is it possible to change the contents of the Example Value (highlighted in yellow)? We would like to display an actual sample value for the testParameter instead of just "string". Is there a data annotation we can use, or some other overriding mechanism available to override this behaviour?
The version of NSwag we are using is NSwag.AspNetCore 13.0.2.
The class that defines the model is:
public class TestQuery : IRequest<TestResponse>
{
[DefaultValue("test1")]
[JsonProperty("testParameter")]
public string TestParameter { get; set; }
}
We are using MediatR hence the IRequest.
Here's the controller class:
[Authorize, Route("api/route/test")]
[ApiController]
public class TestController : ControllerBase
{
private readonly IMediator _mediator;
/// <summary>
/// TestController constructor
/// </summary>
/// <param name="mediator">Dependency Injected mediator reference</param>
public TestController(IMediator mediator)
{
_mediator = mediator;
}
/// <summary>
/// Async method which retrieves a test response for a given testParameter string
/// </summary>
/// <remarks>
///
/// Sample testParameters: "test1", "test2", "test3"
///
/// Sample Request:
/// {
/// "testParameter": "test1"
/// }
///
/// </remarks>
/// <param name="GetTestResponseQuery">Get test response request model</param>
/// <returns>A test response</returns>
[HttpPost]
public async Task<ApiResponse<TestResponse>> GetTestResponse([FromBody]TestQuery testQuery)
{
if (!ModelState.IsValid)
{
throw new Exception($"State for model TestQuery is not valid for service {testQuery.TestParameter}");
}
else
{
var result = await _mediator.Send(testQuery).ConfigureAwait(false);
return new ApiResponse<TestResponse>(result);
}
}
}
Thank you kindly,
Yves Rochon
On your TestResponse class, add the example tag in xml comments of every property, that will be used in the swagger example. eg
public class TestResponse
{
/// <summary>
/// Property description
/// </summary>
/// <example>Sample value</example>
public string Property { get; set; }
}

JsonResult return Json in ASP.NET CORE 2.1

Controller that worked in ASP.NET Core 2.0:
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class GraficResourcesApiController : ControllerBase
{
private readonly ApplicationDbContext _context;
public GraficResourcesApiController(ApplicationDbContext context)
{
_context = context;
}
[HttpGet]
public JsonResult GetGrafic(int ResourceId)
{
var sheduling = new List<Sheduling>();
var events = from e in _context.Grafic.Where(c=>c.ResourceId == ResourceId)
select new
{
id = e.Id,
title = e.Personals.Name,
start = e.DateStart,
end = e.DateStop,
color = e.Personals.Color,
personalId = e.PersonalId,
description = e.ClientName
};
var rows = events.ToArray();
return Json(rows);
}
}
in ASP.NET Core 2.1
return Json (rows);
writes that Json does not exist in the current context. If we remove Json leaving simply
return rows;
then writes that it was not possible to explicitly convert the type List () to JsonResult
How to convert to Json now?
In asp.net-core-2.1 ControllerBase does not have a Json(Object) method. However Controller does.
So either refactor the current controller to be derived from Controller
public class GraficResourcesApiController : Controller {
//...
}
to have access to the Controller.Json Method or you can initialize a new JsonResult yourself in the action
return new JsonResult(rows);
which is basically what the method does internally in Controller
/// <summary>
/// Creates a <see cref="JsonResult"/> object that serializes the specified <paramref name="data"/> object
/// to JSON.
/// </summary>
/// <param name="data">The object to serialize.</param>
/// <returns>The created <see cref="JsonResult"/> that serializes the specified <paramref name="data"/>
/// to JSON format for the response.</returns>
[NonAction]
public virtual JsonResult Json(object data)
{
return new JsonResult(data);
}
/// <summary>
/// Creates a <see cref="JsonResult"/> object that serializes the specified <paramref name="data"/> object
/// to JSON.
/// </summary>
/// <param name="data">The object to serialize.</param>
/// <param name="serializerSettings">The <see cref="JsonSerializerSettings"/> to be used by
/// the formatter.</param>
/// <returns>The created <see cref="JsonResult"/> that serializes the specified <paramref name="data"/>
/// as JSON format for the response.</returns>
/// <remarks>Callers should cache an instance of <see cref="JsonSerializerSettings"/> to avoid
/// recreating cached data with each call.</remarks>
[NonAction]
public virtual JsonResult Json(object data, JsonSerializerSettings serializerSettings)
{
if (serializerSettings == null)
{
throw new ArgumentNullException(nameof(serializerSettings));
}
return new JsonResult(data, serializerSettings);
}
Source

how to mock AuthenticateAsync on AspNetCore.Authentication.Abstractions

i have an action on a controller which calls
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
i'm trying to mock this result in a unit test like so
httpContextMock.AuthenticateAsync(Arg.Any<string>()).Returns(AuthenticateResult.Success(...
however that throws an InvalidOperationException
"No service for type 'Microsoft.AspNetCore.Authentication.IAuthenticationService' has been registered"
what is the correct way to mock this method?
That extension method
/// <summary>
/// Extension method for authenticate.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <returns>The <see cref="AuthenticateResult"/>.</returns>
public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme);
goes through the IServiceProvider RequestServices property.
/// <summary>
/// Gets or sets the <see cref="IServiceProvider"/> that provides access to the request's service container.
/// </summary>
public abstract IServiceProvider RequestServices { get; set; }
Mock the service provider to return a mocked IAuthenticationService and you should be able to fake your way through the test.
authServiceMock.AuthenticateAsync(Arg.Any<HttpContext>(), Arg.Any<string>())
.Returns(Task.FromResult(AuthenticateResult.Success()));
providerMock.GetService(typeof(IAuthenticationService))
.Returns(authServiceMock);
httpContextMock.RequestServices.Returns(providerMock);
//...

Thread.Timer With HttpContext is not working

hello i'm trying to send email at a specific time i found the code.
On global.asax.cs file
protected void Application_Start()
{
ETHOS.Controllers.HomeController mail = new Controllers.HomeController();
mail.ScheduleService();
private void SchedularCallback(object e)
{
this.WriteToFile("Simple Service Log: {0}");
getData();//Email function
this.ScheduleService();
}
public void ScheduleService()
{
try
{
Schedular = new Timer(new TimerCallback(SchedularCallback));
string mode = "DAILY";
this.WriteToFile("Simple Service Mode: " + mode + " {0}");
//Set the Default Time.
//DateTime d = DateTime.Today;
//TimeSpan t = new TimeSpan(12, 40, 00);
//DateTime scheduledTime = d.Date + t;
DateTime scheduledTime = DateTime.Now.AddSeconds(30);
if (DateTime.Now > scheduledTime)
{
//If Scheduled Time is passed set Schedule for the next day.
// scheduledTime = scheduledTime.AddDays(1);
scheduledTime = scheduledTime.AddDays(1);
}
TimeSpan timeSpan = scheduledTime.Subtract(DateTime.Now);
string schedule = string.Format("{0} day(s) {1} hour(s) {2} minute(s) {3} seconds(s)", timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
this.WriteToFile("Simple Service scheduled to run after: " + schedule + " {0}");
//Get the difference in Minutes between the Scheduled and Current Time.
int dueTime = Convert.ToInt32(timeSpan.TotalMilliseconds);
//Change the Timer's Due Time.
Schedular.Change(dueTime, Timeout.Infinite);
}
catch (Exception ex)
{
WriteToFile("Simple Service Error on: {0} " + ex.Message + ex.StackTrace);
}
}
In email function i'm sending email through gmail and on body i'm rendered partial view to string class to rendered my razor view to string
string body = ViewRenderer.RenderPartialView("~/Views/Shared/Email.cshtml", LEM);
problem is that it doesn't getting HttpContext.Current.It show HttpContext.Current = null. I think Both are creating Thread so thats why it is not getting HttpContext so how can i use both with same httpcontext.
here is the RendererView Class below
public class ViewRenderer:ETHOS.Controllers.HomeController
{/// <summary>
/// Required Controller Context
/// </summary>
protected ControllerContext Context { get; set; }
/// <summary>
/// Initializes the ViewRenderer with a Context.
/// </summary>
/// <param name="controllerContext">
/// If you are running within the context of an ASP.NET MVC request pass in
/// the controller's context.
/// Only leave out the context if no context is otherwise available.
/// </param>
public ViewRenderer(ControllerContext controllerContext = null)
{
System.Web.HttpContext ctx = (System.Web.HttpContext) Session["ctx"];
// Create a known controller from HttpContext if no context is passed
if (controllerContext == null)
{
if (System.Web.HttpContext.Current != null)
controllerContext = CreateController<EmptyController>().ControllerContext;
else
throw new InvalidOperationException(
"ViewRenderer must run in the context of an ASP.NET " +
"Application and requires HttpContext.Current to be present.");
}
Context = controllerContext;
}
/// <summary>
/// Renders a full MVC view to a string. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to render the view with</param>
/// <returns>String of the rendered view or null on error</returns>
public string RenderViewToString(string viewPath, object model = null)
{
return RenderViewToStringInternal(viewPath, model, false);
}
/// <summary>
/// Renders a full MVC view to a writer. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to render the view with</param>
/// <returns>String of the rendered view or null on error</returns>
public void RenderView(string viewPath, object model, TextWriter writer)
{
RenderViewToWriterInternal(viewPath, writer, model, false);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <returns>String of the rendered view or null on error</returns>
public string RenderPartialViewToString(string viewPath, object model = null)
{
return RenderViewToStringInternal(viewPath, model, true);
}
/// <summary>
/// Renders a partial MVC view to given Writer. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="writer">Writer to render the view to</param>
public void RenderPartialView(string viewPath, object model, TextWriter writer)
{
RenderViewToWriterInternal(viewPath, writer, model, true);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active Controller context</param>
/// <returns>String of the rendered view or null on error</returns>
public static string RenderView(string viewPath, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderViewToString(viewPath, model);
}
/// <summary>
/// Renders a partial MVC view to the given writer. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="writer">Writer to render the view to</param>
/// <param name="controllerContext">Active Controller context</param>
/// <returns>String of the rendered view or null on error</returns>
public static void RenderView(string viewPath, TextWriter writer, object model,
ControllerContext controllerContext)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderView(viewPath, model, writer);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active Controller context</param>
/// <param name="errorMessage">optional out parameter that captures an error message instead of throwing</param>
/// <returns>String of the rendered view or null on error</returns>
public static string RenderView(string viewPath, object model,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderViewToString(viewPath, model);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
return null;
}
/// <summary>
/// Renders a partial MVC view to the given writer. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active Controller context</param>
/// <param name="writer">Writer to render the view to</param>
/// <param name="errorMessage">optional out parameter that captures an error message instead of throwing</param>
/// <returns>String of the rendered view or null on error</returns>
public static void RenderView(string viewPath, object model, TextWriter writer,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderView(viewPath, model, writer);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active controller context</param>
/// <returns>String of the rendered view or null on error</returns>
public static string RenderPartialView(string viewPath, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderPartialViewToString(viewPath, model);
}
/// <summary>
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">The model to pass to the viewRenderer</param>
/// <param name="controllerContext">Active controller context</param>
/// <param name="writer">Text writer to render view to</param>
/// <param name="errorMessage">optional output parameter to receive an error message on failure</param>
public static void RenderPartialView(string viewPath, TextWriter writer, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderPartialView(viewPath, model, writer);
}
/// <summary>
/// Internal method that handles rendering of either partial or
/// or full views.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">Model to render the view with</param>
/// <param name="partial">Determines whether to render a full or partial view</param>
/// <param name="writer">Text writer to render view to</param>
protected void RenderViewToWriterInternal(string viewPath, TextWriter writer, object model = null, bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);
if (viewEngineResult == null)
throw new FileNotFoundException();
// get the view and attach the model to view data
var view = viewEngineResult.View;
Context.Controller.ViewData.Model = model;
var ctx = new ViewContext(Context, view,
Context.Controller.ViewData,
Context.Controller.TempData,
writer);
view.Render(ctx, writer);
}
/// <summary>
/// Internal method that handles rendering of either partial or
/// or full views.
/// </summary>
/// <param name="viewPath">
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
/// </param>
/// <param name="model">Model to render the view with</param>
/// <param name="partial">Determines whether to render a full or partial view</param>
/// <returns>String of the rendered view</returns>
private string RenderViewToStringInternal(string viewPath, object model,
bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);
if (viewEngineResult == null || viewEngineResult.View == null)
throw new FileNotFoundException();//Resources.ViewCouldNotBeFound);
// get the view and attach the model to view data
var view = viewEngineResult.View;
Context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(Context, view,
Context.Controller.ViewData,
Context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}
/// <summary>
/// Creates an instance of an MVC controller from scratch
/// when no existing ControllerContext is present
/// </summary>
/// <typeparam name="T">Type of the controller to create</typeparam>
/// <returns>Controller for T</returns>
/// <exception cref="InvalidOperationException">thrown if HttpContext not available</exception>
public static T CreateController<T>(RouteData routeData = null, params object[] parameters)
where T : Controller, new()
{
// create a disconnected controller instance
T controller = (T)Activator.CreateInstance(typeof(T), parameters);
// get context wrapper from HttpContext if available
HttpContextBase wrapper = null;
if (System.Web.HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
else
throw new InvalidOperationException(
"Can't create Controller Context if no active HttpContext instance is available.");
if (routeData == null)
routeData = new RouteData();
// add the controller routing if not existing
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}
}
/// <summary>
/// Empty MVC Controller instance used to
/// instantiate and provide a new ControllerContext
/// for the ViewRenderer
/// </summary>
public class EmptyController : Controller
{
}
The HttpContext is only available within the lifetime of an HTTP request. It is not possible to use it outside in such background threads. Also it is considered bad practice to implement recurring background tasks in an ASP.NET applications. The recommended approach is to off-load this task from your web application and put it in a Windows Service or a Console Application that will be executed at regular intervals by the Windows Scheduler.
That being said, if you decide to go against the recommended approaches and still insist on doing this, you might consider using the Razor engine outside of an ASP.NET application so that you don't depend on an HttpContext.
That is because you can't access HttpContext.Current from another thread than the HTTP request thread. There is nothing you can do about that instead of passing the context in, which might be dangerous.
I would seriously advice you to run the code in a process separated from ASP.NET (a Windows service for example).

Use of BaseController even if all dependencies not used?

I have created a base controller with the following constructor:
public BaseController(ICustomer customer, ISiteSettings siteSettings, ILogger logger, ILocalizer localizer)
All my controllers are using the base as they all use all the dependecies. Now I am creating a new controller ErrorController which uses only uses siteSettings and not the other dependencies.
I was wondering if it would make sense to not to use the BaseController as an inheritance in ErrorController and just create a dependency for SiteSettings. On the other hand if the using BaseController is not too expensive, may be I should just keep the consistency with other controllers?
My feelings are since the dependencies are already registered with Unity, hitting the base controller would be a very inexpensive process. Just a thought. Please let me know if you think otherwise.
BaseController Code for your review:
public class BaseController : Controller
{
#region Private Members
/// <summary>
/// Factory to obtain customer object
/// </summary>
protected readonly ICustomer Customer;
/// <summary>
/// Site Settings
/// </summary>
protected readonly ISiteSettings SiteSettings;
/// <summary>
/// The Localization interface
/// </summary>
protected readonly ILocalizer Localizer;
/// <summary>
/// The Logger is used for exception handling
/// </summary>
protected readonly ILogger Logger;
#endregion
#region Ctor
/// <summary>
/// <para>Initializes a new instance of the <see cref="BaseController"/> class.</para>
/// <para>Parameters are injected via Unity.</para>
/// </summary>
/// <param name="customer">The customer.</param>
/// <param name="siteSettings">The site settings.</param>
/// <param name="logger">The logger.</param>
/// <param name="localizer">The localizer.</param>
public BaseController(ICustomer customer, ISiteSettings siteSettings, ILogger logger, ILocalizer localizer)
{
if (customer == null)
{
throw new ArgumentNullException("customer");
}
Customer = customer;
if (siteSettings == null)
{
throw new ArgumentNullException("siteSettings");
}
SiteSettings = siteSettings;
if (logger == null)
{
throw new ArgumentNullException("logger");
}
Logger = logger;
if (localizer == null)
{
throw new ArgumentNullException("localizer");
}
Localizer = localizer;
}
#endregion
}
Unless you have some hairy constructors on those dependencies (not the BaseController constructor itself), this should not be an issue. Your container should handle the grunt work and lifecycle of these. Would use the BaseController for consistency and ease of further expansion.

Categories

Resources