C# Id为c的泛型类#

C# Id为c的泛型类#,c#,entity-framework,generics,C#,Entity Framework,Generics,我不确定这是否可行,但我已经开始了一个新项目,我正在尝试清理使用DbContext的方式。我试图通过使用泛型来做到这一点。 第一部分很简单。我创建了一个存储库类,如下所示: public class Repository<T> where T : class { protected readonly DbContext _dbContext; protected readonly DbSet<T> _dbEntitySet; public Rep

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

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();
    }
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;
    }
}
public interface IEntity<out TKey>
{
    TKey Id { get; }
}
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; }
}
公共类存储库,其中T:class
{
受保护的只读DbContext\u DbContext;
受保护的只读数据库集_dbEntitySet;
公共存储库(InteractiveEchoiceContext dbContext)
{
_dbContext=dbContext;
_dbEntitySet=dbContext.Set();
}
/// 
///获取所有实体
/// 
///选项包括快速加载
/// 
公共可查询列表(参数字符串[]包括)
{
IQueryable查询=\u dbEntitySet;
foreach(包含在包含中的var)
query=query.Include(包含);
返回查询;
}
/// 
///创建一个实体
/// 
/// 
公共void Create(T model)=>\u dbEntitySet.Add(model);
/// 
///更新实体
/// 
/// 
public void Update(T model)=>\u dbEntitySet.Update(model);
/// 
///删除实体
/// 
/// 
public void Remove(T model)=>\u dbContext.Entry(model.State=EntityState.Deleted;
/// 
///保存数据库上下文更改
/// 
/// 
公共异步任务SaveChangesAsync()
{
wait_dbContext.saveChangesSync();
}
现在我想创建一个通用的服务类,它可以使用所有CRUD方法。我试着这样创建它:

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();
    }
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;
    }
}
public interface IEntity<out TKey>
{
    TKey Id { get; }
}
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; }
}
公共类服务:其中T:class的服务
{
公共服务(InteractiveEchoicesContext dbContext):基本(dbContext)
{
}
}
公共类服务:存储库,其中T:class
{
公共服务(InteractiveEchoicesContext dbContext):基本(dbContext)
{
}
公共虚拟异步任务ListAsync()=>等待列表().ToListAsync();
公共虚拟异步任务GetAsync(TKey id)=>Wait List().SingleOrDefaultAsync(m=>m.id==id);
公共虚拟异步任务CreateAsync(T模型)
{
创建(模型);
等待SaveChangesSync();
收益模型;
}
公共虚拟异步任务UpdateAsync(T模型)
{
更新(模型);
等待SaveChangesSync();
收益模型;
}
公共虚拟异步任务DeleteAsync(TKey id)
{
var模型=等待GetAsync(id);
移除(模型);
等待SaveChangesSync();
返回true;
}
}
我正在使用TKey指定基元类型,但问题在于GetAsync方法。显然,此时它不知道对象是什么,因此不会编译。我得到以下错误:

“T”不包含“Id”的定义,并且找不到接受类型为“T”的第一个参数的扩展方法“Id”(是否缺少using指令或程序集引用?)

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

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();
    }
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;
    }
}
public interface IEntity<out TKey>
{
    TKey Id { get; }
}
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; }
}
公共接口的可扩展性
{
TKey Id{get;}
}
然后更新我的所有实体以实现如下界面:

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();
    }
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;
    }
}
public interface IEntity<out TKey>
{
    TKey Id { get; }
}
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; }
}
公共课问题:科学性
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]公共int-Id{get;set;}
[必需,MaxLength(100)]公共字符串文本{get;set;}
公共布尔多重切迹{get;set;}
公共结果组组{get;set;}
公共IEnumerable答案{get;set;}
}
这似乎有效。我想我只有一个问题。
这是最好的方法吗?

您可以拥有如下实体:

public interface IEntity<out TKey>
{
    TKey Id { get; }
}

public class Question<TPrimaryKey> : IEntity<int>
{
     public TPrimaryKey Id { get; set; }
}
公共接口的可扩展性
{
TKey Id{get;}
}
公共课问题:科学性
{
公共TPrimaryKey Id{get;set;}
}
然后,您的存储库如下所示:

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>();
    }
}
公共类存储库,其中tenty:ienty
{
受保护的只读DbContext\u DbContext;
受保护的只读数据库集_dbEntitySet;
公共存储库(InteractiveEchoiceContext dbContext)
{
_dbContext=dbContext;
_dbEntitySet=dbContext.Set();
}
}

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

好吧,您需要一些指向ID/key属性的方法

需要探讨的一些想法:

  • 正如您已经编写的那样,创建一个所有实体都将实现的接口,该接口表示Id属性
  • (额外)您可以拥有一个基本实体类,它实现了上述接口,并具有所需的EF注释(
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    ),以简化操作
  • GetAsync
    方法中使用反射按名称搜索Id属性(显然代码不会像现在这样简单)
  • 创建自定义属性或使用现有属性(例如,将其添加到实体中)以标记Id属性,并使用反射(与之前的想法一样)来查找属性
在所有这些解决方案中,我会选择第一个或第二个,因为它们看起来最“清晰”。如果您决定进行反思,那么我会选择第二个(您有一个关键定义属性)-但这仍然会迫使您检查所有实体并进行更改。如果您有许多实体,则基于文本的属性搜索可能是最快的-但显然,任何没有名为“Id”的属性的实体都会导致问题

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


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

如果您的代码正常工作,而您只是询问这是否正常