实体框架,LINQ和泛型

时间:2011-05-20 20:40:09

标签: entity-framework generics ado.net repository-pattern

我有以下代码:

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

[更新] 我是这样称呼的:

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.

以上代码编译。执行'FindBy'方法时,我收到以下错误:

无法创建“闭包类型”类型的常量值。在此上下文中仅支持基本类型(例如Int32,String和Guid)。

由于我的'Id'的类型是Guid(支持的原始类型之一),我似乎应该能够将其按下来进行工作。

任何人都知道这是否可行?

谢谢,

鲍勃

1 个答案:

答案 0 :(得分:3)

这种方式不起作用。您无法调用Equals,因为EF不知道如何将其转换为SQL。将表达式传递给FirstOrDefault时,它必须始终只是可以转换为SQL的代码。通过表达树的一些手动构建可能可以解决您的问题,但我可以参考已在Stack Overflow上讨论过的其他解决方案。

ObjectContext提供名为GetObjectByKey的方法,这正是您要尝试的方法。问题是它需要EntityKey作为参数。以下是两个答案,说明如何使用此方法以及如何获取EntityKey

在您的情况下,代码将不那么复杂,因为您知道密钥属性的名称,因此您通常只需要这样的代码:

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

此处的问题是您无法从entitySetName获取IObjectSet,因此您必须将其传递给存储库构造函数,或者必须传递ObjectSet

以防万一你将来想要使用DbContext API(EFv4.1)而不是ObjectContext API,因为DbSet提供了Find方法,所以它会大大简化: