我正在为购物车做一个大数据库调用。它包含许多关系,均使用.Include()
方法指定。
现在我只希望EF包含我指定的内容。当我包含一个集合时,它会自动加载集合的关系。
所以我有一个ShoppingCart
,购物车有ShoppingCartProducts
的集合,而且该集合的关系可以追溯到ShoppingCart
和Product
。
所以我想要再包含产品,但不包括购物车,所以我这样做:
IQueryable<ShoppingCart> query = DbContext.ShoppingCarts
.Include(p => p.ShoppingCartProducts)
.Include(p => p.ShoppingCartProducts.Select(x => x.Product))
稍后我执行一个执行查询的.FirstOrDefault()
。通过此调试,每个ShoppingCart
中都包含ShoppingCartProducts
。
这听起来有点小,但在整个应用程序中实际上都是这样。新架构将实体对象转换为具有不同静态方法和扩展的模型。最终导致StackoverflowException,因为它递归地包含它的关系。
那么我如何仅包括我所包含的内容?
我已将LazyLoadingEnabled
变为false,将ProxyCreationEnabled
变为false。我的馆藏/版税未标有virtual
。
检查了这些答案:
DBContext lazyloadingenabled set to true still loads related entities by default 关于集合的包含是真的,但是一旦包含了集合,该集合将加载所有其他关系(我猜)
Entity Framework with Proxy Creation and Lazy Loading disabled is still loading child objects 几乎相同的问题,但不是一个好的答案,只有一个解释
EF 6 Lazy Loading Disabled but Child Record Loads Anyway 使用分离的帮助。
修改
正如Evk所说,这与EF有关,可以自动填补已知关系的空白。现在的问题实际上是如何解决这个问题。
编辑2:
因此,在得到Evk的回答和我自己的解决方法后,我们了解到这些解决方案并不能解决问题。让我试着解释一下:
这些扩展和ConvertToModel
方法在每个存储库中实现,并且只要与它有关系就互相调用。这个概念实际上很棒:如果你有关系,只需转换为模型,如果你没有,就不要做任何事情。然而,由于这个EF&#39; bug&#39;我知道所有已知的关系到处插入。
以下是我们的解决方案无法运作的示例。对于这种情况,代码首先会为ConvertToModel
调用ShoppingCart
,然后是其余部分。但当然可能反之亦然。
ShoppingCartRepository
public static ShoppingCartModel ConvertToModel(ShoppingCart entity)
{
if (entity == null) return null;
ShoppingCartModel model = new ShoppingCartModel
{
Coupons = entity.ShoppingCardCoupons?.SelectShoppingCouponModel(typeof(ShoppingCart)),
Products = entity.ShoppingCartProducts?.SelectShoppingCartProductModel(typeof(ShoppingCart)),
};
return model;
}
ShoppingCartProductRepository
public static IEnumerable<ShoppingCartProductModel> SelectShoppingCartProductModel(this IEnumerable<ShoppingCartProduct> source, Type objSource = null)
{
bool includeRelations = source.GetType() != typeof(DbQuery<ShoppingCartProduct>);
return source.Select(x => new ShoppingCartProductModel
{
ShoppingCart = includeRelations && objSource != typeof(ShoppingCart) ? ShoppingCartRepository.ConvertToModel(x.ShoppingCart) : null,
ShoppingCartCoupons = includeRelations && objSource != typeof(ShoppingCartCoupon) ? x.ShoppingCartCoupons?.SelectShoppingCouponModel(typeof(ShoppingCartProduct)) : null,
});
}
ShoppingCartCouponRepository
public static IEnumerable<ShoppingCartCouponModel> SelectShoppingCouponModel(this IEnumerable<ShoppingCartCoupon> source, Type objSource = null)
{
bool includeRelations = source.GetType() != typeof(DbQuery<ShoppingCartCoupon>);
return source.Select(x => new ShoppingCartCouponModel
{
ShoppingCart = includeRelations && objSource != typeof(ShoppingCart) ? ShoppingCartRepository.ConvertToModel(x.ShoppingCart) : null,
ShoppingCartProduct = includeRelations && objSource != typeof(ShoppingCartProductModel) ? ShoppingCartProductRepository.ConvertToModel(x.ShoppingCartProduct) : null
});
}
当你研究它时,你会发现它可以从ShoppingCart
到ShoppingCartProduct
再到ShoppingCartCoupon
回到ShoppingCart
。
我目前的解决方法是找出聚合根并选择哪一个需要哪一个。但我宁愿有一个优雅的解决方案来解决这个问题。最好是阻止EF加载那些已知的关系,或以某种方式弄清楚属性是否以这种方式加载(反射?)。
答案 0 :(得分:1)
正如评论中所述,实体框架的默认行为,我认为它无法改变。相反,您可以更改代码以防止堆栈溢出异常。如何很好地完成这项工作非常依赖于您的代码库,但我会提供一个草图。在上面的草图中,我使用了其他实体名称(因为我总是检查我的代码是否至少在编译之前进行了编译):
public static partial class Ex {
public static CodeModel ConvertToModel(Code entity) {
if (entity == null) return null;
CodeModel model = new CodeModel();
var map = new Dictionary<object, object>();
map.Add(entity, model);
model.Errors = entity.Errors?.SelectShoppingCartProductModel(map);
return model;
}
public static ErrorModel[] SelectShoppingCartProductModel(this IEnumerable<Error> source, Dictionary<object, object> map = null) {
bool includeRelations = source.GetType() != typeof(DbQuery<Error>); //so it doesn't call other extensions when we are a db query (linq to sql)
return source.Select(x => new ErrorModel {
Code = includeRelations ? (map?.ContainsKey(x.Code) ?? false ? (CodeModel) map[x.Code] : ConvertToModel(x.Code)) : null,
// other such entities might be here, check the map
}).ToArray();
}
}
另一种选择是将当前模型存储在线程局部变量中。如果调用某个ConvertToModel
方法并且此线程局部变量不为null - 这意味着此方法已被递归调用。样品:
public static partial class Ex {
private static readonly ThreadLocal<CodeModel> _code = new ThreadLocal<CodeModel>();
public static CodeModel ConvertToModel(Code entity) {
if (entity == null) return null;
if (_code.Value != null)
return _code.Value;
CodeModel model = new CodeModel();
_code.Value = model;
model.Errors = entity.Errors?.SelectShoppingCartProductModel();
// other setters here
_code.Value = null;
return model;
}
public static ErrorModel[] SelectShoppingCartProductModel(this IEnumerable<Error> source) {
bool includeRelations = source.GetType() != typeof(DbQuery<Error>); //so it doesn't call other extensions when we are a db query (linq to sql)
return source.Select(x => new ErrorModel {
Code = includeRelations ? ConvertToModel(x.Code) : null,
}).ToArray();
}
}
如果您在所有ConvertToModel
方法中实现此功能,则无需传递任何参数或更改代码的其他部分。
答案 1 :(得分:0)
此解决方案检查源对象类型是否不等于我们调用ConvertToModel的类型。
public static ShoppingCartModel ConvertToModel(ShoppingCart entity)
{
if (entity == null) return null;
ShoppingCartModel model = new ShoppingCartModel
{
...
Products = entity.ShoppingCartProducts?.SelectShoppingCartProductModel(typeof(ShoppingCart)),
};
return model;
}
和SelectShoppingCartProductModel
扩展程序:
public static partial class Ex
{
public static IEnumerable<ShoppingCartProductModel> SelectShoppingCartProductModel(this IEnumerable<ShoppingCartProduct> source, Type objSource = null)
{
bool includeRelations = source.GetType() != typeof(DbQuery<ShoppingCartProduct>);//so it doesn't call other extensions when we are a db query (linq to sql)
return source.Select(x => new ShoppingCartProductModel
{
....
ShoppingCart = includeRelations && objSource != typeof(ShoppingCart) ? ShoppingCartRepository.ConvertToModel(x.ShoppingCart) : null,
});
}
}
然而,这可能无法解决整个问题。如果你有另一个实体,让我们在AdditionalCosts
中说ShoppingCart
,它也有ShoppingCartProduct
的引用,它仍会'旋转'。 如果某人有解决方案,那就太棒了!
ShoppingCart
- &gt; ConvertToModel(shoppingCart)
- &gt; SelectAdditionalCostsModel
- &gt; ShoppingCartProduct
- &gt; ConvertToModel(shoppingCartProduct)
- &gt; ShoppingCart
- &gt; ConvertToModel(shoppingCart)
。等等..