2017-09-21 97 views
2
partial class Repository<TEntity> : IRepository<TEntity> where TEntity : class 
{ 
} 

我通用倉庫實現了TEntity一組通用的方法,如C#:擴展通用類

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

public TEntity Get(Expression<Func<TEntity, bool>> predicate) 
{ 
    return _context.Set<TEntity>() 
} 

,我可以訪問諸如

Repository<User>().Get(); 

許多倉庫做了同樣的一套操作,所以它是有益的,但現在我想擴展Repository<User>以支持一些額外的行爲。

partial class Repository<User> : IRepository<User> 
{ 
    public user DoMagicFunction() 
    { 
    } 
} 

,這樣我可以使用的存儲庫像

Repository<User>().DoMagicFunction(); 

我怎麼能延長相同的通用類有些Tentity,而不是延長修改它的新的行爲。

我可以做同樣喜歡創建另一個UserRepository以支持新的功能,但該訪問將成爲

UserRepository.DoMagicFunction(); 

,但我希望它像

Repository<User>().DoMagicFunction(); 

回答

6

您可以使用擴展方法

public static class ExtensionMethods { 

    public static User DoMagicFunction(this Repository<User> repository) { 
     // some magic 
     return null; //or another user 
    } 

}

這樣就可以將函數以一種語法好的方式添加到Repository<User>對象中。

如果你想支持它不僅爲User S,但User小號亞類,可以使功能通用:

public static class ExtensionMethods { 

    public static TEntity DoMagicFunction<TEntity>(this Repository<TEntity> repository) 
     where TEntity : User { 
     // some magic 
     return null; //or another TEntity 
    } 

}
+0

那豈不是更好地保持擴展通用以及和應該支持這個擴展的所有實體只是約束TEntity? –

+0

@JanneMatikainen:那更新是關於什麼的:)但是我們可能使用'TUser'的setter,在這種情況下,這是行不通的。因此,不違反繼承原則並不總是可能的。 –

+0

是的,但可能會將TUser更改爲TEntity。 –

1

如果你想擴展任何存儲庫,你可以這樣做。

public static class RepositoryExtension 
{ 
    public static void MagicMethod<TEntity>(this IRepository<TEntity> repo) where TEntity: class 
    { 
     .... 
    } 
} 

對於特定的資源庫(例如用戶庫),您可以使用類似的過程

public static class RepositoryExtension 
{ 
    public static void MagicMethod(this IRepository<User> repo) 
    { 
     .... 
    } 
} 
+0

你寫這個的方式 - 做擴展方法而不是部分類的重點是什麼?我相信OP的問題是僅爲特定類型的TEntity(即用戶)創建方法。 – decPL

1

C#有一個語言功能,稱爲Extension Methods,你可能使用的是他們從.NET框架不知道(例如linq extensions methods)。在不破壞代碼功能的情況下,擴展您的類甚至擴展方法的接口是很常見的。這是你的情況的一個例子。

假設你有一個通用的接口IRepository

public interface IRepository<TEntity> where TEntity : class, IEntity 
{ 
    IQueryable<TEntity> Entities { get; } 
} 

此接口粘附在SOLID principles尤其OI原則。

現在假設IEntity看起來是這樣的:

public interface IEntity 
{ 
    int Id { get; } 
} 

現在,你可以完全想象一個經常被重複使用的擴展方法是這樣的:

public static class RepositoryExtensions 
{ 
    // similar to your MagicFunction 
    public static TEntity GetById<TEntity>(this IRepository<TEntity> repository, int id) 
     where TEntity : class, IEntity 
    { 
     return repository.Entities.Single(entity => entity.Id == id); 
    } 
} 

以類似的方式,你也可以擴展你的Repository

public static class RepositoryExtensions 
{ 
    public static TEntity GenericMagicFunction<TEntity>(this Repository<TEntity> repository) 
    { 
     //do some stuff 
    } 
} 

您可以現在消費,像這樣:

var repository = new Repository<User>(); 
var user = repository.GenericMagicFunction(); 

你也可以限制你的擴展方法:

public static class RepositoryExtensions 
{ 
    public static User DoMagicFunction(this Repository<User> repository) 
    { 
     //do some stuff 
    } 
} 

但這樣做會戰勝它的目的,你可以寧願只是在Repository<User>類實現這一點。

如果您的系統和架構使用Dependency Injection,那麼您的可能是IRepository<User>注入您的消費類。所以我提供的第一個或第二個擴展方法示例最有意義。

+0

擴展方法如何訪問'Repository '上的私有'DataContext'成員以便使用這種方法檢索數據? –

+0

