【问题标题】:How to reflect over T to build an expression tree for a query?如何反映 T 以构建查询的表达式树?
【发布时间】:2011-01-09 08:06:15
【问题描述】:

我正在尝试构建一个通用类来处理来自 EF 的实体。这个类与存储库对话,但正是这个类创建了发送到存储库的表达式。无论如何,我只是想实现一种虚拟方法,作为常见查询的基础。具体来说,它将接受int,并且只需要对相关实体的主键执行查询。

我一直在搞砸它,我已经建立了一个可能有效也可能无效的反射。我之所以这么说是因为我收到了一个带有 LINQ to Entities 消息的NotSupportedException 无法识别方法 'System.Object GetValue(System.Object, System.Object[])' 方法,并且无法翻译此方法到商店表达式中。然后我尝试了另一种方法,它产生了相同的异常,但出现 LINQ to Entities 不支持 LINQ 表达式节点类型“ArrayIndex”的错误。我知道这是因为 EF 不会像 L2S 那样解析表达式。

无论如何,我希望有更多经验的人可以为我指明正确的方向。我正在发布我所做的两次尝试的整个课程。

public class Provider<T> where T : class {
    protected readonly Repository<T> Repository = null;

    private readonly string TEntityName = typeof(T).Name;

    [Inject]
    public Provider(
        Repository<T> Repository) {
        this.Repository = Repository;
    }

    public virtual void Add(
        T TEntity) {
        this.Repository.Insert(TEntity);
    }

    public virtual T Get(
        int PrimaryKey) {
        //  The LINQ expression node type 'ArrayIndex' is not supported in
        //  LINQ to Entities.
        return this.Repository.Select(
            t =>
                (((int)(t as EntityObject).EntityKey.EntityKeyValues[0].Value) == PrimaryKey)).Single();

        //  LINQ to Entities does not recognize the method
        //  'System.Object GetValue(System.Object, System.Object[])' method,
        //  and this method cannot be translated into a store expression.
        return this.Repository.Select(
            t =>
                (((int)t.GetType().GetProperties().Single(
                    p =>
                        (p.Name == (this.TEntityName + "Id"))).GetValue(t, null)) == PrimaryKey)).Single();
    }

    public virtual IList<T> GetAll() {
        return this.Repository.Select().ToList();
    }

    protected virtual void Save() {
        this.Repository.Update();
    }
}

更新@Gabe

这是我的存储库类的样子:

public class Repository<T> where T : class {
    protected readonly ObjectContext ObjectContext = null;
    private readonly IObjectSet<T> ObjectSet = null;

    [Inject]
    public Repository(
        ObjectContext ObjectContext) {
        this.ObjectContext = ObjectContext;

        this.ObjectSet = this.ObjectContext.CreateObjectSet<T>();
    }

    public virtual void Delete(
        T Entity) {
        this.ObjectSet.DeleteObject(Entity);
    }

    public virtual void Insert(
        T Entity) {
        this.ObjectSet.AddObject(Entity);
    }

    public virtual IQueryable<T> Select() {
        return this.ObjectSet;
    }

    public virtual IQueryable<T> Select(
        Expression<Func<T, bool>> Selector) {
        return this.ObjectSet.Where(Selector);
    }

    public virtual void Update() {
        this.ObjectContext.SaveChanges();
    }
}

方法的名称基于 SQL 函数,而不是基于 LINQ 方法,我认为您对我的存储库的功能感到困惑。

【问题讨论】:

  • IQueryProvider 中使用反射时,您必须手动创建表达式树以正确显示您的意图。看看这些对同一问题有答案的问题stackoverflow.com/questions/4546463/…stackoverflow.com/questions/4611559/…
  • 您似乎认为 Select 基于您的 Get 函数完成了 Where 的工作。 Where 的工作是选择要返回的行(例如,具有匹配主键的行),而 Select 只是选择要返回的列(通常都是 EF 中的所有列)。
  • @Gabe,请看我上面的更新,我解释了为什么会这样。
  • 我明白了,您的Select 函数实际上调用了Where。请记住,我可能不是唯一会感到困惑的人。

标签: c# linq entity-framework reflection expression-trees


【解决方案1】:

正如 Pauli 所暗示的,您需要手动创建 Expression 树,尽管在这种情况下反射不是必要的。以下是编写 Get 函数的方法:

public virtual T Get(
    int PrimaryKey)
{
    var param = Expression.Parameter(typeof(T));
    // create expression for param => param.TEntityNameId == PrimaryKey
    var lambda = Expression.Lambda<Func<T, bool>>(
        Expression.Equal(
            Expression.Property(param, TEntityName + "Id"),
            Expression.Constant(PrimaryKey)),
        param);
    return this.Repository.Single(lambda);
}

另外,请注意您的 GetAll 函数不需要 Select -- return Repository.ToList(); 也可以工作。

【讨论】:

  • ...这一定是我的新年决心,学习这样写表达式树而不感到头疼:)
  • @Pauli Østerø:是的,这将导致更好的代码。 ;)
  • 相比一个EF看不懂的表达式,我只能看到代码无限好:)
  • @Gabe,我爱你!这太棒了,我需要学习如何更多/更好地使用这些表达方式。
  • 哦,在代码很简单的日子里,您不必成为“语言律师”也可以编写代码:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多