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 關注
- 153 瀏覽
添加回答
舉報