How can I generate an expression tree for a query by reflecting over T?

c# entity-framework expression-trees linq reflection

Question

I'm attempting to create a generic class that can interact with EF instances. This class produces the expressions sent to the repositories, even if it is another class that communicates with the repositories. Anyway, all I'm attempting to do is create a single virtual method that will serve as the foundation for standard querying. It will specifically accept an anint and it just needs to do a query on the object in question's primary key.

I played around with it and created a reflection that may or might not work. Considering that I get aNotSupportedException containing the message The method "System.Object GetValue(System.Object, System.Object[])" is not supported by LINQ to Entities, and thus cannot be converted into a store expression. After that, I attempted a different strategy, which led to the same exception but with the error In LINQ to Entities, the LINQ expression node type "ArrayIndex" is not supported.. I am aware of the reason why—EF won't interpret the phrase the same way L2S would.

Anyhow, I'm hoping someone with a little more expertise can enlighten me on this. I've uploaded my two efforts along with the complete class.

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();
    }
}

For @Gabe, UPDATE

My repository class looks like this:

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();
    }
}

I believe you are misinterpreting how my repository works since the names of the methods are based on SQL functions rather than LINQ methods.

1
1
1/9/2011 12:06:15 AM

Accepted Answer

Pauli suggests that you have to manually buildExpression trees, even if in this instance reflection is essential. Here's an example of how youGet function:

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);
}

Don't forget that yourGetAll function is not required.Select -- return Repository.ToList(); will function equally well.

8
1/8/2011 7:37:09 AM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow