使用新的(增强的视图模型)编辑的 MVC 索引

MVC index to edit with new (enhanced view model)

(已编辑) 我有一个包含产品 (Products) 的 table,它显示在名为 ProductCatalog 的视图中,用户可以在其中找到产品 table 中的所有产品。我为此使用标准(索引)视图。

我现在想要实现的是可以从目录中选择(单击)单个产品并显示该产品的详细视图的场景(类似于 CRUD 中的标准 Edit/Details 方法) . 但是,在此详细信息视图 (ProductDetails) 中,我需要增强模型,因为用户会确定变量,例如应该 posted 到新的 [=34] 的订单数量=] (LineItems) 以及产品的属性(主要是 ProductID)以最终创建订单。

我一直没能做到这一点。不确定我是否需要使用多个视图模型或做一些我不知道的其他花哨的东西。

所以总而言之,问题是如何从索引到详细信息以及如何从详细信息到 post 某个数据库的不同视图模型。所有这一切都在 EF 中。

型号:

    public class Products
{
    public int                                                      ID                                  { get; set; }
    public int                                                      CategoryID                          { get; set; }
    public string                                                   ProductName                         { get; set; }
    public string                                                   ProductDescription                  { get; set; }
    public string                                                   ProductPicturePath                  { get; set; }
    public string                                                   UnitCost                            { get; set; }
    public string                                                   UnitPrice                           { get; set; }
    public string                                                   LowestUnitPrice                     { get; set; }
    public string                                                   SubscriptionPrice                   { get; set; }
    public string                                                   UnitMargin                          { get; set; }
    public string                                                   UnitProfit                          { get; set; }
    public bool                                                     InCatalog                           { get; set; }
}


    public class ProductViewModel
{
    public int                                                      ProductID                           { get; set; }

    public string                                                   ProductName                         { get; set; }

    public string                                                   ProductDescription                  { get; set; }

    public string                                                   ProductPicturePath                  { get; set; }

    [RegularExpression(@"^\((\d{3}?)\)$", ErrorMessage              = "Du brauchst die Anzahl nicht ausschreiben - verwende Ziffern.")]
    [Display(Name                                                   = "Bestellmenge")]
    public string                                                   SubscriptionQuantity                { get; set; }

    [Display(Name                                                   = "Lieferrhytmus")]
    public string                                                   SubscriptionCadenceCategory         { get; set; }

    public string                                                   SubscriptionCadenceValue            { get; set; }

    [Display(Name                                                   = "Preis im Abonnement")]
    public string                                                   SubscriptionPrice                   { get; set; }

    public bool                                                     IsSingleOrder                       { get; set; }

    [RegularExpression(@"^\((\d{3}?)\)$", ErrorMessage              = "Du brauchst die Anzahl nicht ausschreiben - verwende Ziffern.")]
    [Display(Name                                                   = "Bestellmenge")]
    public string                                                   Quantity                            { get; set; }

    [Display(Name =                                                 "Preis pro Einheit")]
    public string                                                   UnitPrice                           { get; set; }

    public DateTime                                                 ActivityDateTime                    { get; set; }

    public string                                                   ActivityLatitude                    { get; set; }

    public string                                                   ActivityLongitude                   { get; set; }

    public string                                                   ActivityLocation                    { get; set; }


}

查看

@using freshNclean.Models
@model IEnumerable<freshNclean.Models.Products>
@{
    ViewBag.Title = "Sortiment";
}
<div id="productCatalogContainer" class="container">
    <div id="productCatalogHeaderSection" class="headerSection">
        <h1 id="productCatalogHeaderTitle" class="headerTitle">
            @ViewBag.Title
        </h1>
        <i id="productCatalogHeaderIcon" class="headerIcon fas fa-gem" aria-hidden="true"></i>
    </div>
