Ich habe eine MVC 5-Website, die Entity Framework für die Datenbankinteraktionen verwendet.

Ich möchte eine IEnumerable als private Variable in einem Controller verwenden, damit andere benutzerdefinierte ActionResults in demselben Controller dieselben Informationen verwenden können, ohne jedes Mal eine neue Abfrage durchführen zu müssen. Ich meine nicht die anderen CRUD ActionResults, sondern andere benutzerdefinierte Methoden, die mit den Daten auf der Indexseite arbeiten, die normalerweise eine Teilmenge der vollständigen Datenbanktabelle ist. Es wäre hilfreich, einmal abzufragen und dann dieselben Daten erneut zu verwenden.

In diesem Beispiel habe ich private IEnumerable<CourseList> _data; als Variable auf Klassenebene und IEnumerable<CourseList> data als Variable auf Index () - Ebene. Ich benutze Debug.WriteLine, um festzustellen, ob jede Variable leer ist oder nicht.

Wie ich erwartet habe, sind im Rahmen des Index () ActionResult beide Variablen data und _data nicht null. Im Bereich von ClickedFromIndexPageLink () ist _data - die Variable auf Klassenebene null.

Meine Theorie ist, dass, während ich zuerst Index sequentiell geladen habe, der Controller das nicht weiß. Und was den Controller betrifft, wenn ich _data-Inhalte im anderen ActionResult anfordere, wurde sie noch nicht gefüllt. In Echtzeit habe ich jedoch zuerst auf Index geklickt und sollte daher erwarten, dass _data mit der Indexabfrage gefüllt wird.

Um zu sehen, was ich als Problemumgehung mache, scrollen Sie nach unten und sehen Sie "Methode B (funktioniert, aber wiederholt sich)".

Gibt es eine einfache Möglichkeit, eine IEnumerable auf diese Weise als private Klassenvariable zu verwenden, oder ist meine Problemumgehung der einzig mögliche Ansatz?

Methode A (funktioniert nicht):

Debug.WriteLine () -Ergebnisse:

Begin Index() test *****
Is data Null? Not Null
Is _data Null? Not Null
End Index test *****

Begin ClickedFromIndexPageLink() test*****
Is _data Null? Null
End ClickedFromIndexPageLink test*****

Der Code:

using IenumerableAsClassVariable.Models;
using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Web.Mvc;

namespace IenumerableAsClassVariable.Controllers
{
    // This is the helper class & function used to determine if the IEnumerable is null or empty
    public static class CustomHelpers
    {
        public static string IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
        {
            if (enumerable == null)
                return "Null";
            else
                return "Not Null";
        }
    }

    public class CourseListsController : Controller
    {
        private CreditSlipLogContext db = new CreditSlipLogContext();
        private IEnumerable<CourseList> _data;

        // If IEnumerable is null or empty return true; else false.       

        // GET: CourseLists
        public ActionResult Index()
        {
            IEnumerable<CourseList> data = db.CourseLists.AsEnumerable();
            Debug.WriteLine("-----");
            Debug.WriteLine("Begin Index test *****");
            Debug.WriteLine("Is data Null? " + CustomHelpers.IsNullOrEmpty(data));
            _data = data;
            Debug.WriteLine("Is _data Null? " + CustomHelpers.IsNullOrEmpty(_data));
            Debug.WriteLine("End Index test *****");           

            return View(db.CourseLists.ToList());
        }

        public ActionResult ClickedFromIndexPageLink()
        {

            Debug.WriteLine("Begin ClickedFromIndexPageLink test*****");
            Debug.WriteLine("Is _data Null? " + CustomHelpers.IsNullOrEmpty(_data));
            Debug.WriteLine("End ClickedFromIndexPageLink test*****");

            ViewBag.IsDataNull = CustomHelpers.IsNullOrEmpty(_data);

            return View();
        }

        #region OtherCrudActionResultsAreHidden
        #endregion

    }
}

Methode B (funktioniert, wiederholt sich jedoch):

Wie ich erwarte, sind meine Ergebnisse nicht null:

Begin ClickedFromIndexPageLink test*****
Is data Null? Not Null
Is _data Null? Not Null
End ClickedFromIndexPageLink test*****

Dies liegt daran, dass ich im ActionResult erneut abfrage, genau wie im Index () ACtionResult:

