EntitySet - IList.Add 没有设置分配是否有合理的原因?

发布于 2024-11-11 05:36:41 字数 1300 浏览 6 评论 0原文

有 3 种方法可以将项目添加到大多数列表...

  • 通过直接公共 API 方法,通常
  • 通过通用 IList.Add(T)Add(SomeType) >
  • 通过非泛型 IList.Add(object) 接口方法

进行接口,您通常期望它们的行为或多或少相同。然而,LINQ 的 EntitySet 在 3.5 和 4.0 上都是特有的; IList API 不会将集合标记为“已分配” - 其他两种机制< /strong> - 这听起来微不足道,但很重要,因为它严重影响样板代码中的序列化(即导致其被跳过)。

示例:

EntitySet<string> set1 = new EntitySet<string>();
set1.Add("abc");
Debug.Assert(set1.Count == 1); // pass
Debug.Assert(set1.HasLoadedOrAssignedValues, "direct"); // pass

EntitySet<string> set2 = new EntitySet<string>();
IList<string> typedList = set2;
typedList.Add("abc");
Debug.Assert(set2.Count == 1); // pass
Debug.Assert(set2.HasLoadedOrAssignedValues, "typed list"); // pass

EntitySet<string> set3 = new EntitySet<string>();
IList untypedList = set3;
untypedList.Add("abc");
Debug.Assert(set3.Count == 1); // pass
Debug.Assert(set3.HasLoadedOrAssignedValues, "untyped list"); // FAIL

现在……这让我深感惊讶;如此之多以至于我花了两个多小时通过代码向上跟踪来隔离发生的事情。那么...

这有任何合理的理由吗?或者这只是一个错误?

(FWIW,3.5 中的 set.Assign(set) 也存在问题,但现在已在 4.0 中修复。)

There are 3 ways of adding items to most lists...

  • via a direct public API method, typically Add(SomeType)
  • via the generic IList<T>.Add(T) interface
  • via the non-generic IList.Add(object) interface method

and you normally expect them to behave more or less the same. However, LINQ's EntitySet<T> is... peculiar on both 3.5 and 4.0; the IList API does not flag the set as "assigned" - the other two mechanisms do - this sounds trivial, but it is important in that it heavily influences serialization (i.e. causes it to be skipped) in the boilerplate code.

Example:

EntitySet<string> set1 = new EntitySet<string>();
set1.Add("abc");
Debug.Assert(set1.Count == 1); // pass
Debug.Assert(set1.HasLoadedOrAssignedValues, "direct"); // pass

EntitySet<string> set2 = new EntitySet<string>();
IList<string> typedList = set2;
typedList.Add("abc");
Debug.Assert(set2.Count == 1); // pass
Debug.Assert(set2.HasLoadedOrAssignedValues, "typed list"); // pass

EntitySet<string> set3 = new EntitySet<string>();
IList untypedList = set3;
untypedList.Add("abc");
Debug.Assert(set3.Count == 1); // pass
Debug.Assert(set3.HasLoadedOrAssignedValues, "untyped list"); // FAIL

Now... this is deeply surprising to me; so much so that it took me over 2 hours of tracking upwards through code to isolate what was happening. So...

is there any sane reason for this? Or is this just a bug?

