Cách lưu trữ dữ liệu trong ứng dụng MVC


252

Tôi đã đọc rất nhiều thông tin về bộ nhớ đệm trang và bộ đệm trang một phần trong ứng dụng MVC. Tuy nhiên, tôi muốn biết làm thế nào bạn sẽ lưu trữ dữ liệu.

Trong kịch bản của tôi, tôi sẽ sử dụng LINQ to Entities (khung thực thể). Trong cuộc gọi đầu tiên tới GetNames (hoặc bất kể phương thức nào) tôi muốn lấy dữ liệu từ cơ sở dữ liệu. Tôi muốn lưu kết quả vào bộ đệm và trong cuộc gọi thứ hai để sử dụng phiên bản được lưu trong bộ nhớ cache nếu nó tồn tại.

Bất cứ ai cũng có thể đưa ra một ví dụ về cách thức này sẽ hoạt động, nơi mà điều này nên được thực hiện (mô hình?) Và nếu nó sẽ hoạt động.

Tôi đã thấy điều này được thực hiện trong các ứng dụng ASP.NET truyền thống, điển hình cho dữ liệu rất tĩnh.


1
Khi xem xét các câu trả lời dưới đây, hãy chắc chắn xem xét liệu bạn có muốn bộ điều khiển của mình có kiến ​​thức về / trách nhiệm đối với các mối quan tâm truy cập dữ liệu và bộ đệm không. Nói chung bạn muốn tách cái này. Xem Mẫu Kho lưu trữ để biết cách tốt để làm như vậy: deviq.com/reposeective-potype
ssmith

Câu trả lời:


75

Tham khảo dll System.Web trong mô hình của bạn và sử dụng System.Web.Caching.Cache

    public string[] GetNames()
    {
      string[] names = Cache["names"] as string[];
      if(names == null) //not in cache
      {
        names = DB.GetNames();
        Cache["names"] = names;
      }
      return names;
    }

Một chút đơn giản hóa nhưng tôi đoán rằng sẽ làm việc. Đây không phải là cụ thể của MVC và tôi đã luôn sử dụng phương pháp này để lưu trữ dữ liệu.


89
Tôi không đề xuất giải pháp này: đổi lại, bạn có thể lấy lại một đối tượng null, vì nó đang đọc lại trong bộ đệm và nó có thể đã bị xóa khỏi bộ đệm. Tôi muốn làm: chuỗi công khai [] GetNames () {string [] noms = Cache ["name"]; if (noms == null) {noms = DB.GetNames (); Bộ nhớ cache ["tên"] = noms; } trả lại (noms); }
Oli

Tôi đồng ý với Oli .. nhận kết quả từ cuộc gọi thực tế đến DB tốt hơn là nhận chúng từ bộ đệm
CodeClimber

1
Điều này có hoạt động với DB.GetNames().AsQueryablephương pháp trì hoãn truy vấn không?
Đuổi theo Florell

Trừ khi bạn thay đổi giá trị trả về từ chuỗi [] thành IEnumerable <chuỗi>
terjetyl

12
Nếu bạn không đặt hết hạn..khi bộ đệm hết hạn theo mặc định?
Chaka

403

Đây là một lớp / dịch vụ trợ giúp bộ đệm đơn giản và đẹp mà tôi sử dụng:

using System.Runtime.Caching;  

public class InMemoryCache: ICacheService
{
    public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
    {
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10));
        }
        return item;
    }
}

interface ICacheService
{
    T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}

Sử dụng:

cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));

Nhà cung cấp bộ đệm sẽ kiểm tra xem có bất cứ thứ gì có tên là "id id" trong bộ đệm hay không và nếu không, nó sẽ gọi một phương thức ủy nhiệm để lấy dữ liệu và lưu trữ trong bộ đệm.

Thí dụ:

var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())

