1

I'm trying to build a generic class to work with entities from EF. This class talks to repositories, but it's this class that creates the expressions sent to the repositories. Anyway, I'm just trying to implement one virtual method that will act as a base for common querying. Specifically, it will accept a an int and it only needs to perform a query over the primary key of the entity in question.

I've been screwing around with it and I've built a reflection which may or may not work. I say that because I get a NotSupportedException with a message of 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. So then I tried another approach and it produced the same exception but with the error of The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities. I know it's because EF will not parse the expression the way L2S will.

Anyway, I'm hopping someone with a bit more experience can point me into the right direction on this. I'm posting the entire class with both attempts I've made.

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

UPDATE for @Gabe

This is what my repository class looks like:

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

The names of the methods are based on the SQL functions, not on the LINQ methods, which is where I think you're getting confused on how my repository functions.

Gup3rSuR4c
  • 9,145
  • 10
  • 68
  • 126
  • It looks like you think that `Select` does the job of `Where` based on your `Get` function. The job of `Where` is to choose which rows to return (e.g. the one with the matching primary key), while `Select` just chooses which columns to return (usually all of them in EF). – Gabe Jan 08 '11 at 07:42
  • @Gabe, please look at my update above where I explain why that is. – Gup3rSuR4c Jan 08 '11 at 22:45
  • I see, your `Select` function actually calls `Where`. Just keep in mind that I'm probably not the only person who would find that confusing. – Gabe Jan 09 '11 at 00:54
  • When working with reflection in your `IQueryProvider` you have to manually create an expression tree to correctly show your intent. Look at these question that have answers to the same issue http://stackoverflow.com/questions/4546463/help-with-linq-and-generics-using-getvalue-inside-a-query http://stackoverflow.com/questions/4611559/iqueryable-dynamic-ordering-filtering-with-getvalue-fails – Pauli Østerø Jan 08 '11 at 07:01

1 Answers1

8

As Pauli alludes to, you need to manually create Expression trees, although reflection isn't necessary in this case. Here's how you could write your Get 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);
}

Also, note that your GetAll function doesn't need Select -- return Repository.ToList(); will work just as well.

Gabe
  • 84,912
  • 12
  • 139
  • 238