C#:扩展泛型类

时间:2017-09-21 13:14:26

标签: c# generics

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

如何为某些Tentity扩展相同的泛型类以扩展新行为而不是修改它。

我可以像创建另一个UserRepository来支持新功能一样,但访问者将成为

UserRepository.DoMagicFunction();

但我希望它像

Repository<User>().DoMagicFunction();

5 个答案:

答案 0 :(得分:8)

您可以使用扩展方法

public static class ExtensionMethods {

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

}

因此,这将以一种语法上很好的方式将函数添加到Repository<User>个对象。

如果您不仅要支持User s,而且支持支持User s 的子类,则可以使该函数具有通用性:

public static class ExtensionMethods {

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

}

答案 1 :(得分: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) 
    {
        ....
    }
}

答案 2 :(得分: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>。因此,我提供的第一个或第二个扩展方法示例最有意义。

答案 3 :(得分:1)

扩展方法不是可行的方法,因为实现该方法的代码只能访问它们扩展的类的公共/内部成员,并且您可能希望存储库的DataContext是私有的。

在我看来,你的方法需要稍微改变。

如果您将来想要将Delete方法添加到通用存储库,但是您有一些永远不应删除的实体,该怎么办?您最终会得到一个类似于 @NgModule({ imports: [BrowserModule, FormsModule, HttpModule, AppRoutingModule, PaginationModule.forRoot(), ModalModule.forRoot()], declarations: [AppComponent, ConfigurationsComponent, CreateConfigurationComponent, PageNotFoundComponent], bootstrap: [AppComponent], providers: [ConfigurationsService, CreateConfigurationService, ImageMapper] }) export class AppModule { } 的存储库实例,您必须记住它永远不会调用delete,否则您将不得不创建一个PurchaseOrder的后代抛出{ {1}}如果被叫。两者都是糟糕的实现。

相反,您应该完全删除Repository<T>界面。保留您的InvalidOperationException类,但为每个只有您需要的方法的实体明确定义存储库接口。

IRepository<T>

当您需要在存储库中使用其他方法时,将它们添加到您的IPurchaseOrderRepository并创建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>>();

答案 4 :(得分: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();
    }
}