(FWIW, there was also an issue in set.Assign(set) in 3.5, but this is now fixed in 4.0.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里兽 2024-11-18 05:36:41

有趣的是,现在多个版本都已经发现了这个问题(您说 3.5 的问题已在 4.0 中修复)。 这是 2007 年的帖子. 4.0 中的其余 IList 方法已正确绑定到 IList 方法。我认为有两种可能的解释(关于错误/功能多样性):

  1. 这是 Microsoft 尚未修复的实际错误。
  2. 其他一些 Microsoft 代码利用利用此功能来添加项目,而无需设置HasLoadedOrAssignedValues

这可能是两者兼而有之 - 框架内其他代码所指望的错误。听起来就像有人对自己说:

没有人会真正将其转换为 IList,然后调用 Add 方法,对吧?

Interestingly, this has been identified for several versions now (you stated that a 3.5 issue was fixed in 4.0). Here is a post from 2007. The rest of the IList methods in 4.0 are correctly tied to the IList<T> methods. I think that there are 2 likely explanations (of the bug/feature variety):

  1. This is an actual bug that Microsoft has not yet fixed.
  2. This is a feature that some other Microsoft code is exploiting leveraging to add items without setting the HasLoadedOrAssignedValues.

It is probably both - a bug that other code inside the framework is counting on. Sounds like someone said to themselves:

No one is really going to cast this into an IList and then call the Add method, right?

清风疏影 2024-11-18 05:36:41

令人惊讶的是,这种差异似乎根源于 IList.AddIList.Add 方法实际上具有不同的语义

  • :如果要添加的实体已存在,则 code>IList.Add 方法会失败
  • LIst.Add 方法会删除实体,然后重新添加已存在的实体

明显原因为了这区别在于 IList.Add 接口方法被定义为返回添加实体的索引,对于 IList.Add 的典型实现来说,该索引始终是 Count 添加之前集合的

无论如何,由于这两个实现是有意不同的,因此作者似乎只是无意中省略了 IList.Add 版本中的 this.OnModified() 调用。

Surprisingly, the difference seems rooted in the fact that the IList.Add and IList<T>.Add methods actually have different semantics:

  • The IList.Add method fails if the entity being added is already present
  • The LIst<T>.Add method removes and then re-adds an entity if is is already present

The apparent reason for this difference is that IList.Add interface method is defined to return the index of the added entity, which for a typical implementation of IList.Add will always be the Count of the collection prior to the Add.

In any case, because the two implementations are intentionally different, it seems the authors simply accidentally omitted the this.OnModified() call in the IList.Add version.

不打扰别人 2024-11-18 05:36:41

对我来说看起来像一个错误。 ILSpy 显示了两种实现之间的差异:

int IList.Add(object value)
{
    TEntity tEntity = value as TEntity;
    if (tEntity == null || this.IndexOf(tEntity) >= 0)
    {
        throw Error.ArgumentOutOfRange("value");
    }
    this.CheckModify();
    int count = this.entities.Count;
    this.entities.Add(tEntity);
    this.OnAdd(tEntity);
    return count;
}

// System.Data.Linq.EntitySet<TEntity>
/// <summary>Adds an entity.</summary>
/// <param name="entity">The entity to add.</param>
public void Add(TEntity entity)
{
    if (entity == null)
    {
        throw Error.ArgumentNull("entity");
    }
    if (entity != this.onAddEntity)
    {
        this.CheckModify();
        if (!this.entities.Contains(entity))
        {
            this.OnAdd(entity);
            if (this.HasSource)
            {
                this.removedEntities.Remove(entity);
            }
            this.entities.Add(entity);
            this.OnListChanged(ListChangedType.ItemAdded, this.entities.IndexOf(entity));
        }
        this.OnModified();
    }
}

看起来 IList 实现只是忽略了调用 LINQ to SQL 可能依赖的几个事件调用程序(OnListChangedOnModified)来跟踪其变化。如果这是故意的,我会期望他们也省略对 OnAdd 的调用。

为什么他们不简单地让 IList.Add 将值转换为 TEntity 并调用通用的 Add 方法,这超出了我的理解。

Looks like a bug to me. ILSpy shows the differences between the two implementations:

int IList.Add(object value)
{
    TEntity tEntity = value as TEntity;
    if (tEntity == null || this.IndexOf(tEntity) >= 0)
    {
        throw Error.ArgumentOutOfRange("value");
    }
    this.CheckModify();
    int count = this.entities.Count;
    this.entities.Add(tEntity);
    this.OnAdd(tEntity);
    return count;
}

// System.Data.Linq.EntitySet<TEntity>
/// <summary>Adds an entity.</summary>
/// <param name="entity">The entity to add.</param>
public void Add(TEntity entity)
{
    if (entity == null)
    {
        throw Error.ArgumentNull("entity");
    }
    if (entity != this.onAddEntity)
    {
        this.CheckModify();
        if (!this.entities.Contains(entity))
        {
            this.OnAdd(entity);
            if (this.HasSource)
            {
                this.removedEntities.Remove(entity);
            }
            this.entities.Add(entity);
            this.OnListChanged(ListChangedType.ItemAdded, this.entities.IndexOf(entity));
        }
        this.OnModified();
    }
}

It looks like the IList implementation simply neglects to call a couple of event invokers (OnListChanged and OnModified) that LINQ to SQL probably relies on to track its changes. If this had been intentional, I would have expected them to also leave out the call to OnAdd.

Why they don't simply have IList.Add cast the value to TEntity and call the generic Add method is beyond me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文