2 回答

TA贡献1802条经验 获得超10个赞
我怀疑问题的发生是因为TEntity只能间接定义,或者说是传递定义。对于编译器,弄清楚是什么的唯一方法是深入TEntity检查。TRepository但是,C# 编译器不会深入检查类型,而只会观察它们的直接签名。
我相信通过TRepository从等式中移除,你所有的麻烦都会消失:
public static class MixedRepositoryExtensions {
public static Task<TEntity> FindBySelectorAsync<TEntity, TSelector>(
this IReadableAndListableRepository<TEntity> repository,
TSelector selector)
where TEntity : class, ISearchableEntity<TSelector>
=> repository.Entities.SingleOrDefaultAsync(x => x.Matches(selector));
}
当您将此方法应用于实现存储库接口的具体对象时,它自己的通用类型参数将用于推断FindBySelectorAsync方法的签名。
如果问题在于能够在几个不相等的扩展方法中为存储库指定约束列表,那么我认为 .NET 平台是限制,而不是 C# 本身。由于 F# 也编译成字节代码,因此 F# 中的泛型类型将受到与 C# 中相同的约束。
我找不到动态解决方案,即动态解决所有类型的解决方案。然而,有一种技巧可以保留完整的静态类型功能,但需要每个具体存储库添加一个额外的属性获取器。此属性不能作为扩展继承或附加,因为它在每个具体类型中的返回类型会有所不同。这是演示这个想法的代码(属性简称为FixTypes):
public class EntityHolder<TTarget, TEntity>
{
public TTarget Target { get; }
public EntityHolder(TTarget target)
{
Target = target;
}
}
public class PersonsRepository
: IRepository<Person>, IReadableRepository<Person>,
IListableRepository<Person>
{
public IQueryable<Person> Entities { get; } = ...
// This is the added property getter
public EntityHolder<PersonsRepository, Person> FixTypes =>
new EntityHolder<PersonsRepository, Person>(this);
}
public static class MixedRepositoryExtensions
{
// Note that method is attached to EntityHolder, not a repository
public static Task<TEntity> FindBySelectorAsync<TRepository, TEntity, TSelector>(
this EntityHolder<TRepository, TEntity> repository, TSelector selector)
where TRepository : IReadableRepository<TEntity>, IListableRepository<TEntity>
where TEntity : class, ISearchableEntity<TSelector>
=> repository.Target.Entities.SingleOrDefaultAsync(x => x.Matches(selector));
// Note that Target must be added before accessing Entities
}
定义了属性 getter 的存储库FixTypes可以以通常的方式使用,但扩展方法仅在其FixTypes属性的结果上定义:
new PersonsRepository().FixTypes.FindBySelectorAsync(ageSelector);

TA贡献1777条经验 获得超3个赞
这个存储库结构是不是设计过度了?存储库要么是只读的,要么是读写的。
public interface IReadOnlyRepository<TEntity>
where TEntity : class
{
Task<TEntity> FindAsync(TEntity entity);
IQueryable<TEntity> Entities { get; }
// etc.
}
// The read-write version inherits from the read-only interface.
public interface IRepository<TEntity> : IReadOnlyRepository<TEntity>
where TEntity : class
{
void Update(TEntity entity);
void Insert(TEntity entity);
// etc.
}
此外,您可以TSelector通过将设计更改为
public interface ISelector<TEntity>
where TEntity : class
{
bool Matches(TEntity entity);
}
现在,只需要一个类型参数
public static class MixedRepositoryExtensions {
public static Task<TEntity> FindBySelectorAsync<TEntity>(
this IReadOnlyRepository<TEntity> repository,
ISelector<TEntity> selector
) where TEntity : class
=> repository.Entities.SingleOrDefaultAsync(x => selector.Matches(x));
}
- 2 回答
- 0 关注
- 174 浏览
添加回答
举报