是否可以将IEnumerable用作私有类级变量?

时间:2015-12-30 15:50:15

标签: c# asp.net-mvc linq

我有一个MVC 5网站,它使用Entity Framework进行数据库交互。

我想在控制器中使用IEnumerable作为私有变量,以便同一控制器中的其他自定义ActionResults可以使用相同的信息,而无需每次都重新查询。我不是指其他CRUD ActionResults,而是指其他自定义方法,这些方法使用在Index页面上看到的数据执行操作,该页面通常是完整数据库表的子集。查询一次,然后重复使用相同的数据会很有帮助。

在这个例子中,我有private IEnumerable<CourseList> _data;作为类级变量,IEnumerable<CourseList> data作为Index() - 级变量。我使用Debug.WriteLine来确定每个变量是否为空。

正如我所料,在Index()ActionResult的范围内, data _data 的变量都不为空。在ClickedFromIndexPageLink(), _data 的范围内 - 类级变量为null。

我的理论是,虽然我首先按顺序加载了索引,但控制器并不知道。就控制器而言,当我在另一个ActionResult中请求_data内容时,它还没有被填充。但是,实时,我首先点击了索引,因此应该看到_data填充了索引查询。

要查看我的解决方法,请向下滚动以查看“方法B(确实有效,但重复)”。

有没有简单的方法让IEnumerable以这种方式用作私有类变量,或者我的解决方法是唯一可行的方法?

方法A(不起作用):

Debug.WriteLine()结果:

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

3 个答案:

答案 0 :(得分:5)

每次调用action方法都是一个单独的Http请求。请记住, Http是无状态,并且一个请求不知道先前的请求是做什么的。所以你不会得到你在上一个动作方法调用中设置的私有变量值。

您可以考虑缓存在缓存过期之前可用于多个请求的数据。您可以使用dot net中提供的MemoryCache类。

快速示例

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

当然,您可以将其从控制器代码移到新的类/层,以保持关注点分离。

如果您希望在存储到缓存之前将实体对象转换为简单的POCO / 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应用程序中执行相同类型的缓存,可以使用this answer中所述的IMemoryCache实现。

答案 1 :(得分:0)

Shyju的回答是正确的,因为您需要缓存,但我建议您选择以下选项来管理MemoryCache

1)在System.Web.Caching中使用ASP.NET缓存。通过Cache.Add添加到缓存,使用索引器(Cache["key"])或Get进行检索。请注意,这是一个静态引用,因此如果您需要在业务逻辑库中获取此信息,则需要将数据设置为业务对象的依赖项(您可能希望注入此{{1}至少进入控制器的构造函数。)

2)使用单身人士。如果您不打算更改此数据,则只需创建一个静态IEnumerableIList并在应用程序启动时设置一次(使其成为静态,而不是实例属性是实现此目的的关键)坚持要求)。如果需要,您可以通过更传统的单例模式进行包装。您还可以使用IoC容器并使用初始化方法将其注册为单例,并让容器将其注入需要的位置。 *请注意,像这样的静态属性本质上不是线程安全的。如果需要更改此数据,请使用其中一个线程安全(并发)集合。

总之,这是您想要的事件序列:

(设计时间) - 定义静态的东西

(申请开始) - 将静态事物设置为数据/初始化静态事物

(应用程序运行时) - 访问静态的东西

答案 2 :(得分:0)

@Shyju回答了我的问题:

  

每次调用action方法都是一个单独的Http请求。请记住,Http是无状态的,并且一个请求不知道先前的请求是做什么的。所以你不会得到你在上一个动作方法调用中设置的私有变量值。

我还没有深入研究缓存,但他的部分答案激励我调整我的代码,因此我只编写一次索引查询,但可以从我想要的同一个控制器中的任何方法调用它。最后,它仍然使用方法B(在我的问题中提出),但它使我能够只输入一次索引查询,并减少来自重复代码的拼写错误或其他简单的编码错误。

{{1}}