<!-- table section -->
    <section id="productCatalogListPartialSection" class="table">
        <div id="productCatalogSeparatorSection" class="separatorSection">
            <hr id="productCatalogSeparator" class="separator" />
        </div>
        <div id="productCatalog" class="productTableSection row">
             @foreach (var item in Model)
            {
                if (item.InCatalog == true)
                {
                    <a id="productCatalogProductArea" class="tableArea col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-3 col-lg-offset-2 col-lg-3" href="@Url.Action("ProductDetails", "freshNclean", new { id = item.ID })">
                        @Html.HiddenFor(modelItem => item.ID, new { @class = "tableField col-xs-12 col-sm-12 col-md-12 col-lg-12" })
                        <img id="productCatalogProductImage" class="tableImage col-xs-12 col-sm-12 col-md-12 col-lg-12" src="@Url.Content(item.ProductPicturePath)" alt="Produktbild" />
                        <div id="productCatalogProductNameField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                            @Html.DisplayFor(modelItem => item.ProductName)
                        </div>
                        <div id="productCatalogProductDescriptionField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                            @Html.DisplayFor(modelItem => item.ProductDescription)
                        </div>

                        <div id="productCatalogLowestUnitPriceField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                            ab @Html.DisplayFor(modelItem => item.LowestUnitPrice)
                        </div>
                    </a>
                }
            }
        </div>

        <div id="productCatalogListPartialMenuSeparatorSection" class="separatorSection">
            <hr id="productCatalogListPartialMenuSeparator" class="separator" />
        </div>
        @Html.ActionLink("zum Warenkorb", "ShowShoppingCart", "", htmlAttributes: new { @class = "formButton col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-3 col-md-6 col-lg-offset-3 col-lg-6" })
    </section>
</div>
<!-- link back to menu -->
<div id="productCatalogReturnToMenuSection" class="linkSection">
    @Html.ActionLink("zurück zum Menü", "Profile", "", htmlAttributes: new { @id = "productCatalogReturnToMenuButton", @class = "link" })
</div>
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
<!-- Google Places -->
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBYK8aBCsb1dFrzXqIgUq07ZwO3w3_fGCs&libraries=places&callback=initAutocomplete" async defer></script>
}

产品详情(查看)

@model freshNclean.Models.Products
@{
    ViewBag.Title = "Sortiment";
}
<div id="productCatalogContainer" class="container">
<div id="productCatalogHeaderSection" class="headerSection">
    <h1 id="productCatalogHeaderTitle" class="headerTitle">
        @ViewBag.Title
    </h1>
    <i id="productCatalogHeaderIcon" class="headerIcon fas fa-gem" aria-hidden="true"></i>
