我不确定这是否可行,但我已经开始了一个新项目,我正在努力清理我使用 DbContext 的方式。我试图通过使用泛型来做到这一点。 第一部分很简单。我创建了一个 Repository 类,如下所示:
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方法的通用服务类。我试图像这样创建它:
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'的定义,并且没有扩展方法'Id'可以找到接受类型'T'的第一个参数(你是否缺少using指令或汇编引用?)
现在我知道这是因为我没有说 T 是什么。 看看 IdentityFramework 如何处理这个问题,他们使用 IUser ,我也可以这样做,但这意味着我的所有实体都必须实现此接口。 如果我创建了这样的界面:
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; }
}
这似乎有效。我想我只有一个问题。 这是最好的方法吗?
答案 0 :(得分:3)
你可以拥有这样的实体:
public interface IEntity<out TKey>
{
TKey Id { get; }
}
public class Question<TPrimaryKey> : IEntity<int>
{
public 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>();
}
}
在我看来,这是做到这一点的最佳方式&#34; Generic&#34;。
答案 1 :(得分:0)
嗯,你需要一些方法指向ID / key属性。
要探索的一些想法:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
)以简化操作GetAsync
方法中按名称搜索Id属性(显然代码不会像现在这样简单)KeyAttribute
)并将其添加到您的实体以标记Id属性并使用反射与之前的想法一起查找属性在所有这些解决方案中,我会选择第一个或第二个解决方案,因为它们似乎最“清晰”。如果您决定进行反思,那么我会选择第二个(您有一个键定义属性) - 但这仍然会迫使您浏览所有实体并进行更改。如果您有很多实体,那么基于文本的属性搜索可能是最快的 - 但显然任何没有名为“Id”的属性的实体都会导致问题。
显然YMMV取决于你真正想要完成的事情。请注意,您可以尝试组合反射解决方案(按名称查找,如果找不到按键属性搜索)。
还要记住,虽然反射是一种强大的工具,但它会对性能产生影响。