Использование отражения для удаления объекта из коллекции EntityCollection служб RIA?

Чтобы упростить повторное использование элементов управления, мы создали решение с тремя отдельными проектами: библиотека элементов управления, клиент Silverlight и серверная часть ASP.NET. В управляющей библиотеке нет ссылок на классы модели данных, сгенерированные RIA Services, поэтому, когда ей нужно взаимодействовать с ней, мы используем отражение.

До сих пор это работало нормально, но я столкнулся с трудностями. У меня есть элемент управления DataGrid, где пользователь может выбрать строку, нажать кнопку «Удалить», и он должен удалить объект из коллекции. В классе DataGrid у меня есть следующий метод:

private void RemoveEntity(Entity entity)
{
    // Use reflection to remove the item from the collection
    Type sourceType = typeof(System.Windows.Ria.EntityCollection<>);
    Type genericType = sourceType.MakeGenericType(entity.GetType());
    System.Reflection.MethodInfo removeMethod = genericType.GetMethod("Remove");
    removeMethod.Invoke(this._dataGrid.ItemsSource, new object[] { entity });

    // Equivalent to: ('Foo' derives from Entity)
    //   EntityCollection<Foo> ec;
    //   ec.Remove(entity);
}

Это работает на стороне клиента, но в службе домена во время метода Submit () генерируется следующая ошибка:

«Оператор UPDATE конфликтует с ограничением FOREIGN KEY« ******** ». Конфликт произошел в базе данных« ******** », таблица« ******** », столбец '********'. Заявление было прекращено."

Я заметил одну вещь: метод службы UpdateFoo () вызывается вместо метода DeleteFoo () в службе домена. Дальнейшая проверка показывает, что объект переходит в набор изменений ModifiedEntities, а не в набор изменений RemovedEntities. Не знаю, проблема ли в этом, но это не кажется правильным.

Любая помощь будет оценена, спасибо,


ОБНОВИТЬ

Я определил, что проблема определенно связана с вызовом отражения метода EntityCollection.Remove (). По какой-то причине его вызов вызывает изменение свойства EntityState объекта на EntityState.Modified вместо EntityState.Deleted, как должно.

Даже если я попытаюсь удалить из коллекции, полностью обойдя DataGrid, я получаю ту же проблему:

Entity selectedEntity = this.DataContext.GetType().GetProperty("SelectedEntity").GetValue(this.DataContext, null) as Entity;
object foo = selectedEntity.GetType().GetProperty("Foo").GetValue(selectedEntity, null);
foo.GetType().InvokeMember("Remove", BindingFlags.InvokeMethod, null, foo, new object[] { entity });

В качестве теста я попытался изменить метод службы домена UpdateFoo () для реализации удаления, и он успешно удалил объект. Это указывает на то, что вызов службы RIA работает правильно, просто вызывается неправильный метод (Update вместо Delete).

public void UpdateFoo(Foo currentFoo)
{
    // Original update implementation
    //if ((currentFoo.EntityState == EntityState.Detached))
    //    this.ObjectContext.AttachAsModified(currentFoo, this.ChangeSet.GetOriginal(currentFoo));

    // Delete implementation substituted in
    Foo foo = this.ChangeSet.GetOriginal(currentFoo);
    if ((foo.EntityState == EntityState.Detached))
        this.ObjectContext.Attach(foo);
    this.ObjectContext.DeleteObject(foo);
}

person Nick Gotch    schedule 05.01.2010    source источник


Ответы (3)


Что означает «столбец» в ошибке «Ограничение внешнего ключа»? Это поле в строке сетки и коллекции, которая соответствует этому столбцу? Возможно ли, что объект, который вы пытаетесь удалить, является столбцом в строке, а не самой строкой, которая вызывает обновление строки (обнуление столбца), а не удаление строки?

