EntitySet - 是否有一个理所当然的IList.Add没有设置分配?

时间:2011-05-31 23:18:43

标签: c# .net linq linq-to-sql entityset

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

  • 通过直接的公共API方法,通常为Add(SomeType)
  • 通过通用IList<T>.Add(T)界面
  • 通过非通用IList.Add(object)接口方法

并且您通常希望他们的行为或多或少相同。但是,LINQ的EntitySet<T>在3.5和4.0上都是奇特的; IList API 将该设置标记为“已分配” - 其他两种机制 执行 - 这听起来微不足道,但重要的是它会严重影响样板代码中的序列化(即导致它被跳过)。

示例:

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

现在......这让我感到非常惊讶;以至于我花了2个多小时跟踪代码以隔离发生的事情。所以......

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

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

3 个答案:

答案 0 :(得分:20)

有趣的是,现在已经确定了几个版本(你说明在4.0中修复了3.5版本)。 Here is a post from 2007. 4.0中的其他IList方法与IList<T>方法正确绑定。我认为有两种可能的解释(错误/特征变种):

  1. 这是Microsoft尚未修复的实际错误。
  2. 这是一项功能,其他一些Microsoft代码利用利用添加项目而不设置HasLoadedOrAssignedValues
  3. 这可能是两者 - 框架内的其他代码所依赖的错误。听起来有人对自己说:

      

    没有人真的要将它转换为IList,然后调用Add方法,对吗?

答案 1 :(得分:8)

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

  • 如果要添加的实体已存在,IList.Add方法将失败
  • LIst<T>.Add方法删除然后重新添加实体(如果已存在)

这种差异的明显原因是IList.Add接口方法被定义为返回已添加实体的索引,对于IList.Add的典型实现,该索引始终为Count Add之前的集合。

在任何情况下,由于这两个实现有意不同,似乎作者只是意外地忽略了this.OnModified()版本中的IList.Add调用。

答案 2 :(得分:4)

对我来说看起来像个错误。 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方法超出了我的范围。