簡體   English   中英

C#:擴展泛型類

[英]C# : Extending Generic class

partial class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
}

我的通用存儲庫為TEntity實現了一組通用的方法

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();

如何為Some Tentity擴展相同的泛型類以擴展新行為而不是修改它。

我可以像創建另一個UserRepository來支持新功能一樣,但訪問者將成為

UserRepository.DoMagicFunction();

但我希望它像

Repository<User>().DoMagicFunction();

您可以使用擴展方法

public static class ExtensionMethods {

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

}

因此,這將以一種語法上很好的方式將函數添加到Repository<User>對象。

如果您不僅要為User提供支持,還要User的子類提供支持 ,您可以使該功能通用:

public static class ExtensionMethods {

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

}

C#有一個名為Extension Methods的語言功能,你可能在不知道的情況下從.NET框架中使用它們(例如linq擴展方法 )。 在不破壞代碼功能的情況下,使用擴展方法擴展類甚至接口是很常見的。 以下是您案例的示例。

假設您有一個通用的IRepository接口:

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

該接口遵循SOLID原則尤其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>類中實現它。

如果您的系統和體系結構使用依賴注入 ,那么您可能正在向您的消費類注入IRepository<User> 因此,我提供的第一個或第二個擴展方法示例最有意義。

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

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) 
    {
        ....
    }
}

擴展方法不是可行的方法,因為實現該方法的代碼只能訪問它們擴展的類的公共/內部成員,並且您可能希望存儲庫的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>();

擴展方法是這種情況的最佳選擇。

注意:我沒有檢查但你應該檢查依賴注入仍然正常工作。

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

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();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM