Entity Framework, LINQ and Generics
I have the following code:
public interface IKeyed<TKey>
{
TKey Id { get; }
}
// This is the entity framework generated model. I have added the
// IKeyed<Guid> interface
public partial class Person : IKeyed<Guid>
{
public Guid Id { get; set; }
}
public class Repository<TKey, TEntity> : IKeyedRepository<TKey, TEntity>
where TEntity : class, IKeyed<TKey>
{
private readonly IObjectSet<TEntity> _objectSet;
public Repository(IOjectSet<TEntity> objectSet)
{
_objectSet = objectSet;
}
public TEntity FindBy(TKey id)
{
return _objectSet.FirstOrDefault(x =开发者_开发技巧> x.Id.Equals(id));
}
}
[Update] Here is how I am calling this:
Db2Entities context = new Db2Entities(_connectionString); // This is the EF context
IObjectSet<Person> objectSet = context.CreateObjectSet<Person>();
IKeyedRepository<Guid, Person> repo = new Repository<Guid, Person>(objectSet);
Guid id = Guid.NewGuid();
Person person = repo.FindBy(id); // This throws the exception.
The above code compiles. When the 'FindBy' method is executed, I get the following error:
Unable to create a constant value of type 'Closure type'. Only primitive types (for instance Int32, String and Guid) are supported in this context.
Since the type of my 'Id' is a Guid (one of the primitive types supported) it seems like I should be able to massage this into working.
Anyone know if this is possible?
Thanks,
Bob
It doesn't work this way. You cannot call Equals
because EF doesn't know how to translate it to SQL. When you pass expression to FirstOrDefault
it must be always only code which can be translated to SQL. It is probably possible to solve your problem with some manual building of expression tree but I can reference other solutions already discussed on Stack Overflow.
ObjectContext
offers method named GetObjectByKey
which is exactly what you are trying to do. The problem is that it requires EntityKey
as parameter. Here are two answers which show how to use this method and how to get EntityKey
:
- Entity Framework Simple Generic GetByID but has differents PK Name
- generic GetById for complex PK
In your case the code will be less complicated because you know the name of the key property so you generally need only something like this:
public virtual TEntity FindBy(TKey id)
{
// Build entity key
var entityKey = new EntityKey(_entitySetName, "Id", key);
// Query first current state manager and if entity is not found query database!!!
return (TEntity)Context.GetObjectByKey(entityKey);
}
The problem here is that you cannot get entitySetName
from IObjectSet
so you must either pass it to repository constructor or you must pass ObjectSet
.
Just in case you will want to use DbContext API (EFv4.1) in the future instead of ObjectContext API it will be much simplified because DbSet
offers Find
method:
- generic repository EF4 CTP5 getById
精彩评论