【问题标题】:Is it possible to use an IEnumerable as a private class-level variable?是否可以使用 IEnumerable 作为私有类级变量?
【发布时间】:2015-12-30 15:50:14
【问题描述】:

我有一个使用实体框架进行数据库交互的 MVC 5 网站。

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

在此示例中,我将 private IEnumerable<CourseList> _data; 作为类级变量,将IEnumerable<CourseList> data 作为 Index() 级变量。我使用Debug.WriteLine 来判断每个变量是否为空。

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

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

要查看我的解决方法,请向下滚动以查看“方法 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();
        }

【问题讨论】:

    标签: c# asp.net-mvc linq


    【解决方案1】:

    每次调用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&lt;CourseViewModel&gt;

    请记住,缓存将保留数据,直到缓存过期。因此,最好保留通常不会经常更改的数据(例如:查找数据等)。如果您正在缓存事务数据,则每次对数据进行更改时都需要更新缓存(例如:添加新课程、删除一门课程等)

    MemoryCache 类驻留在System.Runtime.Caching 命名空间中,该命名空间驻留在System.Runtime.Caching.dll 中。所以你需要添加对这个程序集的引用。

    如果您想在您的 ASP.NET5/MVC6 应用程序中执行相同类型的缓存,您可以使用IMemoryCache 实现,如this answer 中所述。

    【讨论】:

    • 我注意到在您的示例中,您使用了“单列”列表。此示例是否适用于具有多列的模型类型 IEnumerable?
    • 是的。我将使用您的示例数据更新答案。
    • 这个答案解决了问题的核心(你需要缓存),但我强烈推荐 System.Web.Caching CacheCacheDependency 类型进行缓存,这样你就不必管理缓存,也让你的代码更易于维护。
    • 这是我试图复制代码的地方。我找到了System.Web.Caching 命名空间,但找不到MemoryCache.Default
    • @Rubix_Revenge 你会选择其中一个。 MemoryCache 位于单独的程序集中 (System.Runtime.Caching)。要将某些内容存储在您正在谈论的缓存中,只需调用静态方法Cache.Add
    【解决方案2】:

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

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

    2) 使用单例。如果您不打算更改此数据,您可以简单地创建一个静态 IListIReadOnlyList 并在应用程序启动时设置一次(使其成为静态,而不是实例属性是使其在请求之间持续存在的关键) .如果需要,您可以通过更传统的单例模式包装它。您还可以使用 IoC 容器并将其注册为带有初始化方法的单例,然后让容器将其注入到需要的地方。 *请注意,像这样的静态属性本质上不是线程安全的。如果您需要更改此数据,请使用线程安全(并发)集合之一。

    总而言之,这是您想要的事件顺序:

    (设计时间) - 定义静态事物

    (应用程序启动) - 将静态事物设置为数据/初始化静态事物

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

    【讨论】:

    • 我正在使用我的代码作为我正在尝试做的具体示例。某些模型(和视图)中的记录每小时更改数次;其他一些不会在一年内改变。无论如何,我认为我在问题中发布的“方法 B”看起来并不那么糟糕。
    • @Rubix_Revenge 如果您的数据需要比应用程序回收更频繁地刷新,您需要一个更强大的缓存解决方案,可以配置过期。或者,就像您说的那样,您可以重新评估每次查询的成本/收益。 FWIW,您可以使用自定义过期配置 ASP.NET 缓存条目。
    【解决方案3】:

    @Shyju 回答了我的问题:

    每次调用action方法都是一个单独的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
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-30
      • 1970-01-01
      • 2014-01-02
      • 2017-12-05
      • 2012-12-02
      相关资源
      最近更新 更多