简体   繁体   English

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

[英]Is it possible to use an IEnumerable as a private class-level variable?

I have an MVC 5 website that uses Entity Framework for the database interactions. 我有一个使用实体框架进行数据库交互的MVC 5网站。

I would like to use an IEnumerable as a private variable in a controller so that other custom ActionResults in the same controller can use the same information without having to re-query each time. 我想将IEnumerable用作控制器中的私有变量,以便同一控制器中的其他自定义ActionResults可以使用相同的信息,而不必每次都重新查询。 I don't mean the other CRUD ActionResults, but rather other custom methods that do things with the data seen on the Index page, which is usually a subset of the full database table. 我并不是说其他​​CRUD ActionResult,而是其他自定义方法来处理在“索引”页面上看到的数据,该页面通常是完整数据库表的子集。 It would be helpful to query once, then re-use the same data. 查询一次,然后重新使用相同的数据将很有帮助。

In this example, I have private IEnumerable<CourseList> _data; 在这个例子中,我有private IEnumerable<CourseList> _data; as a class-level variable and IEnumerable<CourseList> data as an Index()-level variable. 作为类级变量,而IEnumerable<CourseList> data作为Index()级变量。 I use Debug.WriteLine to determine if each variable is empty or not. 我使用Debug.WriteLine确定每个变量是否为空。

As I expect, within the scope of the Index() ActionResult both variables data and _data are not null. 如我所料,在Index()ActionResult的范围内,变量data_data都不为空。 Within the scope of ClickedFromIndexPageLink(), _data -- the class-level variable is null. 内ClickedFromIndexPageLink()的范围内,_data -类级别变量为空。

My theory is that while i have sequentially loaded Index first, the controller doesn't know that. 我的理论是,虽然我先顺序加载了Index,但控制器不知道这一点。 And as far as the controller is concerned, when I request _data contents in the other ActionResult, it hasn't been filled yet. 就控制器而言,当我在另一个ActionResult中请求_data内容时,它尚未被填充。 However, in real time, I have clicked Index first and so should expect to see _data filled with the Index query. 然而,在现实的时候,我已经点击指数第一,所以应该会看到_data充满指数查询。

To see what I do as a workaround, scroll down to see "Method B (does work, but is repetitive)". 若要查看我作为变通办法所执行的操作,请向下滚动以查看“方法B(有效,但重复)”。

Ts there any simple way to have an IEnumerable used as a private class variable in this manner, or is my workaround the only possible approach? 有没有一种简单的方法可以将IEnumerable以这种方式用作私有类变量,或者我的解决方法是唯一可能的方法吗?

Method A (doesn't work): 方法A(无效):

Debug.WriteLine() results: 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*****

The 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

    }
}

Method B (does work, but is repetitive): 方法B(有效,但重复):

As I expect, my results aren't null: 如我所料,我的结果不是null:

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

This is because I re-query in the ActionResult, just as I do in the Index() ACtionResult: 这是因为我在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();
        }

Everytime you call an action method is a separate Http request. 每次调用操作方法都是一个单独的Http请求。 Remember, Http is stateless and one request has no idea what the previous request did. 记住, Http是无状态的 ,一个请求不知道前一个请求做了什么。 so you won't get the private variable value you did set in your previous action method call. 因此您将不会获得在上一个操作方法调用中设置的私有变量值。

You may consider caching the data which will be available to multiple requests until the cache expires. 您可以考虑缓存可用于多个请求的数据,直到缓存过期。 You may use the MemoryCache class available in dot net. 您可以使用点网中可用的MemoryCache类。

Quick sample 快速样品

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

Of course you may move this away from your controller code to a new class/layer to keep separation of concern. 当然,您可以将其从控制器代码移到新的类/层,以保持关注点分离。

If you prefer to convert your entity object to simply POCO/ViewModel collection before storing in cache, 如果您希望先将实体对象转换为POCO / ViewModel集合,然后再存储在缓存中,

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

 cache.Set(cacheKey, courseVms , policy);

And your GetCourses method will be returning a List<CourseViewModel> 并且您的GetCourses方法将返回List<CourseViewModel>