你爲什麼要訪問一個「私人領域」?這是私人的原因;)如果你需要類似的東西,這將暗示一個糟糕的設計,你需要重新考慮你的設計。我甚至不會在像Repository這樣的具體服務上創建擴展方法,但這完全取決於你的架構。我傾向於使用[SOLID]體系結構(https://en.wikipedia.org/wiki/SOLID_(object-oriented_design))。但是你可能會使用一個糟糕的或遺留的架構。微軟也在像IEnumerable 這樣的接口上使用linq擴展方法。 – QuantumHive

+0

你不想訪問一個私有字段 - 但你想訪問一個後代類的受保護字段。我很高興你認爲公開領域允許外部訪問公用事業方法是不好的做法,這樣做暗示需要重新設計。這就是爲什麼我建議不要將內部字段設置爲Internal並在我的答案中重新設計存儲庫:) –

1

擴展方法不是要走的路,因爲實現該方法的代碼只能訪問它們擴展的類的公共/內部成員,並且您可能希望將存儲庫的DataContext設置爲私有。

在我看來,你的方法需要稍微改變。

如果將來想要向通用存儲庫添加Delete方法,但是您有一些永遠不應該被刪除的實體呢?您最終會得到一個類似PurchaseOrder的存儲庫實例,您必須記得永遠不要調用delete,否則您將不得不創建一個Repository<T>的後代,如果調用它,則會拋出InvalidOperationException。這兩者都是糟糕的實現。

而應完全刪除IRepository<T>接口。保留Repository<T>類,但爲每個只有您需要的方法的實體顯式定義一個存儲庫接口。

public class Repository<TKey, TEntity>...... 
{ 
    public TEntity Get<TEntity>(TKey key).... 
    public void Delete(TEntity instance).... 
    ...etc... 
} 

public interface IPurchaseOrderRepository { 
    PurchaseOrder Get(int orderNumber); 
    // Note: No delete is exposed 
} 

MyDependencyInjection.Register<IPurchaseOrderRepository, Repository<PurchaseOrder, int>>(); 

當你需要在你的資料庫的其他方法將它們添加到您的IPurchaseOrderRepository和創造的Repository<T>

public interface IPurchaseOrderRepository { 
    PurchaseOrder Get(int orderNumber); 
    void DoSomethingElse(int orderNumber); 
} 

public class PurchaseOrderRepository: Repository<PurchaseOrder, int> { 
    public void DoSomethingElse(int orderNumber) {.......} 
} 


MyDependencyInjection.Register<IPurchaseOrderRepository, PurchaseOrderRepository>(); 
+1

OP只是想擴展他的界面,這可以通過語言功能來實現(所以我認爲這只是一個技術問題)。您的答案具有更大的範圍,可以開始討論如何使用和應用設計模式,分層體系結構和一些軟件原則,如[SOLID](https://en.wikipedia.org/wiki/SOLID_(object )的原則。我不認爲這取決於我們告訴OP他的設計應該不同,應該開始討論其他一些線索。你的答案是主觀的,我不認爲它是一個很好的答案。 – QuantumHive

+1

雖然這是一個小題目,謝謝你的回答。 –

+0

@QuantumHive我的答案客觀地解釋瞭如何實現他想做的事,即使用一個基類'Repository '類,並且在某些存儲庫接口上具有其他方法。 我還去解釋爲什麼他應該採取這種方法,而不是建議的其他方法。 我的回答您的意見是主觀的,我不認爲這是一個好的意見;-) –

1

Extension method後代是這種情況下的最佳選擇。

注意:我還沒有檢查,但你應該檢查Dependency Injection仍然正常工作。

您可以使用下面的代碼進行測試:

public class Employee 
{ 
} 

public class User 
{ 
} 

public interface IRepo<TEntity> where TEntity : class 
{ 
    TEntity Get(int id); 
    DbSet<TEntity> Get(Expression<Func<TEntity, bool>> predicate); 
    DbContext GetContext(); 
} 

public class Repo<TEntity> : IRepo<TEntity> where TEntity : class 
{ 
    DbContext _context; 
    public TEntity Get(int id) 
    { 
     return _context.Set<TEntity>() 
         .Find(id); 
    } 

    public DbSet<TEntity> Get(Expression<Func<TEntity, bool>> predicate) 
    { 
     return _context.Set<TEntity>(); 
    } 

    public DbContext GetContext() 
    { 
     return _context; 
    } 
} 

public static class RepoExtensions 
{ 
    public static ChangeTracker DoMagic(this Repo<User> userRepo) 
    { 
     return userRepo.GetContext().ChangeTracker; 
    } 
} 

public static class Test 
{ 
    public static void DoTest() 
    { 
     Repo<User> repoUser = new Repo<User>(); 
     repoUser.DoMagic(); 

     Repo<Employee> repoEmployee = new Repo<Employee>(); 
     //repoEmployee.DoMagic(); 
    } 
}