【问题标题】:MVC5 Linq to ViewModel to Razor View code improvement?MVC5 Linq to ViewModel to Razor View 代码改进?
【发布时间】:2014-06-04 08:20:43
【问题描述】:

我想从两个 EF 表(菜单和菜单项)中获取数据。然后我想将结果放入 ViewModel。所以我可以轻松地循环浏览 Razor 视图中的数据。

我已经开始工作了。但我是 .Net MVC 的新手,所以我想知道我的代码是否可以改进。也许查询可以合并并且更短。而且我正在使用 AsEnumerable(),我不确定这是否是最好的方法。

我认为我的尝试还可以。但我想听听你的想法。欢迎任何关于代码改进的建议。谢谢。

控制器类:

   public ActionResult Index(int? id)
        {
            if (id == null) return RedirectToAction("Index", new { controller = "Menu"      });

        var menu =
            (from m in _db.Menus
             join mi in _db.MenuItems on m.Id equals mi.MenuId
             where m.Id == id
             select m).First();

        var menuItems =
            (from mi in _db.MenuItems.AsEnumerable()
             join m in _db.Menus on mi.MenuId equals m.Id
             where m.Id == id
             select new MenuItem
             {
                 Id = mi.Id,
                 Name = mi.Name,
                 Href = mi.Href,
                 CssClass = mi.CssClass,
                 CssId = mi.CssId,
                 Title = mi.Title,
                 Weight = mi.Weight
             });

        var model = new MenuModelView
        {
            Id = menu.Id,
            Name = menu.Name,
            CssClass = menu.CssClass,
            CssId = menu.CssId,
            Deleted = menu.Deleted,
            MenuItems = menuItems
        };

        return View(model);
    }

ViewModel 类:

using System.Collections.Generic;

namespace DesignCrew.Areas.Admin.Models
{
    public class MenuModelView
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string CssClass { get; set; }
        public string CssId { get; set; }
        public bool Deleted { get; set; }
        public IEnumerable<MenuItem> MenuItems { get; set; }
    }
}

剃刀视图:

@model DesignCrew.Areas.Admin.Models.MenuModelView

@{
    ViewBag.Title = "Index";
}

<h2>Menu - @Html.DisplayFor(model => model.Name, new { @class = "control-label col-md-2" })</h2>

<p>
    @Html.ActionLink("Create New Item", "Create")
</p>

<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().Id)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().Href)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().Title)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().CssClass)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().CssId)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().ParentId)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems.First().Weight)
        </th>
        <th>Options</th>
    </tr>

    @foreach (var item in Model.MenuItems)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Href)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.CssClass)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.CssId)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ParentId)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Weight)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id = item.Id }, new { @class = "link-menu" }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.Id }, new { @class = "link-menu" })
            </td>
        </tr>
    }
</table>

【问题讨论】:

  • 鉴于您似乎在 Viewmodel 中重新使用您的 EF 实体,您不需要明确地将 newmap (select new MenuItem {}...) 实体克隆到ViewModel - 只需使用 EF 引用的那些。此外,即使您确实有类似的专用 ViewModels,像 AutoMapper 这样的工具也可以减轻手动映射的痛苦。
  • 另外,你可能不需要AsEnumerable()
  • 你能提供一些例子吗?
  • 不确定这是否有帮助,但我回答了类似的菜单相关问题。这是Link

标签: c# asp.net-mvc linq razor


【解决方案1】:

如果您的 EF 模型在 MenuMenuItem 之间有一个可导航的外键,则无需显式连接这两个表,并且可以在获取父 Menu,并简单地从父导航到子导航:

var menu = _db.Menus
              .Include("MenuItems") // Or, use the typed version on newer EF's
              .First(m => m.id == id); // Many LINQ expressions allow predicates

return new MenuModelView
 {
    Menu = menu.Name,
    CssClass = menu.CssClass,
    CssId = menu.CssId,
    Deleted = menu.Deleted,
    MenuItems = menu.MenuItems
 }

而且,由于MenuModelViewMenu EF 实体似乎有很多共同点,您可以考虑使用AutoMapper。配置后,这将允许您将手动映射步骤替换为:

return Mapper.Map<Menu, MenuModelView>(menu);

编辑

这是一个廉价而讨厌的ViewModel,它包装了您的 EF 实体,并用表示层数据对其进行了扩充。纯粹主义者可能会注意到,您应该为封装的 EF 模型创建新类,不过话说回来,您的 EF 模型似乎模仿了 Html :)

public class MenuModelView
{
   // Presentation tier stuff
   public string PageTitle { get; set; }
   public string MetaTagsForSEO { get; set; }
   public bool IsThisARegisteredUserSoSkipTheAdverts { get; set; }

   // Your EF / Domain Model
   public Menu Menu { get; set; }
}

你的剃须刀@ModelMenuModelView

【讨论】:

  • 谢谢,这对我有用。这样甚至不需要 ViewModel 类: var model = _db.Menus .Include("MenuItems") .First(m => m.MenuId == id);返回视图(模型);
  • 不,保留视图模型。您可能需要为视图添加额外的特定于表示层的好东西。在本例中,您的 EF 模型似乎为一堆 UI 字段建模,因此表示层/域/数据层之间的区别并不十分清楚。
  • 你能给我一个 MenuModelView 类中特定于表示层的好东西的简单示例吗?同样,我是 .NET MVC 的新手 :o)
  • 我添加了一个混合 ViewModel 示例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多