c# - 如何从 T 的类型中获取 T 的类型?

标签 c# generics asp.net-core-mvc

我正在尝试将我的组织使用的框架重写为 .Net Core;此时特别是通用存储库。 我被困在以下问题上。

我们有一个 BaseEntity 定义如下:

public abstract class BaseEntity<T> : IBaseEntity<T>
{
    [Key]
    public virtual T Id { get; set; }
}

它继承自接口(interface):

public interface IBaseEntity<T>
{
    [Key]
    T Id { get; set; }
}

我这样定义一个实体:

public class Employee : BaseEntity<int>
{
    public string OfficeBureau { get; set; }
    public string Supervisor { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Notes { get; set; }

    // TODO:
    // public virtual ICollection<Case> Cases { get; set; }
}

应用上下文:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<Employee> Employees { get; set; }
}

repo 界面:

public interface IGenericRepository<T>
    where T : BaseEntity<T>
{
    IQueryable<T> GetAll();     // No need to async IQueryable
    Task<T> GetAsync(int id);
    Task<T> InsertAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);

    #region Possible TODOs:
    // Should this one go in the Service Layer?
    // Task<IEnumerable<T>> FindBy(Expression<Func<T, bool>> predicate);

    //Task AddRange(IEnumerable<T> entities);
    //Task RemoveRange(IEnumerable<T> entities);
    #endregion

    Task SaveAsync();
}

repo 实现:

public class GenericRepository<T> : IGenericRepository<T>
    where T: BaseEntity<T>
{
    private readonly ApplicationDbContext _context;
    private DbSet<T> _entities;

    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
        _entities = context.Set<T>();
    }

    // No need to async IQueryable
    public IQueryable<T> GetAll() => _entities.AsQueryable();

    public Task<T> GetAsync(int id)
    {
        throw new NotImplementedException();
    }

现在看一下签名:

public Task<T> GetAsync(int id)

这是我的困境。 将所有实体设置为从 BaseEntity 继承有什么意义,这样如果我们在此处搜索 GetById 或 GetAsync 的硬编码 int 类型,我们就可以动态获得 int 或 guid 的 id?

这是一个常见的模式吗?这是代码原作者的短板吗?还是我在这个模式中遗漏了什么?
如果这是一个缺点,有没有办法通过反射和搜索来动态获取实体 ID 的类型?如果是这样,性能会受到影响吗?

我是否应该取消 BaseEntity 上 id 类型的泛型,并强制框架的用户在使用该框架的整个应用程序中始终使所有实体具有相同类型的 ID?

除此之外,通用 repo 的主要目的是成立的。不必为每个实体使用相同的方法编写 repo 协议(protocol)。 在这里感谢任何建议。

最佳答案

您的问题是您对“T”是什么感到困惑。您已经完成了大部分工作,但是您正在代码中途切换 T 的“上下文”。一开始它指的是“Id”的类型,然后你把它变成“Model”的类型

试试这个。我将做一些重命名以使其更清晰而不是使用 T。

您的基本实体应如下所示:

public interface IBaseEntity<Key>
{
    [Key]
    Key Id { get; set; }
}

员工本质上应该是同一件事:

public class Employee : BaseEntity<int>
{
    public string OfficeBureau { get; set; }
    public string Supervisor { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Notes { get; set; }

    // TODO:
    // public virtual ICollection<Case> Cases { get; set; }
}

现在是您的通用存储库。现在仔细看下面的代码。这是你出错的地方。

public interface IGenericRepository<T, Key>
    where T : BaseEntity<Key>
{
    IQueryable<T> GetAll();     // No need to async IQueryable
    Task<T> GetAsync(Key id);
    Task<T> InsertAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);
}

所以你注意到我现在传递了两个泛型类型。一个是此存储库调用的模型类型,另一个是模型使用的 key 类型。

您的通用存储库应如下所示

public class GenericRepository<T, Key> : IGenericRepository<T, Key>
{
    private readonly ApplicationDbContext _context;
    private DbSet<T> _entities;

    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
        _entities = context.Set<T>();
    }

    // No need to async IQueryable
    public IQueryable<T> GetAll() => _entities.AsQueryable();

    public Task<T> GetAsync(Key id)
    {
        throw new NotImplementedException();
    }
}

然后当您创建一个实际的存储库时,它可能如下所示。

public class UserRepository : GenericRepository<User, int>, IGenericRepository<User, int>, IUserRepository
{
...
}

关于c# - 如何从 T 的类型中获取 T 的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42374646/

相关文章:

c# - 转换为十六进制?

c# - 将子类添加到 List<parent> 时,特定于子类的数据是否丢失?

generics - Kotlin 中的 "Bidirectional"类型投影/方差?

c# - 使用非空构造函数初始化泛型

entity-framework - 在 EF Core 中,如何执行分组并选择逗号分隔值列表?

asp.net-core - ASP.NET Core Identity - 扩展密码哈希器

c# - OracleBulkCopy内存泄漏(OutOfMemory异常)

java - 在泛型类中使用类的子类型

c# - ASP.NET Core 相当于 ASP.NET MVC 5 的 HttpException

c# - 无法使用全文搜索来搜索正文