person fupsduck    schedule 11.01.2010
comment
Ошибка, связанная с ограничением внешнего ключа, возникает из-за того, что вместо процедуры удаления вызывается процедура обновления, поэтому эта ошибка на самом деле не является основной причиной. Как я указывал в обновлении, замена кода метода Delete на метод Update успешно удаляет запись, поэтому проблема не в базовом вызове SQL, а в том, как метод Remove неправильно маркирует EntityState - person Nick Gotch; 11.01.2010
comment
Я понимаю - я хочу сказать - возможно ли, что объект, который вы считаете строкой, на самом деле является столбцом? Это могло бы объяснить, почему обновление вызывается вместо удаления. Потому что вы пытаетесь обнулить столбец вместо удаления строки. Под столбцом здесь я подразумеваю поле в строке. - person fupsduck; 11.01.2010
comment
Столбец, на который он ссылается, представляет собой столбец идентификатора типа контейнера для каждой сущности в модели. Этот столбец хранится внутри объекта и управляется EF, но недоступен напрямую. В условиях отсутствия отражения у меня не было проблем с получением CurrentItem DataGrid и вызовом Remove () из родительской коллекции. В этом случае я все еще использую CurrentItem для получения сущности, только я не могу напрямую преобразовать ее, потому что сборка элемента управления не имеет доступа к этому типу. Отвечая на ваш последний вопрос: когда я отлаживаю, я вижу, что тип объекта, который я передаю, является ожидаемым типом времени выполнения. - person Nick Gotch; 11.01.2010
comment
ДА!! Наконец решил эту проблему! Заслуга принадлежит этому ответу, поскольку он привел меня в правильном направлении. Все, что я сделал, это отключил принудительное ограничение внешнего ключа в столбце идентификатора в таблице родительского типа в базе данных, и внезапно метод завершился успешно. Он по-прежнему помечается как измененный, а не удаленный (который я все еще хотел бы исправить), но, по крайней мере, теперь он успешно удаляет и изменяет записи. Спасибо!! - person Nick Gotch; 11.01.2010

Я исследовал подобную проблему.

Я считаю, что проблема в том, что вы вызываете remove со ссылкой на EntityCollections внутри DomainContext в качестве корневой ссылки, а не с использованием самого DomainContext в качестве корня.

So...

ParentEntityCollection.EntityCollectionForTEntity.Remove (TEntity);

Создает EntityState.Modified вместо EntityState.Deleted

Попробуйте вместо этого ...

DomainContext.EntityCollectionForTEntity.Remove (TEntity);

Думаю, это даст желаемый результат.

Надеюсь это поможет.

person Computing Mastery    schedule 29.04.2011

Я прочитал ваше обновление и, похоже, вы определили, что проблема в отражении.

Вы пробовали убрать отражение с картинки?

As in:

private void RemoveEntity(Entity entity) 
{ 
    // Use reflection to remove the item from the collection 
    Type sourceType = typeof(System.Windows.Ria.EntityCollection<>); 
    Type genericType = sourceType.MakeGenericType(entity.GetType()); 

    // Make sure we have the right type
    // and let the framework take care of the proper invoke routine
    if (genericType.IsAssignableFrom(this._dataGrid.ItemsSource.GetType()))
        ((Object) this._dataGrid.ItemsSource).Remove(entity);
} 

Да, я знаю, что это некрасиво, но иногда ...

Отредактировано для добавления

Я обновил код, чтобы удалить ключевое слово is.

Теперь что касается использования объекта для вызова метода Remove, я считаю, что он может сработать из-за его позднего связывания.

person Paulo Santos    schedule 11.01.2010
comment
Я не вижу способа сделать этот вызов из сборки элемента управления без использования отражения. Ваш метод не будет работать, потому что ключевое слово is требует передачи явного имени типа, а также Object не содержит метода Remove. Если вы можете придумать другой способ обойти размышления, я бы с удовольствием попробовал его. - person Nick Gotch; 11.01.2010
comment
Это решает проблему «есть», но для работы позднего связывания по-прежнему требуется общий интерфейс. IEntityCollection был бы идеальным, но похоже, что MS определила его как `` внутреннюю '', поэтому нет доступа к нему за пределами отражения - person Nick Gotch; 11.01.2010