2008-09-19 18 views
37

Tôi có một trang web dựa trên MVC, đang sử dụng mẫu Repository/Service để truy cập dữ liệu. Dịch vụ được viết để sử dụng trong phần lớn các ứng dụng (console, winform và web). Hiện tại, các bộ điều khiển giao tiếp trực tiếp với các dịch vụ. Điều này đã hạn chế khả năng áp dụng bộ nhớ đệm thích hợp.Caching Data Objects khi sử dụng Repository/Service Pattern và MVC

tôi thấy lựa chọn của tôi như sau:

  • Viết một wrapper cho các ứng dụng web, mà thực hiện các IWhatEverService mà làm bộ nhớ đệm.
  • Áp dụng bộ nhớ đệm trong mỗi bộ điều khiển bằng bộ nhớ cache Chế độ xem cho mỗi hành động.
  • Đừng lo lắng về việc lưu trữ dữ liệu và chỉ triển khai OutputCaching cho mỗi Hành động.

Tôi có thể thấy ưu và nhược điểm của từng loại. Điều gì là/nên thực hành tốt nhất là cho bộ nhớ đệm với Kho lưu trữ/Dịch vụ

Trả lời

15

Cách dễ nhất là xử lý bộ nhớ đệm trong nhà cung cấp kho lưu trữ của bạn. Bằng cách đó bạn không phải thay đổi bất kỳ mã nào trong phần còn lại của ứng dụng; nó sẽ được quên lãng thực tế là các dữ liệu được phục vụ ra khỏi một bộ nhớ cache chứ không phải là kho lưu trữ.

Vì vậy, tôi muốn tạo một giao diện mà các bộ điều khiển sử dụng để giao tiếp với phần phụ trợ, và trong việc thực hiện điều này, tôi sẽ thêm logic bộ nhớ đệm. Gói tất cả lên trong một cây cung tốt đẹp với một số DI, và ứng dụng của bạn sẽ được thiết lập để thử nghiệm dễ dàng.

+1

Làm cách nào tôi có thể thực hiện việc này và giữ cho Vị trí lưu trữ và Dịch vụ bất khả tri? Tôi muốn sử dụng lại R/S cho các ứng dụng không phải web khác. – LaptopHeaven

+2

Hmmm .... Tôi không thấy vấn đề ở đây. Bạn có thể tạo một Kho lưu trữ tiêu chuẩn hoặc một CacheableRepository mà có một sự phụ thuộc ICache (bất cứ điều gì có thể được). Chỉ vì bạn đang "lưu bộ nhớ đệm" không có nghĩa là bạn PHẢI sử dụng httpcache – Will

+1

Bạn có thể triển khai ICache sử dụng bộ nhớ cache ASP.NET (trong đó, btw, có thể được sử dụng trong winforms hoặc bất kỳ ứng dụng nào khác), hoặc sử dụng Bộ nhớ cache của nhóm P & P hoặc việc triển khai bộ nhớ cache của chính bạn. – Will

2

Kiểm tra triển khai của tôi về dịch vụ bộ nhớ đệm:
How to cache data in a MVC application
(tôi không muốn lặp lại câu trả lời ở đây ...)
Fell tự do để bình luận!

34

Steve Smith đã thực hiện hai bài đăng trên blog tuyệt vời thể hiện cách sử dụng mẫu CachedRepository của bạn để đạt được kết quả bạn đang tìm kiếm.

Introducing the CachedRepository Pattern

Building a CachedRepository via Strategy Pattern

Trong hai bài viết này, ông chỉ cho bạn cách thiết lập mô hình này và cũng giải thích lý do tại sao nó rất hữu ích. Bằng cách sử dụng mẫu này, bạn sẽ nhận được bộ nhớ đệm mà không cần mã hiện có của bạn nhìn thấy bất kỳ logic bộ nhớ đệm nào. 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.

public class CachedAlbumRepository : IAlbumRepository 
{ 
    private readonly IAlbumRepository _albumRepository; 

    public CachedAlbumRepository(IAlbumRepository albumRepository) 
    { 
     _albumRepository = albumRepository; 
    } 

    private static readonly object CacheLockObject = new object(); 

    public IEnumerable<Album> GetTopSellingAlbums(int count) 
    { 
     Debug.Print("CachedAlbumRepository:GetTopSellingAlbums"); 
     string cacheKey = "TopSellingAlbums-" + count; 
     var result = HttpRuntime.Cache[cacheKey] as List<Album>; 
     if (result == null) 
     { 
      lock (CacheLockObject) 
      { 
       result = HttpRuntime.Cache[cacheKey] as List<Album>; 
       if (result == null) 
       { 
        result = _albumRepository.GetTopSellingAlbums(count).ToList(); 
        HttpRuntime.Cache.Insert(cacheKey, result, null, 
         DateTime.Now.AddSeconds(60), TimeSpan.Zero); 
       } 
      } 
     } 
     return result; 
    } 
} 
+1

Bài viết rất hay! – Flappy

4

Dựa trên câu trả lời cung cấp bởi Brendan, tôi định nghĩa một kho lưu trữ chung cho trường hợp đặc biệt của danh sách tương đối nhỏ mà ít khi thay đổi, nhưng nặng nề đọc.

1. Giao diện

public interface IRepository<T> : IRepository 
    where T : class 
{ 
    IQueryable<T> AllNoTracking { get; } 

    IQueryable<T> All { get; } 
    DbSet<T> GetSet { get; } 

    T Get(int id); 

    void Insert(T entity); 
    void BulkInsert(IEnumerable<T> entities); 
    void Delete(T entity); 
    void RemoveRange(IEnumerable<T> range); 
    void Update(T entity); 
} 

