score:20

Accepted answer

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?

score:4

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.

score:8

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.


Related Query

More Query from same tag