假设我有一个数据库支持的菜单,使用带有“Item”和“Category”实体的Entity Framework。
我有两个类(ItemMappingState和CategoryMappingState)几乎完全相同并执行相同的操作(管理项目和类别的外部映射),但是一个使用Item,另一个使用Category。类之间唯一真正的区别(以及阻止我根据接口使管理器类通用的原因)是Item有一个名为MappedItems的属性,Category有一个名为MappedCategories的属性。
由于在Linq-To-Entities查询中使用了这些属性,我认为我不能创建任何类型的公共接口,因为接口方法/属性将无法在Linq-To-Entities中正确转换在运行时查询(如果我错了,请纠正我)。
我真的很烦我,我有两个相当大的类几乎相同,一行一行,我不能将它重构为一个适用于这两种类型的类,因为它们是实体类型。我想我可能会为不同的行添加运行时类型检查并转换为具体类型,但我仍然需要一个接口来处理所有其他相同的属性。
这里的底线是,是否可以为EF类型创建一个公共接口,并且实际上能够在Linq-To-Entities查询中使用它们。
例如:
interface IItems {
Guid ID {get;set;}
ICollection<IID> Items {get;set;}
}
interface IID {
Guid ID {get;set;}
}
class A: IItems {
public Guid ID {get;set;}
public ICollection<AItem> Items {get;set;} //Navigation property
}
class AItem: IID {
public Guid ID {get;set;}
}
class B: IItems {
public Guid ID {get;set;}
public ICollection<BItem> Items {get;set;} //Navigation property
}
class BItem: IID {
public Guid ID {get;set;}
}
class AManager {
public IEnumerable<Guid> GetItemIDs(Guid id) {
return DbContext.Set<A>().FirstOrDefault(x => x.ID == id).Select(x => x.Items.ID);
}
}
class BManager {
public IEnumerable<Guid> GetItemIDs(Guid id) {
return DbContext.Set<B>().FirstOrDefault(x => x.ID == id).Select(x => x.Items.ID);
}
}
如您所见,AManager和BManager几乎完全相同。他们做同样的事情;它们具有相同的签名,它们使用相同的属性名称有效地运行相同的查询。问题是他们从两个不同的表中提取数据,因此在从DbContext获取Set时我必须使用具体类型。由于必须如此,因此无法针对像IID这样的公共接口编写查询。我想做的是这样一个经理:
class Manager<TQueryInterface,TEntity> where TQueryInterface:IItems where TEntity:IItems {
public IEnumerable<Guid> GetItemIDs() {
return DbContext.Set<TQueryInterface,TEntity>().FirstOrDefault(x => x.ID == id).Select(x => x.Items.ID); //query written against TQueryInterface, but runs against table for TEntity in the database
}
}
或者只是
class IItemsManager<TEntity>
where TEntity:IItems
{
public IEnumerable<Guid> GetItemIDs()
{
//query written against IItems, but runs against table for TEntity in the database
return DbContext
.Set<IItems,TEntity>()
.FirstOrDefault(x => x.ID == id)
.Select(x => x.Items.ID);
}
}
这是一个简单的案例,但证明了这个问题。似乎没有办法编写结构相同但作用于EF中的不同表的复杂查询,因为DbSet(以及查询本身)必须绑定到具体类型,而不是通用接口。 Set方法必须确认一个独立的查询接口类型,但是由具体的实体类型实现。本质上,它将接口与实体类型分离并指定两者,以便一个标识表,另一个标识查询接口。
答案 0 :(得分:0)
我的代码(重命名的实体)很可能会被修改以供您使用:
public interface IProductDO { }
public class Product1DO : IProductDO { }
public class Product2DO : IProductDO { }
public async Task<IProductDO> GetProductAsync(Expression<Func<IProductDO, bool>> predicate)
{
var result = await _context.Product1s
.Where(predicate)
.AsNoTracking()
.FirstOrDefaultAsync() as Product1DO;
}
所以我想你可以做到这一点或类似的东西:
class IItemsManager<TEntity>
where TEntity:IItems
{
public IEnumerable<Guid> GetItemIDs()
{
return DbContext
.Set<TEntity>()
// this cast may not even be necessary
// depends on if the linq query is aware of the generic constraint
.Where<IItems>(x => x.ID == id)
.FirstOrDefault()
.Select(x => x.Items.ID);
}
}