7

我有一个使用 razor 进行服务器端渲染的页面,您可以在其中添加来自不同列表的几个元素,填充一些字段并在提交时从中创建请求。

每次从任何列表中添加/获取项目时,我都会将带有提交按钮的帖子发送到特定操作,例如“CustomerSelected”。我这样做是因为我需要为添加的项目重新创建其他视图组件。在这些方法中,我想将添加的对象添加到 db 上下文中,所以在提交时我可以说SaveChanges()而不必在同一个方法中组装所有内容。但是在 .net core db 上下文中是每个请求的,建议保持这种方式。在这种情况下,我如何在请求之间存储这些临时实体对象,以便以后如果有人决定提交它们,我可以说SaveChanges()或者丢弃它们?

我想要这样的东西:

public IActionResult CustomerAdded(int customerId)
{
    var customer = _context.Customers.First(c => c.IdCustomer == customerId);
    var message = _context.Messages.First(m => m.IdMessage = _idMessage);
    message.Customers.Add(customer);
    return View();
}

public IActionResult ItemAdded(int itemId)
{
    var item = _context.Items.First(c => c.IdItem == itemId);
    var message = _context.Messages.First(m => m.IdMessage = _idMessage);
    message.Items.Add(item);
    return View();
}

public IActionResult Submit()
{
    _context.SaveChanges();
    return View();
}

如果这是不可能的,那么我正在考虑在每个方法中添加单个元素并将它们保存在那里,然后在提交时我将构建最后一个最终元素。但是,如果有人在没有提交的情况下关闭了浏览器,那么我的数据库中的数据不完整。我将不得不运行某种工作来删除它们,对于这样一个简单的任务来说似乎太多了。

4

3 回答 3

5

在这种情况下使用服务器资源来跟踪更改并不是一个好主意。在购物篮、列表或批量编辑等场景中,最好在客户端跟踪更改。

您需要在服务器端生成视图并不意味着您需要跟踪DbContext. 从服务器获取索引视图并创建视图,但在客户端跟踪更改。然后保存,将所有数据发布到服务器以根据您拥有的跟踪信息保存更改。

客户端更改跟踪的机制取决于需求和场景,例如您可以使用 html 输入跟踪更改,您可以使用 cookie 跟踪更改,您可以使用浏览器内存中的 javascript 对象(如角度场景)跟踪更改。

这是这篇文章,我将展示一个使用 html 输入和模型绑定的示例。要了解有关此主题的更多信息,请查看 Phill Haack 的这篇文章:模型绑定到列表

例子

在以下示例中,我描述了客户列表的列表编辑方案。为了简单起见,我想:

  • 您有一个要在客户端编辑的客户列表。您可能想要添加、编辑或删除项目。
  • 添加新项目时,新行的行模板应来自服务器。
  • 删除时,您可以通过单击行上的复选框将项目标记为已删除。
  • 添加/编辑时,您希望在单元格附近显示验证错误。
  • 您想在最后保存更改,请单击“保存”按钮。

要实现上述场景然后您需要创建以下模型、操作和视图:

可追踪<T> 模型

这个类是一个帮助我们进行客户端跟踪和列表编辑的模型:

public class Trackable<T>
{
    public Trackable() { }
    public Trackable(T model) { Model = model; }
    public Guid Index { get; set; } = Guid.NewGuid();
    public bool Deleted { get; set; }
    public bool Added { get; set; }
    public T Model { get; set; }
}

客户模型

客户型号:

public class Customer
{
    [Display(Name ="Id")]
    public int Id { get; set; }

    [StringLength(20, MinimumLength = 1)]
    [Required]
    [Display(Name ="First Name")]
    public string FirstName { get; set; }

    [StringLength(20, MinimumLength = 1)]
    [Required]
    [Display(Name ="Last Name")]
    public string LastName { get; set; }

    [EmailAddress]
    [Required]
    [Display(Name ="Email Name")]
    public string Email { get; set; }
}

Index.cshtml 视图

Index 视图负责渲染List<Trackable<Customer>>。在渲染每条记录时,我们使用RowTemplate视图。添加新项目时使用的相同视图。

