EntitySet - есть ли вменяемая причина, по которой IList.Add не устанавливает назначенное? - PullRequest
40 голосов
/ 01 июня 2011

Существует 3 способа добавления элементов в большинство списков ...

  • через метод прямого публичного API, обычно Add(SomeType)
  • через общий IList<T>.Add(T) интерфейс
  • через неуниверсальный IList.Add(object) метод интерфейса

и вы обычно ожидаете, что они будут вести себя более или менее одинаково. Однако LINQ EntitySet<T> свойственен как для 3.5, так и для 4.0; IList API не помечает набор как «назначенный» - два других механизма do - это звучит тривиально, но важно, что это сильно влияет на сериализацию (т. е. вызывает ее пропуск) в шаблонном коде.

Пример: * * тысяча двадцать-восемь

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, также была проблема в set.Assign(set) в 3.5, но теперь она исправлена ​​в 4.0.)

Ответы [ 3 ]

20 голосов
/ 01 июня 2011

Интересно, что это было определено для нескольких версий (вы указали, что проблема 3.5 была исправлена ​​в 4.0). Это сообщение от 2007 года. Остальные методы IList в 4.0 правильно связаны с методами IList<T>.Я думаю, что есть 2 вероятных объяснения (из-за ошибки / разновидности):

  1. Это фактическая ошибка, которую Microsoft еще не исправила.
  2. Это особенность, которую некоторыедругой код Microsoft использует использование для добавления элементов без установки HasLoadedOrAssignedValues.

Это, вероятно, и то и другое - ошибка, связанная с другим кодом внутри фреймворкарассчитывает на.Похоже, кто-то сказал себе:

Никто на самом деле не собирается преобразовывать это в IList и затем вызывать метод Add, верно?

8 голосов
/ 01 июня 2011

Удивительно, но разница, похоже, коренится в том, что методы IList.Add и IList<T>.Add на самом деле имеют различную семантику :

  • Метод IList.Add завершается ошибкой, если добавляемый объект уже присутствует
  • Метод LIst<T>.Add удаляет, а затем повторно добавляет сущность, если она уже существует

Очевидная причина этого различия заключается в том, что интерфейсный метод IList.Add определен так, чтобы возвращать индекс добавленной сущности, который для типичной реализации IList.Add всегда будет Count коллекции до Add.

В любом случае, поскольку две реализации намеренно отличаются, кажется, что авторы просто случайно пропустили вызов this.OnModified() в версии IList.Add.

4 голосов
/ 01 июня 2011

Похоже, ошибка для меня. 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 просто игнорирует вызов нескольких инициаторов событий (OnListChanged и OnModified), на которые LINQ to SQL, вероятно, полагается для отслеживания своих изменений. Если бы это было преднамеренно, я бы ожидал, что они также пропустят вызов OnAdd.

Почему они просто не IList.Add приводят значение к TEntity и не вызывают общий метод Add, мне неведомо.

...