Итерация по неизвестным свойствам IQueryable? - PullRequest
1 голос
/ 11 ноября 2009

Простите, если об этом уже спрашивали; Я не смог найти ничего похожего после нескольких поисков:

Я пытаюсь написать ActionFilter в MVC, который будет «перехватывать» IQueryable и аннулировать все родительско-дочерние отношения во время выполнения. Я делаю это потому, что Linq не сериализует объекты должным образом, если они имеют отношения родитель-потомок (он выдает ошибку циклической ссылки, потому что родитель ссылается на дочерний элемент, который ссылается на родителя и т. Д.), И мне нужен объект сериализовано в Json для вызова Ajax. Я попытался пометить дочерние отношения в файле DBML со статусом внутренней конфиденциальности, и хотя это устраняет проблему сериализации, оно также скрывает дочерние элементы от движка представления при визуализации страницы, вызывая появление другой ошибки. Итак, исправляя одну проблему, я вызываю другую.

Единственное, что устраняет обе проблемы, - это вручную установить нулевые значения дочерних элементов непосредственно перед возвратом сериализации, но я стараюсь избегать этого, потому что это громоздко, не может использоваться повторно и т. Д. Я бы лучше использовал ActionFilter проверять сериализуемый IQueryable и аннулировать любые члены с типом EntitySet (как представлены внешние ключи / ассоциации). Тем не менее, у меня нет большого опыта работы с Reflection и я не могу найти примеров, иллюстрирующих, как сделать что-то подобное Так ... это возможно с Reflection? Есть ли лучший способ сделать то же самое? Я выложу соответствующий код завтра, когда вернусь за рабочим компьютером.

Спасибо

Daniel


Как и обещал, код:

[GridAction]  
public ActionResult _GetGrid()  
{  
  IQueryable<Form> result = formRepository.GetAll();  
  foreach (Form f in result)  
    {
      f.LineItems = null;
      f.Notes = null;
    }

  return View(new GridModel<Form> { Data = result });
}

Добавлена ​​еще одна проблема: я использую новые Telerik MVC Extensions, поэтому я на самом деле не сериализую Json - я просто возвращаю IQueryable в IGridModel, а фильтр действий [GridAction] выполняет отдых.

Ответы [ 2 ]

1 голос
/ 11 ноября 2009

Итак, на случай, если кому-то интересно, вот как я наконец решил эту проблему: я изменил шаблон T4 Damien Guard, добавив атрибут [ScriptIgnore] над объектами типа Association. Это позволяет сериализатору JSON не беспокоить их сериализацию, тем самым предотвращая проблему циклической ссылки, которую я получал. Сгенерированный код выглядит примерно так:

private EntitySet<LineItem> _LineItems;
    [ScriptIgnore]
    [Association(Name=@"Form_LineItem", Storage=@"_LineItems", ThisKey=@"Id", OtherKey=@"FormId")]
    public EntitySet<LineItem> LineItems
    {
        get {
            return _LineItems;
        }
        set {
            _LineItems.Assign(value);
        }
    }

Это исправляет проблему с сериализацией, которую я имел, не отключая использование дочерних таблиц через LINQ. Действие сетки на контроллере выглядит примерно так:

[GridAction]
public ActionResult _GetGrid()
{
  return View(new GridModel<Form> { Data = formRepository.GetAll() });
}
0 голосов
/ 11 ноября 2009

Существует два варианта, один из которых - игнорировать эти свойства во время сериализации, используя [XmlIgnore]. Другой - обнулить свойства, используя отражение.

Игнорировать при сериализации, простой пример использования, который показывает, как использовать значение по умолчанию при сериализации:

[Serializable]
public class MyClass
{
    [XmlIgnore]
    public int IgnoredVal { get; set; }

    public int Val { get; set; }
}

public void UsageSample()
{
    var xmlSerializer = new XmlSerializer(typeof(MyClass));
    var memoryStream = new MemoryStream();
    var toSerialize = new MyClass { IgnoredVal = 1, Val = 2 };
    xmlSerializer.Serialize(memoryStream, toSerialize);
    memoryStream.Position = 0;
    var deserialize = (MyClass)xmlSerializer.Deserialize(memoryStream);

    Assert.AreEqual(0, deserialize.IgnoredVal);
    Assert.AreEqual(2, deserialize.Val);
}

Обнулить с отражением , пример кода:

public void NullifyEntitySetProperties(object obj)
{
    var entitySetProperties = obj.GetType().GetProperties()
        .Where(property => property.PropertyType == typeof(EntitySet));

    foreach (var property in entitySetProperties)
    {
        property.SetValue(obj, null, null);
    }
}

По моему мнению, если в вашем коде можно использовать первую опцию, лучше. Этот вариант более прямой и экономичный.

...