在IEnumerable

时间:2017-04-12 15:30:08

标签: c# entity-framework linq generics

好的,所以有一点背景资料。 我有一个看起来像这样的方法:

private static IEnumerable<T> Yield<T>(this IEnumerable<T> models, int numberResults) => numberResults > 0 ? models.Take(numberResults) : models;

这对我的项目来说很好。我的项目使用实体框架。但现在它有身份框架,因此它现在使用实体框架

我现在需要列出一些用户,但使用上面相同的功能。 我的方法如下:

public async Task<List<UserViewModel>> ListByQueryAsync(string query, int yield)
{

    // Put our query in lowercase
    var loweredQuery = query.ToLower();

    // Get our users and search
    var users = base.Users.Where(m => m.UserName.ToLower().Contains(loweredQuery) || m.FirstName.ToLower().Contains(loweredQuery) || m.LastName.ToLower().Contains(loweredQuery)); //.Yield(yield);

    // Return our users as a list
    return await users.Select(m => UserFactory.Create(m)).ToListAsync();
}
用户变量是 IQueryable ,如果我是正确的,那么实现 IEnumerable ,如果它被转换为任何具体类,它将执行 SQL 。所以我想得到一个用户列表,然后使用我的静态方法收益结果量。 起初我只是复制了上面的方法并写了这个:

private static IQuerable<T> Yield<T>(this IQuerable<T> models, int numberResults) => numberResults > 0 ? models.Take(numberResults) : models;

这样做很好,但是作为DRY原则的倡导者我决定我重复相同的方法,所以我认为因为 IQuerable 实现 IEumerable 我能够编写一个可以处理bot公共方法的私有方法。所以我写了这个:

public static class LinqExtensions
{
    public static IEnumerable<T> Yield<T>(this IEnumerable<T> models, int numberResults) => models.YieldEnumerable(numberResults);
    public static IQueryable<T> Yield<T>(this IQueryable<T> models, int numberResults) => models.YieldEnumerable(numberResults);

    /// <summary>
    /// Lists database entities by a number of results or lists them all 
    /// </summary>
    /// <typeparam name="T">The generic entity to list</typeparam>
    /// <param name="models">The query to yield</param>
    /// <param name="numberResults">The number of results to yield</param>
    /// <returns></returns>
    private static IEnumerable<T> YieldEnumerable<T>(this IEnumerable<T> models, int numberResults) => numberResults > 0 ? models.Take(numberResults) : models;
}

现在我收到一条错误说明:

  

无法将类型'System.Collections.Generic.IEnumerable'隐式转换为'System.Linq.IQuerable'。

我可以将IQueryable作为列表或其他内容投射,但这意味着我的 Yield 方法实际上会执行 SQL 而我不希望它执行那。 有谁知道如何解决这个问题?

2 个答案:

答案 0 :(得分:2)

它不起作用的原因是你实际上是从两个不同的类中调用两个不同的Take方法。

您的Yield<T>(this IEnumerable<T> models, int numberResults)正在public static IEnumerable<TSource> Take(this IEnumerable<TSource> source, int count)课程中呼叫System.Linq.Enumerable

您的Yield<T>(this IQuerable<T> models, int numberResults)正在public static IQuerable<TSource> Take(this IQuerable<TSource> source, int count)班级致电System.Linq.Queryable

请注意,返回类型不同,您获取的错误是因为您的第二个函数需要返回IQueryable<T>,但YieldEnumerable<T>只返回IEnumerable<T>。虽然你可以隐含地将IQueryable<T>投射到IEnumerable<T>,但是你不能走另一条路。

您在答案中发布的工作强制执行这两种方法Queryable.Take,内部Queryable.Take正在创建一个包装器,最终为Enumerable.Take版本调用IEnumeable

但是就个人而言,我只是将其作为两种方法保留,因为您正在调用两个单独的方法Enumerable.YieldQueryable.Yeild,因此您不会违反DRY。 DRY的目的是降低代码复杂性,使用包装器只会引入50%的代码(3行而不是2行),从而引入开销并增加代码复杂性。

答案 1 :(得分:0)

我想我可以通过改变方法来解决这个问题:

public static class LinqExtensions
{
    public static IEnumerable<T> Yield<T>(this IEnumerable<T> models, int numberResults) => models.AsQueryable().YieldQueryable(numberResults);
    public static IQueryable<T> Yield<T>(this IQueryable<T> models, int numberResults) => models.YieldQueryable(numberResults);

    /// <summary>
    /// Lists database entities by a number of results or lists them all 
    /// </summary>
    /// <typeparam name="T">The generic entity to list</typeparam>
    /// <param name="models">The query to yield</param>
    /// <param name="numberResults">The number of results to yield</param>
    /// <returns></returns>
    private static IQueryable<T> YieldQueryable<T>(this IQueryable<T> models, int numberResults) => numberResults > 0 ? models.Take(numberResults) : models;
}

它会进行编译,用户仍然会以 IQuerable 的形式返回,因此它看起来很有效。