3
Tôi đã điều chỉnh điều này để cơ chế lưu trữ được sử dụng cho mỗi phiên của người dùng bằng cách sử dụng HttpContext.Cản.Session.Session thay thế. Tôi cũng đã đặt một thuộc tính Cache trên lớp BaseContoder của mình để dễ dàng truy cập và cập nhật hàm tạo cho phép DI kiểm tra đơn vị. Hi vọng điêu nay co ich.
WestDiscGolf

1
Bạn cũng có thể làm cho lớp và phương thức này tĩnh để có thể sử dụng lại giữa các bộ điều khiển khác.
Alex

5
Lớp này không nên phụ thuộc vào HTTPContext. Tôi đơn giản hóa nó chỉ cho mục đích ví dụ ở đây. Đối tượng bộ đệm phải được chèn thông qua hàm tạo - nó có thể được thay thế bằng các cơ chế bộ đệm khác. Tất cả điều này đạt được với IoC / DI, cùng với vòng đời tĩnh (singleton).
Hrvoje Hudo

3
@Brendan - và tệ hơn nữa, nó có các chuỗi ma thuật thay thế cho các khóa bộ đệm, thay vì suy ra chúng từ tên và tham số của phương thức.
ssmith

5
Đây là một giải pháp cấp thấp tuyệt vời. Giống như những người khác đã ám chỉ, bạn muốn bọc nó trong một loại lớp cụ thể, an toàn cho tên miền. Truy cập trực tiếp vào bộ điều khiển của bạn sẽ là một cơn ác mộng bảo trì vì các chuỗi ma thuật.
Josh Noe

43

Tôi đang đề cập đến bài viết của TT và đề xuất cách tiếp cận sau:

Tham khảo dll System.Web trong mô hình của bạn và sử dụng System.Web.Caching.Cache

public string[] GetNames()
{ 
    var noms = Cache["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        Cache["names"] = noms; 
    }

    return ((string[])noms);
}

Bạn không nên trả lại giá trị đọc lại từ bộ đệm, vì bạn sẽ không bao giờ biết nếu tại thời điểm cụ thể đó, nó có còn trong bộ đệm hay không. Ngay cả khi bạn đã chèn nó vào câu lệnh trước đó, nó có thể đã biến mất hoặc chưa bao giờ được thêm vào bộ đệm - bạn chỉ không biết.

Vì vậy, bạn thêm dữ liệu đọc từ cơ sở dữ liệu và trả lại trực tiếp, không đọc lại từ bộ đệm.


Nhưng không phải dòng Cache["names"] = noms;đặt trong bộ đệm?
Omar

2
@Baddie Vâng. Nhưng ví dụ này khác với Oli đầu tiên được đề cập, bởi vì anh ta không truy cập lại bộ đệm - vấn đề là chỉ cần thực hiện: return (chuỗi []) Cache ["name"]; .. COULD dẫn đến một giá trị null được trả về, vì COULD đã hết hạn. Nó không có khả năng, nhưng nó có thể xảy ra. Ví dụ này tốt hơn, bởi vì chúng tôi lưu trữ giá trị thực được trả về từ db trong bộ nhớ, lưu trữ giá trị đó và sau đó trả về giá trị đó, chứ không phải giá trị đọc lại từ bộ đệm.
jamiebarrow

Hoặc ... giá trị đọc lại từ bộ đệm, nếu nó vẫn tồn tại (! = Null). Do đó, toàn bộ điểm của bộ nhớ đệm. Điều này chỉ để nói rằng nó kiểm tra hai lần cho các giá trị null và đọc cơ sở dữ liệu khi cần thiết. Rất thông minh, cảm ơn Oli!
Sean Kendle

Bạn có thể vui lòng chia sẻ một số liên kết nơi tôi có thể đọc về bộ nhớ đệm ứng dụng dựa trên Giá trị khóa. Tôi không thể tìm thấy liên kết.
Không thể phá vỡ

@Oli, Cách sử dụng bản ghi Cache này từ trang CSHTML hoặc HTML
Deepan Raj

37

Đối với khung .NET 4.5+

thêm tài liệu tham khảo: System.Runtime.Caching

thêm sử dụng câu lệnh: using System.Runtime.Caching;

public string[] GetNames()
{ 
    var noms = System.Runtime.Caching.MemoryCache.Default["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        System.Runtime.Caching.MemoryCache.Default["names"] = noms; 
    }

    return ((string[])noms);
}

Trong .NET Framework 3.5 và các phiên bản trước đó, ASP.NET đã cung cấp triển khai bộ đệm trong bộ nhớ trong không gian tên System.Web.Caching. Trong các phiên bản trước của .NET Framework, bộ nhớ đệm chỉ có sẵn trong không gian tên System.Web và do đó cần có sự phụ thuộc vào các lớp ASP.NET. Trong .NET Framework 4, không gian tên System.R.78.Caching chứa các API được thiết kế cho cả ứng dụng Web và không phải Web.

Thêm thông tin:


Nơi Db.GerNames()đến từ đâu?
Thiếu niên

DB.GetNames chỉ là một phương thức từ DAL tìm nạp một số tên từ cơ sở dữ liệu. Đây là bất cứ điều gì bạn thường lấy.
juFo

Điều này nên được đặt lên hàng đầu vì nó có giải pháp phù hợp hiện tại
BYISHIMO Audace

2
Cảm ơn, cần thiết để thêm gói nuget System.R.78.Caching (v4.5).
Steve Greene

26

Steve Smith đã thực hiện hai bài đăng blog tuyệt vời trong đó trình bày cách sử dụng mẫu Bộ nhớ đệm của mình trong ASP.NET MVC. Nó sử dụng mẫu kho lưu trữ một cách hiệu quả và cho phép bạn nhận bộ đệm mà không phải thay đổi mã hiện có.

http://ardalis.com/Intesing-the-CachedRep repository-Potype

http://ardalis.com/building-a-cachedreposeective-via-strargety-potype

Trong hai bài viết này, ông chỉ cho bạn cách thiết lập mẫu này và cũng giải thích lý do tại sao nó hữu ích. Bằng cách sử dụng mẫu này, bạn nhận được bộ đệm ẩn mà không có mã hiện tại của bạn nhìn thấy bất kỳ logic bộ đệm. Về cơ bản, bạn sử dụng kho lưu trữ được lưu trữ như thể nó là bất kỳ kho lưu trữ nào khác.


1
Bài viết tuyệt vời! Cám ơn vì đã chia sẻ!!
Đánh dấu

Liên kết đã chết vào năm 2013-08-31.
CBono


Bạn có thể vui lòng chia sẻ một số liên kết nơi tôi có thể đọc về bộ nhớ đệm ứng dụng dựa trên Giá trị khóa. Tôi không thể tìm thấy liên kết.
Không thể phá vỡ

4

Bộ nhớ đệm ứng dụng được phân phối và một kỹ thuật bộ nhớ đệm trong bộ nhớ lưu trữ dữ liệu theo cặp giá trị khóa bằng bộ nhớ vật lý trên nhiều máy chủ. AppFoven cung cấp các cải tiến về hiệu suất và khả năng mở rộng cho các ứng dụng .NET Framework. Khái niệm và kiến ​​trúc


Điều này là dành riêng cho Azure, không phải ASP.NET MVC nói chung.
Henry C

3

Mở rộng câu trả lời của @Hrvoje Hudo ...

Mã số:

using System;
using System.Runtime.Caching;

public class InMemoryCache : ICacheService
{
    public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
    {
        TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
        }
        return item;
    }

    public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class
    {
        string cacheKey = string.Format(cacheKeyFormat, id);
        TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
        if (item == null)
        {
            item = getItemCallback(id);
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
        }
        return item;
    }
}

interface ICacheService
{
    TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
    TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
}

Ví dụ

Bộ nhớ đệm một mục (khi mỗi mục được lưu trữ dựa trên ID của nó vì lưu vào toàn bộ danh mục cho loại mục sẽ quá chuyên sâu).

Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);

Bộ nhớ đệm tất cả mọi thứ

IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);

Tại sao TId

