2015-12-30 3 views
0

У меня есть веб-сайт MVC 5, который использует Entity Framework для взаимодействия с базами данных.Можно ли использовать IEnumerable как частную переменную уровня класса?

Я хотел бы использовать IEnumerable как частную переменную в контроллере, чтобы другие пользовательские ActionResults в одном контроллере могли использовать одну и ту же информацию без повторного запроса каждый раз. Я не имею в виду другие CRUD ActionResults, а другие пользовательские методы, которые делают вещи с данными, просматриваемыми на странице Index, которые обычно являются подмножеством полной таблицы базы данных. Было бы полезно запросить один раз, а затем повторно использовать те же данные.

В этом примере у меня есть private IEnumerable<CourseList> _data; как переменная уровня класса и IEnumerable<CourseList> data в качестве переменной уровня Index(). Я использую Debug.WriteLine, чтобы определить, является ли каждая переменная пустой или нет.

Как я ожидал, в пределах индекса() ActionResult обеих переменных данных и _data не нуль. В пределах области ClickedFromIndexPageLink(), _data - переменная уровня класса имеет значение NULL.

Моя теория заключается в том, что, хотя я сначала загружал Index сначала, контроллер этого не знает. И что касается контроллера, когда я запрашиваю содержимое _data в другом ActionResult, оно еще не заполнено. Однако в режиме реального времени у есть индекс, который сначала щелкнул по индексу, поэтому должен увидеть _data, заполненный запросом индекса.

Чтобы увидеть, что я делаю в качестве обходного пути, прокрутите вниз, чтобы увидеть «Метод B (работает, но повторяется)».

Ц. Существует ли какой-либо простой способ иметь IEnumerable, используемый в качестве частной переменной класса таким образом, или мой метод обхода является единственным возможным подходом? Результаты

Debug.WriteLine()::

Метод A (не работает)

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***** 

Код:

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 

    } 
} 

Метод B (работает, но повторяется):

Как я ожидал, что мои результаты не нуль:

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

Это потому, что я повторно запрос в ActionResult, так же как и я в 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(); 
     } 

ответ

5

Каждый раз, когда вы вызываете метод действия, это отдельный запрос Http. Помните, Http является апатридом и один запрос не имеет представления о том, что сделал предыдущий запрос. поэтому вы не получите значение частной переменной, которое вы установили в своем предыдущем вызове метода действий.

Вы можете считать кешированием данных, которые будут доступны для нескольких запросов до истечения срока действия кеша.Вы можете использовать класс MemoryCache, доступный в dot net.

Быстрый образец

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

Конечно, вы можете переместить это от кода контроллера в новый класс/слой, чтобы сохранить разделение беспокойства.

Если вы хотите, чтобы преобразовать объект сущности просто коллекции ПОКО/ViewModel перед сохранением в кэше,

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

cache.Set(cacheKey, courseVms , policy); 

И ваш GetCourses метод будет возвращая List<CourseViewModel>

Помните, что кэширование сохранит данные пока кеш не истечет. Поэтому рекомендуется хранить данные, которые обычно не часто меняются (например: поиск данных и т. Д.). Если кэширование ваших транзакционных данных, вам необходимо обновить кэш каждый раз, когда вносятся изменения в данные (например: Новый курс добавляется, один курс удален и т.д ..)

MemoryCache класса находится в пространстве имен System.Runtime.Caching который находится в System.Runtime.Caching.dll. Поэтому вам нужно добавить ссылку на эту сборку.

Если вы хотите сделать такое же кэширование в своем приложении ASP.NET5/MVC6, вы можете использовать реализацию IMemoryCache, как описано в this answer.

+0

В вашем примере я заметил, что вы используете список «один столбец». Будет ли этот пример работать для типизированного IEnumerable с несколькими столбцами? –

+0

Да. Я обновляю ответ с вашими примерными данными. – Shyju

+0

Посмотрите на обновленный ответ @Rubix_Revenge – Shyju

0

ответ Shyju является правильным в том, что вам нужно кэширование, но я бы рекомендовал следующие варианты управления над MemoryCache:

1) Использовать кэш ASP.NET в System.Web.Caching. Добавить в кеш через Cache.Add, получить с помощью индексатора (Cache["key"]) или Get. Обратите внимание, что это статическая ссылка, поэтому, если вам нужно получить это в библиотеке бизнес-логики, вам нужно будет настроить свои данные как зависимость от ваших бизнес-объектов (вы, вероятно, захотите ввести этот IEnumerable в конструктор вашего контроллера, по крайней мере).

2) Используйте синглтон. Если вы не собираетесь изменять эти данные, вы можете просто создать статический IList или IReadOnlyList и установить его один раз при запуске приложения (что делает его статическим, а не свойство экземпляра, является ключом к тому, чтобы это сохранялось в запросах). Вы можете обернуть его с помощью более традиционного одноэлементного паттерна, если хотите. Вы также можете использовать контейнер IoC и зарегистрировать его как одноэлементный с помощью метода инициализации и позволить контейнеру вводить его там, где это необходимо. * Обратите внимание, что статическое свойство, подобное этому, по своей сути не является потокобезопасным. Если вам нужно изменить эти данные, используйте одну из потоковых (параллельных) коллекций.

Подводя итог, вот последовательность событий, которые вы хотите:

(время Design) -define статической вещи

(начало применения) -Установить статическую вещь данных/инициализировать статическую вещь

(среда выполнения приложений) -Access статическая вещь

+0

Я использую свой код как конкретный пример того, что я пытаюсь сделать. Записи в некоторых моделях (и виде) меняются несколько раз в час; некоторые другие не изменятся через год. В любом случае, я думаю, что мой «Метод B», который я опубликовал в своем вопросе, не выглядит таким уж плохим в конце концов. –

+0

@Rubix_Revenge, если ваши данные необходимо обновлять чаще, чем когда приложение перерабатывает, вам нужно более надежное кэширование, которое может настроить срок действия. Или, как вы сказали, вы можете переоценивать затраты/выгоду от запросов каждый раз. FWIW, вы можете настроить записи кэша ASP.NET с пользовательскими истечениями. – moarboilerplate

0

@Shyju ответил на мой вопрос:

Каждый раз, когда вы вызываете метод действия, это отдельный запрос Http.Помните, что Http является апатридом, и один запрос не знает, что сделал предыдущий запрос. поэтому вы не получите значение частной переменной, которое вы установили в своем предыдущем вызове метода действий.

Я еще не углубился в кеширование, но часть его ответа вдохновила меня на корректировку моего кода, поэтому я пишу свой индексный запрос только один раз, но могу вызывать его из любого метода в том же контроллере, который я хочу , В конце концов, он по-прежнему использует метод B (представленный в моем вопросе), но он позволяет мне вводить мой индексный запрос только один раз и уменьшать опечатки или другие простые ошибки кодирования, возникающие из-за дублирования кода.

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 

    } 
} 
Смежные вопросы