views:

147

answers:

2

So I'm trying to create a generic select by ID method for a base repository class. In order to achieve this I'm using EF4 with POCO's. I created an interface with a getter called Id and successfully modified the T4 template in order to have a generic Id property in all the entities that returns the PK.

The problem comes when I use the query. I'm implementing it like this:

public virtual T GetByID(int id)
{
    return Database.ObjectSet<T>().SingleOrDefault(entity => entity.Id == id);
}

And even though all the entities returned by the ObjectSet have the Id property set with their current primary key value, I'm getting a weird error:

The specified type member 'Id' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

Am I missing something?

+3  A: 

If the generic Id only (as you mention) "returns the PK" but is not actually mapped to the PK itself, then there is no way for EF to convert this to a SQL query.

One pattern I've used in the past: if all of your entities will have an int PK called Id then you can have all of them inherit from some baseclass where that Id property is defined (and mapped to) and then add a where clause to your generic method:

public virtual T GetByID(int id) where T : EntityBaseClass

FYI, I've also used this with entities with different types of PKs using generics.

statichippo
The Id getter returns the pk which is a property of the mapped entity. Also the ObjectSet<T>() method has the following signature. public virtual IObjectSet<T> ObjectSet<T>() where T : class, IEntity. Where IEntity is the interface that maps the PK of all my entities.
Raúl Roa
so as I mentioned, if the ID is a property that returns the PK, but is not mapped itself, how is EF supposed to know how to generate a query with it? as far as EF is concerned, this is not mapped to the DB and can't be queried! you need to query against the mapped property itself, a solution to which is to use a base class. Alternatively, if you map the actual ID property (instead of just return the PK from the getter), that'll work too.
statichippo
Thanks for your comment and answer.
Raúl Roa
Another way to go is just supplying `Expression<Func<T, bool>>` parameter for the `FirstOrDefault` method and call it like `repository.Get(e => e.Id == someId)`. See my implementation here: http://github.com/Necroskillz/NecroNetToolkit/blob/master/Source/NecroNet.Toolkit/EntityFramework/Repositories/UltimateEntityRepositoryBase.cs#L124.
Necros
It's not quite what I was looking for. But it's an interesting approach.
Raúl Roa
A: 

I don't know. I think that

public virtual T GetByID(int id)

it's a bad idea because it's a hardcode. What if i got one entity with the guid key ?

Mine repository for STE entities

    public interface IRepository<TE, TK>
    where TE : class, IEntityId<TK>, new()
    where TK : struct
{
    IQueryable<TE> Query();
    IQueryable<TE> Query(Expression<Func<TE, Object>> includeExpression);
    IQueryable<TE> Query(IEnumerable<Expression<Func<TE, Object>>> includeExpressions);

    TE GetById(Expression<Func<TE, Boolean>> predicate);
    void Create(TE entity);
    void Update(TE entity);
    void Delete(TE entity);
}

    public interface IEntityId<out TK> where TK : struct
{
    TK Id { get; }
    Int32 OwnerCode { get; }
}
Brian J. Hakim