Trình trợ giúp thứ hai đặc biệt tốt vì hầu hết các khóa dữ liệu không phải là tổng hợp. Các phương pháp bổ sung có thể được thêm vào nếu bạn sử dụng các phím tổng hợp thường xuyên. Theo cách này, bạn tránh thực hiện tất cả các kiểu nối chuỗi hoặc chuỗi. Định dạng để lấy khóa để chuyển đến trình trợ giúp bộ đệm. Nó cũng làm cho việc truyền phương thức truy cập dữ liệu trở nên dễ dàng hơn vì bạn không phải chuyển ID vào phương thức trình bao bọc ... toàn bộ điều này trở nên rất ngắn gọn và nhất quán cho phần lớn các trường hợp sử dụng.


1
Các định nghĩa giao diện của bạn đang thiếu thông số "termsInMinutes". ;-)
Tech0

3

Đây là một cải tiến cho câu trả lời của Hrvoje Hudo. Việc thực hiện này có một vài cải tiến chính:

  • Khóa bộ nhớ cache được tạo tự động dựa trên chức năng cập nhật dữ liệu và đối tượng được truyền vào đó chỉ định phụ thuộc
  • Vượt qua trong khoảng thời gian cho bất kỳ thời gian bộ nhớ cache
  • Sử dụng khóa cho an toàn chủ đề

Lưu ý rằng điều này có sự phụ thuộc vào Newtonsoft.Json để tuần tự hóa đối tượng phụ thuộc, nhưng điều đó có thể dễ dàng hoán đổi cho bất kỳ phương thức tuần tự hóa nào khác.

ICache.cs

public interface ICache
{
    T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}

InMemoryCache.cs

using System;
using System.Reflection;
using System.Runtime.Caching;
using Newtonsoft.Json;

public class InMemoryCache : ICache
{
    private static readonly object CacheLockObject = new object();

    public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class
    {
        string cacheKey = GetCacheKey(getItemCallback, dependsOn);
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            lock (CacheLockObject)
            {
                item = getItemCallback();
                MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration));
            }
        }
        return item;
    }

    private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class
    {
        var serializedDependants = JsonConvert.SerializeObject(dependsOn);
        var methodType = itemCallback.GetType();
        return methodType.FullName + serializedDependants;
    }
}

Sử dụng:

var order = _cache.GetOrSet(
    () => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
    , new { id = orderId }
    , new TimeSpan(0, 10, 0)
);

2
Các if (item == null)nên bên trong khóa. Bây giờ khi điều này iflà trước khi khóa, điều kiện cuộc đua có thể xảy ra. Hoặc thậm chí tốt hơn, bạn nên giữ iftrước khi khóa, nhưng kiểm tra lại nếu bộ đệm vẫn trống như dòng đầu tiên bên trong khóa. Bởi vì nếu hai luồng đến cùng một lúc, cả hai đều cập nhật bộ đệm. Khóa hiện tại của bạn không hữu ích.
Al Kepp

3
public sealed class CacheManager
{
    private static volatile CacheManager instance;
    private static object syncRoot = new Object();
    private ObjectCache cache = null;
    private CacheItemPolicy defaultCacheItemPolicy = null;

    private CacheEntryRemovedCallback callback = null;
    private bool allowCache = true;

    private CacheManager()
    {
        cache = MemoryCache.Default;
        callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback);

        defaultCacheItemPolicy = new CacheItemPolicy();
        defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
        defaultCacheItemPolicy.RemovedCallback = callback;
        allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ;
    }
    public static CacheManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new CacheManager();
                    }
                }
            }

            return instance;
        }
    }

    public IEnumerable GetCache(String Key)
    {
        if (Key == null || !allowCache)
        {
            return null;
        }

        try
        {
            String Key_ = Key;
            if (cache.Contains(Key_))
            {
                return (IEnumerable)cache.Get(Key_);
            }
            else
            {
                return null;
            }
        }
        catch (Exception)
        {
            return null;
        }
    }

    public void ClearCache(string key)
    {
        AddCache(key, null);
    }

    public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null)
    {
        if (!allowCache) return true;
        try
        {
            if (Key == null)
            {
                return false;
            }

            if (cacheItemPolicy == null)
            {
                cacheItemPolicy = defaultCacheItemPolicy;
            }

            String Key_ = Key;

            lock (Key_)
            {
                return cache.Add(Key_, data, cacheItemPolicy);
            }
        }
        catch (Exception)
        {
            return false;
        }
    }

    private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
    {
        String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
        LogManager.Instance.Info(strLog);
    }
}

3
Xem xét thêm một số lời giải thích
Mike Debela

2

Tôi đã sử dụng nó theo cách này và nó hoạt động cho tôi. https://msdn.microsoft.com/en-us/l Library / system.web.caching.cache.add (v = vs.110) .aspx thông tin tham số cho system.web.caching.cache.add.

public string GetInfo()
{
     string name = string.Empty;
     if(System.Web.HttpContext.Current.Cache["KeyName"] == null)
     {
         name = GetNameMethod();
         System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null);
     }
     else
     {
         name = System.Web.HttpContext.Current.Cache["KeyName"] as string;
     }

      return name;

}

nâng cấp thêm cho công cụ đủ điều kiện với không gian tên đầy đủ của nó !!
Ninjanoel

1

Tôi sử dụng hai lớp. Đầu tiên, đối tượng lõi bộ đệm:

public class Cacher<TValue>
    where TValue : class
{
    #region Properties
    private Func<TValue> _init;
    public string Key { get; private set; }
    public TValue Value
    {
        get
        {
            var item = HttpRuntime.Cache.Get(Key) as TValue;
            if (item == null)
            {
                item = _init();
                HttpContext.Current.Cache.Insert(Key, item);
            }
            return item;
        }
    }
    #endregion

    #region Constructor
    public Cacher(string key, Func<TValue> init)
    {
        Key = key;
        _init = init;
    }
    #endregion

    #region Methods
    public void Refresh()
    {
        HttpRuntime.Cache.Remove(Key);
    }
    #endregion
}

Thứ hai là danh sách các đối tượng bộ đệm:

public static class Caches
{
    static Caches()
    {
        Languages = new Cacher<IEnumerable<Language>>("Languages", () =>
                                                          {
                                                              using (var context = new WordsContext())
                                                              {
                                                                  return context.Languages.ToList();
                                                              }
                                                          });
    }
    public static Cacher<IEnumerable<Language>> Languages { get; private set; }
}

0

Tôi sẽ nói việc triển khai Singleton về vấn đề dữ liệu dai dẳng này có thể là một giải pháp cho vấn đề này trong trường hợp bạn thấy các giải pháp trước đó phức tạp hơn nhiều

 public class GPDataDictionary
{
    private Dictionary<string, object> configDictionary = new Dictionary<string, object>();

    /// <summary>
    /// Configuration values dictionary
    /// </summary>
    public Dictionary<string, object> ConfigDictionary
    {
        get { return configDictionary; }
    }

    private static GPDataDictionary instance;
    public static GPDataDictionary Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new GPDataDictionary();
            }
            return instance;
        }
    }

    // private constructor
    private GPDataDictionary() { }

}  // singleton

Điều này làm việc cho tôi một cách hoàn hảo đó là lý do tại sao tôi giới thiệu nó cho mọi người có thể được giúp đỡ bởi điều này
GeraGamo


-8

Bạn cũng có thể thử và sử dụng bộ nhớ đệm được tích hợp trong ASP MVC:

Thêm thuộc tính sau vào phương thức điều khiển bạn muốn lưu vào bộ đệm:

[OutputCache(Duration=10)]

Trong trường hợp này, ActionResult của điều này sẽ được lưu trong bộ nhớ cache trong 10 giây.

Thêm về điều này ở đây


4
OutputCache là để hiển thị Hành động, câu hỏi liên quan đến dữ liệu lưu trữ không phải là trang.
Coolcoder

nó không có chủ đề nhưng OutputCache cũng lưu trữ dữ liệu cơ sở dữ liệu
Muflix
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.