2. Bình thường/không lưu trữ kho

public class Repository<T> : IRepository<T> where T : class, new() 
{ 
    private readonly IEfDbContext _context; 

    public Repository(IEfDbContext context) 
    { 
     _context = context; 
    } 

    public IQueryable<T> All => _context.Set<T>().AsQueryable(); 

    public IQueryable<T> AllNoTracking => _context.Set<T>().AsNoTracking(); 

    public IQueryable AllNoTrackingGeneric(Type t) 
    { 
     return _context.GetSet(t).AsNoTracking(); 
    } 

    public DbSet<T> GetSet => _context.Set<T>(); 

    public DbSet GetSetNonGeneric(Type t) 
    { 
     return _context.GetSet(t); 
    } 

    public IQueryable AllNonGeneric(Type t) 
    { 
     return _context.GetSet(t); 
    } 

    public T Get(int id) 
    { 
     return _context.Set<T>().Find(id); 
    } 

    public void Delete(T entity) 
    { 
     if (_context.Entry(entity).State == EntityState.Detached) 
      _context.Set<T>().Attach(entity); 

     _context.Set<T>().Remove(entity); 
    } 

    public void RemoveRange(IEnumerable<T> range) 
    { 
     _context.Set<T>().RemoveRange(range); 
    } 

    public void Insert(T entity) 
    { 
     _context.Set<T>().Add(entity); 
    } 

    public void BulkInsert(IEnumerable<T> entities) 
    { 
     _context.BulkInsert(entities); 
    } 

    public void Update(T entity) 
    { 
     _context.Set<T>().Attach(entity); 
     _context.Entry(entity).State = EntityState.Modified; 
    } 

}

3.Generic cache kho được dựa trên phi lưu trữ một

public interface ICachedRepository<T> where T : class, new() 
{ 
    string CacheKey { get; } 

    void InvalidateCache(); 
    void InsertIntoCache(T item); 
} 

public class CachedRepository<T> : ICachedRepository<T>, IRepository<T> where T : class, new() 
{ 
    private readonly IRepository<T> _modelRepository; 
    private static readonly object CacheLockObject = new object(); 

    private IList<T> ThreadSafeCacheAccessAction(Action<IList<T>> action = null) 
    { 
     // refresh cache if necessary 
     var list = HttpRuntime.Cache[CacheKey] as IList<T>; 
     if (list == null) 
     { 
      lock (CacheLockObject) 
      { 
       list = HttpRuntime.Cache[CacheKey] as IList<T>; 
       if (list == null) 
       { 
        list = _modelRepository.All.ToList(); 
        //TODO: remove hardcoding 
        HttpRuntime.Cache.Insert(CacheKey, list, null, DateTime.UtcNow.AddMinutes(10), Cache.NoSlidingExpiration); 
       } 
      } 
     } 

     // execute custom action, if one is required 
     if (action != null) 
     { 
      lock (CacheLockObject) 
      { 
       action(list); 
      } 
     } 

     return list; 
    } 

    public IList<T> GetCachedItems() 
    { 
     IList<T> ret = ThreadSafeCacheAccessAction(); 
     return ret; 
    } 

    /// <summary> 
    /// returns value without using cache, to allow Queryable usage 
    /// </summary> 
    public IQueryable<T> All => _modelRepository.All; 

    public IQueryable<T> AllNoTracking 
    { 
     get 
     { 
      var cachedItems = GetCachedItems(); 
      return cachedItems.AsQueryable(); 
     } 
    } 

    // other methods come here 
    public void BulkInsert(IEnumerable<T> entities) 
    { 
     var enumerable = entities as IList<T> ?? entities.ToList(); 
     _modelRepository.BulkInsert(enumerable); 

     // also inserting items within the cache 
     ThreadSafeCacheAccessAction((list) => 
     { 
      foreach (var item in enumerable) 
       list.Add(item); 
     }); 
    } 

    public void Delete(T entity) 
    { 
     _modelRepository.Delete(entity); 

     ThreadSafeCacheAccessAction((list) => 
     { 
      list.Remove(entity); 
     }); 
    } 
} 

Sử dụng một khuôn khổ DI (Tôi đang sử dụng Ninject), người ta có thể dễ dàng xác định nếu một kho lưu trữ nên được lưu trữ hay không:

// IRepository<T> should be solved using Repository<T>, by default 
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)); 

// IRepository<T> must be solved to Repository<T>, if used in CachedRepository<T> 
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).WhenInjectedInto(typeof(CachedRepository<>)); 

// explicit repositories using caching 
var cachedTypes = new List<Type> 
{ 
    typeof(ImportingSystem), typeof(ImportingSystemLoadInfo), typeof(Environment) 
}; 

cachedTypes.ForEach(type => 
{ 
    // allow access as normal repository 
    kernel 
     .Bind(typeof(IRepository<>).MakeGenericType(type)) 
     .To(typeof(CachedRepository<>).MakeGenericType(type)); 

    // allow access as a cached repository 
    kernel 
     .Bind(typeof(ICachedRepository<>).MakeGenericType(type)) 
     .To(typeof(CachedRepository<>).MakeGenericType(type)); 
    }); 

Vì vậy, , đọc từ kho lưu trữ được lưu trữ xong mà không biết về bộ nhớ đệm. Tuy nhiên, việc thay đổi chúng đòi hỏi phải tiêm từ ICacheRepository<> và gọi các phương pháp thích hợp.

+0

Làm tốt lắm! lên bình chọn vì cách tiếp cận chung :) –

Các vấn đề liên quan