实体框架AsNoTracking打破呼叫鲜明 [英] Entity Framework AsNoTracking breaks call to Distinct
问题描述
我想从一个页面上的产品先前加载列表装入不同颜色的列表。因此,要拉我做的产品有:
VAR产品=产品
.INCLUDE(P =指p .ProductColor)
.ToList();
然后我做的产品他们一些处理我想要得到所有不同颜色的列表在产品上,所以我这样做:
VAR颜色=产品
。选择(p =指p .ProductColor)
.Distinct();
和这个伟大的工程,但是如果我添加了一个调用 .AsNoTracking( )
原来的产品叫,我现在得到我在产品列表中的每个条目的颜色列表中的条目。
为什么会出现在这两个的区别?有没有一种方法,以保持实体框架从跟踪的对象(它们被用于只读),并获得所需的行为?
这是加入后我的查询调用 AsNoTracking()
VAR产品=产品
.AsNoTracking()
.INCLUDE(p => p.ProductColor)
.ToList();
AsNoTracking
休息鲜明
,因为 AsNoTracking
休息标识映射。由于装载了 AsNoTracking()实体
将不会被附加到上下文缓存EF物化的新实体的每一行从查询返回,而启用跟踪时,它会检查是否有实体确实存在的背景下,如果是,它不会创建一个新的对象,只是使用附加的对象实例,而不是相同的密钥值。
例如对于如果你有2个产品无一不是绿色:
-
没有
AsNoTracking()
您的查询一定会实现3个对象:2产品
对象和1ProductColor
对象(绿色)。产品1有(在ProductColor
属性)绿色的参考和产品2有一个参考的对同一个对象实例的绿色,即
object.ReferenceEquals(product1.ProductColor,product2.ProductColor)==真
-
通过
AsNoTracking()
您的查询一定会实现4个对象:2产品对象和2色的对象(包括代表绿色,并有相同的键的值)。产品1有(在ProductColor
属性)绿色的参考和产品2有绿色的引用,但这是的另一个对象实例的,即object.ReferenceEquals(product1.ProductColor,product2.ProductColor)==虚假
现在,如果你调用鲜明的()
上在内存集合(LINQ到对象)为默认的比较鲜明的()
无参数的比较对象引用身份。所以,如果你1只得到1绿色的对象,但如果2,你会得到2绿色物体。
要得到期望的结果你已经运行查询后与 AsNoTracking()
您需要通过实体键的比较。您可以使用鲜明
这需要一个的IEqualityComparer
的第二个重载为参数。其实施的一个例子这里是,你会使用 ProductColor
来比较两个对象
或者 - 这似乎比繁琐的IEqualityComparer $ C更容易我$ C>实施 - 你重写
鲜明的()
在 GROUPBY
(用 ProductColor
键属性为分组键):
VAR颜色=产品
。选择( p => p.ProductColor)
.GroupBy(PC => pc.ProductColorId)
。选择(G => g.First());
的
第一()
基本上意味着你扔所有重复路程,只保留每个键值的第一个对象实例。
I am trying to load a list of distinct colors from previously loaded list of products on a page. So to pull in the products I do this:
var products = Products
.Include(p => p.ProductColor)
.ToList();
Then I do some processing on the products them I want to get a list of all of the distinct colors used by the products, so I do this:
var colors = products
.Select(p => p.ProductColor)
.Distinct();
And this works great, however if I add a call to .AsNoTracking()
to the original products call, I now get an entry in my color list for each entry in the product list.
Why is there a difference in these two? Is there a way to keep Entity Framework from tracking the objects (they're being used for read only) and to get the desired behavior?
Here is my query after adding the call to AsNoTracking()
var products = Products
.AsNoTracking()
.Include(p => p.ProductColor)
.ToList();
AsNoTracking
"breaks" Distinct
because AsNoTracking
"breaks" identity mapping. Since entities loaded with AsNoTracking()
won't get attached to the context cache EF materializes new entities for every row returned from the query whereas when tracking is enabled it would check if an entity with the same key value does already exist in the context and if yes, it wouldn't create a new object and just use the attached object instance instead.
For example, if you have 2 products and both are Green:
Without
AsNoTracking()
your query will materialize 3 objects: 2Product
objects and 1ProductColor
object (Green). Product 1 has a reference to Green (inProductColor
property) and Product 2 has a reference to the same object instance Green, i.e.object.ReferenceEquals(product1.ProductColor, product2.ProductColor) == true
With
AsNoTracking()
your query will materialize 4 objects: 2 product objects and 2 color objects (both represent Green and have the same key value). Product 1 has a reference to Green (inProductColor
property) and Product 2 has a reference to Green but this is another object instance, i.e.object.ReferenceEquals(product1.ProductColor, product2.ProductColor) == false
Now, if you call Distinct()
on a collection in memory (LINQ-to-Objects) the default comparison for Distinct()
without parameter is comparing object reference identities. So, in case 1 you get only 1 Green object, but in case 2 you'll get 2 Green objects.
To get the desired result after you have run the query with AsNoTracking()
you need a comparison by the entity key. You can either use the second overload of Distinct
which takes an IEqualityComparer
as parameter. An example for its implementation is here and you would use the key property of ProductColor
to compare two objects.
Or - which seems easier to me than the tedious IEqualityComparer
implementation - you rewrite the Distinct()
using a GroupBy
(with the ProductColor
key property as the grouping key):
var colors = products
.Select(p => p.ProductColor)
.GroupBy(pc => pc.ProductColorId)
.Select(g => g.First());
The First()
basically means that you are throwing all duplicates away and just keep the first object instance per key value.
这篇关于实体框架AsNoTracking打破呼叫鲜明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!