协变表达式<Func<TBase, bool>>

Covariant Expression<Func<TBase, bool>>

我们有一些共同的代码。我们想要一个接受 Expression 的方法,如下所示

public interface ISmallInterface
{
    // ...
}

public interface IExample<TBase>
{
    IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate);
}

public class SmallClass : ISmallInterface
{
    // ...
}

由此我们有了一些基本的实现

public abstract class AlphaBase<TBase> : IExample<TBase>
{
    public IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate)
    {
        // ...
    }
}

在我们的核心逻辑中,我们使用它来构建组件。然后在这个 Gamma 示例中,我们想要一个方法或 属性 公开一个 IExample<ISmallInterface>.

public class Beta : AlphaBase<SmallClass>
{
    // ...
}

public class Gamma
{
    public IExample<ISmallInterface> GetThing()
    {
        var b = new Beta();
        return b;
    }
}

但是,这会产生编译错误。

Cannot implicitly convert type 'Beta' to 'IExample<ISmallInterface>'. An explicit conversion exists (are you missing a cast?)

IExample 更改为使用协变类型参数修复了此转换问题,但破坏了 Where 方法。

public interface IExample<out TBase>
{
    IQueryable<TBase> Where(Expression<Func<TBase, bool>> predicate);
}

给出编译器错误

Invalid variance: The type parameter 'TBase' must be invariantly valid on 'IExample<TBase>.Where(Expression<Func<TBase, bool>>)'. 'TBase' is covariant.

在我们的案例中,我们只能使用 Func 个参数。

public interface IExample<out TBase>
{
    IQueryable<TBase> Where(Func<TBase, bool> predicate);
}

public abstract class AlphaBase<TBase> : IExample<TBase>
{
    public IQueryable<TBase> Where(Func<TBase, bool> predicate)
    {
        throw new NotImplementedException();
    }
}

编译并运行。但是,使用 Expression<Func<TBase, bool>> 会很方便。

是否有某种解决方法可以将 Expression<Func<TBase, bool>> 与协变类型一起使用?

(dotnet core 2.2,如果重要的话,我认为是 C#7.3)

解决方案很简单,似乎遵循 "the Linq way."

只需创建一个具有正确签名的扩展方法并在那里实现它。现在界面中没有 Where 方法。按照上面的例子,代码看起来像

public interface IExample<out TBase>
{
    // no Where, Count, or FirstOrDefault methods that accept Expression defined here

    // This is a lazy loading method and doesn't execute the query
    IQueryable<TBase> GetAll(ApplicationUser user);
}

public static class ExtensionMethods
{
    public static IQueryable<TBase> Where<TBase>(
        this IExample<TBase> repository,
        ApplicationUser user,
        Expression<Func<TBase, bool>> predicate)
    {
        return repository.GetAll(user).Where(predicate);
    }

    public static int Count<TBase>(
        this IExample<TBase> repository,
        ApplicationUser user,
        Expression<Func<TBase, bool>> predicate)
    {
        return repository.GetAll(user).Count(predicate);
    }

    public static TBase FirstOrDefault<TBase>(
        this IExample<TBase> repository,
        ApplicationUser user,
        Expression<Func<TBase, bool>> predicate)
    {
        return repository.GetAll(user).FirstOrDefault(predicate);
    }
}