</div>
<!-- form -->
<section id="productDetailsForm" class="form">
    @using (Html.BeginForm("ShowProduct", "freshNclean", FormMethod.Post, new { @id = "productDetailsFormContainer", @class = "form-horizontal", @role = "form" }))
    {
        @Html.AntiForgeryToken()
<!-- user activities (hidden) -->
        @Html.HiddenFor(m => m.ActivityLatitude, new { @id = "productDetailsActivityLatitudeField", @class = "userActivityField" })
        @Html.HiddenFor(m => m.ActivityLongitude, new { @id = "productDetailsActivityLongitudeField", @class = "userActivityField" })
        @Html.HiddenFor(m => m.ActivityLocation, new { @id = "productDetailsActivityLocationField", @class = "userActivityField" })
<!-- autopopulate user activity fields with location -->
        <script>
            $(document).ready(function defaultValue() {
                if (!navigator.geolocation) return;
                navigator.geolocation.getCurrentPosition(function (userCoordinates) {
                    geocoder = new google.maps.Geocoder();
                    userLatLng = new google.maps.LatLng(userCoordinates.coords.latitude, userCoordinates.coords.longitude);
                    document.getElementById('productDetailsActivityLatitudeField').value = userCoordinates.coords.latitude;
                    document.getElementById('productDetailsActivityLongitudeField').value = userCoordinates.coords.longitude;
                    geocoder.geocode({ 'latLng': userLatLng }, function (results, status) {
                        if (status == google.maps.GeocoderStatus.OK) {
                            var result = results[0];
                            locationPlaceholder = "";
                            for (var i = 0, len = result.address_components.length; i < len; i++) {
                                var addressComponent = result.address_components[i];
                                if (addressComponent.types.indexOf("locality") >= 0) locationPlaceholder = addressComponent.long_name;
                            }
                            if (locationPlaceholder != '') {
                                document.getElementById('productDetailsActivityLocationField').value = locationPlaceholder;
                            }
                        }
                    });
                });
            });
        </script>
<!-- form: product display -->
        <div id="productDetailsProductDisplaySeparatorSection" class="separatorSection">
            <hr id="productDetailsProductDisplaySeparator" class="separator" />
        </div>
        <div id="productDetailsProductDisplaySection" class="formSection row">
            @Html.HiddenFor(m => m.ProductID, new { @id = "productDetailsProductIDField", @class = "tableField col-xs-12 col-sm-12 col-md-12 col-lg-12" })
            <img id="productDetailsProductImage" class="tableImage col-xs-12 col-sm-12 col-md-12 col-lg-12" src="@Model.ProductPicturePath" alt="Produktbild" />
            <div id="productDetailsProductName" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                @Html.DisplayFor(m => m.ProductName)
            </div>
            <div id="productDetailsProductDescriptionField" class="tableField col-xs-12 col-sm-12 col-md-12 col-lg-12">
                @Html.DisplayFor(m => m.ProductDescription)
            </div>
<!-- define subscription quantity -->
            <a id="productDetailsSubscriptionMinusButton" class="tableButton col-xs-offset-2 col-xs-2 col-sm-offset-2 col-sm-2 col-md-offset-3 col-md-2 col-lg-offset-3 col-lg-2">
                -
            </a>
            @Html.TextBoxFor(m => m.SubscriptionQuantity, new { @id = "productDetailsSubscriptionQuantityField", @class = "tableField col-xs-offset-1 col-xs-2 col-sm-offset-1 col-sm-2 col-md-offset-0 col-md-2 col-lg-offset-0 col-lg-2" placeholder = "0" })
            <a id="productDetailsSubscriptionPlusButton" class="tableButton col-xs-offset-1 col-xs-2 col-sm-offset-1 col-sm-2 col-md-offset-0 col-md-2 col-lg-offset-0 col-lg-2">
                +
            </a>
            @Html.LabelFor(m => m.SubscriptionCadenceCategory, new { @id = "productDetailsSubscriptionCadenceCategoryLabel", @class = "tableLabel" })
            @Html.TextBoxFor(m => m.SubscriptionCadenceCategory, new { @id = "productDetailsSubscriptionCadenceCategoryField", @class= "tableField" })
            @Html.LabelFor(m => m.SubscriptionCadenceValue, new { @id = "productDetailsSubscriptionCadenceValueLabel", @class = "tableLabel" })
            @Html.TextBoxFor(m => m.SubscriptionCadenceValue, new { @id = "productDetailsSubscriptionCadenceValueField", @class= "tableField" })

        </div>

        <div id="productCatalogListPartialMenuSeparatorSection" class="separatorSection">
            <hr id="productCatalogListPartialMenuSeparator" class="separator" />
        </div>
        @Html.ActionLink("zum Warenkorb", "ShowShoppingCart", "", htmlAttributes: new { @class = "formButton col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-3 col-md-6 col-lg-offset-3 col-lg-6" })
    </section>
</div>
<!-- link back to menu -->
<div id="productCatalogReturnToMenuSection" class="linkSection">
    @Html.ActionLink("zurück zum Menü", "Profile", "", htmlAttributes: new { @id = "productCatalogReturnToMenuButton", @class = "link" })
</div>
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
<!-- Google Places -->
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBYK8aBCsb1dFrzXqIgUq07ZwO3w3_fGCs&libraries=places&callback=initAutocomplete" async defer></script>
}

控制器

        // GET: /freshNclean/ProductDetails
    public ActionResult ShowProduct(int? id)
    {
        // define variables
        var userID                                                  = User.Identity.GetUserId();
        DateTime nowUTC                                             = DateTime.Now.ToUniversalTime();
        DateTime nowLocal                                           = DateTime.Now.ToLocalTime();
        if (id                                                      == null)
        {
            // track user activity: get method includes activity name and timestamp without location
            var NOPRODUCTID                                         = new UserActivities
            {
                UserID                                              = userID,
                ActivityName                                        = "ProductDetails_NoProductID",
                ActivityTimeStampUTC                                = nowUTC,
                ActivityLatitude                                    = "n/a",
                ActivityLongitude                                   = "n/a",
                ActivityLocation                                    = "n/a"
            };
            DATADB.UserActivityList.Add(NOPRODUCTID);
            DATADB.SaveChanges();
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Products model                                              = DATADB.ProductList.Find(id);
        if(model == null)
        {
            // track user activity: get method includes activity name and timestamp without location
            var PRODUCTSMODELFAILURE                                = new UserActivities
            {
                UserID                                              = userID,
                ActivityName                                        = "ProductDetails_ProductsModelFailure",
                ActivityTimeStampUTC                                = nowUTC,
                ActivityLatitude                                    = "n/a",
                ActivityLongitude                                   = "n/a",
                ActivityLocation                                    = "n/a"
            };
            DATADB.UserActivityList.Add(PRODUCTSMODELFAILURE);
            DATADB.SaveChanges();
            return HttpNotFound();
        }
        // track user activity: get method includes activity name and timestamp without location
        var LOADED                                                  = new UserActivities
        {
            UserID                                                  = userID,
            ActivityName                                            = "ProductDetails_Loaded",
            ActivityTimeStampUTC                                    = nowUTC,
            ActivityLatitude                                        = "n/a",
            ActivityLongitude                                       = "n/a",
            ActivityLocation                                        = "n/a"
        };
        DATADB.UserActivityList.Add(LOADED);
        DATADB.SaveChanges();
        return View(model);
    }

注意:由于技术原因,我无法 运行 此代码。因此,它可能包含未捕获的错误。让我知道任何此类事情。

所以,这是一个过于完整的示例。 思路如下:

  • 我们调用索引视图
  • 我们从数据库中获取所有用户并将列表存储在我们的 ViewModel 中(如果没有它,我们可以只传递 PersonEntity 的列表)
  • 我们显示使用 HTML table
  • 列出所有人员的视图
  • 当我们单击 Details link 时,我们将转到 DetailRow 操作。 person.Id 作为 GET 参数传递。
  • DetailRow 视图中,我们查询数据库,但只请求与作为参数传递的 Id 相匹配的 PersonEntity
  • 我们要求 entity framework 使用 .Include 方法包含 Detail 属性。
  • 我们return我们得到的结果,映射到视图模型后将是一个PersonEntity
  • 我们在 DetailRow 视图中显示 returned Person

粗线是我认为直接回答你问题的那些。 在此示例中,我假设您使用了 Entity Framework,否则您将需要 "translate" 对 Context class 的调用对某种 DAO 或内联SQL 如果你是个肮脏的人 ;-)

