首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Id为c#的泛型类

Id为c#的泛型类
EN

Stack Overflow用户
提问于 2017-11-27 12:55:19
回答 2查看 3K关注 0票数 2

我不确定这是否可能,但我已经启动了一个新项目,我正在努力清理我与DbContext的工作方式。我试图通过使用泛型来做到这一点。第一部分很简单。我创建了一个存储库类,如下所示:

代码语言:javascript
复制
public class Repository<T> where T : class
{
    protected readonly DbContext _dbContext;
    protected readonly DbSet<T> _dbEntitySet;

    public Repository(InteractiveChoicesContext dbContext)
    {
        _dbContext = dbContext;
        _dbEntitySet = dbContext.Set<T>();
    }

    /// <summary>
    /// Gets all the entities
    /// </summary>
    /// <param name="includes">Option includes for eager loading</param>
    /// <returns></returns>
    public IQueryable<T> List(params string[] includes)
    {
        IQueryable<T> query = _dbEntitySet;
        foreach (var include in includes)
            query = query.Include(include);
        return query;
    }

    /// <summary>
    ///     Creates an entity
    /// </summary>
    /// <param name="model"></param>
    public void Create(T model) => _dbEntitySet.Add(model);

    /// <summary>
    ///     Updates an entity
    /// </summary>
    /// <param name="model"></param>
    public void Update(T model) => _dbEntitySet.Update(model);

    /// <summary>
    /// Removes an entity
    /// </summary>
    /// <param name="model"></param>
    public void Remove(T model) => _dbContext.Entry<T>(model).State = EntityState.Deleted;

    /// <summary>
    /// Saves the database context changes
    /// </summary>
    /// <returns></returns>
    public async Task SaveChangesAsync()
    {
        await _dbContext.SaveChangesAsync();
    }

现在,我想创建一个通用的服务类,它可以使用所有CRUD方法。我试着像这样创建它:

代码语言:javascript
复制
public class Service<T> : Service<T, int> where T : class
{
    public Service(InteractiveChoicesContext dbContext) : base(dbContext)
    {
    }
}

public class Service<T, TKey>: Repository<T> where T: class
{
    public Service(InteractiveChoicesContext dbContext) : base(dbContext)
    {
    }
    public virtual async Task<IEnumerable<T>> ListAsync() => await List().ToListAsync();
    public virtual async Task<T> GetAsync(TKey id) => await List().SingleOrDefaultAsync(m => m.Id == id);
    public virtual async Task<T> CreateAsync(T model)
    {
        Create(model);
        await SaveChangesAsync();
        return model;
    }
    public virtual async Task<T> UpdateAsync(T model)
    {
        Update(model);
        await SaveChangesAsync();
        return model;
    }
    public virtual async Task<bool> DeleteAsync(TKey id)
    {
        var model = await GetAsync(id);
        Remove(model);
        await SaveChangesAsync();
        return true;
    }
}

我使用TKey来指定基本类型,但问题在于GetAsync方法。显然,它现在不知道对象是什么,所以它不会编译。我知道这个错误:

'T‘不包含'Id’的定义,也找不到接受'T‘类型的第一个参数的扩展方法'Id’(您缺少使用指令还是程序集引用?)

现在我知道这是因为我没有说T是什么。看看IdentityFramework是如何处理这个问题的,他们使用IUser,我也可以这样做,但这意味着我的所有实体都必须实现这个接口。如果我创建了这样一个接口:

代码语言:javascript
复制
public interface IEntity<out TKey>
{
    TKey Id { get; }
}

然后更新我的所有实体来实现这样的接口:

代码语言:javascript
复制
public class Question : IEntity<int>
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; }
    [Required, MaxLength(100)] public string Text { get; set; }
    public bool MultipleChoice { get; set; }

    public OutcomeGroup Group { get; set; }
    public IEnumerable<Answer> Answers { get; set; }
}

这似乎很管用。我想我只有一个问题。这是最好的方法吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-11-27 13:10:13

你可以拥有这样的实体:

代码语言:javascript
复制
public interface IEntity<out TKey>
{
    TKey Id { get; }
}

public class Question<TPrimaryKey> : IEntity<int>
{
     public TPrimaryKey Id { get; set; }
}

然后,您的存储库如下:

代码语言:javascript
复制
public class Repository<TEntity, TPrimaryKey> where TEntity: IEntity<TPrimaryKey>
{
    protected readonly DbContext _dbContext;
    protected readonly DbSet<TEntity> _dbEntitySet;

    public Repository(InteractiveChoicesContext dbContext)
    {
        _dbContext = dbContext;
        _dbEntitySet = dbContext.Set<TEntity>();
    }
}

在我看来,这是最好的方法来做到这“通用”。

票数 4
EN

Stack Overflow用户

发布于 2017-11-27 13:12:37

那么,您需要一些方法来指向ID / key属性。

一些需要探讨的想法:

  • 正如您已经写过的,创建一个表示Id属性的所有实体都要实现的接口。
  • (额外的)您可以有一个基本实体类,它实现上述接口,并具有所需的EF注释([DatabaseGenerated(DatabaseGeneratedOption.Identity)]),以使事情变得更简单。
  • 使用反射在GetAsync方法中按名称搜索Id属性(显然,代码不会像现在这样简单)
  • 创建自定义或使用现有属性(例如,KeyAttribute并将其添加到实体中以标记Id属性并使用反射来查找属性

在所有这些解决方案中,我会选择第一个或第二个解决方案,因为它们似乎是最“清晰”的。如果您决定进行反射,那么我将选择第二个(其中有一个键定义属性),但这仍然迫使您遍历所有实体并进行更改。如果您有很多实体,那么基于文本的属性搜索可能是最快的,但显然,任何没有"Id“属性的实体都会引发问题。

显然,YMMV取决于您实际想要完成的任务。请注意,您可以尝试并组合反射解决方案(按名称查找,如果找不到search属性)。

还请记住,虽然反射是一个强大的工具,但它会对性能产生影响。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47511499

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档