我有一个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();
}
答案 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)使用单身人士。如果您不打算更改此数据,则只需创建一个静态IEnumerable
或IList
并在应用程序启动时设置一次(使其成为静态,而不是实例属性是实现此目的的关键)坚持要求)。如果需要,您可以通过更传统的单例模式进行包装。您还可以使用IoC容器并使用初始化方法将其注册为单例,并让容器将其注入需要的位置。 *请注意,像这样的静态属性本质上不是线程安全的。如果需要更改此数据,请使用其中一个线程安全(并发)集合。
(设计时间) - 定义静态的东西
(申请开始) - 将静态事物设置为数据/初始化静态事物
(应用程序运行时) - 访问静态的东西
答案 2 :(得分:0)
@Shyju回答了我的问题:
每次调用action方法都是一个单独的Http请求。请记住,Http是无状态的,并且一个请求不知道先前的请求是做什么的。所以你不会得到你在上一个动作方法调用中设置的私有变量值。
我还没有深入研究缓存,但他的部分答案激励我调整我的代码,因此我只编写一次索引查询,但可以从我想要的同一个控制器中的任何方法调用它。最后,它仍然使用方法B(在我的问题中提出),但它使我能够只输入一次索引查询,并减少来自重复代码的拼写错误或其他简单的编码错误。
{{1}}