EntitySet - есть ли разумная причина, по которой IList.Add не назначается?
Есть 3 способа добавления элементов в большинство списков...
- с помощью прямого публичного API-метода, обычно
Add(SomeType)
- через общий интерфейс
IList<T>.Add(T)
- через не общий метод интерфейса
IList.Add(object)
и вы обычно ожидаете, что они будут вести себя более или менее одинаково. Однако LINQ EntitySet<T>
является... особенным как для 3.5, так и для 4.0; API IList
не указывает флаг как "назначенный" - другие два механизма 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.)
Ответы
Ответ 1
Интересно, что это было идентифицировано для нескольких версий сейчас (вы заявили, что проблема 3.5 была исправлена в 4.0). Вот сообщение от 2007. Остальные методы IList
в 4.0 правильно привязаны к методам IList<T>
. Я думаю, что есть 2 вероятных объяснения (разнообразия ошибок/признаков):
- Это настоящая ошибка, которую Microsoft еще не исправила.
- Это функция, в которой используется другой код Microsoft
, использующий для добавления элементов без установки HasLoadedOrAssignedValues
.
Вероятно, и то, и другое - ошибка, на которую рассчитывает другой код внутри фреймворка. Похоже, кто-то сказал себе:
Никто не собирается вносить это в IList, а затем вызывает метод Add, правильно?
Ответ 2
Удивительно, но разница, по-видимому, связана с тем, что методы IList.Add
и IList<T>.Add
действительно имеют различную семантику:
- Метод
IList.Add
завершается с ошибкой, если добавляемый объект уже присутствует
- Метод
LIst<T>.Add
удаляет, а затем повторно добавляет объект, если он уже присутствует.
Очевидная причина этой разницы заключается в том, что метод интерфейса IList.Add
определен для возврата индекса добавленного объекта, который для типичной реализации IList.Add
всегда будет Count
коллекции до Add
.
В любом случае, поскольку две реализации преднамеренно различны, кажется, что авторы просто случайно опустили вызов this.OnModified()
в версии IList.Add
.
Ответ 3
Похож на ошибку. 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
вне меня.