[ACCEPTED]-Using a generic model in ASP.NET MVC Razor-razor

Accepted answer
Score: 32

given that @model is expecting a type - not 2 a type declaration you could use:

@model DtoViewModel<IDto>

and take 1 advantage of generic covariance

Score: 24

Such syntax is not supported by Razor, sorry.

0

Score: 7

Assuming, you want to use a generic type 12 in order to avoid code duplications in each 11 view of ViewModel<T> you can do it this way:

1. create 10 a view for the parts of ViewModel<T> that are unique 9 to the view

ModelView.cshtml:

@model ViewModel<specificType>

@{Layout = "~/Views/Shared/Layout.cshtml";}
<h2 class="sub-header">Specific type view</h2>

2. create a view for the common 8 parts, that should be rendered in each view 7 of <T>

Grid.cshtml:

@{ var webGrid = new WebGrid(Model.PageItems); }

<div class="row" style="overflow: auto">
    @webGrid.GetHtml("table-striped", mode: WebGridPagerModes.All, firstText: "First", lastText: "Last")
</div>

Since it's a partial view, you don't 6 need to declare the type of Model again. It will 5 simply use the model you defined in the 4 parent view, that renders it. The property 3 IList<T> PageItems of your model, will remain strongly typed 2 with <specificType>.

3. Don't forget to actually render 1 the partial view of your common parts

ModelView.cshtml:

@RenderPage("~/Views/Shared/Grid.cshtml")
Score: 1

This is not ideal, but it works and could 1 get pretty creative with this pattern.

@model YourNameSpace.MyModel


public MyModel
{
    public MyGenericType<string> ModelAStuff {get;set;}
    public MyGenericType<int> ModelBStuff {get;set;}
    public MyGenericType<DateTime> ModelCStuff {get;set;}
}

public class MyGenericType<T>
{
  //use T how ever you like
  public T Color {get;set;}
  public T Year  {get;set;}
}
Score: 1

I found this thread and did some further 3 digging. Looks like the feature is on the 2.2.0 backlog. If anyone wants 2 to get involved you can check out the issue 1 on Github. https://github.com/aspnet/Mvc/issues/7152

Score: 0

Microsoft uses interfaces in many of their libraries 3 to overcome this type of restriction. Here 2 is my implementation that allows me to send 1 in any generic model. My view is a partial.

Note: because I want the caller to define where the values come from, the model mostly takes lambda accessors.

Partial reference from parent view:

<partial name="EntitySelector.partial" model="
    new EntitySelectionModel<Image>
    { 
        DbSet = (new EcoContext()).Images,
        GetDbEntityDisplayName = e => e.DisplayName,
        GetDbEntityId = e => e.Id,
        GetDbEntityImageValue = e => e.Value,
        GetDbEntityName = e => e.Name,
        Multiselect = false     
    }" />

Model with interface:

using Microsoft.EntityFrameworkCore;

namespace MyNamespace
{
    public interface IEntitySelectionModel
    {
        IQueryable DbSet { get; }
        Func<object, string> GetDbEntityDisplayName { get; }
        Func<object, int> GetDbEntityId { get; }
        Func<object, byte[]> GetDbEntityImageValue { get; }
        Func<object, string> GetDbEntityName { get; }
        bool Multiselect { get; }
    }

    public class EntitySelectionModel<TEntity> : IEntitySelectionModel
            where TEntity : class
    {
        public DbSet<TEntity> DbSet { get; set; }
        IQueryable IEntitySelectionModel.DbSet => DbSet;
        public Func<TEntity, string> GetDbEntityDisplayName { get; set; }
        Func<object, string> IEntitySelectionModel.GetDbEntityDisplayName => e => GetDbEntityDisplayName(e as TEntity);
        public Func<TEntity, int> GetDbEntityId { get; set; }
        Func<object, int> IEntitySelectionModel.GetDbEntityId => e => GetDbEntityId(e as TEntity);
        public Func<TEntity, byte[]> GetDbEntityImageValue { get; set; }
        Func<object, byte[]> IEntitySelectionModel.GetDbEntityImageValue => e => GetDbEntityImageValue(e as TEntity);
        public Func<TEntity, string> GetDbEntityName { get; set; }
        Func<object, string> IEntitySelectionModel.GetDbEntityName => e => GetDbEntityName(e as TEntity);
        public bool Multiselect { get; set; }
    }
}

Partial view implementation:

@model IEntitySelectionModel

<dialog @(Model.Multiselect ? "multiple" : "") >
    @foreach(var entity in Model.DbSet)
    {
        var name = Model.GetDbEntityName(entity);
        <figure id="@Model.GetDbEntityId(entity)" name="@name">
            <img src="@(await Model.GetDbEntityImageValue(entity).Resize(name, 16, 16))" alt="The Pulpit Rock" />
            <figcaption>@Model.GetDbEntityDisplayName(entity)</figcaption>
        </figure>
    }
</dialog>

More Related questions