Remember, caching will keep the data until the cache expires. 请记住,缓存将保留数据,直到缓存过期。 So it is a good idea to keep data which won't usually change that often (Ex: Look up data etc). 因此,最好保留通常不会经常更改的数据(例如:查找数据等)。 If you are caching your transactional data, you need to update the cache every time a change is made to the data (Ex : A new course is added, one course is deleted etc..) 如果要缓存事务数据,则每次对数据进行更改时都需要更新缓存(例如:添加了新课程,删除了一个课程,等等。)

MemoryCache class resides in System.Runtime.Caching namespace which resides in System.Runtime.Caching.dll . MemoryCache类位于System.Runtime.Caching命名空间中,该命名空间位于System.Runtime.Caching.dll So you need to add a reference to this assembly. 因此,您需要添加对此程序集的引用。

If you want to do the same kind of caching within your ASP.NET5/ MVC6 application, You may use the IMemoryCache implementation as explained in this answer . 如果要在ASP.NET5 / MVC6应用程序中进行相同类型的缓存,则可以按照此答案中的说明使用IMemoryCache实现。

Shyju's answer is right in that you need caching but I'd recommend the following options over managing a MemoryCache : Shyju的答案是正确的,因为您需要缓存,但是我建议在管理MemoryCache使用以下选项:

1) Use the ASP.NET cache in System.Web.Caching . 1)在System.Web.Caching使用ASP.NET缓存。 Add to cache via Cache.Add , retrieve using the indexer ( Cache["key"] ) or Get . 通过Cache.Add添加到缓存中,使用索引器( Cache["key"] )或Get检索。 Note that this is a static reference so if you need to get at this in a business logic library you'll need to set up your data as a dependency of your business objects (you probably are going to want to inject this IEnumerable into the constructor of your controller at the very least). 请注意,这是一个静态引用,因此,如果需要在业务逻辑库中进行此操作,则需要将数据设置为业务对象的依赖项(您可能希望将此IEnumerable注入构造函数中至少是您的控制器)。

2) Use a singleton. 2)使用单例。 If you're not going to change this data, you can simply create a static IList or IReadOnlyList and set it once at application start (making it static, not an instance property is the key to making this persist across requests). 如果不打算更改此数据,则可以简单地创建一个静态IListIReadOnlyList并在应用程序启动时对其进行一次设置(将其设置为静态,而不是实例属性是使其在请求中持久存在的关键)。 You can wrap it via the more traditional singleton pattern if you want. 如果需要,可以通过更传统的单例模式包装它。 You could also use an IoC container and register it as a singleton with an initializer method and let the container inject it where it is needed. 您还可以使用IoC容器,并使用初始化方法将其注册为单例,然后让该容器将其注入到需要的地方。 *Note that a static property like this is inherently not thread-safe. *请注意,这样的静态属性本质上不是线程安全的。 If you need to change this data, use one of the thread-safe (concurrent) collections. 如果需要更改此数据,请使用线程安全(并发)集合之一。

To sum up, here is the sequence of events you want: 总结一下,这是您想要的事件顺序:

(Design time) -define static thing (设计时间)-定义静态事物

(Application start) -Set static thing to data/initialize static thing (应用程序启动)-将静态内容设置为数据/初始化静态内容

(Application runtime) -Access static thing (应用程序运行时)-访问静态事物

@Shyju answered my question: @Shyju回答了我的问题:

Everytime you call an action method is a separate Http request. 每次调用操作方法都是一个单独的Http请求。 Remember, Http is stateless and one request has no idea what the previous request did. 记住,Http是无状态的,一个请求不知道前一个请求做了什么。 so you won't get the private variable value you did set in your previous action method call. 因此您将不会获得在上一个操作方法调用中设置的私有变量值。

I have not yet delved into caching, but part of his answer inspired me to adjust my code, so that I write my index query only once, but can call it from whatever method in the same controller that I want. 我尚未深入研究缓存,但是他的部分回答启发了我调整代码,因此我只编写一次索引查询,但可以从所需控制器中的任何方法调用它。 In the end, it's still using Method B (presented in my question), but it enables me to type my index query only once and reduce typo or other simple coding errors that come from reduplicating code. 最后,它仍然使用方法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

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM