简体   繁体   中英

How do you implement this generic class?

I have a frontend ASP.NET MVC website, which contacts a backend service. The frontend and backend use DTO's to communicate.

public class MyDto
{
    public string Name {get;set;}
}

I don't want to show this DTO directly to users of my website, so I made a viewmodel that takes the same data.

public class MyViewModel
{
    public string Name {get;set;}
}

Now, for all these viewmodels, the update, post and other methods are all the same. Instead of retyping the same controller actions for every viewmodel, I decided to create an abstract controller that contains all these methods. So I made an interface so all my view models would have the same behaviour:

 public interface IViewModelControllerActions<T>
 { 
    IUpdateData<T> UpdateData { get; } //IUpdateData defined somewhere else
    IUploadData<T> UploadData { get; } // IUploadData defined somewhere else
    // definitions for other actions
 }

Which I added to my viewmodel:

public class MyViewModel : IViewModelControllerActions<MyDto>
{
    public string Name {get;set;}

    public IUpdateData<T> UpdateData { get { /* Do stuff to get UpdateData */ } }
    public IUploadData <T> UploadData { get { /* Do stuff to get UploadData */ } }
}

And create the abstract controller:

 public abstract class ViewModelController<T>: Controller where T : IViewModelControllerActions<T>
 {
      [HttpPost]
      public ActionResult Upload(T input)
      {
           // do stuff with T.UploadData to upload
      }

      [HttpPost]
      public ActionResult Update(T input)
      {
           // do stuff with T.UpdateData to update
      }

      // other methods
 }

The idea is: when a new dto and viewmodel are introduced, all I have to do is implement the IViewModelControllerActions interface on my viewmodel:

public class MyNewCoolerViewModel : IViewModelControllerActions<MyNewCoolerDto>
{
    public string Name {get;set;}
    public string AnotherNewName {get;set;}
    public int ANumber {get;set;}

    public IUpdateData<T> UpdateData { get { /* Do stuff to get UpdateData */ } }
    public IUploadData <T> UploadData { get { /* Do stuff to get UploadData */ } }
}

And create a new empty controller that inherits from the abstract one:

public class MyNewCoolerController : ViewModelController<MyNewCoolerViewModel> {}

So that all logic inside the controller will be already implemented. However, I get this error message:

The type MyNewCoolerViewModel cannot be used as type parameter T in the generic type or method ViewModelController<T>. There is no implicit reference conversion from MyNewCoolerViewModel to IViewModelControllerActions<MyNewCoolerViewModel>

I understand the error message, however, I don't understand how to fix it. How do I get this setup to work?

Have you looked at/considered Automapper ?

I think you're overcomplicating it - I realize it's not a direct answer to your question, but the problem of DTO -> ViewModel is typically solved using a mapper of some kind. What would be wrong with that approach in your project?

This feels kind of like a puzzle, but after looking at it for a while, the main issue is that T in the controller is self referential:

abstract class ViewModelController<T>: Controller where T : IViewModelControllerActions<T>

Which in this case means that T has to be of type IViewModelControllerActions<IViewModelControllerActions<T>> . The controller should be either ViewModelController<T,U>:Controller where T:IViewModelController<U> or ViewModelController<IViewModelControllerAction<T>> depending on how you intend to use it

But to be honest, this doesn't (imo) align very well with the MVC-architecture. As for controller and reuse, I think you could skip the entire IViewModelControllerAction-interface and just override the methods on a base class, something like this:

public abstract class ViewModelController<T,U>:Controller{ public ActionResult Update(T item){...} public abstract void Save(U dto); }

The U might be optional depending on how you intend to implement things, but it's a start.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM