Entity Framework: ObjectSet and its (generics) variance
I use: EntityFramework + POCO
Вот эта вещь:
public interface IBaseType
{
int Id { get; set; }
}
public class BaseType : IBaseType
{
public virtual int Id { get; set; }
}
public class DerivedType : BaseType
{
}
Эта проблема:
public class EntityFetcher<T> where T : BaseType
{
public object GetById(int id)
{
ObjectSet<T> objectSet = (ObjectSet<T>)GetTheObjectSet(typeof(T));
return objectSet.SingleOrDefault((o) => o.Id == id);
}
}
Если T
является BaseType
this all works perfectly, but:
The problem is that in EntityFramework when one class inherits another they share the ObjectSet
and, therefore, if T
является DerivedType
тогда GetTheObjectSet
возвращается ObjectSet<BaseType>
, который не может быть приведен к ObjectSet<DerivedType>
,
Is there a way to actually cast this this thing or somehow else execute the SingleOrDefault
? Can those things be cast using the IObjectSet<>
а также IBaseType
?
3 ответа
Ответ на эту проблему был следующим:
public T GetById(int id)
{
// The line that throws InvalidCast with T = DerivedType
//ObjectSet<T> objectSet = (ObjectSet<T>)GetTheObjectSet(typeof(T));
// This is a valid cast since ObjectSet<T> is derived from ObjectQuery
ObjectQuery objectQuery = (ObjectQuery)GetTheObjectSet(typeof(T));
return objectQuery.OfType<T>().SingleOrDefault((e) => e.Id == id);
}
Решением было привести ObjectSet к ObjectQuery и выполнить запрос там. Наиболее важной частью здесь является то, что ObjectQuery не является универсальным, поэтому приведение проходит нормально.
Я должен отдать должное Беннору МакКарти, поскольку именно он указал мне на OfType + приведение к ObjectQuery (тот факт, что ObjectSet: ObjectQuery). Спасибо!
Я думаю, что вы ищете это:
public T GetById(int id)
{
ObjectSet<T> objectSet = (ObjectSet<T>)GetTheObjectSet(typeof(T));
return objectSet.OfType<T>().SingleOrDefault((o) => o.Id == id);
}
Метод OfType объекта ObjectQuery (из которого происходит ObjectSet) будет возвращать объекты производного типа.
Я проверил один из моих тестовых проектов, который в настоящее время далек от состояния сборки, но раньше он работал:
public interface IEntity
{
int Id { get; }
byte[] Timestamp { get; set; }
}
public abstract class Entity : IEntity
{
public int Id { get; set; }
public byte[] Timestamp { get; set; }
}
public class PocoData : Entity
{
...
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
protected ObjectContext Context;
protected ObjectSet<T> ObjectSet;
public Repository(ObjectContext context)
{
Context = context;
ObjectSet = context.CreateObjectSet<T>();
}
public virtual T GetById(int id)
{
return ObjectSet.SingleOrDefault(o => o.Id == id);
}
...
}
Главное, что класс Entity не моделируется в файле EDMX. Все объекты, смоделированные в файле EDMX, имеют свои собственные Id и Timestamp, но мои классы POCO используют общий базовый класс. Я использовал репозиторий