public ActionResult ClickedFromIndexPageLink()
        {
            IEnumerable<CourseList> data = db.CourseLists.AsEnumerable();

            Debug.WriteLine("Begin ClickedFromIndexPageLink test*****");
            Debug.WriteLine("Is data Null? " + CustomHelpers.IsNullOrEmpty(data));
            _data = data;
            Debug.WriteLine("Is _data Null? " + CustomHelpers.IsNullOrEmpty(_data));
            Debug.WriteLine("End ClickedFromIndexPageLink test*****");

            ViewBag.IsDataNull = CustomHelpers.IsNullOrEmpty(_data);

            return View();
        }
0
user4864716 30 Dez. 2015 im 18:50

3 Antworten

Beste Antwort

Jedes Mal, wenn Sie eine Aktionsmethode aufrufen, erfolgt eine separate HTTP-Anforderung. Denken Sie daran, HTTP ist zustandslos und eine Anfrage hat keine Ahnung, was die vorherige Anfrage getan hat. Sie erhalten also nicht den Wert der privaten Variablen, den Sie in Ihrem vorherigen Aufruf der Aktionsmethode festgelegt haben.

Sie können das Zwischenspeichern der Daten in Betracht ziehen, die für mehrere Anforderungen verfügbar sind, bis der Cache abläuft. Sie können die in dot net verfügbare Klasse MemoryCache verwenden.

Schnelle Probe

const string  CacheKey = "courses";
public ActionResult Index()
{
    var courseList = GetCourses();
    // do something with courseList 
    return View(courseList );
}
public ActionResult List()
{
    var course = GetCourses();
   // do something with courseList 
    return View(course);
}

private List<Category> GetCourses()
{
    var db = new YourDbContext();
    var cache = MemoryCache.Default;
    if (!cache.Contains(CacheKey))  // checks to see it exists in cache
    {
        var policy = new CacheItemPolicy();
        policy.AbsoluteExpiration = DateTime.Now.AddDays(1);

        var courses = db.CourseLists.ToList();
        cache.Set(CacheKey , courses, policy);
    }
    return (List<Category>) cache.Get(CacheKey);
}

Natürlich können Sie dies von Ihrem Controller-Code weg in eine neue Klasse / Schicht verschieben, um die Trennung der Bedenken aufrechtzuerhalten.

Wenn Sie Ihr Entitätsobjekt vor dem Speichern im Cache lieber in eine POCO / ViewModel-Sammlung konvertieren möchten,

 var courseVms = db.CourseLists.Select(s=>new CourseViewModel {
                          Id =s.Id, Name=s.Name }).ToList();

 cache.Set(cacheKey, courseVms , policy);

Und Ihre GetCourses-Methode gibt ein List<CourseViewModel> zurück

Denken Sie daran, dass beim Zwischenspeichern die Daten so lange gespeichert bleiben, bis der Cache abläuft. Es ist daher eine gute Idee, Daten aufzubewahren, die sich normalerweise nicht so oft ändern (Beispiel: Daten nachschlagen usw.). Wenn Sie Ihre Transaktionsdaten zwischenspeichern, müssen Sie den Cache jedes Mal aktualisieren, wenn Änderungen an den Daten vorgenommen werden (Beispiel: Ein neuer Kurs wird hinzugefügt, ein Kurs wird gelöscht usw.)

Die Klasse MemoryCache befindet sich im Namespace System.Runtime.Caching, der sich in System.Runtime.Caching.dll befindet. Sie müssen also einen Verweis auf diese Assembly hinzufügen.

Wenn Sie dieselbe Art von Caching in Ihrer ASP.NET5 / MVC6-Anwendung ausführen möchten, können Sie die Implementierung IMemoryCache verwenden, wie unter diese Antwort.

5
Community 23 Mai 2017 im 12:01

Shyjus Antwort ist insofern richtig, als Sie Caching benötigen, aber ich würde die folgenden Optionen für die Verwaltung eines MemoryCache empfehlen:

1) Verwenden Sie den ASP.NET-Cache in System.Web.Caching. Über Cache.Add zum Cache hinzufügen, mit dem Indexer (Cache["key"]) oder Get abrufen. Beachten Sie, dass dies eine statische Referenz ist. Wenn Sie dies in einer Geschäftslogikbibliothek benötigen, müssen Sie Ihre Daten als Abhängigkeit von Ihren Geschäftsobjekten einrichten (wahrscheinlich möchten Sie diese {{X4} einfügen. } zumindest in den Konstruktor Ihres Controllers).

2) Verwenden Sie einen Singleton. Wenn Sie diese Daten nicht ändern möchten, können Sie einfach ein statisches IList oder IReadOnlyList erstellen und beim Start der Anwendung einmal festlegen (statisch machen, keine Instanzeigenschaft ist der Schlüssel dazu über Anfragen bestehen bleiben). Sie können es über das traditionellere Singleton-Muster umbrechen, wenn Sie möchten. Sie können auch einen IoC-Container verwenden und ihn mit einer Initialisierungsmethode als Singleton registrieren und vom Container dort injizieren lassen, wo er benötigt wird. * Beachten Sie, dass eine solche statische Eigenschaft von Natur aus nicht threadsicher ist. Wenn Sie diese Daten ändern müssen, verwenden Sie eine der thread-sicheren (gleichzeitigen) Sammlungen.

Zusammenfassend ist hier die Reihenfolge der gewünschten Ereignisse:

(Entwurfszeit) - statische Sache definieren

(Anwendungsstart) - Statisches Ding auf Daten setzen / statisches Ding initialisieren

(Anwendungslaufzeit) - Zugriff auf statische Dinge

0
moarboilerplate 30 Dez. 2015 im 16:42

@Shyju beantwortete meine Frage:

Jedes Mal, wenn Sie eine Aktionsmethode aufrufen, erfolgt eine separate HTTP-Anforderung. Denken Sie daran, dass HTTP zustandslos ist und eine Anfrage keine Ahnung hat, was die vorherige Anfrage getan hat. Sie erhalten also nicht den Wert der privaten Variablen, den Sie in Ihrem vorherigen Aufruf der Aktionsmethode festgelegt haben.

Ich habe mich noch nicht mit Caching befasst, aber ein Teil seiner Antwort hat mich dazu inspiriert, meinen Code so anzupassen, dass ich meine Indexabfrage nur einmal schreibe, sie aber von jeder Methode in demselben Controller aufrufen kann, den ich möchte. Am Ende wird immer noch Methode B verwendet (in meiner Frage dargestellt), aber ich kann meine Indexabfrage nur einmal eingeben und Tippfehler oder andere einfache Codierungsfehler reduzieren, die durch das Reduzieren von Code entstehen.

using IenumerableAsClassVariable.Models;
using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Web.Mvc;
using System.Web.Caching;

namespace IenumerableAsClassVariable.Controllers
{
    // This is the helper class & function used to determine if the IEnumerable is null or empty
    public static class CustomHelpers
    {
        public static string IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
        {
            if (enumerable == null)
                return "Null";
            else
                return "Not Null";
        }
    }

    public class CourseListsController : Controller
    {
        private CreditSlipLogContext db = new CreditSlipLogContext();   

        // This this the "index" query that is called by the Index 
        // and can be called by any other methods in this controller that I choose.
        private IEnumerable<CourseList> GetIndexQuery()
        {   
            using (var dbc = new CreditSlipLogContext())
            {
                return  db.CourseLists.AsEnumerable();
            }

        }

        // GET: CourseLists
        public ActionResult Index()
        {
            var data = GetIndexQuery();
            Debug.WriteLine("-----");
            Debug.WriteLine("Begin Index test *****");
            Debug.WriteLine("Is data Null? " + CustomHelpers.IsNullOrEmpty(data));                        
            Debug.WriteLine("End Index test *****");           

            return View(db.CourseLists.ToList());
        }

        public ActionResult ClickedFromIndexPageLink()
        {
            var data = GetIndexQuery();
            Debug.WriteLine("-----");
            Debug.WriteLine("Begin Index test *****");
            Debug.WriteLine("Is data Null? " + CustomHelpers.IsNullOrEmpty(data));
            Debug.WriteLine("End Index test *****");  

            ViewBag.IsDataNull = CustomHelpers.IsNullOrEmpty(data);

            return View();
        }

        #region OtherCrudActionResultsAreHidden
        #endregion

    }
}
0
user4864716user4864716 30 Dez. 2015 im 16:54