1

I have a following POCO class. I don not want the parameterless constructor to be public.

public class FileDownloadRequest
    {
       //public FileDownloadRequest() { }
        public FileDownloadRequest(int fileId, RepositoryFolderTypes fileType) //RepositoryFolderTypes is an enum, not a class
        {
            this.FileId = fileId;
            this.FileType = fileType;
        }
        public int FileId { get; set; }
        public RepositoryFolderTypes FileType { get; set; } //an enum
    }

When I am trying a https://10.27.8.6/Files/DownloadFile?fileId=1&folderType=SRC request to the following controller action, I get an error saying that no parameterless constructor exists for this object.

[HttpGet]
public async Task<HttpResponseMessage> DownloadFile([FromUri] FileDownloadRequest request)
{
}

Is it possible to have a non-public constructor, or is a public one absolutely required?

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Bartosz
  • 4,406
  • 7
  • 41
  • 80
  • 1
    [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). _Why_ don't you want a parameterless constructor, which MVC needs by default? – CodeCaster May 30 '17 at 12:12
  • 1
    @TimothyGroote no, that question just mentions the solution for the error the OP gets, namely to add a parameterless contrstructor. – CodeCaster May 30 '17 at 12:12
  • 1
    hiding the parameterless constructor for a POCO with read/write properties is just crap – Sir Rufo May 30 '17 at 12:14
  • 1
    @CodeCaster hmm, should have read better. i figured someone cleared up why it needed to be public in that thread. excuse me. – Timothy Groote May 30 '17 at 12:14
  • 2
    [This answer](https://stackoverflow.com/a/12219333/2029468) to a similar question might help you. – bklaric May 30 '17 at 12:16
  • @SirRufo - you've got a point here. Its just a way to show explicitly which properties are required, though. – Bartosz May 30 '17 at 12:18
  • 1
    @Bartosz Show to ... the controller, the model binder, ... - use data annotations, flag the required properties and then just query if the model is valid. – Sir Rufo May 30 '17 at 12:23

2 Answers2

4

Yes, you can use any constructor you like, but you will have to do the model binding yourself then. The problem is in DefaultModelBinder.CreateModel, which uses a parameterless public constructor.

You have to override the default model binder and create your own. If that is worth the time is up to you.

Steps to take:

  • Override CreateModel;
  • Check the modelType for some generic constraint which models you need to call the constructor with parameters on;
  • Call Activator.CreateInstance(Type, Object[]) with the parameters. You could obtain their values from the bindingContext;
  • Register the model binder, either through the ModelBinder attribute, or globally.

Read more on custom bindings here.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • _"and create your own"_ - and then what? How would that one instantiate this model? (Yes, I know you can [instantiate an object without calling a constructor](https://stackoverflow.com/questions/296584/create-object-instance-without-invoking-constructor)). – CodeCaster May 30 '17 at 12:13
  • @CodeCaster Updated – Patrick Hofman May 30 '17 at 12:19
  • Thanks, that's what I was looking for! – Bartosz May 30 '17 at 12:22
  • 1
    Yeah so that's a whole lot of work for simply not having to create a parameterless constructor and applying `[Required]` attributes, what is what the OP appears to actually want. – CodeCaster May 30 '17 at 12:23
  • If you can change the constructor, yes. If you are stuck with a third-party library, you might not. @CodeCaster – Patrick Hofman May 30 '17 at 12:24
  • 1
    @Bartosz read _"If that is worth the time is up to you"_ and let it sink in. You're not only costing yourself a lot of time right now for things that have a simpler solution, you're also prone to running into bugs and confusing your later self and future developers of this project. – CodeCaster May 30 '17 at 12:24
  • 1
    @Patrick and that's why you don't use models from outside your own MVC project (apart from a separate viewmodels assembly) as view models. Simply create a simple POCO and map to and from that one. :) – CodeCaster May 30 '17 at 12:24
  • 1
    I saw that one coming @CodeCaster :) – Patrick Hofman May 30 '17 at 12:25
  • 2
    Consider it a pet peeve of mine! :D I've seen it go wrong more often than not. Yes, I have played with model binders myself, and it's fun to do, but for the average application you really don't want to. – CodeCaster May 30 '17 at 12:25
  • @CodeCaster - Oh yes, I did consider it and of course I am not going to use that - it makes little sense in this scenario. However, I mainly ask questions to learn whether XXX is possible, not necessarily to implement it. Thanks both. – Bartosz May 30 '17 at 12:29
0

Also, while Patrick's answer is great and shows how to do it (in scenarios where this much effort really makes sense), I just add something I've noticed in another SO post.

Basically, mark the parameterless constructor as [Obsolete("Comment to indicate its for binding only")] and that will prevent others from accidentally calling the parameterless one. (Thus showing explicitly which properties are required by the request object)

Bartosz
  • 4,406
  • 7
  • 41
  • 80