Entity Framework, LINQ и Generics - PullRequest
2 голосов
/ 21 мая 2011

У меня есть следующий код:

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] Вот как я это называю:

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 (один из поддерживаемых типов примитивов), похоже, я должен быть в состоянии превратить это в работу.

Кто-нибудь знает, возможно ли это?

Спасибо

Bob

1 Ответ

3 голосов
/ 21 мая 2011

Так не работает.Вы не можете вызвать Equals, потому что EF не знает, как перевести его на SQL.Когда вы передаете выражение в FirstOrDefault, это всегда должен быть только код, который можно преобразовать в SQL.Вероятно, возможно решить вашу проблему с помощью некоторого ручного построения дерева выражений, но я могу сослаться на другие решения, уже обсуждавшиеся при переполнении стека.

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 метод:

...