web-dev-qa-db-fra.com

Stockage des données entre les requêtes dans dbcontext

J'ai une page avec un rendu côté serveur à l'aide de rasoir, où vous pouvez ajouter quelques éléments de différentes listes, remplir certains champs et créer une demande à partir de cette page. 

Chaque fois qu'un élément est ajouté/extrait d'une liste, j'envoie un message avec le bouton d'envoi à une action spécifique, par exemple. "Sélectionné par le client". Je le fais parce que je dois recréer des composants de vue supplémentaires pour l'élément ajouté. Dans ces méthodes, je voudrais ajouter des objets ajoutés au contexte de la base de données, donc sur soumettre, je peux simplement dire SaveChanges() et ne pas avoir à tout assembler de la même manière. Mais dans .net, le contexte de base de données est défini par demande et il est conseillé de le conserver de cette façon. Dans ce cas, comment puis-je stocker ces objets d'entité temporaires entre les demandes, si plus tard, si quelqu'un décide de les soumettre, je peux dire SaveChanges() ou les ignorer autrement?

J'aimerais avoir quelque chose comme ça:

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();
}

Si cela n’est pas possible, je pensais alors à ajouter des éléments individuels dans chaque méthode et à les enregistrer là-bas, puis à soumettre le dernier élément final. Mais si quelqu'un ferme son navigateur sans le soumettre, j'ai des données incomplètes dans ma base de données. Je devrais exécuter une sorte de travail pour les supprimer et cela semble être trop pour une tâche aussi simple.

7
FCin

Ce n'est pas une bonne idée d'utiliser les ressources du serveur pour suivre les changements dans de tels scénarios. Dans des scénarios tels que le panier, la liste ou l'édition par lot, il est préférable de suivre les modifications côté client. 

Votre exigence de générer des vues côté serveur ne signifie pas que vous deviez suivre les modifications dans DbContext. Obtenez la vue d'index et créez la vue à partir du serveur, mais suivez les modifications sur le client. Ensuite, pour enregistrer, publiez toutes les données sur le serveur pour enregistrer les modifications en fonction des informations de suivi dont vous disposez.

Le mécanisme de suivi des modifications côté client dépend des exigences et du scénario. Par exemple, vous pouvez suivre les modifications à l'aide d'entrées HTML, vous pouvez suivre les modifications à l'aide d'un cookie, vous pouvez suivre les modifications à l'aide d'objets JavaScript dans la mémoire du navigateur, comme des scénarios angulaires.

Voici ce post, je vais montrer un exemple en utilisant des entrées HTML et la liaison de modèle. Pour en savoir plus sur ce sujet, jetez un oeil à cet article de Phill Haack: Model Binding To A List .

Exemple

Dans l'exemple suivant, je décris un scénario d'édition de liste pour une liste de clients. Pour faire simple, je suppose:

  • Vous avez une liste de clients que vous allez éditer chez client. Vous voudrez peut-être ajouter, modifier ou supprimer des éléments.
  • Lors de l'ajout d'un nouvel élément, le modèle de ligne pour la nouvelle ligne doit provenir du serveur.
  • Lors de la suppression, vous marquez un élément comme supprimé en cliquant sur une case à cocher située sur la ligne.
  • Lors de l'ajout/de la modification, vous souhaitez afficher les erreurs de validation près des cellules.
  • Vous souhaitez enregistrer les modifications à la fin, en cliquant sur le bouton Enregistrer.

Pour mettre en œuvre le scénario ci-dessus, vous devez ensuite créer les modèles, actions et vues suivants:

Modèle <T> pouvant être suivi

Cette classe est un modèle qui nous aide dans le suivi côté client et l’édition de listes:

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; }
}

Modèle client

Le modèle client:

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; }
}

Vue Index.cshtml 

La vue Index est chargée de rendre List<Trackable<Customer>>. Lors du rendu de chaque enregistrement, nous utilisons la vue RowTemplate. La même vue que nous utilisons lors de l'ajout d'un nouvel élément.

Dans cette vue, nous avons un bouton d'envoi pour enregistrer et un bouton pour ajouter de nouvelles lignes qui appelle l'action Créer à l'aide de ajax.

Voici la vue d'index:

@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>
}

Vue RowTemplate.cshtml

Cette vue est responsable du rendu d'un enregistrement client. Dans cette vue, nous rendons d'abord la Index dans une zone masquée, puis définissons un préfixe [index] pour les champs, puis nous rendons les champs, y compris les index, les ajouts, les suppressions et les ID de modèle:

Voici la vue 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>

CustomerController

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

Il aura les actions suivantes.

[GET] Index Action

Dans cette action, vous pouvez charger des données à partir de la base de données et les transformer en List<Trackable<Customer>>, puis les transmettre à la vue 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] Create Action

Cette action est responsable du retour du nouveau modèle de ligne. Il sera appelé par un bouton dans la vue index en utilisant ajax:

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

[Action POST] d'indexation

Cette action est responsable de la réception de l'élément suivi du client et de sa sauvegarde. Le modèle qu'il reçoit est List<Trackable<Customer>>. Il supprime d'abord les messages d'erreur de validation pour les lignes supprimées. Supprime ensuite ceux qui sont à la fois supprimés et ajoutés. Ensuite, vérifie si l'état du modèle est valide, tente d'appliquer les modifications à la source de données.

Les éléments ayant la propriété Deleted en tant que true sont supprimés, les éléments ayant Added en tant que true et Deleted en tant que false sont de nouveaux éléments et le reste des éléments est modifié. Ensuite, sans avoir besoin de charger tous les éléments de la base de données, en utilisant simplement une boucle for, appelez db.Entry pour chaque élément et définissez leur état, puis enregistrez les modifications.

[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");
}
3
Reza Aghaei

Qu'en est-il des formulaires dynamiques avec javascript et utilisant type="hidden" ou visibility

Ou en utilisant TempData avec des redirections et en réutilisant ces données dans d'autres vues (formulaire) en tant que input type="hidden"

Couler:

Form1 -> 

La méthode du contrôleur enregistre les données dans TempData et redirige vers Form2 View/Or ViewData et renvoie Form2 View? ->

Form2 a TempData inséré dans le formulaire sous des entrées masquées -> 

Soumettre les deux à la fois

0
Joelty