如何在接口中抽象一个 EntityFramework Core DBSet<T>?



我使用的是EF Core 6,我的DbContext看起来像这样:

public class SpaceBattlesDatabase: DbContext, ISpaceBattlesDataBase
{
public DbSet<Building> Buildings => Set<Building>();
// + other irrelevant code
}

我想用一个接口抽象它,在一个干净的体系结构的中心层中使用。

public interface ISpaceBattlesDataBase
{
public IQueryable<Building> Buildings { get; }
}

但是编译器警告我:

错误CS0738 'SpaceBattlesDatabase'没有实现接口成员'ISpaceBattlesDataBase.Buildings'。"SpaceBattlesDatabase。建筑"不能实现"ISpaceBattlesDataBase。Buildings',因为它没有匹配的返回类型'IQueryable'。

但是根据文档和元数据,DbSet<TEntity>应该实现IQueryable<TEntity>..

IEnumerable<T>也不工作。我错过什么了吗?

如何将DbSet抽象到接口中使用?

谢谢

我错过了什么吗?

c#不支持接口实现的返回类型协方差(我想说Eric Lippert在这里给出的解释可以解释为什么)。在这种特殊情况下,您可以使用显式接口实现:
public class SpaceBattlesDatabase: DbContext, ISpaceBattlesDataBase
{
public DbSet<Building> Buildings => Set<Building>();
IQueryable<Building> ISpaceBattlesDataBase.Buildings => Buildings;
}
public interface ISpaceBattlesDataBase
{
IQueryable<Building> Buildings { get; }
}

由于c# 9特性协变返回类型,从接口继承抽象基类的另一种方法:

public class SpaceBattlesDatabase: SpaceBattlesDataBase
{
public override DbSet<Building> Buildings => Set<Building>();
}
public abstract class SpaceBattlesDataBase : DbContext, ISpaceBattlesDataBase
{
public abstract IQueryable<Building> Buildings { get; }
}
public interface ISpaceBattlesDataBase
{
IQueryable<Building> Buildings { get; }
}

在这些情况下我使用不同的方法。我首先创建一个ContextFactory:

public class ContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[]? args = null)
{
return new ApplicationDbContext();
}
}

然后我创建一个IDataService接口:

public interface IDataService<T>
{
Task<IEnumerable<T>> GetAll();
Task<T> Get(int id);
Task<T> Create(T entity);
Task<bool> CreateMultiple(List<T> entity);
Task<bool> Delete(T entity);
Task<T> Update(T entity);
}

,并像这样实现它:

public class DataService<T> : IDataService<T> where T : class
{
private readonly ContextFactory contextFactory;
public DataService(ContextFactory contextFactory)
{
this.contextFactory = contextFactory;
}
public async Task<T> Create(T entity)
{
using ApplicationDbContext context = contextFactory.CreateDbContext();
EntityEntry<T> createdResult = await context.Set<T>().AddAsync(entity);
await context.SaveChangesAsync();
return createdResult.Entity;
}
public async Task<bool> CreateMultiple(List<T> entity)
{
using ApplicationDbContext context = contextFactory.CreateDbContext();
await context.Set<T>().AddRangeAsync(entity);
await context.SaveChangesAsync();
return true;
}
public async Task<bool> Delete(T entity)
{
try
{
using ApplicationDbContext context = contextFactory.CreateDbContext();
context.Set<T>().Remove(entity);
await context.SaveChangesAsync();
return true;
}
catch (Exception)
{
return false;
}
}
public async Task<T> Get(int id)
{
using ApplicationDbContext context = contextFactory.CreateDbContext();
return await context.Set<T>().FindAsync(id) ?? await new ValueTask<T>();
}
public async Task<IEnumerable<T>> GetAll()
{
using ApplicationDbContext context = contextFactory.CreateDbContext();
return await context.Set<T>().ToListAsync();
}
public async Task<T> Update(T entity)
{
using ApplicationDbContext context = contextFactory.CreateDbContext();
context.Set<T>().Update(entity);
await context.SaveChangesAsync();
return entity;
}
}

您可以根据需要修改它。最后,对于Building模型,我将执行如下操作:

public class BuildingCrudService
{
private readonly IDataService<Building> dataService;
public BuildingCrudService()
{
dataService = new DataService<Building>(new ContextFactory());
}
/// <summary>
/// Adds a building to the database.
/// </summary>
/// <param name="building"></param>
/// <returns>List of buildings.</returns>
public async Task<List<Building>> AddBuildingAsync(Building building)
{
await dataService.Create(building);
return await ListBuildingsAsync();
}
/// <summary>
/// Deletes the building from the database.
/// </summary>
/// <param name="id"></param>
/// <returns>True if deleted, else false.</returns>
public async Task<bool> DeleteBuildingAsync(int id)
{
Building b = await GetBuildingByIDAsync(id);
return await dataService.Delete(b);
}
/// <summary>
/// Fetches the list of all the buildings from the database.
/// </summary>
/// <returns>List of buildings.</returns>
public async Task<List<Building>> ListBuildingsAsync()
{
return (List<Building>)await dataService.GetAll();
}
/// <summary>
/// Searches for a building that matches the id.
/// </summary>
/// <param name="id"></param>
/// <returns>The building that matches the id.</returns>
public async Task<Building> GetBuildingByIDAsync(int id)
{
return await dataService.Get(id);
}
/// <summary>
/// Updates an existing building to the database.
/// </summary>
/// <param name="building"></param>
/// <returns>The updated building.</returns>
public async Task<Building> UpdateBuildingAsync(Building building)
{
Building b = await GetBuildingByIDAsync(building.BuildingId);
b.Name = building.Name;
// Also update other properties
return await dataService.Update(b);
}
}

相关内容