2

I have three models: Animal, Dog and Cat.

Class Animal

public class Animal
{
    
}

Class Dog

public class Dog : Animal
{
    
}

And Class Cat

public class Dog : Animal
{
    
}

There alse two controllers (DogController and CatController),in each controller, there is an index action that return the view to show the list result.

Dog Controller

public class DogController : Controller
{
   public DogController ()
   {       
   }
   public async Task<IActionResult> Index()
   { 
      DogRepository IRepos = new DogRepository ();
      // Get List of Dogs
      IList<Animal> listDogs= await IRepos.GetListDogs();            
      return View(ilIst);
   } 

   [Httpost]
   public async Task<IActionResult> Add(Animal Dog)
   { 
      ....
      // Add dog to Database
                  
      return RedirectToAction("Index");
   } 


}

Index view for Dogs

@model IEnumerable<Dog> 

@{
    ViewData["Title"] = "Index";
}
<div class="row">
    <div class="table-responsive">
        <table class="table">           
            </thead>
            <tbody>
                @foreach (var item in Model)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Dog_ID)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Dog_Name)
                        </td>
                    </tr>
                }
        </table>
    </div>
</div>

In the index Action of the Dog Controller, the return type is IList<Animal> and the model type in the index view is IEnumerable<Dog>. When the application is executed, an error is generated

An unhandled exception occurred while processing the request. InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List1[Animal]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable1[Dog]'.

So, it is important to cast the list of Animals sent by the action in the controller to list of the Dogs type of the model in the view. How can we Cast the List of Animal to list of Dogs in the view, the @model IEnumerable as IEnumerable is not working.

The same thing in the post action, how can we cast the Dog model from the view to the Animal model in the action

5
  • 1
    Why is listDogs an IList<Animal> and not an IList<Dog>? Shouldn't you just change that variable's type? Commented Sep 18, 2020 at 13:26
  • In addition to the correct answer above, I see you are using an extra class called DogRepository to contain your logic. Don't call it IRepos, that is the name you would give your interface. Consider changing it to something like Repository<T> so you can make the code generic and instantiate as e.g. Repository<Dog>. You can make this part of a 'Scoped' service and add it to the controller via Dependency Injection to which is a really neat way to code avoid duplication. Commented Sep 19, 2020 at 12:11
  • This is the object polymophism. I want to use the parent class to declare an object and then I can assign any object of the child classes to it. It can be used in the case of a function that accepts a parameter for example. the parameter can be of type parent class, so we can send to this function any object of the child classes. This is the case of my example I declare the objects of type Aimal and I can thus assign to this object either a Dog or a Cat. The problem now is how to send this object of type Animal to the view which accepts a Model of type Dog and Cat.It generates an error Commented Sep 19, 2020 at 16:23
  • The problem is that the model in your Dogs view is more specific than the collection type you are sending to it. If you were to change to <Animal> in your view it would accept a collection of dogs or cats, but the model would only recognise fields in the base type, which is probably not what you want. There is nothing wrong with having a specific view for dogs or cats, but you need to pass a specific collection from your controller. Also, please consider passing a DTO not the actual db type ;) Commented Sep 20, 2020 at 6:34
  • absolutely, I think that the polymorphism is done from child class to mother class and not from mother class to child class. effect I have to put the model with the Animal type to which I send it a Dog or cat object Commented Sep 22, 2020 at 19:55

1 Answer 1

1

Polymorphism seems not work for page model. You can define partial views for Cat and Dog, and use child class for the partial view and use the base class as the model for the main view.

Main View (Index):

@model IEnumerable<Animal>

@{
    ViewData["Title"] = "Index";
}

<div class="row">
    <div class="table-responsive">
        <table class="table">
            <thead>
            </thead>
            <tbody>
                @foreach (var item in Model)
                {
                    @if (item is Dog)
                    {
                        <partial name="_DogPartial" model="item" />
                    }
                }
        </table>
    </div>
</div>

Partial View (_DogPartial):

@model Dog
<tr>
    <td>
        @Html.DisplayFor(modelItem => Model.Dog_ID)
    </td>
    <td>
        @Html.DisplayFor(modelItem => Model.Dog_Name)
    </td>
</tr>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.