我正在使用VS2010 .Net4 Linq-to-EntityFramework,并希望明确地急切加载一些子数据。我想提供类似于 DataLoadOptions 或 LoadWith 的功能,这些功能可用于Linq-to-SQL IIUC,但不适用于Linq-to-EF。< / p>
(顺便说一句,这是为了我可以记录数据以便稍后在测试期间播放。我们使用了延迟加载,我需要找到这些事件并替换为急切的加载。 DataLoadOptions 方法将允许一种干净的方式来实现它。)
我正在努力提供MosesOfEgypt blog所述的类型安全的预先加载方案。我修改了T4的产生,并且我认为这是最后一个问题。在.Net4中,实体属性返回 ObjectSet 。但不幸的是,包含函数返回 ObjectQuery ,它是 ObjectSet 的基类。
以下是从修改后的T4模板生成的ObjectContext类的子集:
#region DataLoadOptions Functionality
public DataLoadOptions LoadOptions { get; set; }
private ObjectSet<TEntity> ApplyDataLoadOptions<TEntity>(string queryString) where TEntity : class
{
var query = CreateObjectSet<TEntity>(queryString);
if (LoadOptions != null)
{
var members = LoadOptions.GetPreloadedMembers<TEntity>();
foreach (var member in members)
{
********** query = query.Include(member.Name);
}
}
return query;
}
#endregion
#region ObjectSet Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
public ObjectSet<Address> Addresses
{
get
{
if ((_Addresses == null))
{
_Addresses = ApplyDataLoadOptions<Address>("Addresses");
}
return _Addresses;
}
}
#endregion
以“ * ”开头的行是从 ObjectQuery 转换为 ObjectSet 的地方。这是一个无效的upcast,所以如果在设计时显式转换,将在运行时失败,除非我做错了。
一种解决方案可能是为 ObjectSet.Include 编写扩展方法,以便它返回 ObjectSet 而不是 ObjectQuery 。我想知道如何找到 ObjectQuery.Include 函数的源代码,如果可能的话。而且我不确定这些解决方案是否有效。
另外想知道是否有办法将包含功能的结果从 ObjectQuery 转发到 ObjectSet 。再次,不确定这是否有效。
在.Net4中为Linq-to-EF实现DataLoadOptions功能的任何帮助都将不胜感激。
答案 0 :(得分:1)
你在这里询问的并不是很清楚。
.Include
会返回ObjectQuery<T>
,其中会生成IQueryable<T>
。为什么要将其强制转换为ObjectSet<T>
??
WRT“强类型”Include
,简短的回答是你不能。但是你可以用扩展方法的语法含糖来使它更甜蜜。
我们为每个实体创建枚举(可能有点矫枉过正,但我讨厌魔术字符串),其中包含每个关联。我们的存储库接受这些枚举的数组。然后,我们在枚举上使用扩展方法转换为Include
。
示例(简化)存储库代码:
public ICollection<Order> GetOrdersWithUser(Expression<Func<Order,bool>> predicate, OrderAssocations[] includes)
{
return _ctx.Orders.WithAssociations(includes).Where(predicate).ToList();
}
扩展名:
public static ObjectQuery<Order> WithAssociations(this ObjectQuery<Order> source, OrderAssociations[] includes)
{
var query = source;
foreach (var include in includes)
{
query = query.Include(include.ToNavigationalProperty()));
}
return query;
}
.ToNavigationalProperty()
是枚举的另一种扩展方法,它只返回匹配的Navigational属性。
答案 1 :(得分:0)
不确定这是否能解答您的问题...但是,这就是我如何做出“动态”的热切加载:
/// <summary>
/// Flags to indicate which child entities to eager load
/// </summary>
[Flags]
public enum IncludeFlags
{
None = 1,
All = 1 << 1,
ChildEntity1 = 1 << 2,
ChildEntity2 = 1 << 3,
ChildEntity3 = 1 << 4
}
/// <summary>
/// Method to create my object query
/// </summary>
/// <param name="context">Database context</param>
/// <param name="includeFlags">Indicates which flags to Include</param>
/// <returns></returns>
private static ObjectQuery<MyEntity> GetContext(DataBaseContext context, IncludeFlags includeFlags)
{
ObjectQuery<MyEntity> query = new ObjectQuery<MyEntity>("MyEntity", context);
if ((includeFlags & IncludeFlags.None) != IncludeFlags.None)
{
bool getAll = ((includeFlags & IncludeFlags.All) == IncludeFlags.All);
if (getAll || ((includeFlags & IncludeFlags.ChildEntity1) == IncludeFlags.ChildEntity1))
query = query.Include("ChildEntity1");
if (getAll || ((includeFlags & IncludeFlags.ChildEntity2) == IncludeFlags.ChildEntity2))
query = query.Include("ChildEntity2");
if (getAll || ((includeFlags & IncludeFlags.ChildEntity3) == IncludeFlags.ChildEntity3))
query = query.Include("ChildEntity2.ChildEntity3");
}
return query;
}
public static MyEntity[] GetMyEntities(IncludeFlags flags = IncludeFlags.None)
{
DataBaseContext db = new DataBaseContext();
from e in GetContext(db, flags)
select e;
return e.ToArray();
}
public void GetMyEntities()
{
MyEntity[] entities = Test.GetMyEntities(); // Does not load any child entities
MyEntity[] entities2 = Test.GetMyEntities(IncludeFlags.All); // Loads them all
MyEntity[] entities3 = Test.GetMyEntities(IncludeFlags.ChildEntity1 | IncludeFlags.ChildEntity2); // Only loads child 1 and 2
MyEntity[] entities4 = Test.GetMyEntities(IncludeFlags.ChildEntity3); // Only loads ChildEntity2.ChildEntity3
}
希望这有帮助。