将IEnumerable <tentity>转换为IEnumerable <tresult>,同时确保延迟执行</tresult> </tentity>

时间:2014-05-17 10:43:15

标签: c# linq entity-framework ienumerable deferred-execution

在我的应用程序中,有相当数量的现有&#34;服务命令&#34;通常返回List<TEntity>。但是,我以这样的方式编写它们,直到最后一个语句,当它们被转换为ToList<TEntity>时(或者至少我认为我这样做),才会对任何查询进行求值。

现在我需要开始获取一些特定于上下文的&#34;来自命令的信息,我正在考虑做以下事情:

  1. 保持现有命令与今天大致相同,但要确保它们返回IEnumerable<TEntity>而不是IList<TEntity>
  2. 创建调用旧命令但返回IEnumerable<TResult>的新命令,其中TResult不是实体,而是视图模型,结果模型等 - 对应用程序有用的数据的某种表示
  3. 我需要这个的第一个案例是在搜索Group实体时。在我的架构中,Group带有User - 特定权限,但在我的结果中吐出整个用户和权限列表是不现实的 - 首先,因为可能有很多用户,第二,因为有许多权限,第三,因为该信息不应该对权限不足的用户可用(即&#34;访客&#34;不应该能够看到&#34;成员&#34 ;可以)。

    所以,我希望能够获取原始命令IEnumerable<Group>的结果,并描述每个Group应如何转换为GroupResult,具体如下输入User(在这种情况下为Username)。

    如果我尝试用ForEach迭代原始命令的结果,我知道这将强制执行结果,因此可能导致不必要的更长的执行时间。如果我想进一步撰写&#34; new&#34;的结果怎么办?命令(返回GroupResult)并过滤掉某些组?那么也许我会为输入的用户计算大量的权限,只是为了以后过滤出父GroupResult个对象!

    我想我的问题归结为......我如何告诉C#我如何转换IEnumerable的每个成员而不必在运行该方法时执行此操作?

2 个答案:

答案 0 :(得分:1)

懒惰地将一个类型从一种类型转换为另一种类型,你可以这样做:

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

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

另外,请小心从服务或数据库返回IEnumerable<T>,因为通常需要打开资源来获取数据,所以现在您需要确保在尝试评估可枚举时打开这些资源。保持数据库连接打开是一个坏主意。我更倾向于返回一个你投射为IEnumerable<>的数组。

但是,如果你真的想从一个真正懒惰的服务或数据库中获得IEnmerable<>并且会自动刷新数据,那么你需要尝试使用Microsoft的Reactive Framework Team的“Interactive Extensions”来帮助它。

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

它看起来像这样:

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

每次迭代枚举时都会创建一个新的DB实例,并在枚举完成后处理数据库。值得考虑的事情。

使用NuGet并为Interactive Extensions查找“Ix-Main”。

答案 1 :(得分:0)

您正在寻找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!
}