2011-04-27 60 views
0

我想弄清楚如何使用新的EF代碼優先的東西,我很難找出如何適應Include功能到一個半通用的存儲庫類。 (我說半泛型,因爲類不是泛型的,只是方法,所以我有一個倉庫,它包裝了一個聚合體,本質上,你可以與所有屬於該聚合體的實體進行交互,而不僅僅是一個實體。 )包含在(半)通用庫

我的情況是我已經有一個孩子要加載總是需求,然後是僅可選加載其他的孩子,我想包括我的資源庫方法的簡單的布爾參數的實體。像這樣:

public S GetByID<S>(int entityID, bool loadChildren = false) where S : class 
{ 
    DbSet<S> set = _context.Set<S>(); 

    if (loadChildren) 
    set = FullInclude<S>(set); 
    else 
    set = DefaultInclude<S>(set); 

    return set.Find(entityID); 
} 

protected virtual DbSet<S> DefaultInclude<S>(DbSet<S> set) where S : class 
{ 
    // base implementation just returns the set 
    // derived versions would attach Includes to the set before returning it 
    return set; 
} 

protected virtual DbSet<S> FullInclude<S>(DbSet<S> set) where S : class 
{ 
    // base implementation just returns the set 
    // derived versions would attach Includes to the set before returning it 
    return set; 
} 

這是我的基地倉庫,我希望能夠覆蓋在派生類中那些XyzInclude方法。但是我需要用一個非泛型實現來覆蓋它們,而這顯然不適用於語言級別。

這對Linq2Sql來說非常簡單,因爲我只需配置一個DataLoadOptions對象並將其附加到上下文。與API關閉DbSet我很難做到這一點。我正在尋找關於簡單實施戰略模式或類似的建議。

編輯:我想我的問題的實質是真正瞭解重寫泛型方法與非通用衍生版本。我正在尋找一種能讓我做到這一點的模式或技術。擴展方法是我正在研究的一件事,但如果有人擁有一個,則更喜歡更「純」的解決方案。

回答

0

只是想我會用我結束了與解決方案討論這個。我的存儲庫不是完全通用的,但是所有的方法都是,所以我需要一種方法來存儲包含任何實體類型的內容,並且能夠在方法被調用時將其拉出。

我從拉吉斯拉夫的答案here借來的,我可能會重新審視這種設計,只是對導出庫定義的每個單獨的方法定義自己的包括因爲有什麼包含足夠的不同組合,什麼不能包括重複一些定義相同代碼的代碼可能值得。但無論如何,這是當前設計和它的作品...

基地倉庫:

public abstract class Repository : IQueryableRepository, IWritableRepository 
{ 
    private readonly DbContext _context; 
    private readonly Dictionary<Type, LambdaExpression[]> _includes = new Dictionary<Type, LambdaExpression[]>(); 

    protected Repository(DbContextBase context) 
    { 
    _context = context; 
    RegisterIncludes(_includes); 
    } 

    protected abstract void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes); 

    protected S GetSingle<S>(Expression<Func<S, bool>> query, bool getChildren = false) where S : class 
    { 
    IQueryable<S> entities = _context.Set<S>().AsNoTracking(); 

    if (query != null) 
     entities = entities.Where(query); 

    entities = ApplyIncludesToQuery<S>(entities, getChildren); 

    return entities.FirstOrDefault(); 
    } 

    private IQueryable<S> ApplyIncludesToQuery<S>(IQueryable<S> entities, bool getChildren) where S : class 
    { 
    Expression<Func<S, object>>[] includes = null; 

    if (getChildren && _includes.ContainsKey(typeof(S))) 
     includes = (Expression<Func<S, object>>[])_includes[typeof(S)]; 

    if (includes != null) 
     entities = includes.Aggregate(entities, (current, include) => current.Include(include)); 

    return entities; 
    } 
} 

衍生庫只需要定義自己的包括在一個地方,然後當你調用查詢方法你只需指定是否希望孩子包括在內(請參閱上面的getChildren)。

public class DerivedRepository : Repository 
{ 
    public DerivedRepository(DbContext context) 
    : base(context) { } 

    protected override void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes) 
    { 
    includes.Add(typeof(ParentType), new Expression<Func<ParentType, object>>[] { 
     p => p.SomeChildReference.SomeGrandchild, 
     p => p.SomeOtherChildReference 
    }); 
    } 
} 
+0

這是一個有趣的方法,但我不太喜歡的是該包含在一個位置。我與它的問題是,根據您可能需要不同的情況,例如同時獲得分頁客戶的列表,你不想要個人資料,發佈和發票信息...但是對於用戶詳細信息頁面,您確實需要所有信息 – 2011-07-05 05:31:38

+0

是的,這是我在腦中辯論的一個難題試圖找出最好的方法來做到這一點。我現在用最簡單的選項(在單個地方定義它們),但是演進設計將使每個方法定義它們自己的包含,並將其作爲參數發送到基礎存儲庫,而不是它作爲類級變量。拉蒂斯拉夫的回答,我與上面的鏈接涵蓋了這一點。 – sliderhouserules 2011-08-23 17:29:09

0

不可能的,除非你把課完全通用的,:

public class BaseRepo<S> 
{ 
    protected virtual DbSet<S> DefaultInclude(DbSet<S> set) {return set;} 
} 

public class ProductRepo : BaseRepo<Product> 
{ 
    protected override DbSet<Product> DefaultInclude(DbSet<Product> set) 
    { 
     return set.Include("..."); 
    } 
} 
+0

我更尋找與上述不同的解決方案。我不能使存儲庫完全通用。我在前一個化身中擁有完全通用的Repository,並且出於各種原因必須採用非通用方向。另外,我需要爲任何實體設置「包含」的功能。我的存儲庫設計不是許多人一起使用的半典型的單一存儲庫實體。但感謝您的答案。我很感激。 – sliderhouserules 2011-04-27 15:56:20

+0

我只能說你要求的東西在你目前的設計中根本不可能。你可能想重新思考整個概念。 – jeroenh 2011-04-27 18:26:03