在这个视图中,我们有一个用于保存的提交按钮和一个用于添加新行的按钮,该按钮使用 ajax 调用 Create 操作。

这是索引视图:

@model IEnumerable<Trackable<Customer>>
<h2>Index</h2>
<form method="post" action="Index">
    <p>
        <button id="create">New Customer</button>
        <input type="submit" value="Save All">
    </p>
    <table class="table" id="data">
        <thead>
            <tr>
                <th>
                    Delete
                </th>
                <th>
                    @Html.DisplayNameFor(x => x.Model.FirstName)
                </th>
                <th>
                    @Html.DisplayNameFor(x => x.Model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(x => x.Model.Email)
                </th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model)
            {
                await Html.RenderPartialAsync("RowTemplate", item);
            }
        </tbody>
    </table>
</form>

@section Scripts{
    <script>
        $(function () {
            $('#create').click(function (e) {
                e.preventDefault();
                $.ajax({
                    url: 'Create',
                    method: 'Get',
                    success: function (data) {
                        $('#data tbody tr:last-child').after(data);
                    },
                    error: function (e) { alert(e); }
                });
            });
        });
    </script>
}

RowTemplate.cshtml 视图

此视图负责呈现客户记录。在这个视图中,我们先Index在隐藏中渲染,然后为字段设置前缀[index],然后渲染字段,包括再次索引、添加、删除和模型 id:

这是 RowTemplate 视图:

@model Trackable<Customer>
<tr>
    <td>
        @Html.HiddenFor(x => x.Index)
        @{Html.ViewData.TemplateInfo.HtmlFieldPrefix = $"[{Model.Index}]";}
        @Html.HiddenFor(x => x.Index)
        @Html.HiddenFor(x => x.Model.Id)
        @Html.HiddenFor(x => x.Added)
        @Html.CheckBoxFor(x => x.Deleted)
    </td>
    <td>
        @Html.EditorFor(x => x.Model.FirstName)
        @Html.ValidationMessageFor(x => x.Model.FirstName)
    </td>
    <td>
        @Html.EditorFor(x => x.Model.LastName)
        @Html.ValidationMessageFor(x => x.Model.LastName)
    </td>
    <td>
        @Html.EditorFor(x => x.Model.Email)
        @Html.ValidationMessageFor(x => x.Model.Email)
    </td>
</tr>

客户控制器

public class CustomerController : Controller
{
    private static List<Customer> list;
}

它将有以下动作。

[GET] 索引操作

在此操作中,您可以从数据库加载数据并将其整形为 aList<Trackable<Customer>>并传递给Index视图:

[HttpGet]
public IActionResult Index()
{
    if (list == null)
    {
        list = Enumerable.Range(1, 5).Select(x => new Customer()
        {
            Id = x,
            FirstName = $"A{x}",
            LastName = $"B{x}",
            Email = $"A{x}@B{x}.com"
        }).ToList();
    }
    var model = list.Select(x => new Trackable<Customer>(x)).ToList();
    return View(model);
}

[GET] 创建动作

此操作负责返回新的行模板。它将由索引视图中的按钮使用 ajax 调用:

[HttpGet]
public IActionResult Create()
{
    var model = new Trackable<Customer>(new Customer()) { Added = true };
    return PartialView("RowTemplate", model);
}

[POST] 索引操作

此操作负责从客户端接收跟踪的项目并保存它们。它接收的模型是List<Trackable<Customer>>. 它首先去除已删除行的验证错误消息。然后删除那些既被删除又被添加的。然后检查模型状态是否有效,尝试对数据源应用更改。

属性为 true 的项目Deleted被删除,Added为 true 和Deletedfalse 的项目是新项目,其余项目被编辑。然后不需要从数据库中加载所有项目,只需使用 for 循环,调用db.Entry每个项目并设置它们的状态,最后保存更改。

