Calling an action of another controller - Design consideration for File Uploader - MVC 4
Asked Answered
F

2

6

I have a situation where I'm debating how to architect my controllers.

Consider the following controller:

    public class FileSharingController : Controller
    {

        private readonly ICommandBus commandBus;

        public FileSharingController(ICommandBus commandBus)
        {
            this.commandBus = commandBus;
        }

        [HttpPost]     
        public ActionResult PrepareMetadata(int blocksCount, string fileName, long fileSize)
        {
             ...
        }

        [HttpPost]
        public ActionResult ClearFileMetadata(string fileName){
            ...
        }

        [HttpPost] [ValidateInput(false)] //$.ajax({ data: html5FormDataFileChunk , processData: false ... })
        public ActionResult UploadBlock(string fileName, int blockId){

             var fileUploadCommand = (FileUploadCommand)ExtractFromSessionData(fileName);
             var result = commandBus.Submit(fileUploadCommand);
             ...
        }

        public ActionResult CommitFileUploads(string[] filesToCommit){
             var commitFileUploadCommand = (CommitFileUploadCommand)ExtractFromSessionData(fileName);
             var result = commandBus.Submit(commitFileUploadCommand );
             ...
        }

In this controller, I use the command pattern and pass a model to my commandBus which interfaces with my domain. The first three [HttpPost] methods on the controller are for handling jQuery ajax calls from a responsive file uploading UI.

Consider the situation where a user fills out a form (an interview) and uploads some files along with it. Although the user can upload the files before submitting the form, I don't want the uploaded files to be committed until AFTER they submit the form and it passes validation. That is why the last method on the controller is not an http endpoint. As such I have the following controller:

    public class InterviewController : Controller
    {
        [HttpGet] 
        public ActionResult UserInterview()
        {
            InterviewViewModel viewModel = new InterviewViewModel ();
            return PartialView(viewModel);
        }

        [HttpPost] [AllowAnonymous]
        public ActionResult UserInterview(InterviewViewModel viewModel)
        {
            if(ModelState.IsValid)
            {
                var fileSharingController = new FileSharingController();
                fileSharingController.CommitFileUploads(viewModel.Files);
            }

            return PartialView(viewModel);
        }

    }

The problem is I'm using IoC to inject a commandBus into the FileSharingController so I cannot just instantiate it with default constructor as I am doing.

My options to consider:

  • Create a custom controller factory to allow instantiating my controller anywhere in the code.
  • Turn my FileSharingController in a WebAPI controller and treat as a service

Which is the better design path for this situation? If the latter case, how can I keep the CommitFileUploads() method private? I don't want it to be exposed as an endpoint that can be triggered without first validating the rest of the form.

Fuge answered 8/2, 2013 at 0:28 Comment(0)
A
2

You can instantiate your controller like this:

ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileSharingController(commandBus);

Generic GetService() method is extension method, so make sure that you have "using System.Web.Mvc;" line in the cs file.

But then, it's better to have helper class that is responsible for keeping/storing already uploaded files, and call it from both controllers, instead instantiating controllers manually.

For example:

public class FileUploadManager
{
    public FileUploadManager(ICommandBus commandBus, HttpSessionStateBase sessionState)
    {
         //....
    }
}

and then you call it:

ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileUploadManager(commandBus, this.HttpContext.Session);

Or, if you don't want to use DependencyResolver, you pass ICommandBus to both controller's constructors, and use that reference to instantiate helper class.

Abbreviation answered 14/2, 2013 at 18:56 Comment(0)
B
0

simply just create the object of another conroller and use all its public methods.

Bionomics answered 15/2, 2013 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.