控制器和数据层的代码(理想情况下,每个 class 都在其自己的文件中)

public class KenFExample : Controller
{
    // GET
    public IActionResult Index()
    {
        using (Context context = new Context())
        {
            // Get the whole list of basic entries
            IEnumerable<SimplePersonViewModel> rows = context.Persons.Select(r => new SimplePersonViewModel(r));

            // Construct a new view model with these entries
            BaseListingViewModel model = new BaseListingViewModel(rows);

            return View(model);
        }
    }

    public IActionResult DetailRow(int id)
    {
        using (Context context = new Context())
        {
            // Get only the entry that we are interrested in
            PersonEntity row = context.Persons.Include(p => p.Detail).Single(r => r.Id == id);

            // Construct a new view model with this entry
            PersonViewModel model = new PersonViewModel(row);

            return View(model);
        }
    }
}

public class Context : DbContext
{
    public virtual DbSet<PersonEntity> Persons { get; set; }
    public virtual DbSet<DetailEntity> Details { get; set; }
}

public class PersonViewModel
{
    public PersonViewModel(PersonEntity entity)
    {
        Id = entity.Id;
        FirstName = entity.FirstName;
        LastName = entity.LastName;
        IsHandsome = entity.Detail.IsHandsome;
        Address = entity.Detail.Address;
        Email = entity.Detail.Email;
        Phone = entity.Detail.Phone;
    }

    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsHandsome { get; set; }
    public string Address { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

public class PersonEntity
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual DetailEntity Detail { get; set; }
}

public class DetailEntity
{

    public bool IsHandsome { get; set; }
    public string Address { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public virtual PersonEntity Person { get; set; }

}

public class SimplePersonViewModel
{

    public SimplePersonViewModel(PersonEntity entity)
    {
        Id = entity.Id;
        FirstName = entity.FirstName;
        LastName = entity.LastName;
    }

    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class BaseListingViewModel
{
    public BaseListingViewModel(IEnumerable<SimplePersonViewModel> list)
    {
        Rows = list;
    }

    public IEnumerable<SimplePersonViewModel> Rows { get; set; }
}

这是Index.cshtml视图

@model BaseListingViewModel

<table>
    <thead>
    <tr>
        <td>
            Id
        </td>
        <td>
            First name
        </td>
        <td>
            Last name
        </td>
        <td>
            Is handsome
        </td>
        <td>
            Go to details
        </td>
    </tr></thead>
    @foreach (SimplePersonViewModel person in Model.Rows)
    {
        <tr>
            <td>
                @person.Id
            </td>
            <td>
                @person.FirstName
            </td>
            <td>
                @person.LastName
            </td>
            <td>
                <a href="@Url.Action("DetailRow", new {id = person.Id})">Details</a>
            </td>
        </tr>
    }
</table>

这里是 DetailRow.cshtml 视图

@model RelaxationPortal.Controllers.PersonViewModel

Details for @Model.FirstName @Model.LastName (id: @Model.Id)
<br />
Address is @Model.Address and can be contacted using @Model.Phone

@if (Model.IsHandsome)
{
    <span>He <b>IS</b> handsome</span>
}

else
{
    <span>He isn't so handsome</span>
}