[HttpPost]
public IActionResult Index(List<Trackable<Customer>> model)
{
    //Cleanup model errors for deleted rows
    var deletedIndexes = model.
        Where(x => x.Deleted).Select(x => $"[{x.Index}]");
    var modelStateDeletedKeys = ModelState.Keys.
        Where(x => deletedIndexes.Any(d => x.StartsWith(d)));
    modelStateDeletedKeys.ToList().ForEach(x => ModelState.Remove(x));

    //Removing rows which are added and deleted
    model.RemoveAll(x => x.Deleted && x.Added);

    //If model state is not valid, return view
    if (!ModelState.IsValid)
        return View(model);

    //Deleted rows
    model.Where(x => x.Deleted && !x.Added).ToList().ForEach(x =>
    {
        var i = list.FindIndex(c => c.Id == x.Model.Id);
        if (i >= 0)
            list.RemoveAt(i);
    });

    //Added rows
    model.Where(x => !x.Deleted && x.Added).ToList().ForEach(x =>
    {
        list.Add(x.Model);
    });

    //Edited rows
    model.Where(x => !x.Deleted && !x.Added).ToList().ForEach(x =>
    {
        var i = list.FindIndex(c => c.Id == x.Model.Id);
        if (i >= 0)
            list[i] = x.Model;
    });

    //Reditect to action index
    return RedirectToAction("Index");
}
于 2019-01-03T13:29:26.203 回答
0

type="hidden"使用javascript并使用或visibility 然后一次发送所有内容的动态表单怎么样

或者使用带有重定向的 TempData 并在其他视图(表单)中重用该数据作为input type="hidden"

流动:

表格1->

控制器的方法将数据保存在 TempData 并重定向到 Form2 View/或 ViewData 并返回 Form2 View?->

Form2 已将 TempData 插入到隐藏输入下的表单中->

同时提交两个

于 2019-01-03T13:41:46.237 回答
-1

曲奇饼 !

public class HomeController : Controller
{

public string Index()
{

    HttpCookie cookie = Request.Cookies["message"];
    Message message = null;
    string json = "";

    if (cookie == null)
    {
        message = new Message();
        json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
        cookie = new HttpCookie("message", json);
    }
    Response.Cookies.Add(cookie);
    return json;
}

public string CustomerAdded(int id)
{
    HttpCookie cookie = Request.Cookies["message"];
    Message message = null;
    string json = "";

    if (cookie == null || string.IsNullOrEmpty(cookie.Value))
    {
        message = new Message();
        json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
        cookie = new HttpCookie("message", json);
    }
    else
    {
        json = cookie.Value;
        message = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Message>(json);
    }

    if (message.Customers == null) message.Customers = new List<int>();
    if (message.Items == null) message.Items = new List<int>();

    if (!message.Customers.Contains(id))
    {
        message.Customers.Add(id);
    }


    json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
    cookie = new HttpCookie("message", json);

    Response.Cookies.Add(cookie);

    return json;
}


public string ItemAdded(int id)
{
    HttpCookie cookie = Request.Cookies["message"];
    Message message = null;
    string json = "";

    if (cookie == null || string.IsNullOrEmpty(cookie.Value))
    {
        message = new Message();
        json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
        cookie = new HttpCookie("message", json);
    }
    else
    {
        json = cookie.Value;
        message = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Message>(json);
    }
    if (message.Customers == null) message.Customers = new List<int>();
    if (message.Items == null) message.Items = new List<int>();

    if (!message.Items.Contains(id))
    {
        message.Items.Add(id);
    }

    json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
    cookie = new HttpCookie("message", json);

    Response.Cookies.Add(cookie);

    return json;
}

public string Submit()
{
    HttpCookie cookie = Request.Cookies["message"];
    Message message = null;
    string json = "";

    if (cookie == null || string.IsNullOrEmpty(cookie.Value))
    {
        return "no data";
    }
    else
    {
        json = cookie.Value;
        message = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Message>(json);
    }

    Response.Cookies["message"].Value = "";
    Response.Cookies["message"].Expires = DateTime.Now.AddDays(-1);

    return "Submited";

}
}

示例链接

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

于 2019-01-04T10:06:02.020 回答