【问题标题】:Dynamically generated lookup key for IQueryable为 IQueryable 动态生成的查找键
【发布时间】:2014-02-08 00:29:39
【问题描述】:

我有以下简单的缓存类,我想用它来缓存经常从数据库中读取的项目。

public class Cache<TKey, TElement>
    where TElement : class
{
    private Dictionary<TKey, TElement> cache = new Dictionary<TKey, TElement>();
    private Func<TElement, TKey> dbKey;

    public Cache(Func<TElement, TKey> dbKey)
    {
        this.dbKey = dbKey;
    }

    public TElement Get(TKey key, DataContext db)
    {
        if (cache.ContainsKey(key)) return cache[key];

        // This line throws exception.  See below
        var value = db.GetTable<TElement>().SingleOrDefault(x => dbKey(x).Equals(key));

        if (value != null) cache[key] = value;
        return value;
    }
}

用法如下:

var db = new DataContext();
var userCache = new Cache<string, User>(u => u.Username);
userCache.Get("dave", db);

但是,指示的行会引发 NotSupportedException,并显示消息“方法 'System.Object DynamicInvoke(System.Object[])' 没有支持的 SQL 转换。”我理解为什么抛出异常,但我不确定如何解决它。

缓存旨在在许多不同的数据库表前运行,不同的列用于查找键。我的想法是传入一个 lambda 来指定查找列,正如您在代码中看到的那样。这适用于 IEnumerable,但不适用于 IQueryable。

在 LINQ to SQL 中还有其他方法可以实现这一点吗?

【问题讨论】:

    标签: c# sql linq linq-to-sql


    【解决方案1】:

    您必须使用表达式树,而不是委托和 lambda。

    1. 将你的构造函数参数类型和字段类型改为Expression&lt;Func&lt;TElement, TKey&gt;&gt;

      private Expression<Func<TElement, TKey>> dbKey;
      
      public Cache(Expression<Func<TElement, TKey>> dbKey)
      {
          this.dbKey = dbKey;
      }
      

      感谢编译器在编译时自动从 lambda 表达式生成表达式树,你仍然可以像以前一样调用它:

      var userCache = new Cache<string, User>(u => u.Username);
      
    2. 添加私有辅助方法以将键选择器表达式与相等检查相结合:

      private Expression<Func<TElement, bool>> GetConditionExpression(TKey key)
      {
          var param = Expression.Parameter(typeof(TElement));
      
          return
              Expression.Lambda<Func<TElement, bool>>(
                  Expression.Equal(
                      Expression.Invoke(
                          dbKey,
                          param
                      ),
                      Expression.Constant(key)
                  ),
              param
              );
      }
      
    3. 调用该方法以获得SingleOrDefault 的正确表达式:

      var value = db.GetTable<TElement>().SingleOrDefault(GetConditionExpression(key));
      

    我现在不能用真正的数据库测试它,但应该可以。如果没有,至少应该指出你正确的方向

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-29
      • 2021-12-18
      • 1970-01-01
      • 2022-01-09
      • 2020-11-10
      • 2013-10-24
      • 1970-01-01
      相关资源
      最近更新 更多