[英]How can I load Child entities dynamically in Entity Framework 6
我有一个 Provider 类和一个 Article one。 文章有一个int ProviderId{get;set}
和一个public virtual Provider Provider {get;set;}
属性。
我知道延迟加载以及为什么我无法在上下文之外访问 Article 中的 Provider 属性,但我有一个通用方法可以像这样返回下一个 T:
public static T Next<T>(T currentElement) where T : class, IModel {
T data;
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities()) {
data = context.Set<T>().Single(el => el.Id == currentElement.Id + 1);
}
return data;
}
但是我无法在文章类中检索像 Provider 这样的子实体。 如何包含所有实体? 我应该更新方法并为每个实体创建一个吗?
我读过Eager loading和Explicit loading ,但我不知道如何在我的方法中实现这些。
笔记:
并非我的所有实体都有实体子级,而且我有更多方法,例如Previous<T>()
、 First<T>()
或Last<T>()
可以完成您期望的工作。
您可以创建接受Expression<Func<T, object>>
Next
方法的重载:
public static T Next<T>(T currentElement, Expression<Func<T, object>> navigationProperty) where T : class, IModel
{
T data;
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities())
{
IQueryable<T> dbQuery = context.Set<T>();
if (navigationProperty != null)
dbQuery = dbQuery.Include<T, object>(navigationProperty);
data = dbQuery.AsNoTracking().Single(el => el.Id == currentElement.Id + 1);
}
return data;
}
用法:
var entity = Next(instance, x => x.Provider);
有关更多信息和完整示例,请参阅以下博客文章。
使用实体框架实现通用数据访问层: https : //blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/
您可以扩展您的方法以获取可能包含的列表。 例如:
public static T Next<T>(T currentElement, params Expression<Func<T, object>>[] includes)
where T : class, IModel
{
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities())
{
IQueryable<T> query = context.Set<T>();
//Deferred execution allows us to build up the query
foreach (var include in includes)
{
query = query.Include(include);
}
return query.Single(el => el.Id == currentElement.Id + 1);
}
}
并像这样使用它:
var someEntity = ...;
var nextEntity = Next(someEntity,
e => e.Childcollection1,
e => e.Childcollection2,
//
e => e.ChildcollectionN)
另外一点,要获得下一个实体,您不应该依赖于顺序的 ID 值,例如,如果条目被删除,它们将失去顺序。 相反,考虑这样的事情:
return query.OrderBy(el => el.Id).First(el => el.Id > currentElement.Id);
所以你有一个Provider
和Article
之间的一对多关系:每个Provider
有零个或多个Articles
,并且每个Article
都只属于一个Provider
。
如果您在 Entity Framework 中正确配置了这种一对多关系,则提供者应该引用其许多Articles
:
public virtual ICollection<Article> Articles{get; set;}
此外,您有一个函数Next
,它将类T
的对象作为输入,该类是实现IModel
的类。 Next
返回 Ts 的 DbSet 中的下一个元素,假设您对下一个元素有正确的定义。
显然,您想调整 Next 函数,以便您可以使用此函数来获取下一个Provider
及其所有Articles
。
更通用:如果 T 是一种在其中正确设计了一对多关系的类型,并且您有一个类型为 T 的对象,则您希望 T 的第一个 Id 高于当前 T 的 Id,包括它的所有收藏。
我已将其划分为更小的功能:
鉴于这两个,以及像OrderBy
和FirstOrDefault
这样的函数,您将能够获得第一个 Id 大于当前提供者及其所有文章的提供者。
static class QueryableExtensions
{
// returns true if PropertyInfo implements ICollection<...>
public static bool ImplementsICollection(this PropertyInfo propertyInfo)
{
return propertyInfo.IsGenericType
&& propertyInfo.GetGenericTypeDefinition() == typeof(ICollection<>);
}
// returns all readable & writable PropertyInfos of type T that implement ICollection<...>
public static IEnumerable<PropertyInfo> CollectionProperties<T>()
{
return typeof(T).GetProperties()
.Where(prop => prop.CanRead
&& prop.CanWrite
&& prop.PropertyType.ImplementICollection();
}
// given an IQueryable<T>, adds the Include of all ICollection<...> T has
public static IQueryable<T> IncludeICollection<T>(IQueryable<T> query)
{
var iCollectionProperties = CollectionProperties<T>();
foreach (var collectionProperty in collectionProperties)
{
query = query.Include(prop.Name);
}
return query;
}
// given a IQueryable<T> and a T return the first T in the query with Id higher than T
// for this we need to be sure every T has an IComparable property Id
// so T should implement interface IId (see below)
public T Next<T>(this IQueryable<T> query, T currentItem)
where T : IId // makes sure every T has aproperty Id
{
T nextElement = query
// keep only those DbSet items that have larger Ids:
.Where(item => currentItem.Id < item.Id)
// sort in ascending Id:
.OrderBy(item => item.Id
// keep only the first element of this sorted collection:
.FirstOrDefault();
return T
}
}
我需要确保每个T都有一个Id,否则你无法获得下一个Id。 可能你的 IModel 界面中有这个:
public Interface IId
{
public int Id {get;}
}
在这些功能之后,您的查询将类似于:
public static T Next<T>(T currentElement) where T : class, IModel, IId
{
T data;
if (currentElement.Id >= GetLastId<T>())
return currentElement;
using (DatabaseEntities context = new DatabaseEntities())
{
return context.Set<T>().Next(currentElement);
}
}
我不打算重新回答一个答案,但这里有一个函数,它结合了一点反射来根据模型声明自动加载所有与外键相关的数据。 我已将其编写为通过主键以外的其他内容加载对象的方法,但可以根据需要对其进行修改以执行此操作。
private IQueryable<T> GetFullSet()
{
IEnumerable<IEntityType> entities = _dbContext.Model.GetEntityTypes();
IEntityType thisEntity = entities.SingleOrDefault(x => x.ClrType.Name == typeof(T).Name);
IQueryable<T> set = _dbContext.Set<T>();
foreach (IForeignKey fKey in thisEntity.GetReferencingForeignKeys())
{
set = set.Include(fKey.PrincipalToDependent.Name);
}
return set;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.