是否可以保存我传递给方法的预编译代理?

时间:2011-12-08 12:55:55

标签: c# entity-framework-4 delegates func

我有一个方法,它接受一个委托并在DbContext.Local实体上运行它,如果为null,则尝试在数据库中找到它,如下所示。

   public static T FirstOrDefaultInLocalOrDb<T>(this DbSet<T> myTable, Func<T, string, bool> criteria, string input) where T : class
    {
        var output = myTable.Local.Where(o => criteria((T)o, input)).FirstOrDefault();

        if (output == null)
        {
            Expression<Func<T, bool>> predicate = (u) => criteria(u, input);
            output = myTable.Where(predicate.Compile()).FirstOrDefault();
        }

        return output;
    }   

99%的时间它在本地实体中找到它并且不需要转到数据库。

在我的程序的另一部分中,这一行调用此方法1000次,每次都使用唯一的HomeId。

var currHouse = db.Houses.FirstOrDefaultInLocalOrDb2(delegate(House h, string value) { return h.AllHomesID == value; }, HomeId);

我已经完成了一些性能测试,并且我意识到这种方法运行速度非常慢,我认为这是因为它必须在每次运行时编译委托。

这个方法也在其他地方使用,所以我必须保持它的通用性,但我想知道,因为该行基本上每次都传递相同的委托,只是使用不同的输入值,是否可能以某种方式预编译委托,以便该方法不需要每次编译委托?

更新

我写了这个方法的非泛型版本,我认为这是我试图让我的原始方法做的,只是以通用的方式。这种方法似乎运行得更快。使用System.Diagnostic.Stopwatch,旧方法在大约100ms处运行,而这个方法在大约7ms处运行。

public static House FirstOrDefaultAllHomesIdInLocalOrDb(this DbSet<House> myHouseTable, string allHomesId)
{

    var output = myHouseTable.Local.Where(o => o.AllHomesID == allHomesId).FirstOrDefault();

    if (output == null)
    {
        output = myHouseTable.Where(o => o.AllHomesID == allHomesId).FirstOrDefault();
    }

    return output;
}

1 个答案:

答案 0 :(得分:2)

这里最大的问题是您已将criteria宣布为Func,而不是Expression

这意味着查询必须将myTable.Local的所有行检索到您的应用程序中,然后针对它们运行您的委托。

换句话说,如果您已将类型更改为Expression<...>而不是Func<...>,则执行的SQL将使用这些条件执行“TOP 1”或您的实际数据库引擎所需的任何语法。

相反,你基本上执行select * from myTable,然后停在第一个。

根据实体框架的作用,可能在从数据库中获取行时产生行,但我对此表示怀疑,因为并非所有数据库引擎都支持多个打开游标。

所以在这种情况下,如果表有100万行,500.000匹配,但你只想要第一行,你仍然要检索500.000行并丢弃最后499.999行。