强制转换IEnumerable< tenty >到IEnumerable< result >同时确保延迟执行

本文关键字:IEnumerable 确保 延迟 执行 result tenty 转换 | 更新日期: 2023-09-27 18:03:42

在我的应用程序中,有相当数量的现有"服务命令"通常返回List<TEntity>。然而,我以这样一种方式编写它们,任何查询都不会被评估,直到最后一条语句,当它们被转换为ToList<TEntity>(或者至少我认为我做了)。

现在我需要开始从命令中获取一些"特定于上下文"的信息,我正在考虑做以下事情:

  1. 保持现有的命令大致相同,但确保它们返回IEnumerable<TEntity>而不是IList<TEntity>
  2. 创建调用旧命令的新命令,但返回IEnumerable<TResult>,其中TResult不是实体,而是视图模型,结果模型等-对应用程序有用的数据的一些表示。

我需要的第一个案例是在搜索Group实体时。在我的模式中,Group具有特定于User的权限,但是在我的结果中吐出用户和权限的整个列表是不现实的——首先,因为可能有很多用户,其次,因为有很多权限,第三,因为这些信息不应该提供给没有足够特权的用户(即"guest"不应该能够看到"member"可以做什么)。

因此,我希望能够获得原始命令IEnumerable<Group>的结果,并描述如何将每个Group转换为GroupResult,给定User的特定输入(在本例中为Username)。

如果我尝试用ForEach遍历原始命令的结果,我知道这将强制执行结果,因此可能导致不必要的更长的执行时间。如果我想进一步组合"new"命令(返回GroupResult)的结果并过滤掉某些组,该怎么办?然后,我可能会为输入的用户计算大量的特权,无论如何,只是为了过滤掉父GroupResult对象!

我想我的问题可以归结为……我如何告诉c#我想如何转换IEnumerable的每个成员,而不必在方法运行时进行转换?

强制转换IEnumerable< tenty >到IEnumerable< result >同时确保延迟执行

要将一个可枚举对象从一种类型惰性强制转换为另一种类型,可以这样做:

IEnumerable<TResult> result = source.Cast<TResult>();

假设源枚举对象的元素可以强制转换为TResult。如果他们不能,你需要使用.Select(x => ... )的标准投影。

另外,要小心从服务或数据库返回IEnumerable<T>,因为通常需要打开资源来获取数据,所以现在您需要确保每当尝试计算枚举值时这些资源都是打开的。保持数据库连接是一个坏主意。我更倾向于返回一个你已经转换为IEnumerable<>的数组。

然而,如果你真的想从一个服务或数据库中获得一个真正懒惰的IEnmerable<>,并且会自动刷新数据,那么你需要尝试微软的响应式框架团队的"交互式扩展"来帮助它。

他们有一个很好的IEnumerable<>扩展名为Using,它创建了一个"热"可枚举对象,为每次迭代打开一个资源。

它看起来像这样:

var d =
    EnumerableEx
        .Using(
            () => new DB(),
            db => db.Data.Where(x => x == 2));

每次迭代可枚举对象时,它都会创建一个新的DB实例,并在可枚举对象完成时释放数据库。值得考虑的事情。

使用NuGet查找"Ix-Main"作为交互式扩展

您正在查找yield return命令。

当您定义一个返回IEnumerable的方法,并通过yield return返回它的数据时,返回值将在消费方法中迭代。这就是它的样子:

IEnumerable<GroupResult> GetGroups(string userName)
{
    foreach(var group in context.Groups.Where(g => <some user-specific condition>))
    {
        var result = new GroupResult()
        ... // Further compose the result.
        yield return result;
    }
}

在消费代码:

var groups = GetGroups("tacos");
// At this point no eumeration has occurred yet. Any breakpoints in GetGroups
// have not been hit.
foreach(var g in groups)
{
    // Now iteration in GetGroups starts!
}